LMIC for MOTE_L152RC

Dependents:   lmic_transmit

LoRa WAN in C for NA-mote 72

Currently version 1.5


LoRaWAN network configuration for end-device

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

AppEUI

Uniquely identifies application provider of end-device.

Least-significant byte first, 8 bytes. Use LMIC_reverse_memcpy() for AppEUI to keep same byte order as that on lora server.

example C code

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

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

DevEUI

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

Least-significant byte first, 8 bytes. Use LMIC_reverse_memcpy() for DevEUI to keep same byte order as that on lora server.

example C code

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

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

AppKey (aka DevKey)

128-bit (16byte) AES key.

example C code

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

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

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


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

  • log in to server
  • click on Applications
  • find your application and click it
  • go to configure motes
  • to create a mote, you may enter a new DevEUI
    • you may copy-paste the 16byte application key from an already existing mote, if you desire.

Information

DevEUI is entered in reverse order into C-code from that shown on server (unique device ID).

AppEUI is entered in reverse order into C-code from that shown on server.

AppEUI is equivalent to "Application"

transmit power limits

FCC Part 15 rules permit one watt of transmit power when more than 50 channels are used. When received by a 64-channel gateway, the maximum power may be used.

However, if end-device is sending to a 8-channel gateway (single SX1301), the maximum transmit power permitted is +20dBm.

To configure LMIC for use with 8-channel gateway, CHNL_HYBRID should be defined in in config.h, and should be undefined for use with 64-channel gateway.

CHNL_HYBRID125KHz500KHz
defined valuechannelschannel
00 to 764
18 to 1565
216 to 2366
324 to 3167
432 to 3968
540 to 4769
648 to 5570
756 to 6371
undef0 to 6364 to 71
Committer:
dudmuck
Date:
Fri Jul 17 21:09:16 2015 +0000
Revision:
5:e4ba433f0ac1
Parent:
0:f2716e543d97
Child:
7:9095e54e381f
add txpow_limit to limit transmit power

Who changed what in which revision?

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