IBM LoRa MAC in C (LMiC) mbed library port

Dependents:   lora-temperature LoRaWAN-lmic-app_HS LoRaWAN-lmic-app_huynh

LoRa WAN in C for sx1276 shield

Currently version 1.5


LoRaWAN network configuration for end-device

The following three pieces of information uniquely identifies end-device to network to allow over-the-air activation. These are stored in the end-device prior to join procedure.

AppEUI

Uniquely identifies application provider of end-device.

Least-significant byte first, 8 bytes, use reverse memcpy() to keep same order as shown on lora server.

example C code

static const u1_t APPEUI[8]  = { 0x01, 0x00, 0x01, 0x00, 0x00, 0x0C, 0x25, 0x00 };

This is copied into LMIC by os_getArtEui() callback function in application.

DevEUI

End-device ID, unique to each end-node.

Least-significant byte first, 8 bytes, use reverse memcpy() to keep same order as shown on lora server.

example C code

static const u1_t DEVEUI[8]  = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x25, 0x00 }; 

This is copied into LMIC by os_getDevEui() callback function in application.

AppKey (aka DevKey)

128-bit (16byte) AES key.

example C code

static const u1_t DEVKEY[16] = { 0xe4, 0x72, 0x71, 0xc5, 0xf5, 0x30, 0xa9, 0x9f, 0xcf, 0xc4, 0x0e, 0xab, 0xea, 0xd7, 0x19, 0x42 };

This is copied into LMIC by os_getDevKey() callback function in application.

Using over-the air activation, the end-device (LMIC) performs a join procedure every time it starts for first time, or has lost session context information. When join procedure has successfully completed, the end-device will have a network session key (NwkSKey) and an application session key (AppSKey), which are used for encryption and message integrity check.


US915 configuration with http://us01-iot.semtech.com/

  • log in to server
  • click on Applications
  • find your application and click it
  • go to configure motes
  • to create a mote, you may enter a new DevEUI
    • you may copy-paste the 16byte application key from an already existing mote, if you desire.
