LMIC lib Lora OTA device for Everynet

Fork of LMiC-10secs by Alcatel-Lucent IoT Development

Committer:
pnysten
Date:
Fri Sep 23 09:15:43 2016 +0000
Revision:
10:8f33a411b53f
Parent:
9:c5820ce68bd6
LMIC lib Lora Personalized device for Everynet

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