CHNL_HYBRID125KHz500KHz
defined valuechannelschannel
00 to 764
18 to 1565
216 to 2366
324 to 3167
432 to 3968
540 to 4769
648 to 5570
756 to 6371
undef0 to 6364 to 71
Committer:
mluis
Date:
Tue Mar 31 13:36:56 2015 +0000
Revision:
1:d3b7bde3995c
Parent:
0:62d1edcc13d1
Child:
2:974cafbfb159
Child:
3:519c71d29a06
Updated LMiC to release v1.4 (http://www.zurich.ibm.com/pdf/lrsc/lmic-release-v1.4.zip)

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mluis 0:62d1edcc13d1 1 /*******************************************************************************
mluis 1:d3b7bde3995c 2 * Copyright (c) 2014-2015 IBM Corporation.
mluis 0:62d1edcc13d1 3 * All rights reserved. This program and the accompanying materials
mluis 0:62d1edcc13d1 4 * are made available under the terms of the Eclipse Public License v1.0
mluis 0:62d1edcc13d1 5 * which accompanies this distribution, and is available at
mluis 0:62d1edcc13d1 6 * http://www.eclipse.org/legal/epl-v10.html
mluis 0:62d1edcc13d1 7 *
mluis 0:62d1edcc13d1 8 * Contributors:
mluis 0:62d1edcc13d1 9 * IBM Zurich Research Lab - initial API, implementation and documentation
mluis 0:62d1edcc13d1 10 *******************************************************************************/
mluis 0:62d1edcc13d1 11
mluis 1:d3b7bde3995c 12 //! \file
mluis 0:62d1edcc13d1 13 #include "lmic.h"
mluis 0:62d1edcc13d1 14
mluis 1:d3b7bde3995c 15 #if !defined(MINRX_SYMS)
mluis 1:d3b7bde3995c 16 #define MINRX_SYMS 5
mluis 1:d3b7bde3995c 17 #endif // !defined(MINRX_SYMS)
mluis 0:62d1edcc13d1 18 #define PAMBL_SYMS 8
mluis 0:62d1edcc13d1 19 #define PAMBL_FSK 5
mluis 0:62d1edcc13d1 20 #define PRERX_FSK 1
mluis 0:62d1edcc13d1 21 #define RXLEN_FSK (1+5+2)
mluis 0:62d1edcc13d1 22
mluis 0:62d1edcc13d1 23 #define BCN_INTV_osticks sec2osticks(BCN_INTV_sec)
mluis 0:62d1edcc13d1 24 #define TXRX_GUARD_osticks ms2osticks(TXRX_GUARD_ms)
mluis 0:62d1edcc13d1 25 #define JOIN_GUARD_osticks ms2osticks(JOIN_GUARD_ms)
mluis 0:62d1edcc13d1 26 #define DELAY_DNW1_osticks sec2osticks(DELAY_DNW1)
mluis 0:62d1edcc13d1 27 #define DELAY_DNW2_osticks sec2osticks(DELAY_DNW2)
mluis 0:62d1edcc13d1 28 #define DELAY_JACC1_osticks sec2osticks(DELAY_JACC1)
mluis 0:62d1edcc13d1 29 #define DELAY_JACC2_osticks sec2osticks(DELAY_JACC2)
mluis 0:62d1edcc13d1 30 #define DELAY_EXTDNW2_osticks sec2osticks(DELAY_EXTDNW2)
mluis 0:62d1edcc13d1 31 #define BCN_RESERVE_osticks ms2osticks(BCN_RESERVE_ms)
mluis 0:62d1edcc13d1 32 #define BCN_GUARD_osticks ms2osticks(BCN_GUARD_ms)
mluis 0:62d1edcc13d1 33 #define BCN_WINDOW_osticks ms2osticks(BCN_WINDOW_ms)
mluis 0:62d1edcc13d1 34 #define AIRTIME_BCN_osticks us2osticks(AIRTIME_BCN)
mluis 1:d3b7bde3995c 35 #if defined(CFG_eu868)
mluis 1:d3b7bde3995c 36 #define DNW2_SAFETY_ZONE ms2osticks(3000)
mluis 1:d3b7bde3995c 37 #endif
mluis 1:d3b7bde3995c 38 #if defined(CFG_us915)
mluis 1:d3b7bde3995c 39 #define DNW2_SAFETY_ZONE ms2osticks(750)
mluis 1:d3b7bde3995c 40 #endif
mluis 1:d3b7bde3995c 41
mluis 1:d3b7bde3995c 42 // Special APIs - for development or testing
mluis 1:d3b7bde3995c 43 #define isTESTMODE() 0
mluis 0:62d1edcc13d1 44
mluis 0:62d1edcc13d1 45 DEFINE_LMIC;
mluis 0:62d1edcc13d1 46 DECL_ON_LMIC_EVENT;
mluis 0:62d1edcc13d1 47
mluis 0:62d1edcc13d1 48
mluis 0:62d1edcc13d1 49 // Fwd decls.
mluis 0:62d1edcc13d1 50 static void engineUpdate(void);
mluis 0:62d1edcc13d1 51 static void startScan (void);
mluis 0:62d1edcc13d1 52
mluis 0:62d1edcc13d1 53
mluis 0:62d1edcc13d1 54 // ================================================================================
mluis 0:62d1edcc13d1 55 // BEG OS - default implementations for certain OS suport functions
mluis 0:62d1edcc13d1 56
mluis 1:d3b7bde3995c 57 #if !defined(HAS_os_calls)
mluis 0:62d1edcc13d1 58
mluis 1:d3b7bde3995c 59 #if !defined(os_rlsbf2)
mluis 0:62d1edcc13d1 60 u2_t os_rlsbf2 (xref2cu1_t buf) {
mluis 0:62d1edcc13d1 61 return (u2_t)(buf[0] | (buf[1]<<8));
mluis 0:62d1edcc13d1 62 }
mluis 0:62d1edcc13d1 63 #endif
mluis 0:62d1edcc13d1 64
mluis 1:d3b7bde3995c 65 #if !defined(os_rlsbf4)
mluis 0:62d1edcc13d1 66 u4_t os_rlsbf4 (xref2cu1_t buf) {
mluis 0:62d1edcc13d1 67 return (u4_t)(buf[0] | (buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24));
mluis 0:62d1edcc13d1 68 }
mluis 0:62d1edcc13d1 69 #endif
mluis 0:62d1edcc13d1 70
mluis 0:62d1edcc13d1 71
mluis 1:d3b7bde3995c 72 #if !defined(os_rmsbf4)
mluis 0:62d1edcc13d1 73 u4_t os_rmsbf4 (xref2cu1_t buf) {
mluis 0:62d1edcc13d1 74 return (u4_t)(buf[3] | (buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24));
mluis 0:62d1edcc13d1 75 }
mluis 0:62d1edcc13d1 76 #endif
mluis 0:62d1edcc13d1 77
mluis 0:62d1edcc13d1 78
mluis 1:d3b7bde3995c 79 #if !defined(os_wlsbf2)
mluis 0:62d1edcc13d1 80 void os_wlsbf2 (xref2u1_t buf, u2_t v) {
mluis 0:62d1edcc13d1 81 buf[0] = v;
mluis 0:62d1edcc13d1 82 buf[1] = v>>8;
mluis 0:62d1edcc13d1 83 }
mluis 0:62d1edcc13d1 84 #endif
mluis 0:62d1edcc13d1 85
mluis 1:d3b7bde3995c 86 #if !defined(os_wlsbf4)
mluis 0:62d1edcc13d1 87 void os_wlsbf4 (xref2u1_t buf, u4_t v) {
mluis 0:62d1edcc13d1 88 buf[0] = v;
mluis 0:62d1edcc13d1 89 buf[1] = v>>8;
mluis 0:62d1edcc13d1 90 buf[2] = v>>16;
mluis 0:62d1edcc13d1 91 buf[3] = v>>24;
mluis 0:62d1edcc13d1 92 }
mluis 0:62d1edcc13d1 93 #endif
mluis 0:62d1edcc13d1 94
mluis 1:d3b7bde3995c 95 #if !defined(os_wmsbf4)
mluis 0:62d1edcc13d1 96 void os_wmsbf4 (xref2u1_t buf, u4_t v) {
mluis 0:62d1edcc13d1 97 buf[3] = v;
mluis 0:62d1edcc13d1 98 buf[2] = v>>8;
mluis 0:62d1edcc13d1 99 buf[1] = v>>16;
mluis 0:62d1edcc13d1 100 buf[0] = v>>24;
mluis 0:62d1edcc13d1 101 }
mluis 0:62d1edcc13d1 102 #endif
mluis 0:62d1edcc13d1 103
mluis 1:d3b7bde3995c 104 #if !defined(os_getBattLevel)
mluis 0:62d1edcc13d1 105 u1_t os_getBattLevel (void) {
mluis 0:62d1edcc13d1 106 return MCMD_DEVS_BATT_NOINFO;
mluis 0:62d1edcc13d1 107 }
mluis 0:62d1edcc13d1 108 #endif
mluis 0:62d1edcc13d1 109
mluis 1:d3b7bde3995c 110 #if !defined(os_crc16)
mluis 1:d3b7bde3995c 111 // New CRC-16 CCITT(XMODEM) checksum for beacons:
mluis 0:62d1edcc13d1 112 u2_t os_crc16 (xref2u1_t data, uint len) {
mluis 1:d3b7bde3995c 113 u2_t remainder = 0;
mluis 1:d3b7bde3995c 114 u2_t polynomial = 0x1021;
mluis 1:d3b7bde3995c 115 for( uint i = 0; i < len; i++ ) {
mluis 1:d3b7bde3995c 116 remainder ^= data[i] << 8;
mluis 1:d3b7bde3995c 117 for( u1_t bit = 8; bit > 0; bit--) {
mluis 1:d3b7bde3995c 118 if( (remainder & 0x8000) )
mluis 1:d3b7bde3995c 119 remainder = (remainder << 1) ^ polynomial;
mluis 1:d3b7bde3995c 120 else
mluis 1:d3b7bde3995c 121 remainder <<= 1;
mluis 1:d3b7bde3995c 122 }
mluis 0:62d1edcc13d1 123 }
mluis 1:d3b7bde3995c 124 return remainder;
mluis 0:62d1edcc13d1 125 }
mluis 0:62d1edcc13d1 126 #endif
mluis 0:62d1edcc13d1 127
mluis 0:62d1edcc13d1 128 #endif // !HAS_os_calls
mluis 0:62d1edcc13d1 129
mluis 0:62d1edcc13d1 130 // END OS - default implementations for certain OS suport functions
mluis 0:62d1edcc13d1 131 // ================================================================================
mluis 0:62d1edcc13d1 132
mluis 0:62d1edcc13d1 133 // ================================================================================
mluis 0:62d1edcc13d1 134 // BEG AES
mluis 0:62d1edcc13d1 135
mluis 0:62d1edcc13d1 136 static void micB0 (u4_t devaddr, u4_t seqno, int dndir, int len) {
mluis 0:62d1edcc13d1 137 os_clearMem(AESaux,16);
mluis 0:62d1edcc13d1 138 AESaux[0] = 0x49;
mluis 0:62d1edcc13d1 139 AESaux[5] = dndir?1:0;
mluis 0:62d1edcc13d1 140 AESaux[15] = len;
mluis 0:62d1edcc13d1 141 os_wlsbf4(AESaux+ 6,devaddr);
mluis 0:62d1edcc13d1 142 os_wlsbf4(AESaux+10,seqno);
mluis 0:62d1edcc13d1 143 }
mluis 0:62d1edcc13d1 144
mluis 0:62d1edcc13d1 145
mluis 0:62d1edcc13d1 146 static int aes_verifyMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) {
mluis 0:62d1edcc13d1 147 micB0(devaddr, seqno, dndir, len);
mluis 0:62d1edcc13d1 148 os_copyMem(AESkey,key,16);
mluis 0:62d1edcc13d1 149 return os_aes(AES_MIC, pdu, len) == os_rmsbf4(pdu+len);
mluis 0:62d1edcc13d1 150 }
mluis 0:62d1edcc13d1 151
mluis 0:62d1edcc13d1 152
mluis 0:62d1edcc13d1 153 static void aes_appendMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) {
mluis 0:62d1edcc13d1 154 micB0(devaddr, seqno, dndir, len);
mluis 0:62d1edcc13d1 155 os_copyMem(AESkey,key,16);
mluis 0:62d1edcc13d1 156 // MSB because of internal structure of AES
mluis 0:62d1edcc13d1 157 os_wmsbf4(pdu+len, os_aes(AES_MIC, pdu, len));
mluis 0:62d1edcc13d1 158 }
mluis 0:62d1edcc13d1 159
mluis 0:62d1edcc13d1 160
mluis 0:62d1edcc13d1 161 static void aes_appendMic0 (xref2u1_t pdu, int len) {
mluis 0:62d1edcc13d1 162 os_getDevKey(AESkey);
mluis 0:62d1edcc13d1 163 os_wmsbf4(pdu+len, os_aes(AES_MIC|AES_MICNOAUX, pdu, len)); // MSB because of internal structure of AES
mluis 0:62d1edcc13d1 164 }
mluis 0:62d1edcc13d1 165
mluis 0:62d1edcc13d1 166
mluis 0:62d1edcc13d1 167 static int aes_verifyMic0 (xref2u1_t pdu, int len) {
mluis 0:62d1edcc13d1 168 os_getDevKey(AESkey);
mluis 0:62d1edcc13d1 169 return os_aes(AES_MIC|AES_MICNOAUX, pdu, len) == os_rmsbf4(pdu+len);
mluis 0:62d1edcc13d1 170 }
mluis 0:62d1edcc13d1 171
mluis 0:62d1edcc13d1 172
mluis 0:62d1edcc13d1 173 static void aes_encrypt (xref2u1_t pdu, int len) {
mluis 0:62d1edcc13d1 174 os_getDevKey(AESkey);
mluis 0:62d1edcc13d1 175 os_aes(AES_ENC, pdu, len);
mluis 0:62d1edcc13d1 176 }
mluis 0:62d1edcc13d1 177
mluis 0:62d1edcc13d1 178
mluis 0:62d1edcc13d1 179 static void aes_cipher (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t payload, int len) {
mluis 0:62d1edcc13d1 180 if( len <= 0 )
mluis 1:d3b7bde3995c 181 return;
mluis 0:62d1edcc13d1 182 os_clearMem(AESaux, 16);
mluis 0:62d1edcc13d1 183 AESaux[0] = AESaux[15] = 1; // mode=cipher / dir=down / block counter=1
mluis 0:62d1edcc13d1 184 AESaux[5] = dndir?1:0;
mluis 0:62d1edcc13d1 185 os_wlsbf4(AESaux+ 6,devaddr);
mluis 0:62d1edcc13d1 186 os_wlsbf4(AESaux+10,seqno);
mluis 0:62d1edcc13d1 187 os_copyMem(AESkey,key,16);
mluis 0:62d1edcc13d1 188 os_aes(AES_CTR, payload, len);
mluis 0:62d1edcc13d1 189 }
mluis 0:62d1edcc13d1 190
mluis 0:62d1edcc13d1 191
mluis 0:62d1edcc13d1 192 static void aes_sessKeys (u2_t devnonce, xref2cu1_t artnonce, xref2u1_t nwkkey, xref2u1_t artkey) {
mluis 0:62d1edcc13d1 193 os_clearMem(nwkkey, 16);
mluis 0:62d1edcc13d1 194 nwkkey[0] = 0x01;
mluis 0:62d1edcc13d1 195 os_copyMem(nwkkey+1, artnonce, LEN_ARTNONCE+LEN_NETID);
mluis 0:62d1edcc13d1 196 os_wlsbf2(nwkkey+1+LEN_ARTNONCE+LEN_NETID, devnonce);
mluis 0:62d1edcc13d1 197 os_copyMem(artkey, nwkkey, 16);
mluis 0:62d1edcc13d1 198 artkey[0] = 0x02;
mluis 0:62d1edcc13d1 199
mluis 0:62d1edcc13d1 200 os_getDevKey(AESkey);
mluis 0:62d1edcc13d1 201 os_aes(AES_ENC, nwkkey, 16);
mluis 0:62d1edcc13d1 202 os_getDevKey(AESkey);
mluis 0:62d1edcc13d1 203 os_aes(AES_ENC, artkey, 16);
mluis 0:62d1edcc13d1 204 }
mluis 0:62d1edcc13d1 205
mluis 0:62d1edcc13d1 206 // END AES
mluis 0:62d1edcc13d1 207 // ================================================================================
mluis 0:62d1edcc13d1 208
mluis 0:62d1edcc13d1 209
mluis 0:62d1edcc13d1 210 // ================================================================================
mluis 0:62d1edcc13d1 211 // BEG LORA
mluis 0:62d1edcc13d1 212
mluis 1:d3b7bde3995c 213 #if defined(CFG_eu868) // ========================================
mluis 0:62d1edcc13d1 214
mluis 0:62d1edcc13d1 215 #define maxFrameLen(dr) ((dr)<=DR_SF9 ? maxFrameLens[(dr)] : 0xFF)
mluis 0:62d1edcc13d1 216 const u1_t maxFrameLens [] = { 64,64,64,123 };
mluis 0:62d1edcc13d1 217
mluis 0:62d1edcc13d1 218 const u1_t _DR2RPS_CRC[] = {
mluis 0:62d1edcc13d1 219 ILLEGAL_RPS,
mluis 0:62d1edcc13d1 220 (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 221 (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 222 (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 223 (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 224 (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 225 (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 226 (u1_t)MAKERPS(SF7, BW250, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 227 (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 228 ILLEGAL_RPS
mluis 0:62d1edcc13d1 229 };
mluis 0:62d1edcc13d1 230
mluis 0:62d1edcc13d1 231 static const s1_t TXPOWLEVELS[] = {
mluis 0:62d1edcc13d1 232 20, 14, 11, 8, 5, 2, 0,0, 0,0,0,0, 0,0,0,0
mluis 0:62d1edcc13d1 233 };
mluis 0:62d1edcc13d1 234 #define pow2dBm(mcmd_ladr_p1) (TXPOWLEVELS[(mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT])
mluis 0:62d1edcc13d1 235
mluis 1:d3b7bde3995c 236 #elif defined(CFG_us915) // ========================================
mluis 0:62d1edcc13d1 237
mluis 0:62d1edcc13d1 238 #define maxFrameLen(dr) ((dr)<=DR_SF11CR ? maxFrameLens[(dr)] : 0xFF)
mluis 0:62d1edcc13d1 239 const u1_t maxFrameLens [] = { 24,66,142,255,255,255,255,255, 66,142 };
mluis 0:62d1edcc13d1 240
mluis 0:62d1edcc13d1 241 const u1_t _DR2RPS_CRC[] = {
mluis 0:62d1edcc13d1 242 ILLEGAL_RPS,
mluis 0:62d1edcc13d1 243 MAKERPS(SF10, BW125, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 244 MAKERPS(SF9 , BW125, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 245 MAKERPS(SF8 , BW125, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 246 MAKERPS(SF7 , BW125, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 247 MAKERPS(SF8 , BW500, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 248 ILLEGAL_RPS ,
mluis 0:62d1edcc13d1 249 ILLEGAL_RPS ,
mluis 0:62d1edcc13d1 250 ILLEGAL_RPS ,
mluis 0:62d1edcc13d1 251 MAKERPS(SF12, BW500, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 252 MAKERPS(SF11, BW500, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 253 MAKERPS(SF10, BW500, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 254 MAKERPS(SF9 , BW500, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 255 MAKERPS(SF8 , BW500, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 256 MAKERPS(SF7 , BW500, CR_4_5, 0, 0),
mluis 0:62d1edcc13d1 257 ILLEGAL_RPS
mluis 0:62d1edcc13d1 258 };
mluis 0:62d1edcc13d1 259
mluis 0:62d1edcc13d1 260 #define pow2dBm(mcmd_ladr_p1) ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1)))
mluis 0:62d1edcc13d1 261
mluis 0:62d1edcc13d1 262 #endif // ================================================
mluis 0:62d1edcc13d1 263
mluis 0:62d1edcc13d1 264 static const u1_t SENSITIVITY[7][3] = {
mluis 0:62d1edcc13d1 265 // ------------bw----------
mluis 0:62d1edcc13d1 266 // 125kHz 250kHz 500kHz
mluis 0:62d1edcc13d1 267 { 141-109, 141-109, 141-109 }, // FSK
mluis 0:62d1edcc13d1 268 { 141-127, 141-124, 141-121 }, // SF7
mluis 0:62d1edcc13d1 269 { 141-129, 141-126, 141-123 }, // SF8
mluis 0:62d1edcc13d1 270 { 141-132, 141-129, 141-126 }, // SF9
mluis 0:62d1edcc13d1 271 { 141-135, 141-132, 141-129 }, // SF10
mluis 0:62d1edcc13d1 272 { 141-138, 141-135, 141-132 }, // SF11
mluis 0:62d1edcc13d1 273 { 141-141, 141-138, 141-135 } // SF12
mluis 0:62d1edcc13d1 274 };
mluis 0:62d1edcc13d1 275
mluis 0:62d1edcc13d1 276 int getSensitivity (rps_t rps) {
mluis 0:62d1edcc13d1 277 return -141 + SENSITIVITY[getSf(rps)][getBw(rps)];
mluis 0:62d1edcc13d1 278 }
mluis 0:62d1edcc13d1 279
mluis 0:62d1edcc13d1 280 ostime_t calcAirTime (rps_t rps, u1_t plen) {
mluis 0:62d1edcc13d1 281 u1_t bw = getBw(rps); // 0,1,2 = 125,250,500kHz
mluis 0:62d1edcc13d1 282 u1_t sf = getSf(rps); // 0=FSK, 1..6 = SF7..12
mluis 0:62d1edcc13d1 283 if( sf == FSK ) {
mluis 1:d3b7bde3995c 284 return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8
mluis 1:d3b7bde3995c 285 * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000;
mluis 0:62d1edcc13d1 286 }
mluis 0:62d1edcc13d1 287 u1_t sfx = 4*(sf+(7-SF7));
mluis 0:62d1edcc13d1 288 u1_t q = sfx - (sf >= SF11 ? 8 : 0);
mluis 0:62d1edcc13d1 289 int tmp = 8*plen - sfx + 28 + (getNocrc(rps)?0:16) - (getIh(rps)?20:0);
mluis 0:62d1edcc13d1 290 if( tmp > 0 ) {
mluis 1:d3b7bde3995c 291 tmp = (tmp + q - 1) / q;
mluis 1:d3b7bde3995c 292 tmp *= getCr(rps)+5;
mluis 1:d3b7bde3995c 293 tmp += 8;
mluis 0:62d1edcc13d1 294 } else {
mluis 1:d3b7bde3995c 295 tmp = 8;
mluis 0:62d1edcc13d1 296 }
mluis 0:62d1edcc13d1 297 tmp = (tmp<<2) + /*preamble*/49 /* 4 * (8 + 4.25) */;
mluis 0:62d1edcc13d1 298 // bw = 125000 = 15625 * 2^3
mluis 0:62d1edcc13d1 299 // 250000 = 15625 * 2^4
mluis 0:62d1edcc13d1 300 // 500000 = 15625 * 2^5
mluis 0:62d1edcc13d1 301 // sf = 7..12
mluis 0:62d1edcc13d1 302 //
mluis 0:62d1edcc13d1 303 // osticks = tmp * OSTICKS_PER_SEC * 1<<sf / bw
mluis 0:62d1edcc13d1 304 //
mluis 0:62d1edcc13d1 305 // 3 => counter reduced divisor 125000/8 => 15625
mluis 0:62d1edcc13d1 306 // 2 => counter 2 shift on tmp
mluis 0:62d1edcc13d1 307 sfx = sf+(7-SF7) - (3+2) - bw;
mluis 0:62d1edcc13d1 308 int div = 15625;
mluis 0:62d1edcc13d1 309 if( sfx > 4 ) {
mluis 1:d3b7bde3995c 310 // prevent 32bit signed int overflow in last step
mluis 1:d3b7bde3995c 311 div >>= sfx-4;
mluis 1:d3b7bde3995c 312 sfx = 4;
mluis 0:62d1edcc13d1 313 }
mluis 0:62d1edcc13d1 314 // Need 32bit arithmetic for this last step
mluis 0:62d1edcc13d1 315 return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div;
mluis 0:62d1edcc13d1 316 }
mluis 0:62d1edcc13d1 317
mluis 0:62d1edcc13d1 318 extern inline s1_t rssi2s1 (int v);
mluis 0:62d1edcc13d1 319 extern inline int s12rssi (s1_t v);
mluis 0:62d1edcc13d1 320 extern inline float s12snr (s1_t v);
mluis 0:62d1edcc13d1 321 extern inline s1_t snr2s1 (double v);
mluis 0:62d1edcc13d1 322
mluis 0:62d1edcc13d1 323 extern inline rps_t updr2rps (dr_t dr);
mluis 0:62d1edcc13d1 324 extern inline rps_t dndr2rps (dr_t dr);
mluis 0:62d1edcc13d1 325 extern inline int isFasterDR (dr_t dr1, dr_t dr2);
mluis 0:62d1edcc13d1 326 extern inline int isSlowerDR (dr_t dr1, dr_t dr2);
mluis 0:62d1edcc13d1 327 extern inline dr_t incDR (dr_t dr);
mluis 0:62d1edcc13d1 328 extern inline dr_t decDR (dr_t dr);
mluis 0:62d1edcc13d1 329 extern inline dr_t assertDR (dr_t dr);
mluis 0:62d1edcc13d1 330 extern inline dr_t validDR (dr_t dr);
mluis 0:62d1edcc13d1 331 extern inline dr_t lowerDR (dr_t dr, u1_t n);
mluis 0:62d1edcc13d1 332
mluis 0:62d1edcc13d1 333 extern inline sf_t getSf (rps_t params);
mluis 0:62d1edcc13d1 334 extern inline rps_t setSf (rps_t params, sf_t sf);
mluis 0:62d1edcc13d1 335 extern inline bw_t getBw (rps_t params);
mluis 0:62d1edcc13d1 336 extern inline rps_t setBw (rps_t params, bw_t cr);
mluis 0:62d1edcc13d1 337 extern inline cr_t getCr (rps_t params);
mluis 0:62d1edcc13d1 338 extern inline rps_t setCr (rps_t params, cr_t cr);
mluis 0:62d1edcc13d1 339 extern inline int getNocrc (rps_t params);
mluis 0:62d1edcc13d1 340 extern inline rps_t setNocrc (rps_t params, int nocrc);
mluis 0:62d1edcc13d1 341 extern inline int getIh (rps_t params);
mluis 0:62d1edcc13d1 342 extern inline rps_t setIh (rps_t params, int ih);
mluis 0:62d1edcc13d1 343 extern inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc);
mluis 0:62d1edcc13d1 344 extern inline int sameSfBw (rps_t r1, rps_t r2);
mluis 0:62d1edcc13d1 345
mluis 0:62d1edcc13d1 346 // END LORA
mluis 0:62d1edcc13d1 347 // ================================================================================
mluis 0:62d1edcc13d1 348
mluis 0:62d1edcc13d1 349
mluis 0:62d1edcc13d1 350 // Adjust DR for TX retries
mluis 0:62d1edcc13d1 351 // - indexed by retry count
mluis 0:62d1edcc13d1 352 // - return steps to lower DR
mluis 0:62d1edcc13d1 353 static const u1_t DRADJUST[2+TXCONF_ATTEMPTS] = {
mluis 0:62d1edcc13d1 354 // normal frames - 1st try / no retry
mluis 0:62d1edcc13d1 355 0,
mluis 0:62d1edcc13d1 356 // confirmed frames
mluis 1:d3b7bde3995c 357 0,0,1,0,1,0,1,0,0
mluis 0:62d1edcc13d1 358 };
mluis 0:62d1edcc13d1 359
mluis 0:62d1edcc13d1 360
mluis 0:62d1edcc13d1 361 // Table below defines the size of one symbol as
mluis 0:62d1edcc13d1 362 // symtime = 256us * 2^T(sf,bw)
mluis 0:62d1edcc13d1 363 // 256us is called one symunit.
mluis 1:d3b7bde3995c 364 // SF:
mluis 1:d3b7bde3995c 365 // BW: |__7___8___9__10__11__12
mluis 1:d3b7bde3995c 366 // 125kHz | 2 3 4 5 6 7
mluis 1:d3b7bde3995c 367 // 250kHz | 1 2 3 4 5 6
mluis 1:d3b7bde3995c 368 // 500kHz | 0 1 2 3 4 5
mluis 0:62d1edcc13d1 369 //
mluis 0:62d1edcc13d1 370 // Times for half symbol per DR
mluis 0:62d1edcc13d1 371 // Per DR table to minimize rounding errors
mluis 0:62d1edcc13d1 372 static const ostime_t DR2HSYM_osticks[] = {
mluis 1:d3b7bde3995c 373 #if defined(CFG_eu868)
mluis 0:62d1edcc13d1 374 #define dr2hsym(dr) (DR2HSYM_osticks[(dr)])
mluis 0:62d1edcc13d1 375 us2osticksRound(128<<7), // DR_SF12
mluis 0:62d1edcc13d1 376 us2osticksRound(128<<6), // DR_SF11
mluis 0:62d1edcc13d1 377 us2osticksRound(128<<5), // DR_SF10
mluis 0:62d1edcc13d1 378 us2osticksRound(128<<4), // DR_SF9
mluis 0:62d1edcc13d1 379 us2osticksRound(128<<3), // DR_SF8
mluis 0:62d1edcc13d1 380 us2osticksRound(128<<2), // DR_SF7
mluis 0:62d1edcc13d1 381 us2osticksRound(128<<1), // DR_SF7B
mluis 0:62d1edcc13d1 382 us2osticksRound(80) // FSK -- not used (time for 1/2 byte)
mluis 1:d3b7bde3995c 383 #elif defined(CFG_us915)
mluis 0:62d1edcc13d1 384 #define dr2hsym(dr) (DR2HSYM_osticks[(dr)&7]) // map DR_SFnCR -> 0-6
mluis 0:62d1edcc13d1 385 us2osticksRound(128<<5), // DR_SF10 DR_SF12CR
mluis 0:62d1edcc13d1 386 us2osticksRound(128<<4), // DR_SF9 DR_SF11CR
mluis 0:62d1edcc13d1 387 us2osticksRound(128<<3), // DR_SF8 DR_SF10CR
mluis 0:62d1edcc13d1 388 us2osticksRound(128<<2), // DR_SF7 DR_SF9CR
mluis 0:62d1edcc13d1 389 us2osticksRound(128<<1), // DR_SF8C DR_SF8CR
mluis 0:62d1edcc13d1 390 us2osticksRound(128<<0) // ------ DR_SF7CR
mluis 0:62d1edcc13d1 391 #endif
mluis 0:62d1edcc13d1 392 };
mluis 0:62d1edcc13d1 393
mluis 0:62d1edcc13d1 394
mluis 0:62d1edcc13d1 395 static ostime_t calcRxWindow (u1_t secs, dr_t dr) {
mluis 0:62d1edcc13d1 396 ostime_t rxoff, err;
mluis 0:62d1edcc13d1 397 if( secs==0 ) {
mluis 1:d3b7bde3995c 398 // aka 128 secs (next becaon)
mluis 1:d3b7bde3995c 399 rxoff = LMIC.drift;
mluis 1:d3b7bde3995c 400 err = LMIC.lastDriftDiff;
mluis 0:62d1edcc13d1 401 } else {
mluis 1:d3b7bde3995c 402 // scheduled RX window within secs into current beacon period
mluis 1:d3b7bde3995c 403 rxoff = (LMIC.drift * (ostime_t)secs) >> BCN_INTV_exp;
mluis 1:d3b7bde3995c 404 err = (LMIC.lastDriftDiff * (ostime_t)secs) >> BCN_INTV_exp;
mluis 0:62d1edcc13d1 405 }
mluis 0:62d1edcc13d1 406 u1_t rxsyms = MINRX_SYMS;
mluis 0:62d1edcc13d1 407 err += (ostime_t)LMIC.maxDriftDiff * LMIC.missedBcns;
mluis 0:62d1edcc13d1 408 LMIC.rxsyms = MINRX_SYMS + (err / dr2hsym(dr));
mluis 0:62d1edcc13d1 409
mluis 0:62d1edcc13d1 410 return (rxsyms-PAMBL_SYMS) * dr2hsym(dr) + rxoff;
mluis 0:62d1edcc13d1 411 }
mluis 0:62d1edcc13d1 412
mluis 0:62d1edcc13d1 413
mluis 0:62d1edcc13d1 414 // Setup beacon RX parameters assuming we have an error of ms (aka +/-(ms/2))
mluis 0:62d1edcc13d1 415 static void calcBcnRxWindowFromMillis (u1_t ms, bit_t ini) {
mluis 0:62d1edcc13d1 416 if( ini ) {
mluis 1:d3b7bde3995c 417 LMIC.drift = 0;
mluis 1:d3b7bde3995c 418 LMIC.maxDriftDiff = 0;
mluis 1:d3b7bde3995c 419 LMIC.missedBcns = 0;
mluis 1:d3b7bde3995c 420 LMIC.bcninfo.flags |= BCN_NODRIFT|BCN_NODDIFF;
mluis 0:62d1edcc13d1 421 }
mluis 0:62d1edcc13d1 422 ostime_t hsym = dr2hsym(DR_BCN);
mluis 0:62d1edcc13d1 423 LMIC.bcnRxsyms = MINRX_SYMS + ms2osticksCeil(ms) / hsym;
mluis 0:62d1edcc13d1 424 LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - (LMIC.bcnRxsyms-PAMBL_SYMS) * hsym;
mluis 0:62d1edcc13d1 425 }
mluis 0:62d1edcc13d1 426
mluis 0:62d1edcc13d1 427
mluis 0:62d1edcc13d1 428 // Setup scheduled RX window (ping/multicast slot)
mluis 0:62d1edcc13d1 429 static void rxschedInit (xref2rxsched_t rxsched) {
mluis 0:62d1edcc13d1 430 os_clearMem(AESkey,16);
mluis 0:62d1edcc13d1 431 os_clearMem(LMIC.frame+8,8);
mluis 0:62d1edcc13d1 432 os_wlsbf4(LMIC.frame, LMIC.bcninfo.time);
mluis 0:62d1edcc13d1 433 os_wlsbf4(LMIC.frame+4, LMIC.devaddr);
mluis 0:62d1edcc13d1 434 os_aes(AES_ENC,LMIC.frame,16);
mluis 0:62d1edcc13d1 435 u1_t intvExp = rxsched->intvExp;
mluis 0:62d1edcc13d1 436 ostime_t off = os_rlsbf2(LMIC.frame) & (0x0FFF >> (7 - intvExp)); // random offset (slot units)
mluis 0:62d1edcc13d1 437 rxsched->rxbase = (LMIC.bcninfo.txtime +
mluis 1:d3b7bde3995c 438 BCN_RESERVE_osticks +
mluis 1:d3b7bde3995c 439 ms2osticks(BCN_SLOT_SPAN_ms * off)); // random offset osticks
mluis 0:62d1edcc13d1 440 rxsched->slot = 0;
mluis 0:62d1edcc13d1 441 rxsched->rxtime = rxsched->rxbase - calcRxWindow(/*secs BCN_RESERVE*/2+(1<<intvExp),rxsched->dr);
mluis 0:62d1edcc13d1 442 rxsched->rxsyms = LMIC.rxsyms;
mluis 0:62d1edcc13d1 443 }
mluis 0:62d1edcc13d1 444
mluis 0:62d1edcc13d1 445
mluis 0:62d1edcc13d1 446 static bit_t rxschedNext (xref2rxsched_t rxsched, ostime_t cando) {
mluis 0:62d1edcc13d1 447 again:
mluis 0:62d1edcc13d1 448 if( rxsched->rxtime - cando >= 0 )
mluis 1:d3b7bde3995c 449 return 1;
mluis 0:62d1edcc13d1 450 u1_t slot;
mluis 0:62d1edcc13d1 451 if( (slot=rxsched->slot) >= 128 )
mluis 1:d3b7bde3995c 452 return 0;
mluis 0:62d1edcc13d1 453 u1_t intv = 1<<rxsched->intvExp;
mluis 0:62d1edcc13d1 454 if( (rxsched->slot = (slot += (intv))) >= 128 )
mluis 1:d3b7bde3995c 455 return 0;
mluis 0:62d1edcc13d1 456 rxsched->rxtime = rxsched->rxbase
mluis 1:d3b7bde3995c 457 + ((BCN_WINDOW_osticks * (ostime_t)slot) >> BCN_INTV_exp)
mluis 1:d3b7bde3995c 458 - calcRxWindow(/*secs BCN_RESERVE*/2+slot+intv,rxsched->dr);
mluis 0:62d1edcc13d1 459 rxsched->rxsyms = LMIC.rxsyms;
mluis 0:62d1edcc13d1 460 goto again;
mluis 0:62d1edcc13d1 461 }
mluis 0:62d1edcc13d1 462
mluis 0:62d1edcc13d1 463
mluis 0:62d1edcc13d1 464 static ostime_t rndDelay (u1_t secSpan) {
mluis 0:62d1edcc13d1 465 u2_t r = os_getRndU2();
mluis 0:62d1edcc13d1 466 ostime_t delay = r;
mluis 0:62d1edcc13d1 467 if( delay > OSTICKS_PER_SEC )
mluis 1:d3b7bde3995c 468 delay = r % (u2_t)OSTICKS_PER_SEC;
mluis 0:62d1edcc13d1 469 if( secSpan > 0 )
mluis 1:d3b7bde3995c 470 delay += ((u1_t)r % secSpan) * OSTICKS_PER_SEC;
mluis 0:62d1edcc13d1 471 return delay;
mluis 0:62d1edcc13d1 472 }
mluis 0:62d1edcc13d1 473
mluis 0:62d1edcc13d1 474
mluis 0:62d1edcc13d1 475 static void txDelay (ostime_t reftime, u1_t secSpan) {
mluis 0:62d1edcc13d1 476 reftime += rndDelay(secSpan);
mluis 0:62d1edcc13d1 477 if( LMIC.globalDutyRate == 0 || (reftime - LMIC.globalDutyAvail) > 0 ) {
mluis 1:d3b7bde3995c 478 LMIC.globalDutyAvail = reftime;
mluis 1:d3b7bde3995c 479 LMIC.opmode |= OP_RNDTX;
mluis 0:62d1edcc13d1 480 }
mluis 0:62d1edcc13d1 481 }
mluis 0:62d1edcc13d1 482
mluis 0:62d1edcc13d1 483
mluis 0:62d1edcc13d1 484 static void setDrJoin (u1_t reason, u1_t dr) {
mluis 0:62d1edcc13d1 485 EV(drChange, INFO, (e_.reason = reason,
mluis 1:d3b7bde3995c 486 e_.deveui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 487 e_.dr = dr|DR_PAGE,
mluis 1:d3b7bde3995c 488 e_.txpow = LMIC.adrTxPow,
mluis 1:d3b7bde3995c 489 e_.prevdr = LMIC.datarate|DR_PAGE,
mluis 1:d3b7bde3995c 490 e_.prevtxpow = LMIC.adrTxPow));
mluis 0:62d1edcc13d1 491 LMIC.datarate = dr;
mluis 1:d3b7bde3995c 492 DO_DEVDB(LMIC.datarate,datarate);
mluis 0:62d1edcc13d1 493 }
mluis 0:62d1edcc13d1 494
mluis 0:62d1edcc13d1 495
mluis 0:62d1edcc13d1 496 static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
mluis 0:62d1edcc13d1 497 EV(drChange, INFO, (e_.reason = reason,
mluis 1:d3b7bde3995c 498 e_.deveui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 499 e_.dr = dr|DR_PAGE,
mluis 1:d3b7bde3995c 500 e_.txpow = pow,
mluis 1:d3b7bde3995c 501 e_.prevdr = LMIC.datarate|DR_PAGE,
mluis 1:d3b7bde3995c 502 e_.prevtxpow = LMIC.adrTxPow));
mluis 1:d3b7bde3995c 503
mluis 0:62d1edcc13d1 504 if( pow != KEEP_TXPOW )
mluis 1:d3b7bde3995c 505 LMIC.adrTxPow = pow;
mluis 1:d3b7bde3995c 506 if( LMIC.datarate != dr ) {
mluis 1:d3b7bde3995c 507 LMIC.datarate = dr;
mluis 1:d3b7bde3995c 508 DO_DEVDB(LMIC.datarate,datarate);
mluis 1:d3b7bde3995c 509 LMIC.opmode |= OP_NEXTCHNL;
mluis 1:d3b7bde3995c 510 }
mluis 0:62d1edcc13d1 511 }
mluis 0:62d1edcc13d1 512
mluis 0:62d1edcc13d1 513
mluis 0:62d1edcc13d1 514 void LMIC_stopPingable (void) {
mluis 0:62d1edcc13d1 515 LMIC.opmode &= ~(OP_PINGABLE|OP_PINGINI);
mluis 0:62d1edcc13d1 516 }
mluis 0:62d1edcc13d1 517
mluis 0:62d1edcc13d1 518
mluis 0:62d1edcc13d1 519 void LMIC_setPingable (u1_t intvExp) {
mluis 0:62d1edcc13d1 520 // Change setting
mluis 0:62d1edcc13d1 521 LMIC.ping.intvExp = (intvExp & 0x7);
mluis 0:62d1edcc13d1 522 LMIC.opmode |= OP_PINGABLE;
mluis 0:62d1edcc13d1 523 // App may call LMIC_enableTracking() explicitely before
mluis 0:62d1edcc13d1 524 // Otherwise tracking is implicitly enabled here
mluis 0:62d1edcc13d1 525 if( (LMIC.opmode & (OP_TRACK|OP_SCAN)) == 0 && LMIC.bcninfoTries == 0 )
mluis 1:d3b7bde3995c 526 LMIC_enableTracking(0);
mluis 0:62d1edcc13d1 527 }
mluis 0:62d1edcc13d1 528
mluis 0:62d1edcc13d1 529
mluis 1:d3b7bde3995c 530 #if defined(CFG_eu868)
mluis 0:62d1edcc13d1 531 // ================================================================================
mluis 0:62d1edcc13d1 532 //
mluis 0:62d1edcc13d1 533 // BEG: EU868 related stuff
mluis 0:62d1edcc13d1 534 //
mluis 1:d3b7bde3995c 535 enum { NUM_DEFAULT_CHANNELS=6 };
mluis 0:62d1edcc13d1 536 static const u4_t iniChannelFreq[12] = {
mluis 0:62d1edcc13d1 537 // Join frequencies and duty cycle limit (0.1%)
mluis 1:d3b7bde3995c 538 EU868_F1|BAND_MILLI, EU868_J4|BAND_MILLI,
mluis 1:d3b7bde3995c 539 EU868_F2|BAND_MILLI, EU868_J5|BAND_MILLI,
mluis 1:d3b7bde3995c 540 EU868_F3|BAND_MILLI, EU868_J6|BAND_MILLI,
mluis 0:62d1edcc13d1 541 // Default operational frequencies
mluis 0:62d1edcc13d1 542 EU868_F1|BAND_CENTI, EU868_F2|BAND_CENTI, EU868_F3|BAND_CENTI,
mluis 0:62d1edcc13d1 543 EU868_F4|BAND_MILLI, EU868_F5|BAND_MILLI, EU868_F6|BAND_DECI
mluis 0:62d1edcc13d1 544 };
mluis 0:62d1edcc13d1 545
mluis 0:62d1edcc13d1 546 static void initDefaultChannels (bit_t join) {
mluis 1:d3b7bde3995c 547 os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
mluis 1:d3b7bde3995c 548 os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
mluis 1:d3b7bde3995c 549 os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
mluis 1:d3b7bde3995c 550
mluis 1:d3b7bde3995c 551 LMIC.channelMap = 0x1F;
mluis 0:62d1edcc13d1 552 u1_t su = join ? 0 : 6;
mluis 0:62d1edcc13d1 553 for( u1_t fu=0; fu<6; fu++,su++ ) {
mluis 1:d3b7bde3995c 554 LMIC.channelFreq[fu] = iniChannelFreq[su];
mluis 1:d3b7bde3995c 555 LMIC.channelDrMap[fu] = DR_RANGE_MAP(DR_SF12,DR_SF7);
mluis 0:62d1edcc13d1 556 }
mluis 1:d3b7bde3995c 557 if( !join ) {
mluis 1:d3b7bde3995c 558 LMIC.channelDrMap[5] = DR_RANGE_MAP(DR_SF12,DR_SF7);
mluis 1:d3b7bde3995c 559 LMIC.channelDrMap[1] = DR_RANGE_MAP(DR_SF12,DR_FSK);
mluis 1:d3b7bde3995c 560 }
mluis 0:62d1edcc13d1 561
mluis 1:d3b7bde3995c 562 LMIC.bands[BAND_MILLI].txcap = 1000; // 0.1%
mluis 1:d3b7bde3995c 563 LMIC.bands[BAND_MILLI].txpow = 14;
mluis 1:d3b7bde3995c 564 LMIC.bands[BAND_MILLI].lastchnl = os_getRndU1() % MAX_CHANNELS;
mluis 1:d3b7bde3995c 565 LMIC.bands[BAND_CENTI].txcap = 100; // 1%
mluis 1:d3b7bde3995c 566 LMIC.bands[BAND_CENTI].txpow = 14;
mluis 1:d3b7bde3995c 567 LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS;
mluis 1:d3b7bde3995c 568 LMIC.bands[BAND_DECI ].txcap = 10; // 10%
mluis 1:d3b7bde3995c 569 LMIC.bands[BAND_DECI ].txpow = 27;
mluis 1:d3b7bde3995c 570 LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS;
mluis 0:62d1edcc13d1 571 LMIC.bands[BAND_MILLI].avail =
mluis 0:62d1edcc13d1 572 LMIC.bands[BAND_CENTI].avail =
mluis 0:62d1edcc13d1 573 LMIC.bands[BAND_DECI ].avail = os_getTime();
mluis 0:62d1edcc13d1 574 }
mluis 0:62d1edcc13d1 575
mluis 1:d3b7bde3995c 576 bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap) {
mluis 1:d3b7bde3995c 577 if( bandidx > BAND_AUX ) return 0;
mluis 1:d3b7bde3995c 578 band_t* b = &LMIC.bands[bandidx];
mluis 1:d3b7bde3995c 579 b->txpow = txpow;
mluis 1:d3b7bde3995c 580 b->txcap = txcap;
mluis 1:d3b7bde3995c 581 b->avail = os_getTime();
mluis 1:d3b7bde3995c 582 b->lastchnl = os_getRndU1() % MAX_CHANNELS;
mluis 1:d3b7bde3995c 583 return 1;
mluis 1:d3b7bde3995c 584 }
mluis 1:d3b7bde3995c 585
mluis 1:d3b7bde3995c 586 bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
mluis 1:d3b7bde3995c 587 if( chidx >= MAX_CHANNELS )
mluis 1:d3b7bde3995c 588 return 0;
mluis 1:d3b7bde3995c 589 if( band == -1 ) {
mluis 1:d3b7bde3995c 590 if( freq >= 869400000 && freq <= 869650000 )
mluis 1:d3b7bde3995c 591 freq |= BAND_DECI; // 10% 27dBm
mluis 1:d3b7bde3995c 592 else if( (freq >= 868000000 && freq <= 868600000) ||
mluis 1:d3b7bde3995c 593 (freq >= 869700000 && freq <= 870000000) )
mluis 1:d3b7bde3995c 594 freq |= BAND_CENTI; // 1% 14dBm
mluis 1:d3b7bde3995c 595 else
mluis 1:d3b7bde3995c 596 freq |= BAND_MILLI; // 0.1% 14dBm
mluis 1:d3b7bde3995c 597 } else {
mluis 1:d3b7bde3995c 598 if( band > BAND_AUX ) return 0;
mluis 1:d3b7bde3995c 599 freq = (freq&~3) | band;
mluis 1:d3b7bde3995c 600 }
mluis 1:d3b7bde3995c 601 LMIC.channelFreq [chidx] = freq;
mluis 1:d3b7bde3995c 602 LMIC.channelDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF12,DR_SF7) : drmap;
mluis 1:d3b7bde3995c 603 LMIC.channelMap |= 1<<chidx; // enabled right away
mluis 1:d3b7bde3995c 604 return 1;
mluis 1:d3b7bde3995c 605 }
mluis 1:d3b7bde3995c 606
mluis 1:d3b7bde3995c 607 void LMIC_disableChannel (u1_t channel) {
mluis 1:d3b7bde3995c 608 LMIC.channelFreq[channel] = 0;
mluis 1:d3b7bde3995c 609 LMIC.channelDrMap[channel] = 0;
mluis 1:d3b7bde3995c 610 LMIC.channelMap &= ~(1<<channel);
mluis 1:d3b7bde3995c 611 }
mluis 1:d3b7bde3995c 612
mluis 0:62d1edcc13d1 613 static u4_t convFreq (xref2u1_t ptr) {
mluis 0:62d1edcc13d1 614 u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100;
mluis 1:d3b7bde3995c 615 if( freq < EU868_FREQ_MIN || freq > EU868_FREQ_MAX )
mluis 1:d3b7bde3995c 616 freq = 0;
mluis 0:62d1edcc13d1 617 return freq;
mluis 0:62d1edcc13d1 618 }
mluis 0:62d1edcc13d1 619
mluis 0:62d1edcc13d1 620 static u1_t mapChannels (u1_t chpage, u2_t chmap) {
mluis 1:d3b7bde3995c 621 // Bad page, disable all channel, enable non-existent
mluis 1:d3b7bde3995c 622 if( chpage != 0 || chmap==0 || (chmap & ~LMIC.channelMap) != 0 )
mluis 1:d3b7bde3995c 623 return 0; // illegal input
mluis 0:62d1edcc13d1 624 for( u1_t chnl=0; chnl<MAX_CHANNELS; chnl++ ) {
mluis 1:d3b7bde3995c 625 if( (chmap & (1<<chnl)) != 0 && LMIC.channelFreq[chnl] == 0 )
mluis 1:d3b7bde3995c 626 chmap &= ~(1<<chnl); // ignore - channel is not defined
mluis 0:62d1edcc13d1 627 }
mluis 0:62d1edcc13d1 628 LMIC.channelMap = chmap;
mluis 0:62d1edcc13d1 629 return 1;
mluis 0:62d1edcc13d1 630 }
mluis 0:62d1edcc13d1 631
mluis 0:62d1edcc13d1 632
mluis 0:62d1edcc13d1 633 static void updateTx (ostime_t txbeg) {
mluis 0:62d1edcc13d1 634 u4_t freq = LMIC.channelFreq[LMIC.txChnl];
mluis 0:62d1edcc13d1 635 // Update global/band specific duty cycle stats
mluis 0:62d1edcc13d1 636 ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
mluis 0:62d1edcc13d1 637 // Update channel/global duty cycle stats
mluis 0:62d1edcc13d1 638 xref2band_t band = &LMIC.bands[freq & 0x3];
mluis 0:62d1edcc13d1 639 LMIC.freq = freq & ~(u4_t)3;
mluis 0:62d1edcc13d1 640 LMIC.txpow = band->txpow;
mluis 0:62d1edcc13d1 641 band->avail = txbeg + airtime * band->txcap;
mluis 0:62d1edcc13d1 642 if( LMIC.globalDutyRate != 0 )
mluis 1:d3b7bde3995c 643 LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
mluis 0:62d1edcc13d1 644 }
mluis 0:62d1edcc13d1 645
mluis 0:62d1edcc13d1 646 static ostime_t nextTx (ostime_t now) {
mluis 1:d3b7bde3995c 647 u1_t bmap=0xF;
mluis 1:d3b7bde3995c 648 do {
mluis 1:d3b7bde3995c 649 ostime_t mintime = now + /*10h*/36000*OSTICKS_PER_SEC;
mluis 1:d3b7bde3995c 650 u1_t band=0;
mluis 1:d3b7bde3995c 651 for( u1_t bi=0; bi<4; bi++ ) {
mluis 1:d3b7bde3995c 652 if( (bmap & (1<<bi)) && mintime - LMIC.bands[bi].avail > 0 )
mluis 1:d3b7bde3995c 653 mintime = LMIC.bands[band = bi].avail;
mluis 0:62d1edcc13d1 654 }
mluis 1:d3b7bde3995c 655 // Find next channel in given band
mluis 1:d3b7bde3995c 656 u1_t chnl = LMIC.bands[band].lastchnl;
mluis 1:d3b7bde3995c 657 for( u1_t ci=0; ci<MAX_CHANNELS; ci++ ) {
mluis 1:d3b7bde3995c 658 if( (chnl = (chnl+1)) >= MAX_CHANNELS )
mluis 1:d3b7bde3995c 659 chnl -= MAX_CHANNELS;
mluis 1:d3b7bde3995c 660 if( (LMIC.channelMap & (1<<chnl)) != 0 && // channel enabled
mluis 1:d3b7bde3995c 661 (LMIC.channelDrMap[chnl] & (1<<(LMIC.datarate&0xF))) != 0 &&
mluis 1:d3b7bde3995c 662 band == (LMIC.channelFreq[chnl] & 0x3) ) { // in selected band
mluis 1:d3b7bde3995c 663 LMIC.txChnl = LMIC.bands[band].lastchnl = chnl;
mluis 1:d3b7bde3995c 664 return mintime;
mluis 1:d3b7bde3995c 665 }
mluis 0:62d1edcc13d1 666 }
mluis 1:d3b7bde3995c 667 if( (bmap &= ~(1<<band)) == 0 ) {
mluis 1:d3b7bde3995c 668 // No feasible channel found!
mluis 1:d3b7bde3995c 669 return mintime;
mluis 1:d3b7bde3995c 670 }
mluis 0:62d1edcc13d1 671 } while(1);
mluis 0:62d1edcc13d1 672 }
mluis 0:62d1edcc13d1 673
mluis 1:d3b7bde3995c 674
mluis 0:62d1edcc13d1 675 static void setBcnRxParams (void) {
mluis 0:62d1edcc13d1 676 LMIC.dataLen = 0;
mluis 0:62d1edcc13d1 677 LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3;
mluis 0:62d1edcc13d1 678 LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN),1),LEN_BCN);
mluis 0:62d1edcc13d1 679 }
mluis 0:62d1edcc13d1 680
mluis 0:62d1edcc13d1 681 #define setRx1Params() /*LMIC.freq/rps remain unchanged*/
mluis 0:62d1edcc13d1 682
mluis 0:62d1edcc13d1 683 static void initJoinLoop (void) {
mluis 1:d3b7bde3995c 684 LMIC.txChnl = os_getRndU1() % 6;
mluis 0:62d1edcc13d1 685 LMIC.adrTxPow = 14;
mluis 0:62d1edcc13d1 686 setDrJoin(DRCHG_SET, DR_SF7);
mluis 0:62d1edcc13d1 687 initDefaultChannels(1);
mluis 0:62d1edcc13d1 688 ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
mluis 1:d3b7bde3995c 689 LMIC.txend = LMIC.bands[BAND_MILLI].avail + rndDelay(8);
mluis 0:62d1edcc13d1 690 }
mluis 0:62d1edcc13d1 691
mluis 0:62d1edcc13d1 692
mluis 0:62d1edcc13d1 693 static ostime_t nextJoinState (void) {
mluis 1:d3b7bde3995c 694 u1_t failed = 0;
mluis 1:d3b7bde3995c 695
mluis 0:62d1edcc13d1 696 // Try 869.x and then 864.x with same DR
mluis 0:62d1edcc13d1 697 // If both fail try next lower datarate
mluis 1:d3b7bde3995c 698 if( ++LMIC.txChnl == 6 )
mluis 1:d3b7bde3995c 699 LMIC.txChnl = 0;
mluis 1:d3b7bde3995c 700 if( (++LMIC.txCnt & 1) == 0 ) {
mluis 1:d3b7bde3995c 701 // Lower DR every 2nd try (having tried 868.x and 864.x with the same DR)
mluis 1:d3b7bde3995c 702 if( LMIC.datarate == DR_SF12 )
mluis 1:d3b7bde3995c 703 failed = 1; // we have tried all DR - signal EV_JOIN_FAILED
mluis 1:d3b7bde3995c 704 else
mluis 1:d3b7bde3995c 705 setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate));
mluis 1:d3b7bde3995c 706 }
mluis 1:d3b7bde3995c 707 // Clear NEXTCHNL because join state engine controls channel hopping
mluis 0:62d1edcc13d1 708 LMIC.opmode &= ~OP_NEXTCHNL;
mluis 1:d3b7bde3995c 709 // Move txend to randomize synchronized concurrent joins.
mluis 1:d3b7bde3995c 710 // Duty cycle is based on txend.
mluis 1:d3b7bde3995c 711 ostime_t time = os_getTime();
mluis 1:d3b7bde3995c 712 if( time - LMIC.bands[BAND_MILLI].avail < 0 )
mluis 1:d3b7bde3995c 713 time = LMIC.bands[BAND_MILLI].avail;
mluis 1:d3b7bde3995c 714 LMIC.txend = time +
mluis 1:d3b7bde3995c 715 (isTESTMODE()
mluis 1:d3b7bde3995c 716 // Avoid collision with JOIN ACCEPT @ SF12 being sent by GW (but we missed it)
mluis 1:d3b7bde3995c 717 ? DNW2_SAFETY_ZONE
mluis 1:d3b7bde3995c 718 // Otherwise: randomize join (street lamp case):
mluis 1:d3b7bde3995c 719 // SF12:255, SF11:127, .., SF7:8secs
mluis 1:d3b7bde3995c 720 : DNW2_SAFETY_ZONE+rndDelay(255>>LMIC.datarate));
mluis 1:d3b7bde3995c 721 // 1 - triggers EV_JOIN_FAILED event
mluis 1:d3b7bde3995c 722 return failed;
mluis 0:62d1edcc13d1 723 }
mluis 0:62d1edcc13d1 724
mluis 0:62d1edcc13d1 725 //
mluis 0:62d1edcc13d1 726 // END: EU868 related stuff
mluis 0:62d1edcc13d1 727 //
mluis 0:62d1edcc13d1 728 // ================================================================================
mluis 1:d3b7bde3995c 729 #elif defined(CFG_us915)
mluis 0:62d1edcc13d1 730 // ================================================================================
mluis 0:62d1edcc13d1 731 //
mluis 0:62d1edcc13d1 732 // BEG: US915 related stuff
mluis 0:62d1edcc13d1 733 //
mluis 0:62d1edcc13d1 734
mluis 1:d3b7bde3995c 735
mluis 0:62d1edcc13d1 736 static void initDefaultChannels (void) {
mluis 0:62d1edcc13d1 737 for( u1_t i=0; i<4; i++ )
mluis 1:d3b7bde3995c 738 LMIC.channelMap[i] = 0xFFFF;
mluis 0:62d1edcc13d1 739 LMIC.channelMap[4] = 0x00FF;
mluis 0:62d1edcc13d1 740 }
mluis 0:62d1edcc13d1 741
mluis 0:62d1edcc13d1 742 static u4_t convFreq (xref2u1_t ptr) {
mluis 0:62d1edcc13d1 743 u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100;
mluis 0:62d1edcc13d1 744 if( freq >= US915_FREQ_MIN && freq <= US915_FREQ_MAX )
mluis 1:d3b7bde3995c 745 freq = 0;
mluis 0:62d1edcc13d1 746 return freq;
mluis 0:62d1edcc13d1 747 }
mluis 0:62d1edcc13d1 748
mluis 1:d3b7bde3995c 749 bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
mluis 0:62d1edcc13d1 750 if( chidx < 72 || chidx >= 72+MAX_XCHANNELS )
mluis 1:d3b7bde3995c 751 return 0; // channels 0..71 are hardwired
mluis 0:62d1edcc13d1 752 chidx -= 72;
mluis 0:62d1edcc13d1 753 LMIC.xchFreq[chidx] = freq;
mluis 1:d3b7bde3995c 754 LMIC.xchDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF10,DR_SF8C) : drmap;
mluis 0:62d1edcc13d1 755 LMIC.channelMap[chidx>>4] |= (1<<(chidx&0xF));
mluis 0:62d1edcc13d1 756 return 1;
mluis 0:62d1edcc13d1 757 }
mluis 0:62d1edcc13d1 758
mluis 1:d3b7bde3995c 759 void LMIC_disableChannel (u1_t channel) {
mluis 1:d3b7bde3995c 760 if( channel < 72+MAX_XCHANNELS )
mluis 1:d3b7bde3995c 761 LMIC.channelMap[channel/4] &= ~(1<<(channel&0xF));
mluis 1:d3b7bde3995c 762 }
mluis 0:62d1edcc13d1 763
mluis 0:62d1edcc13d1 764 static u1_t mapChannels (u1_t chpage, u2_t chmap) {
mluis 0:62d1edcc13d1 765 if( chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF ) {
mluis 1:d3b7bde3995c 766 u2_t en125 = chpage == MCMD_LADR_CHP_125ON ? 0xFFFF : 0x0000;
mluis 1:d3b7bde3995c 767 for( u1_t u=0; u<4; u++ )
mluis 1:d3b7bde3995c 768 LMIC.channelMap[u] = en125;
mluis 1:d3b7bde3995c 769 LMIC.channelMap[64/16] = chmap;
mluis 0:62d1edcc13d1 770 } else {
mluis 1:d3b7bde3995c 771 if( chpage >= (72+MAX_XCHANNELS+15)/16 )
mluis 1:d3b7bde3995c 772 return 0;
mluis 1:d3b7bde3995c 773 LMIC.channelMap[chpage] = chmap;
mluis 0:62d1edcc13d1 774 }
mluis 0:62d1edcc13d1 775 return 1;
mluis 0:62d1edcc13d1 776 }
mluis 0:62d1edcc13d1 777
mluis 0:62d1edcc13d1 778 static void updateTx (ostime_t txbeg) {
mluis 0:62d1edcc13d1 779 u1_t chnl = LMIC.txChnl;
mluis 0:62d1edcc13d1 780 if( chnl < 64 ) {
mluis 1:d3b7bde3995c 781 LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP;
mluis 1:d3b7bde3995c 782 LMIC.txpow = 30;
mluis 1:d3b7bde3995c 783 return;
mluis 0:62d1edcc13d1 784 }
mluis 0:62d1edcc13d1 785 LMIC.txpow = 26;
mluis 0:62d1edcc13d1 786 if( chnl < 64+8 ) {
mluis 1:d3b7bde3995c 787 LMIC.freq = US915_500kHz_UPFBASE + (chnl-64)*US915_500kHz_UPFSTEP;
mluis 0:62d1edcc13d1 788 } else {
mluis 1:d3b7bde3995c 789 ASSERT(chnl < 64+8+MAX_XCHANNELS);
mluis 1:d3b7bde3995c 790 LMIC.freq = LMIC.xchFreq[chnl-72];
mluis 0:62d1edcc13d1 791 }
mluis 0:62d1edcc13d1 792
mluis 0:62d1edcc13d1 793 // Update global duty cycle stats
mluis 0:62d1edcc13d1 794 if( LMIC.globalDutyRate != 0 ) {
mluis 1:d3b7bde3995c 795 ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
mluis 1:d3b7bde3995c 796 LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
mluis 0:62d1edcc13d1 797 }
mluis 0:62d1edcc13d1 798 }
mluis 0:62d1edcc13d1 799
mluis 0:62d1edcc13d1 800 // US does not have duty cycling - return now as earliest TX time
mluis 0:62d1edcc13d1 801 #define nextTx(now) (_nextTx(),(now))
mluis 0:62d1edcc13d1 802 static void _nextTx (void) {
mluis 1:d3b7bde3995c 803 if( LMIC.chRnd==0 )
mluis 1:d3b7bde3995c 804 LMIC.chRnd = os_getRndU1() & 0x3F;
mluis 1:d3b7bde3995c 805 if( LMIC.datarate >= DR_SF8C ) { // 500kHz
mluis 1:d3b7bde3995c 806 u1_t map = LMIC.channelMap[64/16]&0xFF;
mluis 1:d3b7bde3995c 807 for( u1_t i=0; i<8; i++ ) {
mluis 1:d3b7bde3995c 808 if( (map & (1<<(++LMIC.chRnd & 7))) != 0 ) {
mluis 1:d3b7bde3995c 809 LMIC.txChnl = 64 + (LMIC.chRnd & 7);
mluis 1:d3b7bde3995c 810 return;
mluis 1:d3b7bde3995c 811 }
mluis 1:d3b7bde3995c 812 }
mluis 1:d3b7bde3995c 813 } else { // 125kHz
mluis 1:d3b7bde3995c 814 for( u1_t i=0; i<64; i++ ) {
mluis 1:d3b7bde3995c 815 u1_t chnl = ++LMIC.chRnd & 0x3F;
mluis 1:d3b7bde3995c 816 if( (LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0xF))) != 0 ) {
mluis 1:d3b7bde3995c 817 LMIC.txChnl = chnl;
mluis 1:d3b7bde3995c 818 return;
mluis 1:d3b7bde3995c 819 }
mluis 1:d3b7bde3995c 820 }
mluis 0:62d1edcc13d1 821 }
mluis 1:d3b7bde3995c 822 // No feasible channel found! Keep old one.
mluis 0:62d1edcc13d1 823 }
mluis 0:62d1edcc13d1 824
mluis 0:62d1edcc13d1 825 static void setBcnRxParams (void) {
mluis 0:62d1edcc13d1 826 LMIC.dataLen = 0;
mluis 1:d3b7bde3995c 827 LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl * US915_500kHz_DNFSTEP;
mluis 0:62d1edcc13d1 828 LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN),1),LEN_BCN);
mluis 0:62d1edcc13d1 829 }
mluis 0:62d1edcc13d1 830
mluis 1:d3b7bde3995c 831 #define setRx1Params() { \
mluis 0:62d1edcc13d1 832 LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP; \
mluis 1:d3b7bde3995c 833 if( /* TX datarate */LMIC.dndr < DR_SF8C ) \
mluis 1:d3b7bde3995c 834 LMIC.dndr += DR_SF10CR - DR_SF10; \
mluis 1:d3b7bde3995c 835 else if( LMIC.dndr == DR_SF8C ) \
mluis 1:d3b7bde3995c 836 LMIC.dndr = DR_SF7CR; \
mluis 1:d3b7bde3995c 837 LMIC.rps = dndr2rps(LMIC.dndr); \
mluis 0:62d1edcc13d1 838 }
mluis 0:62d1edcc13d1 839
mluis 0:62d1edcc13d1 840 static void initJoinLoop (void) {
mluis 1:d3b7bde3995c 841 LMIC.chRnd = 0;
mluis 1:d3b7bde3995c 842 LMIC.txChnl = 0;
mluis 0:62d1edcc13d1 843 LMIC.adrTxPow = 20;
mluis 0:62d1edcc13d1 844 ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
mluis 0:62d1edcc13d1 845 LMIC.txend = os_getTime();
mluis 0:62d1edcc13d1 846 setDrJoin(DRCHG_SET, DR_SF7);
mluis 0:62d1edcc13d1 847 }
mluis 0:62d1edcc13d1 848
mluis 0:62d1edcc13d1 849 static ostime_t nextJoinState (void) {
mluis 0:62d1edcc13d1 850 // Try the following:
mluis 0:62d1edcc13d1 851 // SF7/8/9/10 on a random channel 0..63
mluis 0:62d1edcc13d1 852 // SF8C on a random channel 64..71
mluis 0:62d1edcc13d1 853 //
mluis 1:d3b7bde3995c 854 u1_t failed = 0;
mluis 0:62d1edcc13d1 855 if( LMIC.datarate != DR_SF8C ) {
mluis 1:d3b7bde3995c 856 LMIC.txChnl = 64+(LMIC.txChnl&7);
mluis 1:d3b7bde3995c 857 setDrJoin(DRCHG_SET, DR_SF8C);
mluis 0:62d1edcc13d1 858 } else {
mluis 1:d3b7bde3995c 859 LMIC.txChnl = os_getRndU1() & 0x3F;
mluis 1:d3b7bde3995c 860 s1_t dr = DR_SF7 - ++LMIC.txCnt;
mluis 1:d3b7bde3995c 861 if( dr < DR_SF10 ) {
mluis 1:d3b7bde3995c 862 dr = DR_SF10;
mluis 1:d3b7bde3995c 863 failed = 1; // All DR exhausted - signal failed
mluis 1:d3b7bde3995c 864 }
mluis 1:d3b7bde3995c 865 setDrJoin(DRCHG_SET, dr);
mluis 0:62d1edcc13d1 866 }
mluis 1:d3b7bde3995c 867 LMIC.opmode &= ~OP_NEXTCHNL;
mluis 1:d3b7bde3995c 868 LMIC.txend = os_getTime() +
mluis 1:d3b7bde3995c 869 (isTESTMODE()
mluis 1:d3b7bde3995c 870 // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it - GW is still busy)
mluis 1:d3b7bde3995c 871 ? DNW2_SAFETY_ZONE
mluis 1:d3b7bde3995c 872 // Otherwise: randomize join (street lamp case):
mluis 1:d3b7bde3995c 873 // SF10:16, SF9=8,..SF8C:1secs
mluis 1:d3b7bde3995c 874 : rndDelay(16>>LMIC.datarate));
mluis 1:d3b7bde3995c 875 // 1 - triggers EV_JOIN_FAILED event
mluis 1:d3b7bde3995c 876 return failed;
mluis 0:62d1edcc13d1 877 }
mluis 0:62d1edcc13d1 878
mluis 0:62d1edcc13d1 879 //
mluis 0:62d1edcc13d1 880 // END: US915 related stuff
mluis 0:62d1edcc13d1 881 //
mluis 0:62d1edcc13d1 882 // ================================================================================
mluis 0:62d1edcc13d1 883 #else
mluis 0:62d1edcc13d1 884 #error Unsupported frequency band!
mluis 0:62d1edcc13d1 885 #endif
mluis 0:62d1edcc13d1 886
mluis 0:62d1edcc13d1 887
mluis 0:62d1edcc13d1 888 static void runEngineUpdate (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 889 engineUpdate();
mluis 0:62d1edcc13d1 890 }
mluis 0:62d1edcc13d1 891
mluis 0:62d1edcc13d1 892
mluis 0:62d1edcc13d1 893 static void reportEvent (ev_t ev) {
mluis 0:62d1edcc13d1 894 EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
mluis 1:d3b7bde3995c 895 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 896 e_.info = ev));
mluis 0:62d1edcc13d1 897 ON_LMIC_EVENT(ev);
mluis 0:62d1edcc13d1 898 engineUpdate();
mluis 0:62d1edcc13d1 899 }
mluis 0:62d1edcc13d1 900
mluis 0:62d1edcc13d1 901
mluis 1:d3b7bde3995c 902 static void runReset (xref2osjob_t osjob) {
mluis 1:d3b7bde3995c 903 // Disable session
mluis 1:d3b7bde3995c 904 LMIC_reset();
mluis 1:d3b7bde3995c 905 LMIC_startJoining();
mluis 1:d3b7bde3995c 906 reportEvent(EV_RESET);
mluis 1:d3b7bde3995c 907 }
mluis 1:d3b7bde3995c 908
mluis 0:62d1edcc13d1 909 static void stateJustJoined (void) {
mluis 0:62d1edcc13d1 910 LMIC.seqnoDn = LMIC.seqnoUp = 0;
mluis 0:62d1edcc13d1 911 LMIC.rejoinCnt = 0;
mluis 0:62d1edcc13d1 912 LMIC.dnConf = LMIC.adrChanged = LMIC.ladrAns = LMIC.devsAns = 0;
mluis 0:62d1edcc13d1 913 LMIC.moreData = LMIC.dn2Ans = LMIC.snchAns = LMIC.dutyCapAns = 0;
mluis 0:62d1edcc13d1 914 LMIC.pingSetAns = 0;
mluis 0:62d1edcc13d1 915 LMIC.upRepeat = 0;
mluis 0:62d1edcc13d1 916 LMIC.adrAckReq = LINK_CHECK_INIT;
mluis 0:62d1edcc13d1 917 LMIC.dn2Dr = DR_DNW2;
mluis 0:62d1edcc13d1 918 LMIC.dn2Freq = FREQ_DNW2;
mluis 0:62d1edcc13d1 919 LMIC.bcnChnl = CHNL_BCN;
mluis 0:62d1edcc13d1 920 LMIC.ping.freq = FREQ_PING;
mluis 0:62d1edcc13d1 921 LMIC.ping.dr = DR_PING;
mluis 0:62d1edcc13d1 922 }
mluis 0:62d1edcc13d1 923
mluis 0:62d1edcc13d1 924
mluis 0:62d1edcc13d1 925 // ================================================================================
mluis 0:62d1edcc13d1 926 // Decoding frames
mluis 0:62d1edcc13d1 927
mluis 0:62d1edcc13d1 928
mluis 0:62d1edcc13d1 929 // Decode beacon - do not overwrite bcninfo unless we have a match!
mluis 0:62d1edcc13d1 930 static int decodeBeacon (void) {
mluis 0:62d1edcc13d1 931 ASSERT(LMIC.dataLen == LEN_BCN); // implicit header RX guarantees this
mluis 0:62d1edcc13d1 932 xref2u1_t d = LMIC.frame;
mluis 1:d3b7bde3995c 933 if(
mluis 1:d3b7bde3995c 934 #if CFG_eu868
mluis 1:d3b7bde3995c 935 d[OFF_BCN_CRC1] != (u1_t)os_crc16(d,OFF_BCN_CRC1)
mluis 1:d3b7bde3995c 936 #elif CFG_us915
mluis 1:d3b7bde3995c 937 os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d,OFF_BCN_CRC1)
mluis 1:d3b7bde3995c 938 #endif
mluis 1:d3b7bde3995c 939 )
mluis 1:d3b7bde3995c 940 return 0; // first (common) part fails CRC check
mluis 0:62d1edcc13d1 941 // First set of fields is ok
mluis 0:62d1edcc13d1 942 u4_t bcnnetid = os_rlsbf4(&d[OFF_BCN_NETID]) & 0xFFFFFF;
mluis 0:62d1edcc13d1 943 if( bcnnetid != LMIC.netid )
mluis 1:d3b7bde3995c 944 return -1; // not the beacon we're looking for
mluis 0:62d1edcc13d1 945
mluis 0:62d1edcc13d1 946 LMIC.bcninfo.flags &= ~(BCN_PARTIAL|BCN_FULL);
mluis 0:62d1edcc13d1 947 // Match - update bcninfo structure
mluis 1:d3b7bde3995c 948 LMIC.bcninfo.snr = LMIC.snr;
mluis 1:d3b7bde3995c 949 LMIC.bcninfo.rssi = LMIC.rssi;
mluis 0:62d1edcc13d1 950 LMIC.bcninfo.txtime = LMIC.rxtime - AIRTIME_BCN_osticks;
mluis 0:62d1edcc13d1 951 LMIC.bcninfo.time = os_rlsbf4(&d[OFF_BCN_TIME]);
mluis 0:62d1edcc13d1 952 LMIC.bcninfo.flags |= BCN_PARTIAL;
mluis 0:62d1edcc13d1 953
mluis 0:62d1edcc13d1 954 // Check 2nd set
mluis 0:62d1edcc13d1 955 if( os_rlsbf2(&d[OFF_BCN_CRC2]) != os_crc16(d,OFF_BCN_CRC2) )
mluis 1:d3b7bde3995c 956 return 1;
mluis 0:62d1edcc13d1 957 // Second set of fields is ok
mluis 0:62d1edcc13d1 958 LMIC.bcninfo.lat = (s4_t)os_rlsbf4(&d[OFF_BCN_LAT-1]) >> 8; // read as signed 24-bit
mluis 0:62d1edcc13d1 959 LMIC.bcninfo.lon = (s4_t)os_rlsbf4(&d[OFF_BCN_LON-1]) >> 8; // ditto
mluis 0:62d1edcc13d1 960 LMIC.bcninfo.info = d[OFF_BCN_INFO];
mluis 0:62d1edcc13d1 961 LMIC.bcninfo.flags |= BCN_FULL;
mluis 0:62d1edcc13d1 962 return 2;
mluis 0:62d1edcc13d1 963 }
mluis 0:62d1edcc13d1 964
mluis 0:62d1edcc13d1 965
mluis 0:62d1edcc13d1 966 static bit_t decodeFrame (void) {
mluis 0:62d1edcc13d1 967 xref2u1_t d = LMIC.frame;
mluis 0:62d1edcc13d1 968 u1_t hdr = d[0];
mluis 0:62d1edcc13d1 969 u1_t ftype = hdr & HDR_FTYPE;
mluis 0:62d1edcc13d1 970 int dlen = LMIC.dataLen;
mluis 0:62d1edcc13d1 971 if( dlen < OFF_DAT_OPTS+4 ||
mluis 1:d3b7bde3995c 972 (hdr & HDR_MAJOR) != HDR_MAJOR_V1 ||
mluis 1:d3b7bde3995c 973 (ftype != HDR_FTYPE_DADN && ftype != HDR_FTYPE_DCDN) ) {
mluis 1:d3b7bde3995c 974 // Basic sanity checks failed
mluis 1:d3b7bde3995c 975 EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
mluis 1:d3b7bde3995c 976 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 977 e_.info = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]),
mluis 1:d3b7bde3995c 978 e_.info2 = hdr + (dlen<<8)));
mluis 0:62d1edcc13d1 979 norx:
mluis 1:d3b7bde3995c 980 LMIC.dataLen = 0;
mluis 1:d3b7bde3995c 981 return 0;
mluis 0:62d1edcc13d1 982 }
mluis 0:62d1edcc13d1 983 // Validate exact frame length
mluis 0:62d1edcc13d1 984 // Note: device address was already read+evaluated in order to arrive here.
mluis 0:62d1edcc13d1 985 int fct = d[OFF_DAT_FCT];
mluis 0:62d1edcc13d1 986 u4_t addr = os_rlsbf4(&d[OFF_DAT_ADDR]);
mluis 0:62d1edcc13d1 987 u4_t seqno = os_rlsbf2(&d[OFF_DAT_SEQNO]);
mluis 0:62d1edcc13d1 988 int olen = fct & FCT_OPTLEN;
mluis 0:62d1edcc13d1 989 int ackup = (fct & FCT_ACK) != 0 ? 1 : 0; // ACK last up frame
mluis 0:62d1edcc13d1 990 int poff = OFF_DAT_OPTS+olen;
mluis 0:62d1edcc13d1 991 int pend = dlen-4; // MIC
mluis 0:62d1edcc13d1 992
mluis 0:62d1edcc13d1 993 if( addr != LMIC.devaddr ) {
mluis 1:d3b7bde3995c 994 EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS,
mluis 1:d3b7bde3995c 995 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 996 e_.info = addr,
mluis 1:d3b7bde3995c 997 e_.info2 = LMIC.devaddr));
mluis 1:d3b7bde3995c 998 goto norx;
mluis 0:62d1edcc13d1 999 }
mluis 0:62d1edcc13d1 1000 if( poff > pend ) {
mluis 1:d3b7bde3995c 1001 EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
mluis 1:d3b7bde3995c 1002 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1003 e_.info = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16)));
mluis 1:d3b7bde3995c 1004 goto norx;
mluis 0:62d1edcc13d1 1005 }
mluis 0:62d1edcc13d1 1006
mluis 0:62d1edcc13d1 1007 int port = -1;
mluis 0:62d1edcc13d1 1008 int replayConf = 0;
mluis 0:62d1edcc13d1 1009
mluis 0:62d1edcc13d1 1010 if( pend > poff )
mluis 1:d3b7bde3995c 1011 port = d[poff++];
mluis 0:62d1edcc13d1 1012
mluis 0:62d1edcc13d1 1013 seqno = LMIC.seqnoDn + (u2_t)(seqno - LMIC.seqnoDn);
mluis 0:62d1edcc13d1 1014
mluis 0:62d1edcc13d1 1015 if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) {
mluis 1:d3b7bde3995c 1016 EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC,
mluis 1:d3b7bde3995c 1017 e_.eui1 = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1018 e_.info1 = Base::lsbf4(&d[pend]),
mluis 1:d3b7bde3995c 1019 e_.info2 = seqno,
mluis 1:d3b7bde3995c 1020 e_.info3 = LMIC.devaddr));
mluis 0:62d1edcc13d1 1021 goto norx;
mluis 0:62d1edcc13d1 1022 }
mluis 1:d3b7bde3995c 1023 if( seqno < LMIC.seqnoDn ) {
mluis 1:d3b7bde3995c 1024 if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) {
mluis 1:d3b7bde3995c 1025 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
mluis 1:d3b7bde3995c 1026 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1027 e_.info = LMIC.seqnoDn,
mluis 1:d3b7bde3995c 1028 e_.info2 = seqno));
mluis 1:d3b7bde3995c 1029 goto norx;
mluis 1:d3b7bde3995c 1030 }
mluis 1:d3b7bde3995c 1031 if( seqno != LMIC.seqnoDn-1 || !LMIC.dnConf || ftype != HDR_FTYPE_DCDN ) {
mluis 1:d3b7bde3995c 1032 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_OBSOLETE,
mluis 1:d3b7bde3995c 1033 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1034 e_.info = LMIC.seqnoDn,
mluis 1:d3b7bde3995c 1035 e_.info2 = seqno));
mluis 1:d3b7bde3995c 1036 goto norx;
mluis 1:d3b7bde3995c 1037 }
mluis 1:d3b7bde3995c 1038 // Replay of previous sequence number allowed only if
mluis 1:d3b7bde3995c 1039 // previous frame and repeated both requested confirmation
mluis 1:d3b7bde3995c 1040 replayConf = 1;
mluis 0:62d1edcc13d1 1041 }
mluis 0:62d1edcc13d1 1042 else {
mluis 1:d3b7bde3995c 1043 if( seqno > LMIC.seqnoDn ) {
mluis 1:d3b7bde3995c 1044 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP,
mluis 1:d3b7bde3995c 1045 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1046 e_.info = LMIC.seqnoDn,
mluis 1:d3b7bde3995c 1047 e_.info2 = seqno));
mluis 1:d3b7bde3995c 1048 }
mluis 1:d3b7bde3995c 1049 LMIC.seqnoDn = seqno+1; // next number to be expected
mluis 1:d3b7bde3995c 1050 DO_DEVDB(LMIC.seqnoDn,seqnoDn);
mluis 1:d3b7bde3995c 1051 // DN frame requested confirmation - provide ACK once with next UP frame
mluis 1:d3b7bde3995c 1052 LMIC.dnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0);
mluis 0:62d1edcc13d1 1053 }
mluis 0:62d1edcc13d1 1054
mluis 0:62d1edcc13d1 1055 if( LMIC.dnConf || (fct & FCT_MORE) )
mluis 1:d3b7bde3995c 1056 LMIC.opmode |= OP_POLL;
mluis 0:62d1edcc13d1 1057
mluis 0:62d1edcc13d1 1058 // We heard from network
mluis 1:d3b7bde3995c 1059 LMIC.adrChanged = LMIC.rejoinCnt = 0;
mluis 1:d3b7bde3995c 1060 if( LMIC.adrAckReq != LINK_CHECK_OFF )
mluis 1:d3b7bde3995c 1061 LMIC.adrAckReq = LINK_CHECK_INIT;
mluis 0:62d1edcc13d1 1062
mluis 0:62d1edcc13d1 1063 // Process OPTS
mluis 1:d3b7bde3995c 1064 int m = LMIC.rssi - RSSI_OFF - getSensitivity(LMIC.rps);
mluis 0:62d1edcc13d1 1065 LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m;
mluis 0:62d1edcc13d1 1066
mluis 0:62d1edcc13d1 1067 xref2u1_t opts = &d[OFF_DAT_OPTS];
mluis 0:62d1edcc13d1 1068 int oidx = 0;
mluis 0:62d1edcc13d1 1069 while( oidx < olen ) {
mluis 1:d3b7bde3995c 1070 switch( opts[oidx] ) {
mluis 1:d3b7bde3995c 1071 case MCMD_LCHK_ANS: {
mluis 1:d3b7bde3995c 1072 //int gwmargin = opts[oidx+1];
mluis 1:d3b7bde3995c 1073 //int ngws = opts[oidx+2];
mluis 1:d3b7bde3995c 1074 oidx += 3;
mluis 1:d3b7bde3995c 1075 continue;
mluis 1:d3b7bde3995c 1076 }
mluis 1:d3b7bde3995c 1077 case MCMD_LADR_REQ: {
mluis 1:d3b7bde3995c 1078 u1_t p1 = opts[oidx+1]; // txpow + DR
mluis 1:d3b7bde3995c 1079 u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels
mluis 1:d3b7bde3995c 1080 u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK; // channel page
mluis 1:d3b7bde3995c 1081 u1_t uprpt = opts[oidx+4] & MCMD_LADR_REPEAT_MASK; // up repeat count
mluis 1:d3b7bde3995c 1082 oidx += 5;
mluis 0:62d1edcc13d1 1083
mluis 1:d3b7bde3995c 1084 LMIC.ladrAns = 0x80 | // Include an answer into next frame up
mluis 1:d3b7bde3995c 1085 MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK;
mluis 1:d3b7bde3995c 1086 if( !mapChannels(chpage, chmap) )
mluis 1:d3b7bde3995c 1087 LMIC.ladrAns &= ~MCMD_LADR_ANS_CHACK;
mluis 1:d3b7bde3995c 1088 dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT);
mluis 1:d3b7bde3995c 1089 if( !validDR(dr) ) {
mluis 1:d3b7bde3995c 1090 LMIC.ladrAns &= ~MCMD_LADR_ANS_DRACK;
mluis 1:d3b7bde3995c 1091 EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
mluis 1:d3b7bde3995c 1092 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1093 e_.info = Base::lsbf4(&d[pend]),
mluis 1:d3b7bde3995c 1094 e_.info2 = Base::msbf4(&opts[oidx-4])));
mluis 1:d3b7bde3995c 1095 }
mluis 1:d3b7bde3995c 1096 if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) {
mluis 1:d3b7bde3995c 1097 // Nothing went wrong - use settings
mluis 1:d3b7bde3995c 1098 LMIC.upRepeat = uprpt;
mluis 1:d3b7bde3995c 1099 setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1));
mluis 1:d3b7bde3995c 1100 }
mluis 1:d3b7bde3995c 1101 LMIC.adrChanged = 1; // Trigger an ACK to NWK
mluis 1:d3b7bde3995c 1102 continue;
mluis 0:62d1edcc13d1 1103 }
mluis 1:d3b7bde3995c 1104 case MCMD_DEVS_REQ: {
mluis 1:d3b7bde3995c 1105 LMIC.devsAns = 1;
mluis 1:d3b7bde3995c 1106 oidx += 1;
mluis 1:d3b7bde3995c 1107 continue;
mluis 1:d3b7bde3995c 1108 }
mluis 1:d3b7bde3995c 1109 case MCMD_DN2P_SET: {
mluis 1:d3b7bde3995c 1110 dr_t dr = (dr_t)(opts[oidx+1] & 0x0F);
mluis 1:d3b7bde3995c 1111 u4_t freq = convFreq(&opts[oidx+2]);
mluis 1:d3b7bde3995c 1112 oidx += 5;
mluis 1:d3b7bde3995c 1113 LMIC.dn2Ans = 0x80; // answer pending
mluis 1:d3b7bde3995c 1114 if( validDR(dr) )
mluis 1:d3b7bde3995c 1115 LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK;
mluis 1:d3b7bde3995c 1116 if( freq != 0 )
mluis 1:d3b7bde3995c 1117 LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK;
mluis 1:d3b7bde3995c 1118 if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK) ) {
mluis 1:d3b7bde3995c 1119 LMIC.dn2Dr = dr;
mluis 1:d3b7bde3995c 1120 LMIC.dn2Freq = freq;
mluis 1:d3b7bde3995c 1121 DO_DEVDB(LMIC.dn2Dr,dn2Dr);
mluis 1:d3b7bde3995c 1122 DO_DEVDB(LMIC.dn2Freq,dn2Freq);
mluis 1:d3b7bde3995c 1123 }
mluis 1:d3b7bde3995c 1124 continue;
mluis 0:62d1edcc13d1 1125 }
mluis 1:d3b7bde3995c 1126 case MCMD_DCAP_REQ: {
mluis 1:d3b7bde3995c 1127 u1_t cap = opts[oidx+1];
mluis 1:d3b7bde3995c 1128 oidx += 2;
mluis 1:d3b7bde3995c 1129 // A value cap=0xFF means device is OFF unless enabled again manually.
mluis 1:d3b7bde3995c 1130 if( cap==0xFF )
mluis 1:d3b7bde3995c 1131 LMIC.opmode |= OP_SHUTDOWN; // stop any sending
mluis 1:d3b7bde3995c 1132 LMIC.globalDutyRate = cap & 0xF;
mluis 1:d3b7bde3995c 1133 LMIC.globalDutyAvail = os_getTime();
mluis 1:d3b7bde3995c 1134 DO_DEVDB(cap,dutyCap);
mluis 1:d3b7bde3995c 1135 LMIC.dutyCapAns = 1;
mluis 1:d3b7bde3995c 1136 continue;
mluis 1:d3b7bde3995c 1137 }
mluis 1:d3b7bde3995c 1138 case MCMD_SNCH_REQ: {
mluis 1:d3b7bde3995c 1139 u1_t chidx = opts[oidx+1]; // channel
mluis 1:d3b7bde3995c 1140 u4_t freq = convFreq(&opts[oidx+2]); // freq
mluis 1:d3b7bde3995c 1141 u1_t drs = opts[oidx+5]; // datarate span
mluis 1:d3b7bde3995c 1142 LMIC.snchAns = 0x80;
mluis 1:d3b7bde3995c 1143 if( freq != 0 && LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(drs&0xF,drs>>4), -1) )
mluis 1:d3b7bde3995c 1144 LMIC.snchAns |= MCMD_SNCH_ANS_DRACK|MCMD_SNCH_ANS_FQACK;
mluis 1:d3b7bde3995c 1145 oidx += 6;
mluis 1:d3b7bde3995c 1146 continue;
mluis 0:62d1edcc13d1 1147 }
mluis 1:d3b7bde3995c 1148 case MCMD_PING_SET: {
mluis 1:d3b7bde3995c 1149 u4_t freq = convFreq(&opts[oidx+1]);
mluis 1:d3b7bde3995c 1150 oidx += 4;
mluis 1:d3b7bde3995c 1151 u1_t flags = 0x80;
mluis 1:d3b7bde3995c 1152 if( freq != 0 ) {
mluis 1:d3b7bde3995c 1153 flags |= MCMD_PING_ANS_FQACK;
mluis 1:d3b7bde3995c 1154 LMIC.ping.freq = freq;
mluis 1:d3b7bde3995c 1155 DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
mluis 1:d3b7bde3995c 1156 DO_DEVDB(LMIC.ping.freq, pingFreq);
mluis 1:d3b7bde3995c 1157 DO_DEVDB(LMIC.ping.dr, pingDr);
mluis 1:d3b7bde3995c 1158 }
mluis 1:d3b7bde3995c 1159 LMIC.pingSetAns = flags;
mluis 1:d3b7bde3995c 1160 continue;
mluis 1:d3b7bde3995c 1161 }
mluis 1:d3b7bde3995c 1162 case MCMD_BCNI_ANS: {
mluis 1:d3b7bde3995c 1163 // Ignore if tracking already enabled
mluis 1:d3b7bde3995c 1164 if( (LMIC.opmode & OP_TRACK) == 0 ) {
mluis 1:d3b7bde3995c 1165 LMIC.bcnChnl = opts[oidx+3];
mluis 1:d3b7bde3995c 1166 // Enable tracking - bcninfoTries
mluis 1:d3b7bde3995c 1167 LMIC.opmode |= OP_TRACK;
mluis 1:d3b7bde3995c 1168 // Cleared later in txComplete handling - triggers EV_BEACON_FOUND
mluis 1:d3b7bde3995c 1169 ASSERT(LMIC.bcninfoTries!=0);
mluis 1:d3b7bde3995c 1170 // Setup RX parameters
mluis 1:d3b7bde3995c 1171 LMIC.bcninfo.txtime = (LMIC.rxtime
mluis 1:d3b7bde3995c 1172 + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BCNI_TUNIT)
mluis 1:d3b7bde3995c 1173 + ms2osticksCeil(MCMD_BCNI_TUNIT/2)
mluis 1:d3b7bde3995c 1174 - BCN_INTV_osticks);
mluis 1:d3b7bde3995c 1175 LMIC.bcninfo.flags = 0; // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared)
mluis 1:d3b7bde3995c 1176 calcBcnRxWindowFromMillis(MCMD_BCNI_TUNIT,1); // error of +/-N ms
mluis 0:62d1edcc13d1 1177
mluis 1:d3b7bde3995c 1178 EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BCNI_ANS,
mluis 1:d3b7bde3995c 1179 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1180 e_.lostmic = Base::lsbf4(&d[pend]),
mluis 1:d3b7bde3995c 1181 e_.info = (LMIC.missedBcns |
mluis 1:d3b7bde3995c 1182 (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks
mluis 1:d3b7bde3995c 1183 - LMIC.bcnRxtime) << 8)),
mluis 1:d3b7bde3995c 1184 e_.time = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks)));
mluis 1:d3b7bde3995c 1185 }
mluis 1:d3b7bde3995c 1186 oidx += 4;
mluis 1:d3b7bde3995c 1187 continue;
mluis 0:62d1edcc13d1 1188 }
mluis 1:d3b7bde3995c 1189 }
mluis 1:d3b7bde3995c 1190 EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
mluis 1:d3b7bde3995c 1191 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1192 e_.info = Base::lsbf4(&d[pend]),
mluis 1:d3b7bde3995c 1193 e_.info2 = Base::msbf4(&opts[oidx])));
mluis 1:d3b7bde3995c 1194 break;
mluis 0:62d1edcc13d1 1195 }
mluis 0:62d1edcc13d1 1196 if( oidx != olen ) {
mluis 1:d3b7bde3995c 1197 EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
mluis 1:d3b7bde3995c 1198 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1199 e_.info = 0x1000000 + (oidx) + (olen<<8)));
mluis 0:62d1edcc13d1 1200 }
mluis 0:62d1edcc13d1 1201
mluis 0:62d1edcc13d1 1202 if( !replayConf ) {
mluis 1:d3b7bde3995c 1203 // Handle payload only if not a replay
mluis 1:d3b7bde3995c 1204 // Decrypt payload - if any
mluis 1:d3b7bde3995c 1205 if( port >= 0 && pend-poff > 0 )
mluis 1:d3b7bde3995c 1206 aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff);
mluis 0:62d1edcc13d1 1207
mluis 1:d3b7bde3995c 1208 EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1209 e_.devaddr = LMIC.devaddr,
mluis 1:d3b7bde3995c 1210 e_.seqno = seqno,
mluis 1:d3b7bde3995c 1211 e_.flags = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN,
mluis 1:d3b7bde3995c 1212 e_.mic = Base::lsbf4(&d[pend]),
mluis 1:d3b7bde3995c 1213 e_.hdr = d[LORA::OFF_DAT_HDR],
mluis 1:d3b7bde3995c 1214 e_.fct = d[LORA::OFF_DAT_FCT],
mluis 1:d3b7bde3995c 1215 e_.port = port,
mluis 1:d3b7bde3995c 1216 e_.plen = dlen,
mluis 1:d3b7bde3995c 1217 e_.opts.length = olen,
mluis 1:d3b7bde3995c 1218 memcpy(&e_.opts[0], opts, olen)));
mluis 0:62d1edcc13d1 1219 } else {
mluis 1:d3b7bde3995c 1220 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY,
mluis 1:d3b7bde3995c 1221 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1222 e_.info = Base::lsbf4(&d[pend]),
mluis 1:d3b7bde3995c 1223 e_.info2 = seqno));
mluis 0:62d1edcc13d1 1224 }
mluis 0:62d1edcc13d1 1225
mluis 0:62d1edcc13d1 1226 if( // NWK acks but we don't have a frame pending
mluis 1:d3b7bde3995c 1227 (ackup && LMIC.txCnt == 0) ||
mluis 1:d3b7bde3995c 1228 // We sent up confirmed and we got a response in DNW1/DNW2
mluis 1:d3b7bde3995c 1229 // BUT it did not carry an ACK - this should never happen
mluis 1:d3b7bde3995c 1230 // Do not resend and assume frame was not ACKed.
mluis 1:d3b7bde3995c 1231 (!ackup && LMIC.txCnt != 0) ) {
mluis 1:d3b7bde3995c 1232 EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK,
mluis 1:d3b7bde3995c 1233 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1234 e_.info = seqno,
mluis 1:d3b7bde3995c 1235 e_.info2 = ackup));
mluis 0:62d1edcc13d1 1236 }
mluis 0:62d1edcc13d1 1237
mluis 0:62d1edcc13d1 1238 if( LMIC.txCnt != 0 ) // we requested an ACK
mluis 1:d3b7bde3995c 1239 LMIC.txrxFlags |= ackup ? TXRX_ACK : TXRX_NACK;
mluis 0:62d1edcc13d1 1240
mluis 0:62d1edcc13d1 1241 if( port < 0 ) {
mluis 1:d3b7bde3995c 1242 LMIC.txrxFlags |= TXRX_NOPORT;
mluis 1:d3b7bde3995c 1243 LMIC.dataBeg = poff;
mluis 1:d3b7bde3995c 1244 LMIC.dataLen = 0;
mluis 0:62d1edcc13d1 1245 } else {
mluis 1:d3b7bde3995c 1246 LMIC.txrxFlags |= TXRX_PORT;
mluis 1:d3b7bde3995c 1247 LMIC.dataBeg = poff;
mluis 1:d3b7bde3995c 1248 LMIC.dataLen = pend-poff;
mluis 0:62d1edcc13d1 1249 }
mluis 0:62d1edcc13d1 1250 return 1;
mluis 0:62d1edcc13d1 1251 }
mluis 0:62d1edcc13d1 1252
mluis 0:62d1edcc13d1 1253
mluis 0:62d1edcc13d1 1254 // ================================================================================
mluis 0:62d1edcc13d1 1255 // TX/RX transaction support
mluis 0:62d1edcc13d1 1256
mluis 0:62d1edcc13d1 1257
mluis 0:62d1edcc13d1 1258 static void setupRx2 (void) {
mluis 0:62d1edcc13d1 1259 LMIC.txrxFlags = TXRX_DNW2;
mluis 0:62d1edcc13d1 1260 LMIC.rps = dndr2rps(LMIC.dn2Dr);
mluis 0:62d1edcc13d1 1261 LMIC.freq = LMIC.dn2Freq;
mluis 0:62d1edcc13d1 1262 LMIC.dataLen = 0;
mluis 0:62d1edcc13d1 1263 os_radio(RADIO_RX);
mluis 0:62d1edcc13d1 1264 }
mluis 0:62d1edcc13d1 1265
mluis 0:62d1edcc13d1 1266
mluis 0:62d1edcc13d1 1267 static void schedRx2 (ostime_t delay, osjobcb_t func) {
mluis 0:62d1edcc13d1 1268 // Add 1.5 symbols we need 5 out of 8. Try to sync 1.5 symbols into the preamble.
mluis 0:62d1edcc13d1 1269 LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(LMIC.dn2Dr);
mluis 0:62d1edcc13d1 1270 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
mluis 0:62d1edcc13d1 1271 }
mluis 0:62d1edcc13d1 1272
mluis 0:62d1edcc13d1 1273 static void setupRx1 (osjobcb_t func) {
mluis 0:62d1edcc13d1 1274 LMIC.txrxFlags = TXRX_DNW1;
mluis 0:62d1edcc13d1 1275 // Turn LMIC.rps from TX over to RX
mluis 0:62d1edcc13d1 1276 LMIC.rps = setNocrc(LMIC.rps,1);
mluis 0:62d1edcc13d1 1277 LMIC.dataLen = 0;
mluis 0:62d1edcc13d1 1278 LMIC.osjob.func = func;
mluis 0:62d1edcc13d1 1279 os_radio(RADIO_RX);
mluis 0:62d1edcc13d1 1280 }
mluis 0:62d1edcc13d1 1281
mluis 0:62d1edcc13d1 1282
mluis 0:62d1edcc13d1 1283 // Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime
mluis 0:62d1edcc13d1 1284 static void txDone (ostime_t delay, osjobcb_t func) {
mluis 0:62d1edcc13d1 1285 if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) {
mluis 1:d3b7bde3995c 1286 rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer!
mluis 1:d3b7bde3995c 1287 LMIC.opmode |= OP_PINGINI;
mluis 0:62d1edcc13d1 1288 }
mluis 0:62d1edcc13d1 1289 // Change RX frequency / rps (US only) before we increment txChnl
mluis 0:62d1edcc13d1 1290 setRx1Params();
mluis 0:62d1edcc13d1 1291 // LMIC.rxsyms carries the TX datarate (can be != LMIC.datarate [confirm retries etc.])
mluis 0:62d1edcc13d1 1292 // Setup receive - LMIC.rxtime is preloaded with 1.5 symbols offset to tune
mluis 0:62d1edcc13d1 1293 // into the middle of the 8 symbols preamble.
mluis 1:d3b7bde3995c 1294 #if defined(CFG_eu868)
mluis 0:62d1edcc13d1 1295 if( /* TX datarate */LMIC.rxsyms == DR_FSK ) {
mluis 1:d3b7bde3995c 1296 LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160);
mluis 1:d3b7bde3995c 1297 LMIC.rxsyms = RXLEN_FSK;
mluis 0:62d1edcc13d1 1298 }
mluis 0:62d1edcc13d1 1299 else
mluis 0:62d1edcc13d1 1300 #endif
mluis 0:62d1edcc13d1 1301 {
mluis 1:d3b7bde3995c 1302 LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(LMIC.dndr);
mluis 1:d3b7bde3995c 1303 LMIC.rxsyms = MINRX_SYMS;
mluis 0:62d1edcc13d1 1304 }
mluis 0:62d1edcc13d1 1305 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
mluis 0:62d1edcc13d1 1306 }
mluis 0:62d1edcc13d1 1307
mluis 0:62d1edcc13d1 1308
mluis 0:62d1edcc13d1 1309 // ======================================== Join frames
mluis 0:62d1edcc13d1 1310
mluis 0:62d1edcc13d1 1311
mluis 0:62d1edcc13d1 1312 static void onJoinFailed (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1313 // Notify app - must call LMIC_reset() to stop joining
mluis 0:62d1edcc13d1 1314 // otherwise join procedure continues.
mluis 0:62d1edcc13d1 1315 reportEvent(EV_JOIN_FAILED);
mluis 0:62d1edcc13d1 1316 }
mluis 0:62d1edcc13d1 1317
mluis 0:62d1edcc13d1 1318
mluis 0:62d1edcc13d1 1319 static bit_t processJoinAccept (void) {
mluis 0:62d1edcc13d1 1320 ASSERT(LMIC.txrxFlags != TXRX_DNW1 || LMIC.dataLen != 0);
mluis 0:62d1edcc13d1 1321 ASSERT((LMIC.opmode & OP_TXRXPEND)!=0);
mluis 0:62d1edcc13d1 1322
mluis 0:62d1edcc13d1 1323 if( LMIC.dataLen == 0 ) {
mluis 0:62d1edcc13d1 1324 nojoinframe:
mluis 1:d3b7bde3995c 1325 if( (LMIC.opmode & OP_JOINING) == 0 ) {
mluis 1:d3b7bde3995c 1326 ASSERT((LMIC.opmode & OP_REJOIN) != 0);
mluis 1:d3b7bde3995c 1327 // REJOIN attempt for roaming
mluis 1:d3b7bde3995c 1328 LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND);
mluis 1:d3b7bde3995c 1329 if( LMIC.rejoinCnt < 10 )
mluis 1:d3b7bde3995c 1330 LMIC.rejoinCnt++;
mluis 1:d3b7bde3995c 1331 reportEvent(EV_REJOIN_FAILED);
mluis 1:d3b7bde3995c 1332 return 1;
mluis 1:d3b7bde3995c 1333 }
mluis 1:d3b7bde3995c 1334 LMIC.opmode &= ~OP_TXRXPEND;
mluis 1:d3b7bde3995c 1335 ostime_t delay = nextJoinState();
mluis 1:d3b7bde3995c 1336 EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC,
mluis 1:d3b7bde3995c 1337 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1338 e_.info = LMIC.datarate|DR_PAGE,
mluis 1:d3b7bde3995c 1339 e_.info2 = osticks2ms(delay)));
mluis 1:d3b7bde3995c 1340 // Build next JOIN REQUEST with next engineUpdate call
mluis 1:d3b7bde3995c 1341 // Optionally, report join failed.
mluis 1:d3b7bde3995c 1342 // Both after a random/chosen amount of ticks.
mluis 1:d3b7bde3995c 1343 os_setTimedCallback(&LMIC.osjob, os_getTime()+delay,
mluis 1:d3b7bde3995c 1344 (delay&1) != 0
mluis 1:d3b7bde3995c 1345 ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed
mluis 1:d3b7bde3995c 1346 : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
mluis 0:62d1edcc13d1 1347 return 1;
mluis 0:62d1edcc13d1 1348 }
mluis 0:62d1edcc13d1 1349 u1_t hdr = LMIC.frame[0];
mluis 0:62d1edcc13d1 1350 u1_t dlen = LMIC.dataLen;
mluis 0:62d1edcc13d1 1351 u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt!
mluis 0:62d1edcc13d1 1352 if( (dlen != LEN_JA && dlen != LEN_JAEXT)
mluis 1:d3b7bde3995c 1353 || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
mluis 1:d3b7bde3995c 1354 EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
mluis 1:d3b7bde3995c 1355 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1356 e_.info = dlen < 4 ? 0 : mic,
mluis 1:d3b7bde3995c 1357 e_.info2 = hdr + (dlen<<8)));
mluis 0:62d1edcc13d1 1358 badframe:
mluis 1:d3b7bde3995c 1359 if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
mluis 1:d3b7bde3995c 1360 return 0;
mluis 1:d3b7bde3995c 1361 goto nojoinframe;
mluis 0:62d1edcc13d1 1362 }
mluis 0:62d1edcc13d1 1363 aes_encrypt(LMIC.frame+1, dlen-1);
mluis 0:62d1edcc13d1 1364 if( !aes_verifyMic0(LMIC.frame, dlen-4) ) {
mluis 1:d3b7bde3995c 1365 EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC,
mluis 1:d3b7bde3995c 1366 e_.info = mic));
mluis 1:d3b7bde3995c 1367 goto badframe;
mluis 0:62d1edcc13d1 1368 }
mluis 0:62d1edcc13d1 1369
mluis 0:62d1edcc13d1 1370 u4_t addr = os_rlsbf4(LMIC.frame+OFF_JA_DEVADDR);
mluis 0:62d1edcc13d1 1371 LMIC.devaddr = addr;
mluis 1:d3b7bde3995c 1372 LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_JA_NETID]) & 0xFFFFFF;
mluis 0:62d1edcc13d1 1373
mluis 1:d3b7bde3995c 1374 #if defined(CFG_eu868)
mluis 0:62d1edcc13d1 1375 initDefaultChannels(0);
mluis 0:62d1edcc13d1 1376 #endif
mluis 0:62d1edcc13d1 1377 if( dlen > LEN_JA ) {
mluis 1:d3b7bde3995c 1378 dlen = OFF_CFLIST;
mluis 1:d3b7bde3995c 1379 #if defined(CFG_eu868)
mluis 1:d3b7bde3995c 1380 u1_t chidx=3;
mluis 1:d3b7bde3995c 1381 #elif defined(CFG_us915)
mluis 1:d3b7bde3995c 1382 u1_t chidx=72;
mluis 0:62d1edcc13d1 1383 #endif
mluis 1:d3b7bde3995c 1384 for( ; chidx<8; chidx++, dlen+=3 )
mluis 1:d3b7bde3995c 1385 LMIC_setupChannel(chidx, os_rlsbf4(&LMIC.frame[dlen-1]) >> 8, 0, -1);
mluis 0:62d1edcc13d1 1386 }
mluis 0:62d1edcc13d1 1387
mluis 0:62d1edcc13d1 1388 // already incremented when JOIN REQ got sent off
mluis 0:62d1edcc13d1 1389 aes_sessKeys(LMIC.devNonce-1, &LMIC.frame[OFF_JA_ARTNONCE], LMIC.nwkKey, LMIC.artKey);
mluis 1:d3b7bde3995c 1390 DO_DEVDB(LMIC.netid, netid);
mluis 1:d3b7bde3995c 1391 DO_DEVDB(LMIC.devaddr, devaddr);
mluis 1:d3b7bde3995c 1392 DO_DEVDB(LMIC.nwkKey, nwkkey);
mluis 1:d3b7bde3995c 1393 DO_DEVDB(LMIC.artKey, artkey);
mluis 0:62d1edcc13d1 1394
mluis 0:62d1edcc13d1 1395 EV(joininfo, INFO, (e_.arteui = MAIN::CDEV->getArtEui(),
mluis 1:d3b7bde3995c 1396 e_.deveui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1397 e_.devaddr = LMIC.devaddr,
mluis 1:d3b7bde3995c 1398 e_.oldaddr = oldaddr,
mluis 1:d3b7bde3995c 1399 e_.nonce = LMIC.devNonce-1,
mluis 1:d3b7bde3995c 1400 e_.mic = mic,
mluis 1:d3b7bde3995c 1401 e_.reason = ((LMIC.opmode & OP_REJOIN) != 0
mluis 1:d3b7bde3995c 1402 ? EV::joininfo_t::REJOIN_ACCEPT
mluis 1:d3b7bde3995c 1403 : EV::joininfo_t::ACCEPT)));
mluis 0:62d1edcc13d1 1404
mluis 0:62d1edcc13d1 1405 ASSERT((LMIC.opmode & (OP_JOINING|OP_REJOIN))!=0);
mluis 0:62d1edcc13d1 1406 if( (LMIC.opmode & OP_REJOIN) != 0 ) {
mluis 1:d3b7bde3995c 1407 // Lower DR every try below current UP DR
mluis 1:d3b7bde3995c 1408 LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt);
mluis 0:62d1edcc13d1 1409 }
mluis 0:62d1edcc13d1 1410 LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI) | OP_NEXTCHNL;
mluis 0:62d1edcc13d1 1411 stateJustJoined();
mluis 0:62d1edcc13d1 1412 reportEvent(EV_JOINED);
mluis 0:62d1edcc13d1 1413 return 1;
mluis 0:62d1edcc13d1 1414 }
mluis 0:62d1edcc13d1 1415
mluis 1:d3b7bde3995c 1416
mluis 1:d3b7bde3995c 1417 static void processRx2Jacc (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1418 if( LMIC.dataLen == 0 )
mluis 1:d3b7bde3995c 1419 LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot
mluis 0:62d1edcc13d1 1420 processJoinAccept();
mluis 0:62d1edcc13d1 1421 }
mluis 0:62d1edcc13d1 1422
mluis 1:d3b7bde3995c 1423
mluis 1:d3b7bde3995c 1424 static void setupRx2Jacc (xref2osjob_t osjob) {
mluis 1:d3b7bde3995c 1425 LMIC.osjob.func = FUNC_ADDR(processRx2Jacc);
mluis 1:d3b7bde3995c 1426 setupRx2();
mluis 1:d3b7bde3995c 1427 }
mluis 1:d3b7bde3995c 1428
mluis 1:d3b7bde3995c 1429
mluis 1:d3b7bde3995c 1430 static void processRx1Jacc (xref2osjob_t osjob) {
mluis 1:d3b7bde3995c 1431 if( LMIC.dataLen == 0 || !processJoinAccept() )
mluis 1:d3b7bde3995c 1432 schedRx2(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc));
mluis 1:d3b7bde3995c 1433 }
mluis 1:d3b7bde3995c 1434
mluis 1:d3b7bde3995c 1435
mluis 0:62d1edcc13d1 1436 static void setupRx1Jacc (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1437 setupRx1(FUNC_ADDR(processRx1Jacc));
mluis 0:62d1edcc13d1 1438 }
mluis 0:62d1edcc13d1 1439
mluis 0:62d1edcc13d1 1440
mluis 0:62d1edcc13d1 1441 static void jreqDone (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1442 txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc));
mluis 0:62d1edcc13d1 1443 }
mluis 0:62d1edcc13d1 1444
mluis 0:62d1edcc13d1 1445 // ======================================== Data frames
mluis 0:62d1edcc13d1 1446
mluis 0:62d1edcc13d1 1447 // Fwd decl.
mluis 0:62d1edcc13d1 1448 static bit_t processDnData(void);
mluis 0:62d1edcc13d1 1449
mluis 1:d3b7bde3995c 1450 static void processRx2DnDataDelay (xref2osjob_t osjob) {
mluis 1:d3b7bde3995c 1451 processDnData();
mluis 1:d3b7bde3995c 1452 }
mluis 1:d3b7bde3995c 1453
mluis 0:62d1edcc13d1 1454 static void processRx2DnData (xref2osjob_t osjob) {
mluis 1:d3b7bde3995c 1455 if( LMIC.dataLen == 0 ) {
mluis 1:d3b7bde3995c 1456 LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot
mluis 1:d3b7bde3995c 1457 // Delay callback processing to avoid up TX while gateway is txing our missed frame!
mluis 1:d3b7bde3995c 1458 // Since DNW2 uses SF12 by default we wait 3 secs.
mluis 1:d3b7bde3995c 1459 os_setTimedCallback(&LMIC.osjob,
mluis 1:d3b7bde3995c 1460 (os_getTime() + DNW2_SAFETY_ZONE + rndDelay(2)),
mluis 1:d3b7bde3995c 1461 processRx2DnDataDelay);
mluis 1:d3b7bde3995c 1462 return;
mluis 1:d3b7bde3995c 1463 }
mluis 0:62d1edcc13d1 1464 processDnData();
mluis 0:62d1edcc13d1 1465 }
mluis 0:62d1edcc13d1 1466
mluis 0:62d1edcc13d1 1467
mluis 0:62d1edcc13d1 1468 static void setupRx2DnData (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1469 LMIC.osjob.func = FUNC_ADDR(processRx2DnData);
mluis 0:62d1edcc13d1 1470 setupRx2();
mluis 0:62d1edcc13d1 1471 }
mluis 0:62d1edcc13d1 1472
mluis 0:62d1edcc13d1 1473
mluis 0:62d1edcc13d1 1474 static void processRx1DnData (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1475 if( LMIC.dataLen == 0 || !processDnData() )
mluis 1:d3b7bde3995c 1476 schedRx2(DELAY_DNW2_osticks, FUNC_ADDR(setupRx2DnData));
mluis 0:62d1edcc13d1 1477 }
mluis 0:62d1edcc13d1 1478
mluis 0:62d1edcc13d1 1479
mluis 0:62d1edcc13d1 1480 static void setupRx1DnData (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1481 setupRx1(FUNC_ADDR(processRx1DnData));
mluis 0:62d1edcc13d1 1482 }
mluis 0:62d1edcc13d1 1483
mluis 0:62d1edcc13d1 1484
mluis 0:62d1edcc13d1 1485 static void updataDone (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1486 txDone(DELAY_DNW1_osticks, FUNC_ADDR(setupRx1DnData));
mluis 0:62d1edcc13d1 1487 }
mluis 0:62d1edcc13d1 1488
mluis 0:62d1edcc13d1 1489 // ========================================
mluis 0:62d1edcc13d1 1490
mluis 0:62d1edcc13d1 1491
mluis 0:62d1edcc13d1 1492 static void buildDataFrame (void) {
mluis 0:62d1edcc13d1 1493 bit_t txdata = ((LMIC.opmode & (OP_TXDATA|OP_POLL)) != OP_POLL);
mluis 0:62d1edcc13d1 1494 u1_t dlen = txdata ? LMIC.pendTxLen : 0;
mluis 0:62d1edcc13d1 1495
mluis 0:62d1edcc13d1 1496 // Piggyback MAC options
mluis 0:62d1edcc13d1 1497 // Prioritize by importance
mluis 0:62d1edcc13d1 1498 int end = OFF_DAT_OPTS;
mluis 0:62d1edcc13d1 1499 if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE)) == (OP_TRACK|OP_PINGABLE) ) {
mluis 1:d3b7bde3995c 1500 // Indicate pingability in every UP frame
mluis 1:d3b7bde3995c 1501 LMIC.frame[end] = MCMD_PING_IND;
mluis 1:d3b7bde3995c 1502 LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4);
mluis 1:d3b7bde3995c 1503 end += 2;
mluis 0:62d1edcc13d1 1504 }
mluis 0:62d1edcc13d1 1505 if( LMIC.dutyCapAns ) {
mluis 1:d3b7bde3995c 1506 LMIC.frame[end] = MCMD_DCAP_ANS;
mluis 1:d3b7bde3995c 1507 end += 1;
mluis 1:d3b7bde3995c 1508 LMIC.dutyCapAns = 0;
mluis 0:62d1edcc13d1 1509 }
mluis 0:62d1edcc13d1 1510 if( LMIC.dn2Ans ) {
mluis 1:d3b7bde3995c 1511 LMIC.frame[end+0] = MCMD_DN2P_ANS;
mluis 1:d3b7bde3995c 1512 LMIC.frame[end+1] = LMIC.dn2Ans & ~MCMD_DN2P_ANS_RFU;
mluis 1:d3b7bde3995c 1513 end += 2;
mluis 1:d3b7bde3995c 1514 LMIC.dn2Ans = 0;
mluis 0:62d1edcc13d1 1515 }
mluis 0:62d1edcc13d1 1516 if( LMIC.devsAns ) { // answer to device status
mluis 1:d3b7bde3995c 1517 LMIC.frame[end+0] = MCMD_DEVS_ANS;
mluis 1:d3b7bde3995c 1518 LMIC.frame[end+1] = LMIC.margin;
mluis 1:d3b7bde3995c 1519 LMIC.frame[end+2] = os_getBattLevel();
mluis 1:d3b7bde3995c 1520 end += 3;
mluis 1:d3b7bde3995c 1521 LMIC.devsAns = 0;
mluis 0:62d1edcc13d1 1522 }
mluis 0:62d1edcc13d1 1523 if( LMIC.ladrAns ) { // answer to ADR change
mluis 1:d3b7bde3995c 1524 LMIC.frame[end+0] = MCMD_LADR_ANS;
mluis 1:d3b7bde3995c 1525 LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU;
mluis 1:d3b7bde3995c 1526 end += 2;
mluis 1:d3b7bde3995c 1527 LMIC.ladrAns = 0;
mluis 0:62d1edcc13d1 1528 }
mluis 0:62d1edcc13d1 1529 if( LMIC.bcninfoTries > 0 ) {
mluis 1:d3b7bde3995c 1530 LMIC.frame[end] = MCMD_BCNI_REQ;
mluis 1:d3b7bde3995c 1531 end += 1;
mluis 0:62d1edcc13d1 1532 }
mluis 0:62d1edcc13d1 1533 if( LMIC.adrChanged ) {
mluis 1:d3b7bde3995c 1534 if( LMIC.adrAckReq < 0 )
mluis 1:d3b7bde3995c 1535 LMIC.adrAckReq = 0;
mluis 1:d3b7bde3995c 1536 LMIC.adrChanged = 0;
mluis 0:62d1edcc13d1 1537 }
mluis 0:62d1edcc13d1 1538 if( LMIC.pingSetAns != 0 ) {
mluis 1:d3b7bde3995c 1539 LMIC.frame[end+0] = MCMD_PING_ANS;
mluis 1:d3b7bde3995c 1540 LMIC.frame[end+1] = LMIC.pingSetAns & ~MCMD_PING_ANS_RFU;
mluis 1:d3b7bde3995c 1541 end += 2;
mluis 1:d3b7bde3995c 1542 LMIC.pingSetAns = 0;
mluis 0:62d1edcc13d1 1543 }
mluis 0:62d1edcc13d1 1544 if( LMIC.snchAns ) {
mluis 1:d3b7bde3995c 1545 LMIC.frame[end+0] = MCMD_SNCH_ANS;
mluis 1:d3b7bde3995c 1546 LMIC.frame[end+1] = LMIC.snchAns & ~MCMD_SNCH_ANS_RFU;
mluis 1:d3b7bde3995c 1547 end += 2;
mluis 1:d3b7bde3995c 1548 LMIC.snchAns = 0;
mluis 0:62d1edcc13d1 1549 }
mluis 0:62d1edcc13d1 1550 ASSERT(end <= OFF_DAT_OPTS+16);
mluis 0:62d1edcc13d1 1551
mluis 0:62d1edcc13d1 1552 u1_t flen = end + (txdata ? 5+dlen : 4);
mluis 0:62d1edcc13d1 1553 if( flen > MAX_LEN_FRAME ) {
mluis 1:d3b7bde3995c 1554 // Options and payload too big - delay payload
mluis 1:d3b7bde3995c 1555 txdata = 0;
mluis 1:d3b7bde3995c 1556 flen = end+4;
mluis 0:62d1edcc13d1 1557 }
mluis 0:62d1edcc13d1 1558 LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DAUP | HDR_MAJOR_V1;
mluis 0:62d1edcc13d1 1559 LMIC.frame[OFF_DAT_FCT] = (LMIC.dnConf | LMIC.adrEnabled
mluis 1:d3b7bde3995c 1560 | (LMIC.adrAckReq >= 0 ? FCT_ADRARQ : 0)
mluis 1:d3b7bde3995c 1561 | (end-OFF_DAT_OPTS));
mluis 0:62d1edcc13d1 1562 os_wlsbf4(LMIC.frame+OFF_DAT_ADDR, LMIC.devaddr);
mluis 0:62d1edcc13d1 1563
mluis 0:62d1edcc13d1 1564 if( LMIC.txCnt == 0 ) {
mluis 1:d3b7bde3995c 1565 LMIC.seqnoUp += 1;
mluis 1:d3b7bde3995c 1566 DO_DEVDB(LMIC.seqnoUp,seqnoUp);
mluis 0:62d1edcc13d1 1567 } else {
mluis 1:d3b7bde3995c 1568 EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX,
mluis 1:d3b7bde3995c 1569 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1570 e_.info = LMIC.seqnoUp-1,
mluis 1:d3b7bde3995c 1571 e_.info2 = ((LMIC.txCnt+1) |
mluis 1:d3b7bde3995c 1572 (DRADJUST[LMIC.txCnt+1] << 8) |
mluis 1:d3b7bde3995c 1573 ((LMIC.datarate|DR_PAGE)<<16))));
mluis 0:62d1edcc13d1 1574 }
mluis 0:62d1edcc13d1 1575 os_wlsbf2(LMIC.frame+OFF_DAT_SEQNO, LMIC.seqnoUp-1);
mluis 0:62d1edcc13d1 1576
mluis 0:62d1edcc13d1 1577 // Clear pending DN confirmation
mluis 0:62d1edcc13d1 1578 LMIC.dnConf = 0;
mluis 0:62d1edcc13d1 1579
mluis 0:62d1edcc13d1 1580 if( txdata ) {
mluis 1:d3b7bde3995c 1581 if( LMIC.pendTxConf ) {
mluis 1:d3b7bde3995c 1582 // Confirmed only makes sense if we have a payload (or at least a port)
mluis 1:d3b7bde3995c 1583 LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DCUP | HDR_MAJOR_V1;
mluis 1:d3b7bde3995c 1584 if( LMIC.txCnt == 0 ) LMIC.txCnt = 1;
mluis 1:d3b7bde3995c 1585 }
mluis 1:d3b7bde3995c 1586 LMIC.frame[end] = LMIC.pendTxPort;
mluis 1:d3b7bde3995c 1587 os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen);
mluis 1:d3b7bde3995c 1588 aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey,
mluis 1:d3b7bde3995c 1589 LMIC.devaddr, LMIC.seqnoUp-1,
mluis 1:d3b7bde3995c 1590 /*up*/0, LMIC.frame+end+1, dlen);
mluis 0:62d1edcc13d1 1591 }
mluis 0:62d1edcc13d1 1592 aes_appendMic(LMIC.nwkKey, LMIC.devaddr, LMIC.seqnoUp-1, /*up*/0, LMIC.frame, flen-4);
mluis 0:62d1edcc13d1 1593
mluis 0:62d1edcc13d1 1594 EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1595 e_.devaddr = LMIC.devaddr,
mluis 1:d3b7bde3995c 1596 e_.seqno = LMIC.seqnoUp-1,
mluis 1:d3b7bde3995c 1597 e_.flags = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP),
mluis 1:d3b7bde3995c 1598 e_.mic = Base::lsbf4(&LMIC.frame[flen-4]),
mluis 1:d3b7bde3995c 1599 e_.hdr = LMIC.frame[LORA::OFF_DAT_HDR],
mluis 1:d3b7bde3995c 1600 e_.fct = LMIC.frame[LORA::OFF_DAT_FCT],
mluis 1:d3b7bde3995c 1601 e_.port = LMIC.pendTxPort,
mluis 1:d3b7bde3995c 1602 e_.plen = txdata ? dlen : 0,
mluis 1:d3b7bde3995c 1603 e_.opts.length = end-LORA::OFF_DAT_OPTS,
mluis 1:d3b7bde3995c 1604 memcpy(&e_.opts[0], LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS)));
mluis 0:62d1edcc13d1 1605 LMIC.dataLen = flen;
mluis 0:62d1edcc13d1 1606 }
mluis 0:62d1edcc13d1 1607
mluis 0:62d1edcc13d1 1608
mluis 0:62d1edcc13d1 1609 // Callback from HAL during scan mode or when job timer expires.
mluis 0:62d1edcc13d1 1610 static void onBcnRx (xref2osjob_t job) {
mluis 0:62d1edcc13d1 1611 // If we arrive via job timer make sure to put radio to rest.
mluis 0:62d1edcc13d1 1612 os_radio(RADIO_RST);
mluis 0:62d1edcc13d1 1613 os_clearCallback(&LMIC.osjob);
mluis 0:62d1edcc13d1 1614 if( LMIC.dataLen == 0 ) {
mluis 1:d3b7bde3995c 1615 // Nothing received - timeout
mluis 1:d3b7bde3995c 1616 LMIC.opmode &= ~(OP_SCAN | OP_TRACK);
mluis 1:d3b7bde3995c 1617 reportEvent(EV_SCAN_TIMEOUT);
mluis 1:d3b7bde3995c 1618 return;
mluis 0:62d1edcc13d1 1619 }
mluis 0:62d1edcc13d1 1620 if( decodeBeacon() <= 0 ) {
mluis 1:d3b7bde3995c 1621 // Something is wrong with the beacon - continue scan
mluis 1:d3b7bde3995c 1622 LMIC.dataLen = 0;
mluis 1:d3b7bde3995c 1623 os_radio(RADIO_RXON);
mluis 1:d3b7bde3995c 1624 os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx));
mluis 1:d3b7bde3995c 1625 return;
mluis 0:62d1edcc13d1 1626 }
mluis 0:62d1edcc13d1 1627 // Found our 1st beacon
mluis 0:62d1edcc13d1 1628 // We don't have a previous beacon to calc some drift - assume
mluis 0:62d1edcc13d1 1629 // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
mluis 0:62d1edcc13d1 1630 calcBcnRxWindowFromMillis(13,1);
mluis 0:62d1edcc13d1 1631 LMIC.opmode &= ~OP_SCAN; // turn SCAN off
mluis 0:62d1edcc13d1 1632 LMIC.opmode |= OP_TRACK; // auto enable tracking
mluis 0:62d1edcc13d1 1633 reportEvent(EV_BEACON_FOUND); // can be disabled in callback
mluis 0:62d1edcc13d1 1634 }
mluis 0:62d1edcc13d1 1635
mluis 0:62d1edcc13d1 1636
mluis 0:62d1edcc13d1 1637 // Enable receiver to listen to incoming beacons
mluis 0:62d1edcc13d1 1638 // netid defines when scan stops (any or specific beacon)
mluis 0:62d1edcc13d1 1639 // This mode ends with events: EV_SCAN_TIMEOUT/EV_SCAN_BEACON
mluis 0:62d1edcc13d1 1640 // Implicitely cancels any pending TX/RX transaction.
mluis 0:62d1edcc13d1 1641 // Also cancels an onpoing joining procedure.
mluis 0:62d1edcc13d1 1642 static void startScan (void) {
mluis 0:62d1edcc13d1 1643 ASSERT(LMIC.devaddr!=0 && (LMIC.opmode & OP_JOINING)==0);
mluis 0:62d1edcc13d1 1644 if( (LMIC.opmode & OP_SHUTDOWN) != 0 )
mluis 1:d3b7bde3995c 1645 return;
mluis 0:62d1edcc13d1 1646 // Cancel onging TX/RX transaction
mluis 0:62d1edcc13d1 1647 LMIC.txCnt = LMIC.dnConf = LMIC.bcninfo.flags = 0;
mluis 0:62d1edcc13d1 1648 LMIC.opmode = (LMIC.opmode | OP_SCAN) & ~(OP_TXRXPEND);
mluis 0:62d1edcc13d1 1649 setBcnRxParams();
mluis 0:62d1edcc13d1 1650 LMIC.rxtime = LMIC.bcninfo.txtime = os_getTime() + sec2osticks(BCN_INTV_sec+1);
mluis 0:62d1edcc13d1 1651 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime, FUNC_ADDR(onBcnRx));
mluis 0:62d1edcc13d1 1652 os_radio(RADIO_RXON);
mluis 0:62d1edcc13d1 1653 }
mluis 0:62d1edcc13d1 1654
mluis 0:62d1edcc13d1 1655
mluis 0:62d1edcc13d1 1656 bit_t LMIC_enableTracking (u1_t tryBcnInfo) {
mluis 0:62d1edcc13d1 1657 if( (LMIC.opmode & (OP_SCAN|OP_TRACK|OP_SHUTDOWN)) != 0 )
mluis 1:d3b7bde3995c 1658 return 0; // already in progress or failed to enable
mluis 0:62d1edcc13d1 1659 // If BCN info requested from NWK then app has to take are
mluis 0:62d1edcc13d1 1660 // of sending data up so that MCMD_BCNI_REQ can be attached.
mluis 0:62d1edcc13d1 1661 if( (LMIC.bcninfoTries = tryBcnInfo) == 0 )
mluis 1:d3b7bde3995c 1662 startScan();
mluis 0:62d1edcc13d1 1663 return 1; // enabled
mluis 0:62d1edcc13d1 1664 }
mluis 0:62d1edcc13d1 1665
mluis 0:62d1edcc13d1 1666
mluis 0:62d1edcc13d1 1667 void LMIC_disableTracking (void) {
mluis 0:62d1edcc13d1 1668 LMIC.opmode &= ~(OP_SCAN|OP_TRACK);
mluis 0:62d1edcc13d1 1669 LMIC.bcninfoTries = 0;
mluis 0:62d1edcc13d1 1670 engineUpdate();
mluis 0:62d1edcc13d1 1671 }
mluis 0:62d1edcc13d1 1672
mluis 0:62d1edcc13d1 1673
mluis 0:62d1edcc13d1 1674 // ================================================================================
mluis 0:62d1edcc13d1 1675 //
mluis 0:62d1edcc13d1 1676 // Join stuff
mluis 0:62d1edcc13d1 1677 //
mluis 0:62d1edcc13d1 1678 // ================================================================================
mluis 0:62d1edcc13d1 1679
mluis 0:62d1edcc13d1 1680 static void buildJoinRequest (u1_t ftype) {
mluis 0:62d1edcc13d1 1681 // Do not use pendTxData since we might have a pending
mluis 0:62d1edcc13d1 1682 // user level frame in there. Use RX holding area instead.
mluis 0:62d1edcc13d1 1683 xref2u1_t d = LMIC.frame;
mluis 0:62d1edcc13d1 1684 d[OFF_JR_HDR] = ftype;
mluis 0:62d1edcc13d1 1685 os_getArtEui(d + OFF_JR_ARTEUI);
mluis 0:62d1edcc13d1 1686 os_getDevEui(d + OFF_JR_DEVEUI);
mluis 0:62d1edcc13d1 1687 os_wlsbf2(d + OFF_JR_DEVNONCE, LMIC.devNonce);
mluis 0:62d1edcc13d1 1688 aes_appendMic0(d, OFF_JR_MIC);
mluis 0:62d1edcc13d1 1689
mluis 0:62d1edcc13d1 1690 EV(joininfo,INFO,(e_.deveui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1691 e_.arteui = MAIN::CDEV->getArtEui(),
mluis 1:d3b7bde3995c 1692 e_.nonce = LMIC.devNonce,
mluis 1:d3b7bde3995c 1693 e_.oldaddr = LMIC.devaddr,
mluis 1:d3b7bde3995c 1694 e_.mic = Base::lsbf4(&d[LORA::OFF_JR_MIC]),
mluis 1:d3b7bde3995c 1695 e_.reason = ((LMIC.opmode & OP_REJOIN) != 0
mluis 1:d3b7bde3995c 1696 ? EV::joininfo_t::REJOIN_REQUEST
mluis 1:d3b7bde3995c 1697 : EV::joininfo_t::REQUEST)));
mluis 0:62d1edcc13d1 1698 LMIC.dataLen = LEN_JR;
mluis 0:62d1edcc13d1 1699 LMIC.devNonce++;
mluis 1:d3b7bde3995c 1700 DO_DEVDB(LMIC.devNonce,devNonce);
mluis 0:62d1edcc13d1 1701 }
mluis 0:62d1edcc13d1 1702
mluis 0:62d1edcc13d1 1703 static void startJoining (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1704 reportEvent(EV_JOINING);
mluis 0:62d1edcc13d1 1705 }
mluis 0:62d1edcc13d1 1706
mluis 0:62d1edcc13d1 1707 // Start join procedure if not already joined.
mluis 0:62d1edcc13d1 1708 bit_t LMIC_startJoining (void) {
mluis 0:62d1edcc13d1 1709 if( LMIC.devaddr == 0 ) {
mluis 1:d3b7bde3995c 1710 // There should be no TX/RX going on
mluis 1:d3b7bde3995c 1711 ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0);
mluis 1:d3b7bde3995c 1712 // Lift any previous duty limitation
mluis 1:d3b7bde3995c 1713 LMIC.globalDutyRate = 0;
mluis 1:d3b7bde3995c 1714 // Cancel scanning
mluis 1:d3b7bde3995c 1715 LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL);
mluis 1:d3b7bde3995c 1716 // Setup state
mluis 1:d3b7bde3995c 1717 LMIC.rejoinCnt = LMIC.txCnt = LMIC.pendTxConf = 0;
mluis 1:d3b7bde3995c 1718 initJoinLoop();
mluis 1:d3b7bde3995c 1719 LMIC.opmode |= OP_JOINING;
mluis 1:d3b7bde3995c 1720 // reportEvent will call engineUpdate which then starts sending JOIN REQUESTS
mluis 1:d3b7bde3995c 1721 os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining));
mluis 1:d3b7bde3995c 1722 return 1;
mluis 0:62d1edcc13d1 1723 }
mluis 1:d3b7bde3995c 1724 return 0; // already joined
mluis 0:62d1edcc13d1 1725 }
mluis 0:62d1edcc13d1 1726
mluis 0:62d1edcc13d1 1727
mluis 0:62d1edcc13d1 1728 // ================================================================================
mluis 0:62d1edcc13d1 1729 //
mluis 0:62d1edcc13d1 1730 //
mluis 0:62d1edcc13d1 1731 //
mluis 0:62d1edcc13d1 1732 // ================================================================================
mluis 0:62d1edcc13d1 1733
mluis 0:62d1edcc13d1 1734 static void processPingRx (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1735 if( LMIC.dataLen != 0 ) {
mluis 1:d3b7bde3995c 1736 LMIC.txrxFlags = TXRX_PING;
mluis 1:d3b7bde3995c 1737 if( decodeFrame() ) {
mluis 1:d3b7bde3995c 1738 reportEvent(EV_RXCOMPLETE);
mluis 1:d3b7bde3995c 1739 return;
mluis 1:d3b7bde3995c 1740 }
mluis 0:62d1edcc13d1 1741 }
mluis 0:62d1edcc13d1 1742 // Pick next ping slot
mluis 0:62d1edcc13d1 1743 engineUpdate();
mluis 0:62d1edcc13d1 1744 }
mluis 0:62d1edcc13d1 1745
mluis 0:62d1edcc13d1 1746
mluis 0:62d1edcc13d1 1747 static bit_t processDnData (void) {
mluis 0:62d1edcc13d1 1748 ASSERT((LMIC.opmode & OP_TXRXPEND)!=0);
mluis 0:62d1edcc13d1 1749
mluis 0:62d1edcc13d1 1750 if( LMIC.dataLen == 0 ) {
mluis 0:62d1edcc13d1 1751 norx:
mluis 1:d3b7bde3995c 1752 if( LMIC.txCnt != 0 ) {
mluis 1:d3b7bde3995c 1753 if( LMIC.txCnt < TXCONF_ATTEMPTS ) {
mluis 1:d3b7bde3995c 1754 LMIC.txCnt += 1;
mluis 1:d3b7bde3995c 1755 setDrTxpow(DRCHG_NOACK, lowerDR(LMIC.datarate, DRADJUST[LMIC.txCnt]), KEEP_TXPOW);
mluis 1:d3b7bde3995c 1756 // Schedule another retransmission
mluis 1:d3b7bde3995c 1757 txDelay(LMIC.rxtime, RETRY_PERIOD_secs);
mluis 1:d3b7bde3995c 1758 LMIC.opmode &= ~OP_TXRXPEND;
mluis 1:d3b7bde3995c 1759 engineUpdate();
mluis 1:d3b7bde3995c 1760 return 1;
mluis 1:d3b7bde3995c 1761 }
mluis 1:d3b7bde3995c 1762 LMIC.txrxFlags = TXRX_NACK | TXRX_NOPORT;
mluis 1:d3b7bde3995c 1763 } else {
mluis 1:d3b7bde3995c 1764 // Nothing received - implies no port
mluis 1:d3b7bde3995c 1765 LMIC.txrxFlags = TXRX_NOPORT;
mluis 0:62d1edcc13d1 1766 }
mluis 1:d3b7bde3995c 1767 if( LMIC.adrAckReq != LINK_CHECK_OFF )
mluis 1:d3b7bde3995c 1768 LMIC.adrAckReq += 1;
mluis 1:d3b7bde3995c 1769 LMIC.dataBeg = LMIC.dataLen = 0;
mluis 0:62d1edcc13d1 1770 txcomplete:
mluis 1:d3b7bde3995c 1771 LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
mluis 1:d3b7bde3995c 1772 if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) {
mluis 1:d3b7bde3995c 1773 LMIC.opmode &= ~OP_LINKDEAD;
mluis 1:d3b7bde3995c 1774 reportEvent(EV_LINK_ALIVE);
mluis 1:d3b7bde3995c 1775 }
mluis 1:d3b7bde3995c 1776 reportEvent(EV_TXCOMPLETE);
mluis 1:d3b7bde3995c 1777 // If we haven't heard from NWK in a while although we asked for a sign
mluis 1:d3b7bde3995c 1778 // assume link is dead - notify application and keep going
mluis 1:d3b7bde3995c 1779 if( LMIC.adrAckReq > LINK_CHECK_DEAD ) {
mluis 1:d3b7bde3995c 1780 // We haven't heard from NWK for some time although we
mluis 1:d3b7bde3995c 1781 // asked for a response for some time - assume we're disconnected. Lower DR one notch.
mluis 1:d3b7bde3995c 1782 EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD,
mluis 1:d3b7bde3995c 1783 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1784 e_.info = LMIC.adrAckReq));
mluis 1:d3b7bde3995c 1785 setDrTxpow(DRCHG_NOADRACK, decDR((dr_t)LMIC.datarate), KEEP_TXPOW);
mluis 1:d3b7bde3995c 1786 LMIC.adrAckReq = LINK_CHECK_CONT;
mluis 0:62d1edcc13d1 1787 LMIC.opmode |= OP_REJOIN|OP_LINKDEAD;
mluis 1:d3b7bde3995c 1788 reportEvent(EV_LINK_DEAD);
mluis 0:62d1edcc13d1 1789 }
mluis 1:d3b7bde3995c 1790 // If this falls to zero the NWK did not answer our MCMD_BCNI_REQ commands - try full scan
mluis 1:d3b7bde3995c 1791 if( LMIC.bcninfoTries > 0 ) {
mluis 1:d3b7bde3995c 1792 if( (LMIC.opmode & OP_TRACK) != 0 ) {
mluis 1:d3b7bde3995c 1793 reportEvent(EV_BEACON_FOUND);
mluis 1:d3b7bde3995c 1794 LMIC.bcninfoTries = 0;
mluis 1:d3b7bde3995c 1795 }
mluis 1:d3b7bde3995c 1796 else if( --LMIC.bcninfoTries == 0 ) {
mluis 1:d3b7bde3995c 1797 startScan(); // NWK did not answer - try scan
mluis 1:d3b7bde3995c 1798 }
mluis 0:62d1edcc13d1 1799 }
mluis 1:d3b7bde3995c 1800 return 1;
mluis 0:62d1edcc13d1 1801 }
mluis 0:62d1edcc13d1 1802 if( !decodeFrame() ) {
mluis 1:d3b7bde3995c 1803 if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
mluis 1:d3b7bde3995c 1804 return 0;
mluis 1:d3b7bde3995c 1805 goto norx;
mluis 0:62d1edcc13d1 1806 }
mluis 0:62d1edcc13d1 1807 goto txcomplete;
mluis 0:62d1edcc13d1 1808 }
mluis 0:62d1edcc13d1 1809
mluis 0:62d1edcc13d1 1810
mluis 0:62d1edcc13d1 1811 static void processBeacon (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1812 ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite
mluis 0:62d1edcc13d1 1813 u1_t flags = LMIC.bcninfo.flags;
mluis 0:62d1edcc13d1 1814 ev_t ev;
mluis 0:62d1edcc13d1 1815
mluis 0:62d1edcc13d1 1816 if( LMIC.dataLen != 0 && decodeBeacon() >= 1 ) {
mluis 1:d3b7bde3995c 1817 ev = EV_BEACON_TRACKED;
mluis 1:d3b7bde3995c 1818 if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) {
mluis 1:d3b7bde3995c 1819 // We don't have a previous beacon to calc some drift - assume
mluis 1:d3b7bde3995c 1820 // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
mluis 1:d3b7bde3995c 1821 calcBcnRxWindowFromMillis(13,0);
mluis 1:d3b7bde3995c 1822 goto rev;
mluis 1:d3b7bde3995c 1823 }
mluis 1:d3b7bde3995c 1824 // We have a previous BEACON to calculate some drift
mluis 1:d3b7bde3995c 1825 s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx);
mluis 1:d3b7bde3995c 1826 if( LMIC.missedBcns > 0 ) {
mluis 1:d3b7bde3995c 1827 drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1);
mluis 1:d3b7bde3995c 1828 }
mluis 1:d3b7bde3995c 1829 if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) {
mluis 1:d3b7bde3995c 1830 s2_t diff = LMIC.drift - drift;
mluis 1:d3b7bde3995c 1831 if( diff < 0 ) diff = -diff;
mluis 1:d3b7bde3995c 1832 LMIC.lastDriftDiff = diff;
mluis 1:d3b7bde3995c 1833 if( LMIC.maxDriftDiff < diff )
mluis 1:d3b7bde3995c 1834 LMIC.maxDriftDiff = diff;
mluis 1:d3b7bde3995c 1835 LMIC.bcninfo.flags &= ~BCN_NODDIFF;
mluis 1:d3b7bde3995c 1836 }
mluis 1:d3b7bde3995c 1837 LMIC.drift = drift;
mluis 1:d3b7bde3995c 1838 LMIC.missedBcns = LMIC.rejoinCnt = 0;
mluis 1:d3b7bde3995c 1839 LMIC.bcninfo.flags &= ~BCN_NODRIFT;
mluis 1:d3b7bde3995c 1840 EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT,
mluis 1:d3b7bde3995c 1841 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1842 e_.info = drift,
mluis 1:d3b7bde3995c 1843 e_.info2 = /*occasion BEACON*/0));
mluis 1:d3b7bde3995c 1844 ASSERT((LMIC.bcninfo.flags & (BCN_PARTIAL|BCN_FULL)) != 0);
mluis 0:62d1edcc13d1 1845 } else {
mluis 1:d3b7bde3995c 1846 ev = EV_BEACON_MISSED;
mluis 1:d3b7bde3995c 1847 LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift;
mluis 1:d3b7bde3995c 1848 LMIC.bcninfo.time += BCN_INTV_sec;
mluis 1:d3b7bde3995c 1849 LMIC.missedBcns++;
mluis 1:d3b7bde3995c 1850 // Delay any possible TX after surmised beacon - it's there although we missed it
mluis 1:d3b7bde3995c 1851 txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4);
mluis 1:d3b7bde3995c 1852 if( LMIC.missedBcns > MAX_MISSED_BCNS )
mluis 1:d3b7bde3995c 1853 LMIC.opmode |= OP_REJOIN; // try if we can roam to another network
mluis 1:d3b7bde3995c 1854 if( LMIC.bcnRxsyms > MAX_RXSYMS ) {
mluis 1:d3b7bde3995c 1855 LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN);
mluis 1:d3b7bde3995c 1856 reportEvent(EV_LOST_TSYNC);
mluis 1:d3b7bde3995c 1857 return;
mluis 1:d3b7bde3995c 1858 }
mluis 0:62d1edcc13d1 1859 }
mluis 0:62d1edcc13d1 1860 LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - calcRxWindow(0,DR_BCN);
mluis 0:62d1edcc13d1 1861 LMIC.bcnRxsyms = LMIC.rxsyms;
mluis 0:62d1edcc13d1 1862 rev:
mluis 1:d3b7bde3995c 1863 #if CFG_us915
mluis 1:d3b7bde3995c 1864 LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7;
mluis 1:d3b7bde3995c 1865 #endif
mluis 0:62d1edcc13d1 1866 if( (LMIC.opmode & OP_PINGINI) != 0 )
mluis 1:d3b7bde3995c 1867 rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer!
mluis 0:62d1edcc13d1 1868 reportEvent(ev);
mluis 0:62d1edcc13d1 1869 }
mluis 0:62d1edcc13d1 1870
mluis 0:62d1edcc13d1 1871
mluis 0:62d1edcc13d1 1872 static void startRxBcn (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1873 LMIC.osjob.func = FUNC_ADDR(processBeacon);
mluis 0:62d1edcc13d1 1874 os_radio(RADIO_RX);
mluis 0:62d1edcc13d1 1875 }
mluis 0:62d1edcc13d1 1876
mluis 0:62d1edcc13d1 1877
mluis 0:62d1edcc13d1 1878 static void startRxPing (xref2osjob_t osjob) {
mluis 0:62d1edcc13d1 1879 LMIC.osjob.func = FUNC_ADDR(processPingRx);
mluis 0:62d1edcc13d1 1880 os_radio(RADIO_RX);
mluis 0:62d1edcc13d1 1881 }
mluis 0:62d1edcc13d1 1882
mluis 0:62d1edcc13d1 1883
mluis 0:62d1edcc13d1 1884 // Decide what to do next for the MAC layer of a device
mluis 0:62d1edcc13d1 1885 static void engineUpdate (void) {
mluis 0:62d1edcc13d1 1886 // Check for ongoing state: scan or TX/RX transaction
mluis 0:62d1edcc13d1 1887 if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 )
mluis 1:d3b7bde3995c 1888 return;
mluis 0:62d1edcc13d1 1889
mluis 0:62d1edcc13d1 1890 if( LMIC.devaddr == 0 && (LMIC.opmode & OP_JOINING) == 0 ) {
mluis 1:d3b7bde3995c 1891 LMIC_startJoining();
mluis 1:d3b7bde3995c 1892 return;
mluis 0:62d1edcc13d1 1893 }
mluis 0:62d1edcc13d1 1894
mluis 0:62d1edcc13d1 1895 ostime_t now = os_getTime();
mluis 0:62d1edcc13d1 1896 ostime_t rxtime = 0;
mluis 0:62d1edcc13d1 1897 ostime_t txbeg = 0;
mluis 0:62d1edcc13d1 1898
mluis 0:62d1edcc13d1 1899 if( (LMIC.opmode & OP_TRACK) != 0 ) {
mluis 1:d3b7bde3995c 1900 // We are tracking a beacon
mluis 1:d3b7bde3995c 1901 ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
mluis 1:d3b7bde3995c 1902 rxtime = LMIC.bcnRxtime - RX_RAMPUP;
mluis 0:62d1edcc13d1 1903 }
mluis 0:62d1edcc13d1 1904
mluis 0:62d1edcc13d1 1905 if( (LMIC.opmode & (OP_JOINING|OP_REJOIN|OP_TXDATA|OP_POLL)) != 0 ) {
mluis 1:d3b7bde3995c 1906 // Need to TX some data...
mluis 1:d3b7bde3995c 1907 // Assuming txChnl points to channel which first becomes available again.
mluis 1:d3b7bde3995c 1908 bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0);
mluis 1:d3b7bde3995c 1909 // Find next suitable channel and return availability time
mluis 1:d3b7bde3995c 1910 if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) {
mluis 1:d3b7bde3995c 1911 txbeg = LMIC.txend = nextTx(now);
mluis 1:d3b7bde3995c 1912 LMIC.opmode &= ~OP_NEXTCHNL;
mluis 0:62d1edcc13d1 1913 } else {
mluis 1:d3b7bde3995c 1914 txbeg = LMIC.txend;
mluis 1:d3b7bde3995c 1915 }
mluis 1:d3b7bde3995c 1916 // Delayed TX or waiting for duty cycle?
mluis 1:d3b7bde3995c 1917 if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 )
mluis 1:d3b7bde3995c 1918 txbeg = LMIC.globalDutyAvail;
mluis 1:d3b7bde3995c 1919 // If we're tracking a beacon...
mluis 1:d3b7bde3995c 1920 // then make sure TX-RX transaction is complete before beacon
mluis 1:d3b7bde3995c 1921 if( (LMIC.opmode & OP_TRACK) != 0 &&
mluis 1:d3b7bde3995c 1922 txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) {
mluis 1:d3b7bde3995c 1923 // Not enough time to complete TX-RX before beacon - postpone after beacon.
mluis 1:d3b7bde3995c 1924 // In order to avoid clustering of postponed TX right after beacon randomize start!
mluis 1:d3b7bde3995c 1925 txDelay(rxtime + BCN_RESERVE_osticks, 16);
mluis 1:d3b7bde3995c 1926 txbeg = 0;
mluis 1:d3b7bde3995c 1927 goto checkrx;
mluis 0:62d1edcc13d1 1928 }
mluis 1:d3b7bde3995c 1929 // Earliest possible time vs overhead to setup radio
mluis 1:d3b7bde3995c 1930 if( txbeg - (now + TX_RAMPUP) < 0 ) {
mluis 1:d3b7bde3995c 1931 // We could send right now!
mluis 1:d3b7bde3995c 1932 dr_t txdr = (dr_t)LMIC.datarate;
mluis 1:d3b7bde3995c 1933 if( jacc ) {
mluis 1:d3b7bde3995c 1934 u1_t ftype;
mluis 1:d3b7bde3995c 1935 if( (LMIC.opmode & OP_REJOIN) != 0 ) {
mluis 1:d3b7bde3995c 1936 txdr = lowerDR(txdr, LMIC.rejoinCnt);
mluis 1:d3b7bde3995c 1937 ftype = HDR_FTYPE_REJOIN;
mluis 1:d3b7bde3995c 1938 } else {
mluis 1:d3b7bde3995c 1939 ftype = HDR_FTYPE_JREQ;
mluis 1:d3b7bde3995c 1940 }
mluis 1:d3b7bde3995c 1941 buildJoinRequest(ftype);
mluis 1:d3b7bde3995c 1942 LMIC.osjob.func = FUNC_ADDR(jreqDone);
mluis 1:d3b7bde3995c 1943 } else {
mluis 1:d3b7bde3995c 1944 if( LMIC.seqnoDn >= 0xFFFFFF80 ) {
mluis 1:d3b7bde3995c 1945 // Imminent roll over - proactively reset MAC
mluis 1:d3b7bde3995c 1946 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
mluis 1:d3b7bde3995c 1947 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1948 e_.info = LMIC.seqnoDn,
mluis 1:d3b7bde3995c 1949 e_.info2 = 0));
mluis 1:d3b7bde3995c 1950 // Device has to react! NWK will not roll over and just stop sending.
mluis 1:d3b7bde3995c 1951 // Thus, we have N frames to detect a possible lock up.
mluis 1:d3b7bde3995c 1952 reset:
mluis 1:d3b7bde3995c 1953 os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset));
mluis 1:d3b7bde3995c 1954 return;
mluis 1:d3b7bde3995c 1955 }
mluis 1:d3b7bde3995c 1956 if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) {
mluis 1:d3b7bde3995c 1957 // Roll over of up seq counter
mluis 1:d3b7bde3995c 1958 EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER,
mluis 1:d3b7bde3995c 1959 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 1960 e_.info2 = LMIC.seqnoUp));
mluis 1:d3b7bde3995c 1961 // Do not run RESET event callback from here!
mluis 1:d3b7bde3995c 1962 // App code might do some stuff after send unaware of RESET.
mluis 1:d3b7bde3995c 1963 goto reset;
mluis 1:d3b7bde3995c 1964 }
mluis 1:d3b7bde3995c 1965 buildDataFrame();
mluis 1:d3b7bde3995c 1966 LMIC.osjob.func = FUNC_ADDR(updataDone);
mluis 1:d3b7bde3995c 1967 }
mluis 1:d3b7bde3995c 1968 LMIC.rps = setCr(updr2rps(txdr), (cr_t)LMIC.errcr);
mluis 1:d3b7bde3995c 1969 LMIC.dndr = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1
mluis 1:d3b7bde3995c 1970 LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL;
mluis 1:d3b7bde3995c 1971 updateTx(txbeg);
mluis 1:d3b7bde3995c 1972 os_radio(RADIO_TX);
mluis 0:62d1edcc13d1 1973 return;
mluis 0:62d1edcc13d1 1974 }
mluis 1:d3b7bde3995c 1975 // Cannot yet TX
mluis 1:d3b7bde3995c 1976 if( (LMIC.opmode & OP_TRACK) == 0 )
mluis 1:d3b7bde3995c 1977 goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX
mluis 1:d3b7bde3995c 1978 // Consider RX tasks
mluis 1:d3b7bde3995c 1979 if( txbeg == 0 ) // zero indicates no TX pending
mluis 1:d3b7bde3995c 1980 txbeg += 1; // TX delayed by one tick (insignificant amount of time)
mluis 0:62d1edcc13d1 1981 } else {
mluis 1:d3b7bde3995c 1982 // No TX pending - no scheduled RX
mluis 1:d3b7bde3995c 1983 if( (LMIC.opmode & OP_TRACK) == 0 )
mluis 1:d3b7bde3995c 1984 return;
mluis 0:62d1edcc13d1 1985 }
mluis 0:62d1edcc13d1 1986
mluis 0:62d1edcc13d1 1987 // Are we pingable?
mluis 0:62d1edcc13d1 1988 checkrx:
mluis 0:62d1edcc13d1 1989 if( (LMIC.opmode & OP_PINGINI) != 0 ) {
mluis 1:d3b7bde3995c 1990 // One more RX slot in this beacon period?
mluis 1:d3b7bde3995c 1991 if( rxschedNext(&LMIC.ping, now+RX_RAMPUP) ) {
mluis 1:d3b7bde3995c 1992 if( txbeg != 0 && (txbeg - LMIC.ping.rxtime) < 0 )
mluis 1:d3b7bde3995c 1993 goto txdelay;
mluis 1:d3b7bde3995c 1994 LMIC.rxsyms = LMIC.ping.rxsyms;
mluis 1:d3b7bde3995c 1995 LMIC.rxtime = LMIC.ping.rxtime;
mluis 1:d3b7bde3995c 1996 LMIC.freq = LMIC.ping.freq;
mluis 1:d3b7bde3995c 1997 LMIC.rps = dndr2rps(LMIC.ping.dr);
mluis 1:d3b7bde3995c 1998 LMIC.dataLen = 0;
mluis 1:d3b7bde3995c 1999 ASSERT(LMIC.rxtime - now+RX_RAMPUP >= 0 );
mluis 1:d3b7bde3995c 2000 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, FUNC_ADDR(startRxPing));
mluis 1:d3b7bde3995c 2001 return;
mluis 1:d3b7bde3995c 2002 }
mluis 1:d3b7bde3995c 2003 // no - just wait for the beacon
mluis 0:62d1edcc13d1 2004 }
mluis 0:62d1edcc13d1 2005
mluis 0:62d1edcc13d1 2006 if( txbeg != 0 && (txbeg - rxtime) < 0 )
mluis 1:d3b7bde3995c 2007 goto txdelay;
mluis 0:62d1edcc13d1 2008
mluis 0:62d1edcc13d1 2009 setBcnRxParams();
mluis 0:62d1edcc13d1 2010 LMIC.rxsyms = LMIC.bcnRxsyms;
mluis 0:62d1edcc13d1 2011 LMIC.rxtime = LMIC.bcnRxtime;
mluis 0:62d1edcc13d1 2012 if( now - rxtime >= 0 ) {
mluis 1:d3b7bde3995c 2013 LMIC.osjob.func = FUNC_ADDR(processBeacon);
mluis 1:d3b7bde3995c 2014 os_radio(RADIO_RX);
mluis 1:d3b7bde3995c 2015 return;
mluis 0:62d1edcc13d1 2016 }
mluis 0:62d1edcc13d1 2017 os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn));
mluis 0:62d1edcc13d1 2018 return;
mluis 0:62d1edcc13d1 2019
mluis 0:62d1edcc13d1 2020 txdelay:
mluis 0:62d1edcc13d1 2021 EV(devCond, INFO, (e_.reason = EV::devCond_t::TX_DELAY,
mluis 1:d3b7bde3995c 2022 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 2023 e_.info = osticks2ms(txbeg-now),
mluis 1:d3b7bde3995c 2024 e_.info2 = LMIC.seqnoUp-1));
mluis 0:62d1edcc13d1 2025 os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate));
mluis 0:62d1edcc13d1 2026 }
mluis 0:62d1edcc13d1 2027
mluis 0:62d1edcc13d1 2028
mluis 0:62d1edcc13d1 2029 void LMIC_setAdrMode (bit_t enabled) {
mluis 0:62d1edcc13d1 2030 LMIC.adrEnabled = enabled ? FCT_ADREN : 0;
mluis 0:62d1edcc13d1 2031 }
mluis 0:62d1edcc13d1 2032
mluis 0:62d1edcc13d1 2033
mluis 0:62d1edcc13d1 2034 // Should we have/need an ext. API like this?
mluis 0:62d1edcc13d1 2035 void LMIC_setDrTxpow (dr_t dr, s1_t txpow) {
mluis 0:62d1edcc13d1 2036 setDrTxpow(DRCHG_SET, dr, txpow);
mluis 0:62d1edcc13d1 2037 }
mluis 0:62d1edcc13d1 2038
mluis 0:62d1edcc13d1 2039
mluis 0:62d1edcc13d1 2040 void LMIC_shutdown (void) {
mluis 0:62d1edcc13d1 2041 os_clearCallback(&LMIC.osjob);
mluis 0:62d1edcc13d1 2042 os_radio(RADIO_RST);
mluis 0:62d1edcc13d1 2043 LMIC.opmode |= OP_SHUTDOWN;
mluis 0:62d1edcc13d1 2044 }
mluis 0:62d1edcc13d1 2045
mluis 0:62d1edcc13d1 2046
mluis 0:62d1edcc13d1 2047 void LMIC_reset (void) {
mluis 0:62d1edcc13d1 2048 EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
mluis 1:d3b7bde3995c 2049 e_.eui = MAIN::CDEV->getEui(),
mluis 1:d3b7bde3995c 2050 e_.info = EV_RESET));
mluis 0:62d1edcc13d1 2051 os_radio(RADIO_RST);
mluis 0:62d1edcc13d1 2052 os_clearCallback(&LMIC.osjob);
mluis 0:62d1edcc13d1 2053
mluis 0:62d1edcc13d1 2054 os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC));
mluis 1:d3b7bde3995c 2055 LMIC.devaddr = 0;
mluis 1:d3b7bde3995c 2056 LMIC.devNonce = os_getRndU2();
mluis 1:d3b7bde3995c 2057 LMIC.opmode = OP_NONE;
mluis 1:d3b7bde3995c 2058 LMIC.errcr = CR_4_5;
mluis 1:d3b7bde3995c 2059 LMIC.adrEnabled = FCT_ADREN;
mluis 1:d3b7bde3995c 2060 LMIC.dn2Dr = DR_DNW2; // we need this for 2nd DN window of join accept
mluis 1:d3b7bde3995c 2061 LMIC.dn2Freq = FREQ_DNW2; // ditto
mluis 1:d3b7bde3995c 2062 LMIC.ping.freq = FREQ_PING; // defaults for ping
mluis 1:d3b7bde3995c 2063 LMIC.ping.dr = DR_PING; // ditto
mluis 1:d3b7bde3995c 2064 LMIC.ping.intvExp = 0xFF;
mluis 1:d3b7bde3995c 2065 #if defined(CFG_us915)
mluis 0:62d1edcc13d1 2066 initDefaultChannels();
mluis 0:62d1edcc13d1 2067 #endif
mluis 1:d3b7bde3995c 2068 DO_DEVDB(LMIC.devaddr, devaddr);
mluis 1:d3b7bde3995c 2069 DO_DEVDB(LMIC.devNonce, devNonce);
mluis 1:d3b7bde3995c 2070 DO_DEVDB(LMIC.dn2Dr, dn2Dr);
mluis 1:d3b7bde3995c 2071 DO_DEVDB(LMIC.dn2Freq, dn2Freq);
mluis 1:d3b7bde3995c 2072 DO_DEVDB(LMIC.ping.freq, pingFreq);
mluis 1:d3b7bde3995c 2073 DO_DEVDB(LMIC.ping.dr, pingDr);
mluis 1:d3b7bde3995c 2074 DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
mluis 0:62d1edcc13d1 2075 }
mluis 0:62d1edcc13d1 2076
mluis 0:62d1edcc13d1 2077
mluis 0:62d1edcc13d1 2078 void LMIC_init (void) {
mluis 0:62d1edcc13d1 2079 LMIC.opmode = OP_SHUTDOWN;
mluis 0:62d1edcc13d1 2080 }
mluis 0:62d1edcc13d1 2081
mluis 0:62d1edcc13d1 2082
mluis 0:62d1edcc13d1 2083 void LMIC_clrTxData (void) {
mluis 0:62d1edcc13d1 2084 LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND|OP_POLL);
mluis 0:62d1edcc13d1 2085 LMIC.pendTxLen = 0;
mluis 0:62d1edcc13d1 2086 if( (LMIC.opmode & (OP_JOINING|OP_SCAN)) != 0 ) // do not interfere with JOINING
mluis 1:d3b7bde3995c 2087 return;
mluis 0:62d1edcc13d1 2088 os_clearCallback(&LMIC.osjob);
mluis 0:62d1edcc13d1 2089 os_radio(RADIO_RST);
mluis 0:62d1edcc13d1 2090 engineUpdate();
mluis 0:62d1edcc13d1 2091 }
mluis 0:62d1edcc13d1 2092
mluis 0:62d1edcc13d1 2093
mluis 0:62d1edcc13d1 2094 void LMIC_setTxData (void) {
mluis 0:62d1edcc13d1 2095 LMIC.opmode |= OP_TXDATA;
mluis 0:62d1edcc13d1 2096 if( (LMIC.opmode & OP_JOINING) == 0 )
mluis 1:d3b7bde3995c 2097 LMIC.txCnt = 0; // cancel any ongoing TX/RX retries
mluis 0:62d1edcc13d1 2098 engineUpdate();
mluis 0:62d1edcc13d1 2099 }
mluis 0:62d1edcc13d1 2100
mluis 0:62d1edcc13d1 2101
mluis 0:62d1edcc13d1 2102 //
mluis 0:62d1edcc13d1 2103 int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) {
mluis 0:62d1edcc13d1 2104 if( dlen > SIZEOFEXPR(LMIC.pendTxData) )
mluis 1:d3b7bde3995c 2105 return -2;
mluis 0:62d1edcc13d1 2106 if( data != (xref2u1_t)0 )
mluis 1:d3b7bde3995c 2107 os_copyMem(LMIC.pendTxData, data, dlen);
mluis 0:62d1edcc13d1 2108 LMIC.pendTxConf = confirmed;
mluis 0:62d1edcc13d1 2109 LMIC.pendTxPort = port;
mluis 0:62d1edcc13d1 2110 LMIC.pendTxLen = dlen;
mluis 0:62d1edcc13d1 2111 LMIC_setTxData();
mluis 0:62d1edcc13d1 2112 return 0;
mluis 0:62d1edcc13d1 2113 }
mluis 0:62d1edcc13d1 2114
mluis 0:62d1edcc13d1 2115
mluis 0:62d1edcc13d1 2116 // Send a payload-less message to signal device is alive
mluis 0:62d1edcc13d1 2117 void LMIC_sendAlive (void) {
mluis 0:62d1edcc13d1 2118 LMIC.opmode |= OP_POLL;
mluis 0:62d1edcc13d1 2119 engineUpdate();
mluis 0:62d1edcc13d1 2120 }
mluis 0:62d1edcc13d1 2121
mluis 0:62d1edcc13d1 2122
mluis 0:62d1edcc13d1 2123 // Check if other networks are around.
mluis 0:62d1edcc13d1 2124 void LMIC_tryRejoin (void) {
mluis 0:62d1edcc13d1 2125 LMIC.opmode |= OP_REJOIN;
mluis 0:62d1edcc13d1 2126 engineUpdate();
mluis 0:62d1edcc13d1 2127 }
mluis 0:62d1edcc13d1 2128
mluis 1:d3b7bde3995c 2129 //! \brief Setup given session keys
mluis 1:d3b7bde3995c 2130 //! and put the MAC in a state as if
mluis 1:d3b7bde3995c 2131 //! a join request/accept would have negotiated just these keys.
mluis 1:d3b7bde3995c 2132 //! It is crucial that the combinations `devaddr/nwkkey` and `devaddr/artkey`
mluis 1:d3b7bde3995c 2133 //! are unique within the network identified by `netid`.
mluis 1:d3b7bde3995c 2134 //! NOTE: on Harvard architectures when session keys are in flash:
mluis 1:d3b7bde3995c 2135 //! Caller has to fill in LMIC.{nwk,art}Key before and pass {nwk,art}Key are NULL
mluis 1:d3b7bde3995c 2136 //! \param netid a 24 bit number describing the network id this device is using
mluis 1:d3b7bde3995c 2137 //! \param devaddr the 32 bit session address of the device. It is strongly recommended
mluis 1:d3b7bde3995c 2138 //! to ensure that different devices use different numbers with high probability.
mluis 1:d3b7bde3995c 2139 //! \param nwkKey the 16 byte network session key used for message integrity.
mluis 1:d3b7bde3995c 2140 //! If NULL the caller has copied the key into `LMIC.nwkKey` before.
mluis 1:d3b7bde3995c 2141 //! \param artKey the 16 byte application router session key used for message confidentiality.
mluis 1:d3b7bde3995c 2142 //! If NULL the caller has copied the key into `LMIC.artKey` before.
mluis 1:d3b7bde3995c 2143 void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey) {
mluis 0:62d1edcc13d1 2144 LMIC.netid = netid;
mluis 0:62d1edcc13d1 2145 LMIC.devaddr = devaddr;
mluis 1:d3b7bde3995c 2146 if( nwkKey != (xref2u1_t)0 )
mluis 1:d3b7bde3995c 2147 os_copyMem(LMIC.nwkKey, nwkKey, 16);
mluis 1:d3b7bde3995c 2148 if( artKey != (xref2u1_t)0 )
mluis 1:d3b7bde3995c 2149 os_copyMem(LMIC.artKey, artKey, 16);
mluis 0:62d1edcc13d1 2150
mluis 1:d3b7bde3995c 2151 #if defined(CFG_eu868)
mluis 0:62d1edcc13d1 2152 initDefaultChannels(0);
mluis 0:62d1edcc13d1 2153 #endif
mluis 1:d3b7bde3995c 2154
mluis 1:d3b7bde3995c 2155 LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI);
mluis 1:d3b7bde3995c 2156 LMIC.opmode |= OP_NEXTCHNL;
mluis 0:62d1edcc13d1 2157 stateJustJoined();
mluis 0:62d1edcc13d1 2158 }
mluis 1:d3b7bde3995c 2159
mluis 1:d3b7bde3995c 2160 // Enable/disable link check validation.
mluis 1:d3b7bde3995c 2161 // LMIC sets the ADRACKREQ bit in UP frames if there were no DN frames
mluis 1:d3b7bde3995c 2162 // for a while. It expects the network to provide a DN message to prove
mluis 1:d3b7bde3995c 2163 // connectivity with a span of UP frames. If this no such prove is coming
mluis 1:d3b7bde3995c 2164 // then the datarate is lowered and a LINK_DEAD event is generated.
mluis 1:d3b7bde3995c 2165 // This mode can be disabled and no connectivity prove (ADRACKREQ) is requested
mluis 1:d3b7bde3995c 2166 // nor is the datarate changed.
mluis 1:d3b7bde3995c 2167 // This must be called only if a session is established (e.g. after EV_JOINED)
mluis 1:d3b7bde3995c 2168 void LMIC_setLinkCheckMode (bit_t enabled) {
mluis 1:d3b7bde3995c 2169 LMIC.adrChanged = 0;
mluis 1:d3b7bde3995c 2170 LMIC.adrAckReq = enabled ? LINK_CHECK_INIT : LINK_CHECK_OFF;
mluis 1:d3b7bde3995c 2171 }
mluis 1:d3b7bde3995c 2172
mluis 1:d3b7bde3995c 2173