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:
Tue Jun 02 19:04:29 2015 +0000
Revision:
0:f2716e543d97
Child:
5:e4ba433f0ac1
lmic-v1.5 for MOTE_L152RC

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 0:f2716e543d97 731 static void initDefaultChannels (void) {
dudmuck 0:f2716e543d97 732 for( u1_t i=0; i<4; i++ )
dudmuck 0:f2716e543d97 733 LMIC.channelMap[i] = 0xFFFF;
dudmuck 0:f2716e543d97 734 LMIC.channelMap[4] = 0x00FF;
dudmuck 0:f2716e543d97 735 }
dudmuck 0:f2716e543d97 736
dudmuck 0:f2716e543d97 737 static u4_t convFreq (xref2u1_t ptr) {
dudmuck 0:f2716e543d97 738 u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100;
dudmuck 0:f2716e543d97 739 if( freq < US915_FREQ_MIN || freq > US915_FREQ_MAX )
dudmuck 0:f2716e543d97 740 freq = 0;
dudmuck 0:f2716e543d97 741 return freq;
dudmuck 0:f2716e543d97 742 }
dudmuck 0:f2716e543d97 743
dudmuck 0:f2716e543d97 744 bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
dudmuck 0:f2716e543d97 745 if( chidx < 72 || chidx >= 72+MAX_XCHANNELS )
dudmuck 0:f2716e543d97 746 return 0; // channels 0..71 are hardwired
dudmuck 0:f2716e543d97 747 chidx -= 72;
dudmuck 0:f2716e543d97 748 LMIC.xchFreq[chidx] = freq;
dudmuck 0:f2716e543d97 749 LMIC.xchDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF10,DR_SF8C) : drmap;
dudmuck 0:f2716e543d97 750 LMIC.channelMap[chidx>>4] |= (1<<(chidx&0xF));
dudmuck 0:f2716e543d97 751 return 1;
dudmuck 0:f2716e543d97 752 }
dudmuck 0:f2716e543d97 753
dudmuck 0:f2716e543d97 754 void LMIC_disableChannel (u1_t channel) {
dudmuck 0:f2716e543d97 755 if( channel < 72+MAX_XCHANNELS )
dudmuck 0:f2716e543d97 756 LMIC.channelMap[channel/4] &= ~(1<<(channel&0xF));
dudmuck 0:f2716e543d97 757 }
dudmuck 0:f2716e543d97 758
dudmuck 0:f2716e543d97 759 static u1_t mapChannels (u1_t chpage, u2_t chmap) {
dudmuck 0:f2716e543d97 760 if( chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF ) {
dudmuck 0:f2716e543d97 761 u2_t en125 = chpage == MCMD_LADR_CHP_125ON ? 0xFFFF : 0x0000;
dudmuck 0:f2716e543d97 762 for( u1_t u=0; u<4; u++ )
dudmuck 0:f2716e543d97 763 LMIC.channelMap[u] = en125;
dudmuck 0:f2716e543d97 764 LMIC.channelMap[64/16] = chmap;
dudmuck 0:f2716e543d97 765 } else {
dudmuck 0:f2716e543d97 766 if( chpage >= (72+MAX_XCHANNELS+15)/16 )
dudmuck 0:f2716e543d97 767 return 0;
dudmuck 0:f2716e543d97 768 LMIC.channelMap[chpage] = chmap;
dudmuck 0:f2716e543d97 769 }
dudmuck 0:f2716e543d97 770 return 1;
dudmuck 0:f2716e543d97 771 }
dudmuck 0:f2716e543d97 772
dudmuck 0:f2716e543d97 773 static void updateTx (ostime_t txbeg) {
dudmuck 0:f2716e543d97 774 u1_t chnl = LMIC.txChnl;
dudmuck 0:f2716e543d97 775 if( chnl < 64 ) {
dudmuck 0:f2716e543d97 776 LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP;
dudmuck 0:f2716e543d97 777 LMIC.txpow = 30;
dudmuck 0:f2716e543d97 778 return;
dudmuck 0:f2716e543d97 779 }
dudmuck 0:f2716e543d97 780 LMIC.txpow = 26;
dudmuck 0:f2716e543d97 781 if( chnl < 64+8 ) {
dudmuck 0:f2716e543d97 782 LMIC.freq = US915_500kHz_UPFBASE + (chnl-64)*US915_500kHz_UPFSTEP;
dudmuck 0:f2716e543d97 783 } else {
dudmuck 0:f2716e543d97 784 ASSERT(chnl < 64+8+MAX_XCHANNELS);
dudmuck 0:f2716e543d97 785 LMIC.freq = LMIC.xchFreq[chnl-72];
dudmuck 0:f2716e543d97 786 }
dudmuck 0:f2716e543d97 787
dudmuck 0:f2716e543d97 788 // Update global duty cycle stats
dudmuck 0:f2716e543d97 789 if( LMIC.globalDutyRate != 0 ) {
dudmuck 0:f2716e543d97 790 ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
dudmuck 0:f2716e543d97 791 LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
dudmuck 0:f2716e543d97 792 }
dudmuck 0:f2716e543d97 793 }
dudmuck 0:f2716e543d97 794
dudmuck 0:f2716e543d97 795 // US does not have duty cycling - return now as earliest TX time
dudmuck 0:f2716e543d97 796 #define nextTx(now) (_nextTx(),(now))
dudmuck 0:f2716e543d97 797 static void _nextTx (void) {
dudmuck 0:f2716e543d97 798 if( LMIC.chRnd==0 )
dudmuck 0:f2716e543d97 799 LMIC.chRnd = os_getRndU1() & 0x3F;
dudmuck 0:f2716e543d97 800 if( LMIC.datarate >= DR_SF8C ) { // 500kHz
dudmuck 0:f2716e543d97 801 u1_t map = LMIC.channelMap[64/16]&0xFF;
dudmuck 0:f2716e543d97 802 for( u1_t i=0; i<8; i++ ) {
dudmuck 0:f2716e543d97 803 if( (map & (1<<(++LMIC.chRnd & 7))) != 0 ) {
dudmuck 0:f2716e543d97 804 LMIC.txChnl = 64 + (LMIC.chRnd & 7);
dudmuck 0:f2716e543d97 805 return;
dudmuck 0:f2716e543d97 806 }
dudmuck 0:f2716e543d97 807 }
dudmuck 0:f2716e543d97 808 } else { // 125kHz
dudmuck 0:f2716e543d97 809 for( u1_t i=0; i<64; i++ ) {
dudmuck 0:f2716e543d97 810 u1_t chnl = ++LMIC.chRnd & 0x3F;
dudmuck 0:f2716e543d97 811 if( (LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0xF))) != 0 ) {
dudmuck 0:f2716e543d97 812 LMIC.txChnl = chnl;
dudmuck 0:f2716e543d97 813 return;
dudmuck 0:f2716e543d97 814 }
dudmuck 0:f2716e543d97 815 }
dudmuck 0:f2716e543d97 816 }
dudmuck 0:f2716e543d97 817 // No feasible channel found! Keep old one.
dudmuck 0:f2716e543d97 818 }
dudmuck 0:f2716e543d97 819
dudmuck 0:f2716e543d97 820 static void setBcnRxParams (void) {
dudmuck 0:f2716e543d97 821 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 822 LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl * US915_500kHz_DNFSTEP;
dudmuck 0:f2716e543d97 823 LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN),1),LEN_BCN);
dudmuck 0:f2716e543d97 824 }
dudmuck 0:f2716e543d97 825
dudmuck 0:f2716e543d97 826 #define setRx1Params() { \
dudmuck 0:f2716e543d97 827 LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP; \
dudmuck 0:f2716e543d97 828 if( /* TX datarate */LMIC.dndr < DR_SF8C ) \
dudmuck 0:f2716e543d97 829 LMIC.dndr += DR_SF10CR - DR_SF10; \
dudmuck 0:f2716e543d97 830 else if( LMIC.dndr == DR_SF8C ) \
dudmuck 0:f2716e543d97 831 LMIC.dndr = DR_SF7CR; \
dudmuck 0:f2716e543d97 832 LMIC.rps = dndr2rps(LMIC.dndr); \
dudmuck 0:f2716e543d97 833 }
dudmuck 0:f2716e543d97 834
dudmuck 0:f2716e543d97 835 static void initJoinLoop (void) {
dudmuck 0:f2716e543d97 836 LMIC.chRnd = 0;
dudmuck 0:f2716e543d97 837 LMIC.txChnl = 0;
dudmuck 0:f2716e543d97 838 LMIC.adrTxPow = 20;
dudmuck 0:f2716e543d97 839 ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
dudmuck 0:f2716e543d97 840 LMIC.txend = os_getTime();
dudmuck 0:f2716e543d97 841 setDrJoin(DRCHG_SET, DR_SF7);
dudmuck 0:f2716e543d97 842 }
dudmuck 0:f2716e543d97 843
dudmuck 0:f2716e543d97 844 static ostime_t nextJoinState (void) {
dudmuck 0:f2716e543d97 845 // Try the following:
dudmuck 0:f2716e543d97 846 // SF7/8/9/10 on a random channel 0..63
dudmuck 0:f2716e543d97 847 // SF8C on a random channel 64..71
dudmuck 0:f2716e543d97 848 //
dudmuck 0:f2716e543d97 849 u1_t failed = 0;
dudmuck 0:f2716e543d97 850 if( LMIC.datarate != DR_SF8C ) {
dudmuck 0:f2716e543d97 851 LMIC.txChnl = 64+(LMIC.txChnl&7);
dudmuck 0:f2716e543d97 852 setDrJoin(DRCHG_SET, DR_SF8C);
dudmuck 0:f2716e543d97 853 } else {
dudmuck 0:f2716e543d97 854 LMIC.txChnl = os_getRndU1() & 0x3F;
dudmuck 0:f2716e543d97 855 s1_t dr = DR_SF7 - ++LMIC.txCnt;
dudmuck 0:f2716e543d97 856 if( dr < DR_SF10 ) {
dudmuck 0:f2716e543d97 857 dr = DR_SF10;
dudmuck 0:f2716e543d97 858 failed = 1; // All DR exhausted - signal failed
dudmuck 0:f2716e543d97 859 }
dudmuck 0:f2716e543d97 860 setDrJoin(DRCHG_SET, dr);
dudmuck 0:f2716e543d97 861 }
dudmuck 0:f2716e543d97 862 LMIC.opmode &= ~OP_NEXTCHNL;
dudmuck 0:f2716e543d97 863 LMIC.txend = os_getTime() +
dudmuck 0:f2716e543d97 864 (isTESTMODE()
dudmuck 0:f2716e543d97 865 // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it - GW is still busy)
dudmuck 0:f2716e543d97 866 ? DNW2_SAFETY_ZONE
dudmuck 0:f2716e543d97 867 // Otherwise: randomize join (street lamp case):
dudmuck 0:f2716e543d97 868 // SF10:16, SF9=8,..SF8C:1secs
dudmuck 0:f2716e543d97 869 : rndDelay(16>>LMIC.datarate));
dudmuck 0:f2716e543d97 870 // 1 - triggers EV_JOIN_FAILED event
dudmuck 0:f2716e543d97 871 return failed;
dudmuck 0:f2716e543d97 872 }
dudmuck 0:f2716e543d97 873
dudmuck 0:f2716e543d97 874 //
dudmuck 0:f2716e543d97 875 // END: US915 related stuff
dudmuck 0:f2716e543d97 876 //
dudmuck 0:f2716e543d97 877 // ================================================================================
dudmuck 0:f2716e543d97 878 #else
dudmuck 0:f2716e543d97 879 #error Unsupported frequency band!
dudmuck 0:f2716e543d97 880 #endif
dudmuck 0:f2716e543d97 881
dudmuck 0:f2716e543d97 882
dudmuck 0:f2716e543d97 883 static void runEngineUpdate (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 884 engineUpdate();
dudmuck 0:f2716e543d97 885 }
dudmuck 0:f2716e543d97 886
dudmuck 0:f2716e543d97 887
dudmuck 0:f2716e543d97 888 static void reportEvent (ev_t ev) {
dudmuck 0:f2716e543d97 889 EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
dudmuck 0:f2716e543d97 890 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 891 e_.info = ev));
dudmuck 0:f2716e543d97 892 ON_LMIC_EVENT(ev);
dudmuck 0:f2716e543d97 893 engineUpdate();
dudmuck 0:f2716e543d97 894 }
dudmuck 0:f2716e543d97 895
dudmuck 0:f2716e543d97 896
dudmuck 0:f2716e543d97 897 static void runReset (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 898 // Disable session
dudmuck 0:f2716e543d97 899 LMIC_reset();
dudmuck 0:f2716e543d97 900 LMIC_startJoining();
dudmuck 0:f2716e543d97 901 reportEvent(EV_RESET);
dudmuck 0:f2716e543d97 902 }
dudmuck 0:f2716e543d97 903
dudmuck 0:f2716e543d97 904 static void stateJustJoined (void) {
dudmuck 0:f2716e543d97 905 LMIC.seqnoDn = LMIC.seqnoUp = 0;
dudmuck 0:f2716e543d97 906 LMIC.rejoinCnt = 0;
dudmuck 0:f2716e543d97 907 LMIC.dnConf = LMIC.adrChanged = LMIC.ladrAns = LMIC.devsAns = 0;
dudmuck 0:f2716e543d97 908 LMIC.moreData = LMIC.dn2Ans = LMIC.snchAns = LMIC.dutyCapAns = 0;
dudmuck 0:f2716e543d97 909 LMIC.pingSetAns = 0;
dudmuck 0:f2716e543d97 910 LMIC.upRepeat = 0;
dudmuck 0:f2716e543d97 911 LMIC.adrAckReq = LINK_CHECK_INIT;
dudmuck 0:f2716e543d97 912 LMIC.dn2Dr = DR_DNW2;
dudmuck 0:f2716e543d97 913 LMIC.dn2Freq = FREQ_DNW2;
dudmuck 0:f2716e543d97 914 LMIC.bcnChnl = CHNL_BCN;
dudmuck 0:f2716e543d97 915 LMIC.ping.freq = FREQ_PING;
dudmuck 0:f2716e543d97 916 LMIC.ping.dr = DR_PING;
dudmuck 0:f2716e543d97 917 }
dudmuck 0:f2716e543d97 918
dudmuck 0:f2716e543d97 919
dudmuck 0:f2716e543d97 920 // ================================================================================
dudmuck 0:f2716e543d97 921 // Decoding frames
dudmuck 0:f2716e543d97 922
dudmuck 0:f2716e543d97 923
dudmuck 0:f2716e543d97 924 // Decode beacon - do not overwrite bcninfo unless we have a match!
dudmuck 0:f2716e543d97 925 static int decodeBeacon (void) {
dudmuck 0:f2716e543d97 926 ASSERT(LMIC.dataLen == LEN_BCN); // implicit header RX guarantees this
dudmuck 0:f2716e543d97 927 xref2u1_t d = LMIC.frame;
dudmuck 0:f2716e543d97 928 if(
dudmuck 0:f2716e543d97 929 #ifdef CFG_eu868
dudmuck 0:f2716e543d97 930 d[OFF_BCN_CRC1] != (u1_t)os_crc16(d,OFF_BCN_CRC1)
dudmuck 0:f2716e543d97 931 #elif defined(CFG_us915)
dudmuck 0:f2716e543d97 932 os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d,OFF_BCN_CRC1)
dudmuck 0:f2716e543d97 933 #endif
dudmuck 0:f2716e543d97 934 )
dudmuck 0:f2716e543d97 935 return 0; // first (common) part fails CRC check
dudmuck 0:f2716e543d97 936 // First set of fields is ok
dudmuck 0:f2716e543d97 937 u4_t bcnnetid = os_rlsbf4(&d[OFF_BCN_NETID]) & 0xFFFFFF;
dudmuck 0:f2716e543d97 938 if( bcnnetid != LMIC.netid )
dudmuck 0:f2716e543d97 939 return -1; // not the beacon we're looking for
dudmuck 0:f2716e543d97 940
dudmuck 0:f2716e543d97 941 LMIC.bcninfo.flags &= ~(BCN_PARTIAL|BCN_FULL);
dudmuck 0:f2716e543d97 942 // Match - update bcninfo structure
dudmuck 0:f2716e543d97 943 LMIC.bcninfo.snr = LMIC.snr;
dudmuck 0:f2716e543d97 944 LMIC.bcninfo.rssi = LMIC.rssi;
dudmuck 0:f2716e543d97 945 LMIC.bcninfo.txtime = LMIC.rxtime - AIRTIME_BCN_osticks;
dudmuck 0:f2716e543d97 946 LMIC.bcninfo.time = os_rlsbf4(&d[OFF_BCN_TIME]);
dudmuck 0:f2716e543d97 947 LMIC.bcninfo.flags |= BCN_PARTIAL;
dudmuck 0:f2716e543d97 948
dudmuck 0:f2716e543d97 949 // Check 2nd set
dudmuck 0:f2716e543d97 950 if( os_rlsbf2(&d[OFF_BCN_CRC2]) != os_crc16(d,OFF_BCN_CRC2) )
dudmuck 0:f2716e543d97 951 return 1;
dudmuck 0:f2716e543d97 952 // Second set of fields is ok
dudmuck 0:f2716e543d97 953 LMIC.bcninfo.lat = (s4_t)os_rlsbf4(&d[OFF_BCN_LAT-1]) >> 8; // read as signed 24-bit
dudmuck 0:f2716e543d97 954 LMIC.bcninfo.lon = (s4_t)os_rlsbf4(&d[OFF_BCN_LON-1]) >> 8; // ditto
dudmuck 0:f2716e543d97 955 LMIC.bcninfo.info = d[OFF_BCN_INFO];
dudmuck 0:f2716e543d97 956 LMIC.bcninfo.flags |= BCN_FULL;
dudmuck 0:f2716e543d97 957 return 2;
dudmuck 0:f2716e543d97 958 }
dudmuck 0:f2716e543d97 959
dudmuck 0:f2716e543d97 960
dudmuck 0:f2716e543d97 961 static bit_t decodeFrame (void) {
dudmuck 0:f2716e543d97 962 xref2u1_t d = LMIC.frame;
dudmuck 0:f2716e543d97 963 u1_t hdr = d[0];
dudmuck 0:f2716e543d97 964 u1_t ftype = hdr & HDR_FTYPE;
dudmuck 0:f2716e543d97 965 int dlen = LMIC.dataLen;
dudmuck 0:f2716e543d97 966 if( dlen < OFF_DAT_OPTS+4 ||
dudmuck 0:f2716e543d97 967 (hdr & HDR_MAJOR) != HDR_MAJOR_V1 ||
dudmuck 0:f2716e543d97 968 (ftype != HDR_FTYPE_DADN && ftype != HDR_FTYPE_DCDN) ) {
dudmuck 0:f2716e543d97 969 // Basic sanity checks failed
dudmuck 0:f2716e543d97 970 EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
dudmuck 0:f2716e543d97 971 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 972 e_.info = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]),
dudmuck 0:f2716e543d97 973 e_.info2 = hdr + (dlen<<8)));
dudmuck 0:f2716e543d97 974 norx:
dudmuck 0:f2716e543d97 975 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 976 return 0;
dudmuck 0:f2716e543d97 977 }
dudmuck 0:f2716e543d97 978 // Validate exact frame length
dudmuck 0:f2716e543d97 979 // Note: device address was already read+evaluated in order to arrive here.
dudmuck 0:f2716e543d97 980 int fct = d[OFF_DAT_FCT];
dudmuck 0:f2716e543d97 981 u4_t addr = os_rlsbf4(&d[OFF_DAT_ADDR]);
dudmuck 0:f2716e543d97 982 u4_t seqno = os_rlsbf2(&d[OFF_DAT_SEQNO]);
dudmuck 0:f2716e543d97 983 int olen = fct & FCT_OPTLEN;
dudmuck 0:f2716e543d97 984 int ackup = (fct & FCT_ACK) != 0 ? 1 : 0; // ACK last up frame
dudmuck 0:f2716e543d97 985 int poff = OFF_DAT_OPTS+olen;
dudmuck 0:f2716e543d97 986 int pend = dlen-4; // MIC
dudmuck 0:f2716e543d97 987
dudmuck 0:f2716e543d97 988 if( addr != LMIC.devaddr ) {
dudmuck 0:f2716e543d97 989 EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS,
dudmuck 0:f2716e543d97 990 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 991 e_.info = addr,
dudmuck 0:f2716e543d97 992 e_.info2 = LMIC.devaddr));
dudmuck 0:f2716e543d97 993 goto norx;
dudmuck 0:f2716e543d97 994 }
dudmuck 0:f2716e543d97 995 if( poff > pend ) {
dudmuck 0:f2716e543d97 996 EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
dudmuck 0:f2716e543d97 997 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 998 e_.info = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16)));
dudmuck 0:f2716e543d97 999 goto norx;
dudmuck 0:f2716e543d97 1000 }
dudmuck 0:f2716e543d97 1001
dudmuck 0:f2716e543d97 1002 int port = -1;
dudmuck 0:f2716e543d97 1003 int replayConf = 0;
dudmuck 0:f2716e543d97 1004
dudmuck 0:f2716e543d97 1005 if( pend > poff )
dudmuck 0:f2716e543d97 1006 port = d[poff++];
dudmuck 0:f2716e543d97 1007
dudmuck 0:f2716e543d97 1008 seqno = LMIC.seqnoDn + (u2_t)(seqno - LMIC.seqnoDn);
dudmuck 0:f2716e543d97 1009
dudmuck 0:f2716e543d97 1010 if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) {
dudmuck 0:f2716e543d97 1011 EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC,
dudmuck 0:f2716e543d97 1012 e_.eui1 = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1013 e_.info1 = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1014 e_.info2 = seqno,
dudmuck 0:f2716e543d97 1015 e_.info3 = LMIC.devaddr));
dudmuck 0:f2716e543d97 1016 goto norx;
dudmuck 0:f2716e543d97 1017 }
dudmuck 0:f2716e543d97 1018 if( seqno < LMIC.seqnoDn ) {
dudmuck 0:f2716e543d97 1019 if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) {
dudmuck 0:f2716e543d97 1020 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
dudmuck 0:f2716e543d97 1021 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1022 e_.info = LMIC.seqnoDn,
dudmuck 0:f2716e543d97 1023 e_.info2 = seqno));
dudmuck 0:f2716e543d97 1024 goto norx;
dudmuck 0:f2716e543d97 1025 }
dudmuck 0:f2716e543d97 1026 if( seqno != LMIC.seqnoDn-1 || !LMIC.dnConf || ftype != HDR_FTYPE_DCDN ) {
dudmuck 0:f2716e543d97 1027 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_OBSOLETE,
dudmuck 0:f2716e543d97 1028 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1029 e_.info = LMIC.seqnoDn,
dudmuck 0:f2716e543d97 1030 e_.info2 = seqno));
dudmuck 0:f2716e543d97 1031 goto norx;
dudmuck 0:f2716e543d97 1032 }
dudmuck 0:f2716e543d97 1033 // Replay of previous sequence number allowed only if
dudmuck 0:f2716e543d97 1034 // previous frame and repeated both requested confirmation
dudmuck 0:f2716e543d97 1035 replayConf = 1;
dudmuck 0:f2716e543d97 1036 }
dudmuck 0:f2716e543d97 1037 else {
dudmuck 0:f2716e543d97 1038 if( seqno > LMIC.seqnoDn ) {
dudmuck 0:f2716e543d97 1039 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP,
dudmuck 0:f2716e543d97 1040 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1041 e_.info = LMIC.seqnoDn,
dudmuck 0:f2716e543d97 1042 e_.info2 = seqno));
dudmuck 0:f2716e543d97 1043 }
dudmuck 0:f2716e543d97 1044 LMIC.seqnoDn = seqno+1; // next number to be expected
dudmuck 0:f2716e543d97 1045 DO_DEVDB(LMIC.seqnoDn,seqnoDn);
dudmuck 0:f2716e543d97 1046 // DN frame requested confirmation - provide ACK once with next UP frame
dudmuck 0:f2716e543d97 1047 LMIC.dnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0);
dudmuck 0:f2716e543d97 1048 }
dudmuck 0:f2716e543d97 1049
dudmuck 0:f2716e543d97 1050 if( LMIC.dnConf || (fct & FCT_MORE) )
dudmuck 0:f2716e543d97 1051 LMIC.opmode |= OP_POLL;
dudmuck 0:f2716e543d97 1052
dudmuck 0:f2716e543d97 1053 // We heard from network
dudmuck 0:f2716e543d97 1054 LMIC.adrChanged = LMIC.rejoinCnt = 0;
dudmuck 0:f2716e543d97 1055 if( LMIC.adrAckReq != LINK_CHECK_OFF )
dudmuck 0:f2716e543d97 1056 LMIC.adrAckReq = LINK_CHECK_INIT;
dudmuck 0:f2716e543d97 1057
dudmuck 0:f2716e543d97 1058 // Process OPTS
dudmuck 0:f2716e543d97 1059 int m = LMIC.rssi - RSSI_OFF - getSensitivity(LMIC.rps);
dudmuck 0:f2716e543d97 1060 LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m;
dudmuck 0:f2716e543d97 1061
dudmuck 0:f2716e543d97 1062 xref2u1_t opts = &d[OFF_DAT_OPTS];
dudmuck 0:f2716e543d97 1063 int oidx = 0;
dudmuck 0:f2716e543d97 1064 while( oidx < olen ) {
dudmuck 0:f2716e543d97 1065 switch( opts[oidx] ) {
dudmuck 0:f2716e543d97 1066 case MCMD_LCHK_ANS: {
dudmuck 0:f2716e543d97 1067 //int gwmargin = opts[oidx+1];
dudmuck 0:f2716e543d97 1068 //int ngws = opts[oidx+2];
dudmuck 0:f2716e543d97 1069 oidx += 3;
dudmuck 0:f2716e543d97 1070 continue;
dudmuck 0:f2716e543d97 1071 }
dudmuck 0:f2716e543d97 1072 case MCMD_LADR_REQ: {
dudmuck 0:f2716e543d97 1073 u1_t p1 = opts[oidx+1]; // txpow + DR
dudmuck 0:f2716e543d97 1074 u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels
dudmuck 0:f2716e543d97 1075 u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK; // channel page
dudmuck 0:f2716e543d97 1076 u1_t uprpt = opts[oidx+4] & MCMD_LADR_REPEAT_MASK; // up repeat count
dudmuck 0:f2716e543d97 1077 oidx += 5;
dudmuck 0:f2716e543d97 1078
dudmuck 0:f2716e543d97 1079 LMIC.ladrAns = 0x80 | // Include an answer into next frame up
dudmuck 0:f2716e543d97 1080 MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK;
dudmuck 0:f2716e543d97 1081 if( !mapChannels(chpage, chmap) )
dudmuck 0:f2716e543d97 1082 LMIC.ladrAns &= ~MCMD_LADR_ANS_CHACK;
dudmuck 0:f2716e543d97 1083 dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT);
dudmuck 0:f2716e543d97 1084 if( !validDR(dr) ) {
dudmuck 0:f2716e543d97 1085 LMIC.ladrAns &= ~MCMD_LADR_ANS_DRACK;
dudmuck 0:f2716e543d97 1086 EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
dudmuck 0:f2716e543d97 1087 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1088 e_.info = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1089 e_.info2 = Base::msbf4(&opts[oidx-4])));
dudmuck 0:f2716e543d97 1090 }
dudmuck 0:f2716e543d97 1091 if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) {
dudmuck 0:f2716e543d97 1092 // Nothing went wrong - use settings
dudmuck 0:f2716e543d97 1093 LMIC.upRepeat = uprpt;
dudmuck 0:f2716e543d97 1094 setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1));
dudmuck 0:f2716e543d97 1095 }
dudmuck 0:f2716e543d97 1096 LMIC.adrChanged = 1; // Trigger an ACK to NWK
dudmuck 0:f2716e543d97 1097 continue;
dudmuck 0:f2716e543d97 1098 }
dudmuck 0:f2716e543d97 1099 case MCMD_DEVS_REQ: {
dudmuck 0:f2716e543d97 1100 LMIC.devsAns = 1;
dudmuck 0:f2716e543d97 1101 oidx += 1;
dudmuck 0:f2716e543d97 1102 continue;
dudmuck 0:f2716e543d97 1103 }
dudmuck 0:f2716e543d97 1104 case MCMD_DN2P_SET: {
dudmuck 0:f2716e543d97 1105 dr_t dr = (dr_t)(opts[oidx+1] & 0x0F);
dudmuck 0:f2716e543d97 1106 u4_t freq = convFreq(&opts[oidx+2]);
dudmuck 0:f2716e543d97 1107 oidx += 5;
dudmuck 0:f2716e543d97 1108 LMIC.dn2Ans = 0x80; // answer pending
dudmuck 0:f2716e543d97 1109 if( validDR(dr) )
dudmuck 0:f2716e543d97 1110 LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK;
dudmuck 0:f2716e543d97 1111 if( freq != 0 )
dudmuck 0:f2716e543d97 1112 LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK;
dudmuck 0:f2716e543d97 1113 if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK) ) {
dudmuck 0:f2716e543d97 1114 LMIC.dn2Dr = dr;
dudmuck 0:f2716e543d97 1115 LMIC.dn2Freq = freq;
dudmuck 0:f2716e543d97 1116 DO_DEVDB(LMIC.dn2Dr,dn2Dr);
dudmuck 0:f2716e543d97 1117 DO_DEVDB(LMIC.dn2Freq,dn2Freq);
dudmuck 0:f2716e543d97 1118 }
dudmuck 0:f2716e543d97 1119 continue;
dudmuck 0:f2716e543d97 1120 }
dudmuck 0:f2716e543d97 1121 case MCMD_DCAP_REQ: {
dudmuck 0:f2716e543d97 1122 u1_t cap = opts[oidx+1];
dudmuck 0:f2716e543d97 1123 oidx += 2;
dudmuck 0:f2716e543d97 1124 // A value cap=0xFF means device is OFF unless enabled again manually.
dudmuck 0:f2716e543d97 1125 if( cap==0xFF )
dudmuck 0:f2716e543d97 1126 LMIC.opmode |= OP_SHUTDOWN; // stop any sending
dudmuck 0:f2716e543d97 1127 LMIC.globalDutyRate = cap & 0xF;
dudmuck 0:f2716e543d97 1128 LMIC.globalDutyAvail = os_getTime();
dudmuck 0:f2716e543d97 1129 DO_DEVDB(cap,dutyCap);
dudmuck 0:f2716e543d97 1130 LMIC.dutyCapAns = 1;
dudmuck 0:f2716e543d97 1131 continue;
dudmuck 0:f2716e543d97 1132 }
dudmuck 0:f2716e543d97 1133 case MCMD_SNCH_REQ: {
dudmuck 0:f2716e543d97 1134 u1_t chidx = opts[oidx+1]; // channel
dudmuck 0:f2716e543d97 1135 u4_t freq = convFreq(&opts[oidx+2]); // freq
dudmuck 0:f2716e543d97 1136 u1_t drs = opts[oidx+5]; // datarate span
dudmuck 0:f2716e543d97 1137 LMIC.snchAns = 0x80;
dudmuck 0:f2716e543d97 1138 if( freq != 0 && LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(drs&0xF,drs>>4), -1) )
dudmuck 0:f2716e543d97 1139 LMIC.snchAns |= MCMD_SNCH_ANS_DRACK|MCMD_SNCH_ANS_FQACK;
dudmuck 0:f2716e543d97 1140 oidx += 6;
dudmuck 0:f2716e543d97 1141 continue;
dudmuck 0:f2716e543d97 1142 }
dudmuck 0:f2716e543d97 1143 case MCMD_PING_SET: {
dudmuck 0:f2716e543d97 1144 u4_t freq = convFreq(&opts[oidx+1]);
dudmuck 0:f2716e543d97 1145 oidx += 4;
dudmuck 0:f2716e543d97 1146 u1_t flags = 0x80;
dudmuck 0:f2716e543d97 1147 if( freq != 0 ) {
dudmuck 0:f2716e543d97 1148 flags |= MCMD_PING_ANS_FQACK;
dudmuck 0:f2716e543d97 1149 LMIC.ping.freq = freq;
dudmuck 0:f2716e543d97 1150 DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
dudmuck 0:f2716e543d97 1151 DO_DEVDB(LMIC.ping.freq, pingFreq);
dudmuck 0:f2716e543d97 1152 DO_DEVDB(LMIC.ping.dr, pingDr);
dudmuck 0:f2716e543d97 1153 }
dudmuck 0:f2716e543d97 1154 LMIC.pingSetAns = flags;
dudmuck 0:f2716e543d97 1155 continue;
dudmuck 0:f2716e543d97 1156 }
dudmuck 0:f2716e543d97 1157 case MCMD_BCNI_ANS: {
dudmuck 0:f2716e543d97 1158 // Ignore if tracking already enabled
dudmuck 0:f2716e543d97 1159 if( (LMIC.opmode & OP_TRACK) == 0 ) {
dudmuck 0:f2716e543d97 1160 LMIC.bcnChnl = opts[oidx+3];
dudmuck 0:f2716e543d97 1161 // Enable tracking - bcninfoTries
dudmuck 0:f2716e543d97 1162 LMIC.opmode |= OP_TRACK;
dudmuck 0:f2716e543d97 1163 // Cleared later in txComplete handling - triggers EV_BEACON_FOUND
dudmuck 0:f2716e543d97 1164 ASSERT(LMIC.bcninfoTries!=0);
dudmuck 0:f2716e543d97 1165 // Setup RX parameters
dudmuck 0:f2716e543d97 1166 LMIC.bcninfo.txtime = (LMIC.rxtime
dudmuck 0:f2716e543d97 1167 + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BCNI_TUNIT)
dudmuck 0:f2716e543d97 1168 + ms2osticksCeil(MCMD_BCNI_TUNIT/2)
dudmuck 0:f2716e543d97 1169 - BCN_INTV_osticks);
dudmuck 0:f2716e543d97 1170 LMIC.bcninfo.flags = 0; // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared)
dudmuck 0:f2716e543d97 1171 calcBcnRxWindowFromMillis(MCMD_BCNI_TUNIT,1); // error of +/-N ms
dudmuck 0:f2716e543d97 1172
dudmuck 0:f2716e543d97 1173 EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BCNI_ANS,
dudmuck 0:f2716e543d97 1174 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1175 e_.lostmic = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1176 e_.info = (LMIC.missedBcns |
dudmuck 0:f2716e543d97 1177 (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks
dudmuck 0:f2716e543d97 1178 - LMIC.bcnRxtime) << 8)),
dudmuck 0:f2716e543d97 1179 e_.time = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks)));
dudmuck 0:f2716e543d97 1180 }
dudmuck 0:f2716e543d97 1181 oidx += 4;
dudmuck 0:f2716e543d97 1182 continue;
dudmuck 0:f2716e543d97 1183 }
dudmuck 0:f2716e543d97 1184 }
dudmuck 0:f2716e543d97 1185 EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
dudmuck 0:f2716e543d97 1186 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1187 e_.info = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1188 e_.info2 = Base::msbf4(&opts[oidx])));
dudmuck 0:f2716e543d97 1189 break;
dudmuck 0:f2716e543d97 1190 }
dudmuck 0:f2716e543d97 1191 if( oidx != olen ) {
dudmuck 0:f2716e543d97 1192 EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
dudmuck 0:f2716e543d97 1193 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1194 e_.info = 0x1000000 + (oidx) + (olen<<8)));
dudmuck 0:f2716e543d97 1195 }
dudmuck 0:f2716e543d97 1196
dudmuck 0:f2716e543d97 1197 if( !replayConf ) {
dudmuck 0:f2716e543d97 1198 // Handle payload only if not a replay
dudmuck 0:f2716e543d97 1199 // Decrypt payload - if any
dudmuck 0:f2716e543d97 1200 if( port >= 0 && pend-poff > 0 )
dudmuck 0:f2716e543d97 1201 aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff);
dudmuck 0:f2716e543d97 1202
dudmuck 0:f2716e543d97 1203 EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1204 e_.devaddr = LMIC.devaddr,
dudmuck 0:f2716e543d97 1205 e_.seqno = seqno,
dudmuck 0:f2716e543d97 1206 e_.flags = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN,
dudmuck 0:f2716e543d97 1207 e_.mic = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1208 e_.hdr = d[LORA::OFF_DAT_HDR],
dudmuck 0:f2716e543d97 1209 e_.fct = d[LORA::OFF_DAT_FCT],
dudmuck 0:f2716e543d97 1210 e_.port = port,
dudmuck 0:f2716e543d97 1211 e_.plen = dlen,
dudmuck 0:f2716e543d97 1212 e_.opts.length = olen,
dudmuck 0:f2716e543d97 1213 memcpy(&e_.opts[0], opts, olen)));
dudmuck 0:f2716e543d97 1214 } else {
dudmuck 0:f2716e543d97 1215 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY,
dudmuck 0:f2716e543d97 1216 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1217 e_.info = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1218 e_.info2 = seqno));
dudmuck 0:f2716e543d97 1219 }
dudmuck 0:f2716e543d97 1220
dudmuck 0:f2716e543d97 1221 if( // NWK acks but we don't have a frame pending
dudmuck 0:f2716e543d97 1222 (ackup && LMIC.txCnt == 0) ||
dudmuck 0:f2716e543d97 1223 // We sent up confirmed and we got a response in DNW1/DNW2
dudmuck 0:f2716e543d97 1224 // BUT it did not carry an ACK - this should never happen
dudmuck 0:f2716e543d97 1225 // Do not resend and assume frame was not ACKed.
dudmuck 0:f2716e543d97 1226 (!ackup && LMIC.txCnt != 0) ) {
dudmuck 0:f2716e543d97 1227 EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK,
dudmuck 0:f2716e543d97 1228 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1229 e_.info = seqno,
dudmuck 0:f2716e543d97 1230 e_.info2 = ackup));
dudmuck 0:f2716e543d97 1231 }
dudmuck 0:f2716e543d97 1232
dudmuck 0:f2716e543d97 1233 if( LMIC.txCnt != 0 ) // we requested an ACK
dudmuck 0:f2716e543d97 1234 LMIC.txrxFlags |= ackup ? TXRX_ACK : TXRX_NACK;
dudmuck 0:f2716e543d97 1235
dudmuck 0:f2716e543d97 1236 if( port < 0 ) {
dudmuck 0:f2716e543d97 1237 LMIC.txrxFlags |= TXRX_NOPORT;
dudmuck 0:f2716e543d97 1238 LMIC.dataBeg = poff;
dudmuck 0:f2716e543d97 1239 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1240 } else {
dudmuck 0:f2716e543d97 1241 LMIC.txrxFlags |= TXRX_PORT;
dudmuck 0:f2716e543d97 1242 LMIC.dataBeg = poff;
dudmuck 0:f2716e543d97 1243 LMIC.dataLen = pend-poff;
dudmuck 0:f2716e543d97 1244 }
dudmuck 0:f2716e543d97 1245 return 1;
dudmuck 0:f2716e543d97 1246 }
dudmuck 0:f2716e543d97 1247
dudmuck 0:f2716e543d97 1248
dudmuck 0:f2716e543d97 1249 // ================================================================================
dudmuck 0:f2716e543d97 1250 // TX/RX transaction support
dudmuck 0:f2716e543d97 1251
dudmuck 0:f2716e543d97 1252
dudmuck 0:f2716e543d97 1253 static void setupRx2 (void) {
dudmuck 0:f2716e543d97 1254 LMIC.txrxFlags = TXRX_DNW2;
dudmuck 0:f2716e543d97 1255 LMIC.rps = dndr2rps(LMIC.dn2Dr);
dudmuck 0:f2716e543d97 1256 LMIC.freq = LMIC.dn2Freq;
dudmuck 0:f2716e543d97 1257 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1258 os_radio(RADIO_RX);
dudmuck 0:f2716e543d97 1259 }
dudmuck 0:f2716e543d97 1260
dudmuck 0:f2716e543d97 1261
dudmuck 0:f2716e543d97 1262 static void schedRx2 (ostime_t delay, osjobcb_t func) {
dudmuck 0:f2716e543d97 1263 // Add 1.5 symbols we need 5 out of 8. Try to sync 1.5 symbols into the preamble.
dudmuck 0:f2716e543d97 1264 LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(LMIC.dn2Dr);
dudmuck 0:f2716e543d97 1265 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
dudmuck 0:f2716e543d97 1266 }
dudmuck 0:f2716e543d97 1267
dudmuck 0:f2716e543d97 1268 static void setupRx1 (osjobcb_t func) {
dudmuck 0:f2716e543d97 1269 LMIC.txrxFlags = TXRX_DNW1;
dudmuck 0:f2716e543d97 1270 // Turn LMIC.rps from TX over to RX
dudmuck 0:f2716e543d97 1271 LMIC.rps = setNocrc(LMIC.rps,1);
dudmuck 0:f2716e543d97 1272 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1273 LMIC.osjob.func = func;
dudmuck 0:f2716e543d97 1274 os_radio(RADIO_RX);
dudmuck 0:f2716e543d97 1275 }
dudmuck 0:f2716e543d97 1276
dudmuck 0:f2716e543d97 1277
dudmuck 0:f2716e543d97 1278 // Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime
dudmuck 0:f2716e543d97 1279 static void txDone (ostime_t delay, osjobcb_t func) {
dudmuck 0:f2716e543d97 1280 if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) {
dudmuck 0:f2716e543d97 1281 rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer!
dudmuck 0:f2716e543d97 1282 LMIC.opmode |= OP_PINGINI;
dudmuck 0:f2716e543d97 1283 }
dudmuck 0:f2716e543d97 1284 // Change RX frequency / rps (US only) before we increment txChnl
dudmuck 0:f2716e543d97 1285 setRx1Params();
dudmuck 0:f2716e543d97 1286 // LMIC.rxsyms carries the TX datarate (can be != LMIC.datarate [confirm retries etc.])
dudmuck 0:f2716e543d97 1287 // Setup receive - LMIC.rxtime is preloaded with 1.5 symbols offset to tune
dudmuck 0:f2716e543d97 1288 // into the middle of the 8 symbols preamble.
dudmuck 0:f2716e543d97 1289 #if defined(CFG_eu868)
dudmuck 0:f2716e543d97 1290 if( /* TX datarate */LMIC.rxsyms == DR_FSK ) {
dudmuck 0:f2716e543d97 1291 LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160);
dudmuck 0:f2716e543d97 1292 LMIC.rxsyms = RXLEN_FSK;
dudmuck 0:f2716e543d97 1293 }
dudmuck 0:f2716e543d97 1294 else
dudmuck 0:f2716e543d97 1295 #endif
dudmuck 0:f2716e543d97 1296 {
dudmuck 0:f2716e543d97 1297 LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(LMIC.dndr);
dudmuck 0:f2716e543d97 1298 LMIC.rxsyms = MINRX_SYMS;
dudmuck 0:f2716e543d97 1299 }
dudmuck 0:f2716e543d97 1300 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
dudmuck 0:f2716e543d97 1301 }
dudmuck 0:f2716e543d97 1302
dudmuck 0:f2716e543d97 1303
dudmuck 0:f2716e543d97 1304 // ======================================== Join frames
dudmuck 0:f2716e543d97 1305
dudmuck 0:f2716e543d97 1306
dudmuck 0:f2716e543d97 1307 static void onJoinFailed (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1308 // Notify app - must call LMIC_reset() to stop joining
dudmuck 0:f2716e543d97 1309 // otherwise join procedure continues.
dudmuck 0:f2716e543d97 1310 reportEvent(EV_JOIN_FAILED);
dudmuck 0:f2716e543d97 1311 }
dudmuck 0:f2716e543d97 1312
dudmuck 0:f2716e543d97 1313
dudmuck 0:f2716e543d97 1314 static bit_t processJoinAccept (void) {
dudmuck 0:f2716e543d97 1315 ASSERT(LMIC.txrxFlags != TXRX_DNW1 || LMIC.dataLen != 0);
dudmuck 0:f2716e543d97 1316 ASSERT((LMIC.opmode & OP_TXRXPEND)!=0);
dudmuck 0:f2716e543d97 1317
dudmuck 0:f2716e543d97 1318 if( LMIC.dataLen == 0 ) {
dudmuck 0:f2716e543d97 1319 nojoinframe:
dudmuck 0:f2716e543d97 1320 if( (LMIC.opmode & OP_JOINING) == 0 ) {
dudmuck 0:f2716e543d97 1321 ASSERT((LMIC.opmode & OP_REJOIN) != 0);
dudmuck 0:f2716e543d97 1322 // REJOIN attempt for roaming
dudmuck 0:f2716e543d97 1323 LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND);
dudmuck 0:f2716e543d97 1324 if( LMIC.rejoinCnt < 10 )
dudmuck 0:f2716e543d97 1325 LMIC.rejoinCnt++;
dudmuck 0:f2716e543d97 1326 reportEvent(EV_REJOIN_FAILED);
dudmuck 0:f2716e543d97 1327 return 1;
dudmuck 0:f2716e543d97 1328 }
dudmuck 0:f2716e543d97 1329 LMIC.opmode &= ~OP_TXRXPEND;
dudmuck 0:f2716e543d97 1330 ostime_t delay = nextJoinState();
dudmuck 0:f2716e543d97 1331 EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC,
dudmuck 0:f2716e543d97 1332 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1333 e_.info = LMIC.datarate|DR_PAGE,
dudmuck 0:f2716e543d97 1334 e_.info2 = osticks2ms(delay)));
dudmuck 0:f2716e543d97 1335 // Build next JOIN REQUEST with next engineUpdate call
dudmuck 0:f2716e543d97 1336 // Optionally, report join failed.
dudmuck 0:f2716e543d97 1337 // Both after a random/chosen amount of ticks.
dudmuck 0:f2716e543d97 1338 os_setTimedCallback(&LMIC.osjob, os_getTime()+delay,
dudmuck 0:f2716e543d97 1339 (delay&1) != 0
dudmuck 0:f2716e543d97 1340 ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed
dudmuck 0:f2716e543d97 1341 : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
dudmuck 0:f2716e543d97 1342 return 1;
dudmuck 0:f2716e543d97 1343 }
dudmuck 0:f2716e543d97 1344 u1_t hdr = LMIC.frame[0];
dudmuck 0:f2716e543d97 1345 u1_t dlen = LMIC.dataLen;
dudmuck 0:f2716e543d97 1346 u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt!
dudmuck 0:f2716e543d97 1347 if( (dlen != LEN_JA && dlen != LEN_JAEXT)
dudmuck 0:f2716e543d97 1348 || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
dudmuck 0:f2716e543d97 1349 EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
dudmuck 0:f2716e543d97 1350 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1351 e_.info = dlen < 4 ? 0 : mic,
dudmuck 0:f2716e543d97 1352 e_.info2 = hdr + (dlen<<8)));
dudmuck 0:f2716e543d97 1353 badframe:
dudmuck 0:f2716e543d97 1354 if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
dudmuck 0:f2716e543d97 1355 return 0;
dudmuck 0:f2716e543d97 1356 goto nojoinframe;
dudmuck 0:f2716e543d97 1357 }
dudmuck 0:f2716e543d97 1358 aes_encrypt(LMIC.frame+1, dlen-1);
dudmuck 0:f2716e543d97 1359 if( !aes_verifyMic0(LMIC.frame, dlen-4) ) {
dudmuck 0:f2716e543d97 1360 EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC,
dudmuck 0:f2716e543d97 1361 e_.info = mic));
dudmuck 0:f2716e543d97 1362 goto badframe;
dudmuck 0:f2716e543d97 1363 }
dudmuck 0:f2716e543d97 1364
dudmuck 0:f2716e543d97 1365 u4_t addr = os_rlsbf4(LMIC.frame+OFF_JA_DEVADDR);
dudmuck 0:f2716e543d97 1366 LMIC.devaddr = addr;
dudmuck 0:f2716e543d97 1367 LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_JA_NETID]) & 0xFFFFFF;
dudmuck 0:f2716e543d97 1368
dudmuck 0:f2716e543d97 1369 #if defined(CFG_eu868)
dudmuck 0:f2716e543d97 1370 initDefaultChannels(0);
dudmuck 0:f2716e543d97 1371 #endif
dudmuck 0:f2716e543d97 1372 if( dlen > LEN_JA ) {
dudmuck 0:f2716e543d97 1373 #if defined(CFG_us915)
dudmuck 0:f2716e543d97 1374 goto badframe;
dudmuck 0:f2716e543d97 1375 #endif
dudmuck 0:f2716e543d97 1376 dlen = OFF_CFLIST;
dudmuck 0:f2716e543d97 1377 for( u1_t chidx=3; chidx<8; chidx++, dlen+=3 ) {
dudmuck 0:f2716e543d97 1378 u4_t freq = convFreq(&LMIC.frame[dlen]);
dudmuck 0:f2716e543d97 1379 if( freq )
dudmuck 0:f2716e543d97 1380 LMIC_setupChannel(chidx, freq, 0, -1);
dudmuck 0:f2716e543d97 1381 }
dudmuck 0:f2716e543d97 1382 }
dudmuck 0:f2716e543d97 1383
dudmuck 0:f2716e543d97 1384 // already incremented when JOIN REQ got sent off
dudmuck 0:f2716e543d97 1385 aes_sessKeys(LMIC.devNonce-1, &LMIC.frame[OFF_JA_ARTNONCE], LMIC.nwkKey, LMIC.artKey);
dudmuck 0:f2716e543d97 1386 DO_DEVDB(LMIC.netid, netid);
dudmuck 0:f2716e543d97 1387 DO_DEVDB(LMIC.devaddr, devaddr);
dudmuck 0:f2716e543d97 1388 DO_DEVDB(LMIC.nwkKey, nwkkey);
dudmuck 0:f2716e543d97 1389 DO_DEVDB(LMIC.artKey, artkey);
dudmuck 0:f2716e543d97 1390
dudmuck 0:f2716e543d97 1391 EV(joininfo, INFO, (e_.arteui = MAIN::CDEV->getArtEui(),
dudmuck 0:f2716e543d97 1392 e_.deveui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1393 e_.devaddr = LMIC.devaddr,
dudmuck 0:f2716e543d97 1394 e_.oldaddr = oldaddr,
dudmuck 0:f2716e543d97 1395 e_.nonce = LMIC.devNonce-1,
dudmuck 0:f2716e543d97 1396 e_.mic = mic,
dudmuck 0:f2716e543d97 1397 e_.reason = ((LMIC.opmode & OP_REJOIN) != 0
dudmuck 0:f2716e543d97 1398 ? EV::joininfo_t::REJOIN_ACCEPT
dudmuck 0:f2716e543d97 1399 : EV::joininfo_t::ACCEPT)));
dudmuck 0:f2716e543d97 1400
dudmuck 0:f2716e543d97 1401 ASSERT((LMIC.opmode & (OP_JOINING|OP_REJOIN))!=0);
dudmuck 0:f2716e543d97 1402 if( (LMIC.opmode & OP_REJOIN) != 0 ) {
dudmuck 0:f2716e543d97 1403 // Lower DR every try below current UP DR
dudmuck 0:f2716e543d97 1404 LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt);
dudmuck 0:f2716e543d97 1405 }
dudmuck 0:f2716e543d97 1406 LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI) | OP_NEXTCHNL;
dudmuck 0:f2716e543d97 1407 stateJustJoined();
dudmuck 0:f2716e543d97 1408 reportEvent(EV_JOINED);
dudmuck 0:f2716e543d97 1409 return 1;
dudmuck 0:f2716e543d97 1410 }
dudmuck 0:f2716e543d97 1411
dudmuck 0:f2716e543d97 1412
dudmuck 0:f2716e543d97 1413 static void processRx2Jacc (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1414 if( LMIC.dataLen == 0 )
dudmuck 0:f2716e543d97 1415 LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot
dudmuck 0:f2716e543d97 1416 processJoinAccept();
dudmuck 0:f2716e543d97 1417 }
dudmuck 0:f2716e543d97 1418
dudmuck 0:f2716e543d97 1419
dudmuck 0:f2716e543d97 1420 static void setupRx2Jacc (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1421 LMIC.osjob.func = FUNC_ADDR(processRx2Jacc);
dudmuck 0:f2716e543d97 1422 setupRx2();
dudmuck 0:f2716e543d97 1423 }
dudmuck 0:f2716e543d97 1424
dudmuck 0:f2716e543d97 1425
dudmuck 0:f2716e543d97 1426 static void processRx1Jacc (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1427 if( LMIC.dataLen == 0 || !processJoinAccept() )
dudmuck 0:f2716e543d97 1428 schedRx2(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc));
dudmuck 0:f2716e543d97 1429 }
dudmuck 0:f2716e543d97 1430
dudmuck 0:f2716e543d97 1431
dudmuck 0:f2716e543d97 1432 static void setupRx1Jacc (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1433 setupRx1(FUNC_ADDR(processRx1Jacc));
dudmuck 0:f2716e543d97 1434 }
dudmuck 0:f2716e543d97 1435
dudmuck 0:f2716e543d97 1436
dudmuck 0:f2716e543d97 1437 static void jreqDone (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1438 txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc));
dudmuck 0:f2716e543d97 1439 }
dudmuck 0:f2716e543d97 1440
dudmuck 0:f2716e543d97 1441 // ======================================== Data frames
dudmuck 0:f2716e543d97 1442
dudmuck 0:f2716e543d97 1443 // Fwd decl.
dudmuck 0:f2716e543d97 1444 static bit_t processDnData(void);
dudmuck 0:f2716e543d97 1445
dudmuck 0:f2716e543d97 1446 static void processRx2DnDataDelay (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1447 processDnData();
dudmuck 0:f2716e543d97 1448 }
dudmuck 0:f2716e543d97 1449
dudmuck 0:f2716e543d97 1450 static void processRx2DnData (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1451 if( LMIC.dataLen == 0 ) {
dudmuck 0:f2716e543d97 1452 LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot
dudmuck 0:f2716e543d97 1453 // Delay callback processing to avoid up TX while gateway is txing our missed frame!
dudmuck 0:f2716e543d97 1454 // Since DNW2 uses SF12 by default we wait 3 secs.
dudmuck 0:f2716e543d97 1455 os_setTimedCallback(&LMIC.osjob,
dudmuck 0:f2716e543d97 1456 (os_getTime() + DNW2_SAFETY_ZONE + rndDelay(2)),
dudmuck 0:f2716e543d97 1457 processRx2DnDataDelay);
dudmuck 0:f2716e543d97 1458 return;
dudmuck 0:f2716e543d97 1459 }
dudmuck 0:f2716e543d97 1460 processDnData();
dudmuck 0:f2716e543d97 1461 }
dudmuck 0:f2716e543d97 1462
dudmuck 0:f2716e543d97 1463
dudmuck 0:f2716e543d97 1464 static void setupRx2DnData (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1465 LMIC.osjob.func = FUNC_ADDR(processRx2DnData);
dudmuck 0:f2716e543d97 1466 setupRx2();
dudmuck 0:f2716e543d97 1467 }
dudmuck 0:f2716e543d97 1468
dudmuck 0:f2716e543d97 1469
dudmuck 0:f2716e543d97 1470 static void processRx1DnData (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1471 if( LMIC.dataLen == 0 || !processDnData() )
dudmuck 0:f2716e543d97 1472 schedRx2(DELAY_DNW2_osticks, FUNC_ADDR(setupRx2DnData));
dudmuck 0:f2716e543d97 1473 }
dudmuck 0:f2716e543d97 1474
dudmuck 0:f2716e543d97 1475
dudmuck 0:f2716e543d97 1476 static void setupRx1DnData (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1477 setupRx1(FUNC_ADDR(processRx1DnData));
dudmuck 0:f2716e543d97 1478 }
dudmuck 0:f2716e543d97 1479
dudmuck 0:f2716e543d97 1480
dudmuck 0:f2716e543d97 1481 static void updataDone (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1482 txDone(DELAY_DNW1_osticks, FUNC_ADDR(setupRx1DnData));
dudmuck 0:f2716e543d97 1483 }
dudmuck 0:f2716e543d97 1484
dudmuck 0:f2716e543d97 1485 // ========================================
dudmuck 0:f2716e543d97 1486
dudmuck 0:f2716e543d97 1487
dudmuck 0:f2716e543d97 1488 static void buildDataFrame (void) {
dudmuck 0:f2716e543d97 1489 bit_t txdata = ((LMIC.opmode & (OP_TXDATA|OP_POLL)) != OP_POLL);
dudmuck 0:f2716e543d97 1490 u1_t dlen = txdata ? LMIC.pendTxLen : 0;
dudmuck 0:f2716e543d97 1491
dudmuck 0:f2716e543d97 1492 // Piggyback MAC options
dudmuck 0:f2716e543d97 1493 // Prioritize by importance
dudmuck 0:f2716e543d97 1494 int end = OFF_DAT_OPTS;
dudmuck 0:f2716e543d97 1495 if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE)) == (OP_TRACK|OP_PINGABLE) ) {
dudmuck 0:f2716e543d97 1496 // Indicate pingability in every UP frame
dudmuck 0:f2716e543d97 1497 LMIC.frame[end] = MCMD_PING_IND;
dudmuck 0:f2716e543d97 1498 LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4);
dudmuck 0:f2716e543d97 1499 end += 2;
dudmuck 0:f2716e543d97 1500 }
dudmuck 0:f2716e543d97 1501 if( LMIC.dutyCapAns ) {
dudmuck 0:f2716e543d97 1502 LMIC.frame[end] = MCMD_DCAP_ANS;
dudmuck 0:f2716e543d97 1503 end += 1;
dudmuck 0:f2716e543d97 1504 LMIC.dutyCapAns = 0;
dudmuck 0:f2716e543d97 1505 }
dudmuck 0:f2716e543d97 1506 if( LMIC.dn2Ans ) {
dudmuck 0:f2716e543d97 1507 LMIC.frame[end+0] = MCMD_DN2P_ANS;
dudmuck 0:f2716e543d97 1508 LMIC.frame[end+1] = LMIC.dn2Ans & ~MCMD_DN2P_ANS_RFU;
dudmuck 0:f2716e543d97 1509 end += 2;
dudmuck 0:f2716e543d97 1510 LMIC.dn2Ans = 0;
dudmuck 0:f2716e543d97 1511 }
dudmuck 0:f2716e543d97 1512 if( LMIC.devsAns ) { // answer to device status
dudmuck 0:f2716e543d97 1513 LMIC.frame[end+0] = MCMD_DEVS_ANS;
dudmuck 0:f2716e543d97 1514 LMIC.frame[end+1] = LMIC.margin;
dudmuck 0:f2716e543d97 1515 LMIC.frame[end+2] = os_getBattLevel();
dudmuck 0:f2716e543d97 1516 end += 3;
dudmuck 0:f2716e543d97 1517 LMIC.devsAns = 0;
dudmuck 0:f2716e543d97 1518 }
dudmuck 0:f2716e543d97 1519 if( LMIC.ladrAns ) { // answer to ADR change
dudmuck 0:f2716e543d97 1520 LMIC.frame[end+0] = MCMD_LADR_ANS;
dudmuck 0:f2716e543d97 1521 LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU;
dudmuck 0:f2716e543d97 1522 end += 2;
dudmuck 0:f2716e543d97 1523 LMIC.ladrAns = 0;
dudmuck 0:f2716e543d97 1524 }
dudmuck 0:f2716e543d97 1525 if( LMIC.bcninfoTries > 0 ) {
dudmuck 0:f2716e543d97 1526 LMIC.frame[end] = MCMD_BCNI_REQ;
dudmuck 0:f2716e543d97 1527 end += 1;
dudmuck 0:f2716e543d97 1528 }
dudmuck 0:f2716e543d97 1529 if( LMIC.adrChanged ) {
dudmuck 0:f2716e543d97 1530 if( LMIC.adrAckReq < 0 )
dudmuck 0:f2716e543d97 1531 LMIC.adrAckReq = 0;
dudmuck 0:f2716e543d97 1532 LMIC.adrChanged = 0;
dudmuck 0:f2716e543d97 1533 }
dudmuck 0:f2716e543d97 1534 if( LMIC.pingSetAns != 0 ) {
dudmuck 0:f2716e543d97 1535 LMIC.frame[end+0] = MCMD_PING_ANS;
dudmuck 0:f2716e543d97 1536 LMIC.frame[end+1] = LMIC.pingSetAns & ~MCMD_PING_ANS_RFU;
dudmuck 0:f2716e543d97 1537 end += 2;
dudmuck 0:f2716e543d97 1538 LMIC.pingSetAns = 0;
dudmuck 0:f2716e543d97 1539 }
dudmuck 0:f2716e543d97 1540 if( LMIC.snchAns ) {
dudmuck 0:f2716e543d97 1541 LMIC.frame[end+0] = MCMD_SNCH_ANS;
dudmuck 0:f2716e543d97 1542 LMIC.frame[end+1] = LMIC.snchAns & ~MCMD_SNCH_ANS_RFU;
dudmuck 0:f2716e543d97 1543 end += 2;
dudmuck 0:f2716e543d97 1544 LMIC.snchAns = 0;
dudmuck 0:f2716e543d97 1545 }
dudmuck 0:f2716e543d97 1546 ASSERT(end <= OFF_DAT_OPTS+16);
dudmuck 0:f2716e543d97 1547
dudmuck 0:f2716e543d97 1548 u1_t flen = end + (txdata ? 5+dlen : 4);
dudmuck 0:f2716e543d97 1549 if( flen > MAX_LEN_FRAME ) {
dudmuck 0:f2716e543d97 1550 // Options and payload too big - delay payload
dudmuck 0:f2716e543d97 1551 txdata = 0;
dudmuck 0:f2716e543d97 1552 flen = end+4;
dudmuck 0:f2716e543d97 1553 }
dudmuck 0:f2716e543d97 1554 LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DAUP | HDR_MAJOR_V1;
dudmuck 0:f2716e543d97 1555 LMIC.frame[OFF_DAT_FCT] = (LMIC.dnConf | LMIC.adrEnabled
dudmuck 0:f2716e543d97 1556 | (LMIC.adrAckReq >= 0 ? FCT_ADRARQ : 0)
dudmuck 0:f2716e543d97 1557 | (end-OFF_DAT_OPTS));
dudmuck 0:f2716e543d97 1558 os_wlsbf4(LMIC.frame+OFF_DAT_ADDR, LMIC.devaddr);
dudmuck 0:f2716e543d97 1559
dudmuck 0:f2716e543d97 1560 if( LMIC.txCnt == 0 ) {
dudmuck 0:f2716e543d97 1561 LMIC.seqnoUp += 1;
dudmuck 0:f2716e543d97 1562 DO_DEVDB(LMIC.seqnoUp,seqnoUp);
dudmuck 0:f2716e543d97 1563 } else {
dudmuck 0:f2716e543d97 1564 EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX,
dudmuck 0:f2716e543d97 1565 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1566 e_.info = LMIC.seqnoUp-1,
dudmuck 0:f2716e543d97 1567 e_.info2 = ((LMIC.txCnt+1) |
dudmuck 0:f2716e543d97 1568 (DRADJUST[LMIC.txCnt+1] << 8) |
dudmuck 0:f2716e543d97 1569 ((LMIC.datarate|DR_PAGE)<<16))));
dudmuck 0:f2716e543d97 1570 }
dudmuck 0:f2716e543d97 1571 os_wlsbf2(LMIC.frame+OFF_DAT_SEQNO, LMIC.seqnoUp-1);
dudmuck 0:f2716e543d97 1572
dudmuck 0:f2716e543d97 1573 // Clear pending DN confirmation
dudmuck 0:f2716e543d97 1574 LMIC.dnConf = 0;
dudmuck 0:f2716e543d97 1575
dudmuck 0:f2716e543d97 1576 if( txdata ) {
dudmuck 0:f2716e543d97 1577 if( LMIC.pendTxConf ) {
dudmuck 0:f2716e543d97 1578 // Confirmed only makes sense if we have a payload (or at least a port)
dudmuck 0:f2716e543d97 1579 LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DCUP | HDR_MAJOR_V1;
dudmuck 0:f2716e543d97 1580 if( LMIC.txCnt == 0 ) LMIC.txCnt = 1;
dudmuck 0:f2716e543d97 1581 }
dudmuck 0:f2716e543d97 1582 LMIC.frame[end] = LMIC.pendTxPort;
dudmuck 0:f2716e543d97 1583 os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen);
dudmuck 0:f2716e543d97 1584 aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey,
dudmuck 0:f2716e543d97 1585 LMIC.devaddr, LMIC.seqnoUp-1,
dudmuck 0:f2716e543d97 1586 /*up*/0, LMIC.frame+end+1, dlen);
dudmuck 0:f2716e543d97 1587 }
dudmuck 0:f2716e543d97 1588 aes_appendMic(LMIC.nwkKey, LMIC.devaddr, LMIC.seqnoUp-1, /*up*/0, LMIC.frame, flen-4);
dudmuck 0:f2716e543d97 1589
dudmuck 0:f2716e543d97 1590 EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1591 e_.devaddr = LMIC.devaddr,
dudmuck 0:f2716e543d97 1592 e_.seqno = LMIC.seqnoUp-1,
dudmuck 0:f2716e543d97 1593 e_.flags = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP),
dudmuck 0:f2716e543d97 1594 e_.mic = Base::lsbf4(&LMIC.frame[flen-4]),
dudmuck 0:f2716e543d97 1595 e_.hdr = LMIC.frame[LORA::OFF_DAT_HDR],
dudmuck 0:f2716e543d97 1596 e_.fct = LMIC.frame[LORA::OFF_DAT_FCT],
dudmuck 0:f2716e543d97 1597 e_.port = LMIC.pendTxPort,
dudmuck 0:f2716e543d97 1598 e_.plen = txdata ? dlen : 0,
dudmuck 0:f2716e543d97 1599 e_.opts.length = end-LORA::OFF_DAT_OPTS,
dudmuck 0:f2716e543d97 1600 memcpy(&e_.opts[0], LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS)));
dudmuck 0:f2716e543d97 1601 LMIC.dataLen = flen;
dudmuck 0:f2716e543d97 1602 }
dudmuck 0:f2716e543d97 1603
dudmuck 0:f2716e543d97 1604
dudmuck 0:f2716e543d97 1605 // Callback from HAL during scan mode or when job timer expires.
dudmuck 0:f2716e543d97 1606 static void onBcnRx (xref2osjob_t job) {
dudmuck 0:f2716e543d97 1607 // If we arrive via job timer make sure to put radio to rest.
dudmuck 0:f2716e543d97 1608 os_radio(RADIO_RST);
dudmuck 0:f2716e543d97 1609 os_clearCallback(&LMIC.osjob);
dudmuck 0:f2716e543d97 1610 if( LMIC.dataLen == 0 ) {
dudmuck 0:f2716e543d97 1611 // Nothing received - timeout
dudmuck 0:f2716e543d97 1612 LMIC.opmode &= ~(OP_SCAN | OP_TRACK);
dudmuck 0:f2716e543d97 1613 reportEvent(EV_SCAN_TIMEOUT);
dudmuck 0:f2716e543d97 1614 return;
dudmuck 0:f2716e543d97 1615 }
dudmuck 0:f2716e543d97 1616 if( decodeBeacon() <= 0 ) {
dudmuck 0:f2716e543d97 1617 // Something is wrong with the beacon - continue scan
dudmuck 0:f2716e543d97 1618 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1619 os_radio(RADIO_RXON);
dudmuck 0:f2716e543d97 1620 os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx));
dudmuck 0:f2716e543d97 1621 return;
dudmuck 0:f2716e543d97 1622 }
dudmuck 0:f2716e543d97 1623 // Found our 1st beacon
dudmuck 0:f2716e543d97 1624 // We don't have a previous beacon to calc some drift - assume
dudmuck 0:f2716e543d97 1625 // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
dudmuck 0:f2716e543d97 1626 calcBcnRxWindowFromMillis(13,1);
dudmuck 0:f2716e543d97 1627 LMIC.opmode &= ~OP_SCAN; // turn SCAN off
dudmuck 0:f2716e543d97 1628 LMIC.opmode |= OP_TRACK; // auto enable tracking
dudmuck 0:f2716e543d97 1629 reportEvent(EV_BEACON_FOUND); // can be disabled in callback
dudmuck 0:f2716e543d97 1630 }
dudmuck 0:f2716e543d97 1631
dudmuck 0:f2716e543d97 1632
dudmuck 0:f2716e543d97 1633 // Enable receiver to listen to incoming beacons
dudmuck 0:f2716e543d97 1634 // netid defines when scan stops (any or specific beacon)
dudmuck 0:f2716e543d97 1635 // This mode ends with events: EV_SCAN_TIMEOUT/EV_SCAN_BEACON
dudmuck 0:f2716e543d97 1636 // Implicitely cancels any pending TX/RX transaction.
dudmuck 0:f2716e543d97 1637 // Also cancels an onpoing joining procedure.
dudmuck 0:f2716e543d97 1638 static void startScan (void) {
dudmuck 0:f2716e543d97 1639 ASSERT(LMIC.devaddr!=0 && (LMIC.opmode & OP_JOINING)==0);
dudmuck 0:f2716e543d97 1640 if( (LMIC.opmode & OP_SHUTDOWN) != 0 )
dudmuck 0:f2716e543d97 1641 return;
dudmuck 0:f2716e543d97 1642 // Cancel onging TX/RX transaction
dudmuck 0:f2716e543d97 1643 LMIC.txCnt = LMIC.dnConf = LMIC.bcninfo.flags = 0;
dudmuck 0:f2716e543d97 1644 LMIC.opmode = (LMIC.opmode | OP_SCAN) & ~(OP_TXRXPEND);
dudmuck 0:f2716e543d97 1645 setBcnRxParams();
dudmuck 0:f2716e543d97 1646 LMIC.rxtime = LMIC.bcninfo.txtime = os_getTime() + sec2osticks(BCN_INTV_sec+1);
dudmuck 0:f2716e543d97 1647 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime, FUNC_ADDR(onBcnRx));
dudmuck 0:f2716e543d97 1648 os_radio(RADIO_RXON);
dudmuck 0:f2716e543d97 1649 }
dudmuck 0:f2716e543d97 1650
dudmuck 0:f2716e543d97 1651
dudmuck 0:f2716e543d97 1652 bit_t LMIC_enableTracking (u1_t tryBcnInfo) {
dudmuck 0:f2716e543d97 1653 if( (LMIC.opmode & (OP_SCAN|OP_TRACK|OP_SHUTDOWN)) != 0 )
dudmuck 0:f2716e543d97 1654 return 0; // already in progress or failed to enable
dudmuck 0:f2716e543d97 1655 // If BCN info requested from NWK then app has to take are
dudmuck 0:f2716e543d97 1656 // of sending data up so that MCMD_BCNI_REQ can be attached.
dudmuck 0:f2716e543d97 1657 if( (LMIC.bcninfoTries = tryBcnInfo) == 0 )
dudmuck 0:f2716e543d97 1658 startScan();
dudmuck 0:f2716e543d97 1659 return 1; // enabled
dudmuck 0:f2716e543d97 1660 }
dudmuck 0:f2716e543d97 1661
dudmuck 0:f2716e543d97 1662
dudmuck 0:f2716e543d97 1663 void LMIC_disableTracking (void) {
dudmuck 0:f2716e543d97 1664 LMIC.opmode &= ~(OP_SCAN|OP_TRACK);
dudmuck 0:f2716e543d97 1665 LMIC.bcninfoTries = 0;
dudmuck 0:f2716e543d97 1666 engineUpdate();
dudmuck 0:f2716e543d97 1667 }
dudmuck 0:f2716e543d97 1668
dudmuck 0:f2716e543d97 1669
dudmuck 0:f2716e543d97 1670 // ================================================================================
dudmuck 0:f2716e543d97 1671 //
dudmuck 0:f2716e543d97 1672 // Join stuff
dudmuck 0:f2716e543d97 1673 //
dudmuck 0:f2716e543d97 1674 // ================================================================================
dudmuck 0:f2716e543d97 1675
dudmuck 0:f2716e543d97 1676 static void buildJoinRequest (u1_t ftype) {
dudmuck 0:f2716e543d97 1677 // Do not use pendTxData since we might have a pending
dudmuck 0:f2716e543d97 1678 // user level frame in there. Use RX holding area instead.
dudmuck 0:f2716e543d97 1679 xref2u1_t d = LMIC.frame;
dudmuck 0:f2716e543d97 1680 d[OFF_JR_HDR] = ftype;
dudmuck 0:f2716e543d97 1681 os_getArtEui(d + OFF_JR_ARTEUI);
dudmuck 0:f2716e543d97 1682 os_getDevEui(d + OFF_JR_DEVEUI);
dudmuck 0:f2716e543d97 1683 os_wlsbf2(d + OFF_JR_DEVNONCE, LMIC.devNonce);
dudmuck 0:f2716e543d97 1684 aes_appendMic0(d, OFF_JR_MIC);
dudmuck 0:f2716e543d97 1685
dudmuck 0:f2716e543d97 1686 EV(joininfo,INFO,(e_.deveui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1687 e_.arteui = MAIN::CDEV->getArtEui(),
dudmuck 0:f2716e543d97 1688 e_.nonce = LMIC.devNonce,
dudmuck 0:f2716e543d97 1689 e_.oldaddr = LMIC.devaddr,
dudmuck 0:f2716e543d97 1690 e_.mic = Base::lsbf4(&d[LORA::OFF_JR_MIC]),
dudmuck 0:f2716e543d97 1691 e_.reason = ((LMIC.opmode & OP_REJOIN) != 0
dudmuck 0:f2716e543d97 1692 ? EV::joininfo_t::REJOIN_REQUEST
dudmuck 0:f2716e543d97 1693 : EV::joininfo_t::REQUEST)));
dudmuck 0:f2716e543d97 1694 LMIC.dataLen = LEN_JR;
dudmuck 0:f2716e543d97 1695 LMIC.devNonce++;
dudmuck 0:f2716e543d97 1696 DO_DEVDB(LMIC.devNonce,devNonce);
dudmuck 0:f2716e543d97 1697 }
dudmuck 0:f2716e543d97 1698
dudmuck 0:f2716e543d97 1699 static void startJoining (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1700 reportEvent(EV_JOINING);
dudmuck 0:f2716e543d97 1701 }
dudmuck 0:f2716e543d97 1702
dudmuck 0:f2716e543d97 1703 // Start join procedure if not already joined.
dudmuck 0:f2716e543d97 1704 bit_t LMIC_startJoining (void) {
dudmuck 0:f2716e543d97 1705 if( LMIC.devaddr == 0 ) {
dudmuck 0:f2716e543d97 1706 // There should be no TX/RX going on
dudmuck 0:f2716e543d97 1707 ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0);
dudmuck 0:f2716e543d97 1708 // Lift any previous duty limitation
dudmuck 0:f2716e543d97 1709 LMIC.globalDutyRate = 0;
dudmuck 0:f2716e543d97 1710 // Cancel scanning
dudmuck 0:f2716e543d97 1711 LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL);
dudmuck 0:f2716e543d97 1712 // Setup state
dudmuck 0:f2716e543d97 1713 LMIC.rejoinCnt = LMIC.txCnt = LMIC.pendTxConf = 0;
dudmuck 0:f2716e543d97 1714 initJoinLoop();
dudmuck 0:f2716e543d97 1715 LMIC.opmode |= OP_JOINING;
dudmuck 0:f2716e543d97 1716 // reportEvent will call engineUpdate which then starts sending JOIN REQUESTS
dudmuck 0:f2716e543d97 1717 os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining));
dudmuck 0:f2716e543d97 1718 return 1;
dudmuck 0:f2716e543d97 1719 }
dudmuck 0:f2716e543d97 1720 return 0; // already joined
dudmuck 0:f2716e543d97 1721 }
dudmuck 0:f2716e543d97 1722
dudmuck 0:f2716e543d97 1723
dudmuck 0:f2716e543d97 1724 // ================================================================================
dudmuck 0:f2716e543d97 1725 //
dudmuck 0:f2716e543d97 1726 //
dudmuck 0:f2716e543d97 1727 //
dudmuck 0:f2716e543d97 1728 // ================================================================================
dudmuck 0:f2716e543d97 1729
dudmuck 0:f2716e543d97 1730 static void processPingRx (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1731 if( LMIC.dataLen != 0 ) {
dudmuck 0:f2716e543d97 1732 LMIC.txrxFlags = TXRX_PING;
dudmuck 0:f2716e543d97 1733 if( decodeFrame() ) {
dudmuck 0:f2716e543d97 1734 reportEvent(EV_RXCOMPLETE);
dudmuck 0:f2716e543d97 1735 return;
dudmuck 0:f2716e543d97 1736 }
dudmuck 0:f2716e543d97 1737 }
dudmuck 0:f2716e543d97 1738 // Pick next ping slot
dudmuck 0:f2716e543d97 1739 engineUpdate();
dudmuck 0:f2716e543d97 1740 }
dudmuck 0:f2716e543d97 1741
dudmuck 0:f2716e543d97 1742
dudmuck 0:f2716e543d97 1743 static bit_t processDnData (void) {
dudmuck 0:f2716e543d97 1744 ASSERT((LMIC.opmode & OP_TXRXPEND)!=0);
dudmuck 0:f2716e543d97 1745
dudmuck 0:f2716e543d97 1746 if( LMIC.dataLen == 0 ) {
dudmuck 0:f2716e543d97 1747 norx:
dudmuck 0:f2716e543d97 1748 if( LMIC.txCnt != 0 ) {
dudmuck 0:f2716e543d97 1749 if( LMIC.txCnt < TXCONF_ATTEMPTS ) {
dudmuck 0:f2716e543d97 1750 LMIC.txCnt += 1;
dudmuck 0:f2716e543d97 1751 setDrTxpow(DRCHG_NOACK, lowerDR(LMIC.datarate, DRADJUST[LMIC.txCnt]), KEEP_TXPOW);
dudmuck 0:f2716e543d97 1752 // Schedule another retransmission
dudmuck 0:f2716e543d97 1753 txDelay(LMIC.rxtime, RETRY_PERIOD_secs);
dudmuck 0:f2716e543d97 1754 LMIC.opmode &= ~OP_TXRXPEND;
dudmuck 0:f2716e543d97 1755 engineUpdate();
dudmuck 0:f2716e543d97 1756 return 1;
dudmuck 0:f2716e543d97 1757 }
dudmuck 0:f2716e543d97 1758 LMIC.txrxFlags = TXRX_NACK | TXRX_NOPORT;
dudmuck 0:f2716e543d97 1759 } else {
dudmuck 0:f2716e543d97 1760 // Nothing received - implies no port
dudmuck 0:f2716e543d97 1761 LMIC.txrxFlags = TXRX_NOPORT;
dudmuck 0:f2716e543d97 1762 }
dudmuck 0:f2716e543d97 1763 if( LMIC.adrAckReq != LINK_CHECK_OFF )
dudmuck 0:f2716e543d97 1764 LMIC.adrAckReq += 1;
dudmuck 0:f2716e543d97 1765 LMIC.dataBeg = LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1766 txcomplete:
dudmuck 0:f2716e543d97 1767 LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
dudmuck 0:f2716e543d97 1768 if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) {
dudmuck 0:f2716e543d97 1769 LMIC.opmode &= ~OP_LINKDEAD;
dudmuck 0:f2716e543d97 1770 reportEvent(EV_LINK_ALIVE);
dudmuck 0:f2716e543d97 1771 }
dudmuck 0:f2716e543d97 1772 reportEvent(EV_TXCOMPLETE);
dudmuck 0:f2716e543d97 1773 // If we haven't heard from NWK in a while although we asked for a sign
dudmuck 0:f2716e543d97 1774 // assume link is dead - notify application and keep going
dudmuck 0:f2716e543d97 1775 if( LMIC.adrAckReq > LINK_CHECK_DEAD ) {
dudmuck 0:f2716e543d97 1776 // We haven't heard from NWK for some time although we
dudmuck 0:f2716e543d97 1777 // asked for a response for some time - assume we're disconnected. Lower DR one notch.
dudmuck 0:f2716e543d97 1778 EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD,
dudmuck 0:f2716e543d97 1779 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1780 e_.info = LMIC.adrAckReq));
dudmuck 0:f2716e543d97 1781 setDrTxpow(DRCHG_NOADRACK, decDR((dr_t)LMIC.datarate), KEEP_TXPOW);
dudmuck 0:f2716e543d97 1782 LMIC.adrAckReq = LINK_CHECK_CONT;
dudmuck 0:f2716e543d97 1783 LMIC.opmode |= OP_REJOIN|OP_LINKDEAD;
dudmuck 0:f2716e543d97 1784 reportEvent(EV_LINK_DEAD);
dudmuck 0:f2716e543d97 1785 }
dudmuck 0:f2716e543d97 1786 // If this falls to zero the NWK did not answer our MCMD_BCNI_REQ commands - try full scan
dudmuck 0:f2716e543d97 1787 if( LMIC.bcninfoTries > 0 ) {
dudmuck 0:f2716e543d97 1788 if( (LMIC.opmode & OP_TRACK) != 0 ) {
dudmuck 0:f2716e543d97 1789 reportEvent(EV_BEACON_FOUND);
dudmuck 0:f2716e543d97 1790 LMIC.bcninfoTries = 0;
dudmuck 0:f2716e543d97 1791 }
dudmuck 0:f2716e543d97 1792 else if( --LMIC.bcninfoTries == 0 ) {
dudmuck 0:f2716e543d97 1793 startScan(); // NWK did not answer - try scan
dudmuck 0:f2716e543d97 1794 }
dudmuck 0:f2716e543d97 1795 }
dudmuck 0:f2716e543d97 1796 return 1;
dudmuck 0:f2716e543d97 1797 }
dudmuck 0:f2716e543d97 1798 if( !decodeFrame() ) {
dudmuck 0:f2716e543d97 1799 if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
dudmuck 0:f2716e543d97 1800 return 0;
dudmuck 0:f2716e543d97 1801 goto norx;
dudmuck 0:f2716e543d97 1802 }
dudmuck 0:f2716e543d97 1803 goto txcomplete;
dudmuck 0:f2716e543d97 1804 }
dudmuck 0:f2716e543d97 1805
dudmuck 0:f2716e543d97 1806
dudmuck 0:f2716e543d97 1807 static void processBeacon (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1808 ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite
dudmuck 0:f2716e543d97 1809 u1_t flags = LMIC.bcninfo.flags;
dudmuck 0:f2716e543d97 1810 ev_t ev;
dudmuck 0:f2716e543d97 1811
dudmuck 0:f2716e543d97 1812 if( LMIC.dataLen != 0 && decodeBeacon() >= 1 ) {
dudmuck 0:f2716e543d97 1813 ev = EV_BEACON_TRACKED;
dudmuck 0:f2716e543d97 1814 if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) {
dudmuck 0:f2716e543d97 1815 // We don't have a previous beacon to calc some drift - assume
dudmuck 0:f2716e543d97 1816 // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
dudmuck 0:f2716e543d97 1817 calcBcnRxWindowFromMillis(13,0);
dudmuck 0:f2716e543d97 1818 goto rev;
dudmuck 0:f2716e543d97 1819 }
dudmuck 0:f2716e543d97 1820 // We have a previous BEACON to calculate some drift
dudmuck 0:f2716e543d97 1821 s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx);
dudmuck 0:f2716e543d97 1822 if( LMIC.missedBcns > 0 ) {
dudmuck 0:f2716e543d97 1823 drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1);
dudmuck 0:f2716e543d97 1824 }
dudmuck 0:f2716e543d97 1825 if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) {
dudmuck 0:f2716e543d97 1826 s2_t diff = LMIC.drift - drift;
dudmuck 0:f2716e543d97 1827 if( diff < 0 ) diff = -diff;
dudmuck 0:f2716e543d97 1828 LMIC.lastDriftDiff = diff;
dudmuck 0:f2716e543d97 1829 if( LMIC.maxDriftDiff < diff )
dudmuck 0:f2716e543d97 1830 LMIC.maxDriftDiff = diff;
dudmuck 0:f2716e543d97 1831 LMIC.bcninfo.flags &= ~BCN_NODDIFF;
dudmuck 0:f2716e543d97 1832 }
dudmuck 0:f2716e543d97 1833 LMIC.drift = drift;
dudmuck 0:f2716e543d97 1834 LMIC.missedBcns = LMIC.rejoinCnt = 0;
dudmuck 0:f2716e543d97 1835 LMIC.bcninfo.flags &= ~BCN_NODRIFT;
dudmuck 0:f2716e543d97 1836 EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT,
dudmuck 0:f2716e543d97 1837 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1838 e_.info = drift,
dudmuck 0:f2716e543d97 1839 e_.info2 = /*occasion BEACON*/0));
dudmuck 0:f2716e543d97 1840 ASSERT((LMIC.bcninfo.flags & (BCN_PARTIAL|BCN_FULL)) != 0);
dudmuck 0:f2716e543d97 1841 } else {
dudmuck 0:f2716e543d97 1842 ev = EV_BEACON_MISSED;
dudmuck 0:f2716e543d97 1843 LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift;
dudmuck 0:f2716e543d97 1844 LMIC.bcninfo.time += BCN_INTV_sec;
dudmuck 0:f2716e543d97 1845 LMIC.missedBcns++;
dudmuck 0:f2716e543d97 1846 // Delay any possible TX after surmised beacon - it's there although we missed it
dudmuck 0:f2716e543d97 1847 txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4);
dudmuck 0:f2716e543d97 1848 if( LMIC.missedBcns > MAX_MISSED_BCNS )
dudmuck 0:f2716e543d97 1849 LMIC.opmode |= OP_REJOIN; // try if we can roam to another network
dudmuck 0:f2716e543d97 1850 if( LMIC.bcnRxsyms > MAX_RXSYMS ) {
dudmuck 0:f2716e543d97 1851 LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN);
dudmuck 0:f2716e543d97 1852 reportEvent(EV_LOST_TSYNC);
dudmuck 0:f2716e543d97 1853 return;
dudmuck 0:f2716e543d97 1854 }
dudmuck 0:f2716e543d97 1855 }
dudmuck 0:f2716e543d97 1856 LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - calcRxWindow(0,DR_BCN);
dudmuck 0:f2716e543d97 1857 LMIC.bcnRxsyms = LMIC.rxsyms;
dudmuck 0:f2716e543d97 1858 rev:
dudmuck 0:f2716e543d97 1859 #ifdef CFG_us915
dudmuck 0:f2716e543d97 1860 LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7;
dudmuck 0:f2716e543d97 1861 #endif
dudmuck 0:f2716e543d97 1862 if( (LMIC.opmode & OP_PINGINI) != 0 )
dudmuck 0:f2716e543d97 1863 rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer!
dudmuck 0:f2716e543d97 1864 reportEvent(ev);
dudmuck 0:f2716e543d97 1865 }
dudmuck 0:f2716e543d97 1866
dudmuck 0:f2716e543d97 1867
dudmuck 0:f2716e543d97 1868 static void startRxBcn (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1869 LMIC.osjob.func = FUNC_ADDR(processBeacon);
dudmuck 0:f2716e543d97 1870 os_radio(RADIO_RX);
dudmuck 0:f2716e543d97 1871 }
dudmuck 0:f2716e543d97 1872
dudmuck 0:f2716e543d97 1873
dudmuck 0:f2716e543d97 1874 static void startRxPing (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1875 LMIC.osjob.func = FUNC_ADDR(processPingRx);
dudmuck 0:f2716e543d97 1876 os_radio(RADIO_RX);
dudmuck 0:f2716e543d97 1877 }
dudmuck 0:f2716e543d97 1878
dudmuck 0:f2716e543d97 1879
dudmuck 0:f2716e543d97 1880 // Decide what to do next for the MAC layer of a device
dudmuck 0:f2716e543d97 1881 static void engineUpdate (void) {
dudmuck 0:f2716e543d97 1882 // Check for ongoing state: scan or TX/RX transaction
dudmuck 0:f2716e543d97 1883 if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 )
dudmuck 0:f2716e543d97 1884 return;
dudmuck 0:f2716e543d97 1885
dudmuck 0:f2716e543d97 1886 if( LMIC.devaddr == 0 && (LMIC.opmode & OP_JOINING) == 0 ) {
dudmuck 0:f2716e543d97 1887 LMIC_startJoining();
dudmuck 0:f2716e543d97 1888 return;
dudmuck 0:f2716e543d97 1889 }
dudmuck 0:f2716e543d97 1890
dudmuck 0:f2716e543d97 1891 ostime_t now = os_getTime();
dudmuck 0:f2716e543d97 1892 ostime_t rxtime = 0;
dudmuck 0:f2716e543d97 1893 ostime_t txbeg = 0;
dudmuck 0:f2716e543d97 1894
dudmuck 0:f2716e543d97 1895 if( (LMIC.opmode & OP_TRACK) != 0 ) {
dudmuck 0:f2716e543d97 1896 // We are tracking a beacon
dudmuck 0:f2716e543d97 1897 ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
dudmuck 0:f2716e543d97 1898 rxtime = LMIC.bcnRxtime - RX_RAMPUP;
dudmuck 0:f2716e543d97 1899 }
dudmuck 0:f2716e543d97 1900
dudmuck 0:f2716e543d97 1901 if( (LMIC.opmode & (OP_JOINING|OP_REJOIN|OP_TXDATA|OP_POLL)) != 0 ) {
dudmuck 0:f2716e543d97 1902 // Need to TX some data...
dudmuck 0:f2716e543d97 1903 // Assuming txChnl points to channel which first becomes available again.
dudmuck 0:f2716e543d97 1904 bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0);
dudmuck 0:f2716e543d97 1905 // Find next suitable channel and return availability time
dudmuck 0:f2716e543d97 1906 if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) {
dudmuck 0:f2716e543d97 1907 txbeg = LMIC.txend = nextTx(now);
dudmuck 0:f2716e543d97 1908 LMIC.opmode &= ~OP_NEXTCHNL;
dudmuck 0:f2716e543d97 1909 } else {
dudmuck 0:f2716e543d97 1910 txbeg = LMIC.txend;
dudmuck 0:f2716e543d97 1911 }
dudmuck 0:f2716e543d97 1912 // Delayed TX or waiting for duty cycle?
dudmuck 0:f2716e543d97 1913 if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 )
dudmuck 0:f2716e543d97 1914 txbeg = LMIC.globalDutyAvail;
dudmuck 0:f2716e543d97 1915 // If we're tracking a beacon...
dudmuck 0:f2716e543d97 1916 // then make sure TX-RX transaction is complete before beacon
dudmuck 0:f2716e543d97 1917 if( (LMIC.opmode & OP_TRACK) != 0 &&
dudmuck 0:f2716e543d97 1918 txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) {
dudmuck 0:f2716e543d97 1919 // Not enough time to complete TX-RX before beacon - postpone after beacon.
dudmuck 0:f2716e543d97 1920 // In order to avoid clustering of postponed TX right after beacon randomize start!
dudmuck 0:f2716e543d97 1921 txDelay(rxtime + BCN_RESERVE_osticks, 16);
dudmuck 0:f2716e543d97 1922 txbeg = 0;
dudmuck 0:f2716e543d97 1923 goto checkrx;
dudmuck 0:f2716e543d97 1924 }
dudmuck 0:f2716e543d97 1925 // Earliest possible time vs overhead to setup radio
dudmuck 0:f2716e543d97 1926 if( txbeg - (now + TX_RAMPUP) < 0 ) {
dudmuck 0:f2716e543d97 1927 // We could send right now!
dudmuck 0:f2716e543d97 1928 txbeg = now;
dudmuck 0:f2716e543d97 1929 dr_t txdr = (dr_t)LMIC.datarate;
dudmuck 0:f2716e543d97 1930 if( jacc ) {
dudmuck 0:f2716e543d97 1931 u1_t ftype;
dudmuck 0:f2716e543d97 1932 if( (LMIC.opmode & OP_REJOIN) != 0 ) {
dudmuck 0:f2716e543d97 1933 txdr = lowerDR(txdr, LMIC.rejoinCnt);
dudmuck 0:f2716e543d97 1934 ftype = HDR_FTYPE_REJOIN;
dudmuck 0:f2716e543d97 1935 } else {
dudmuck 0:f2716e543d97 1936 ftype = HDR_FTYPE_JREQ;
dudmuck 0:f2716e543d97 1937 }
dudmuck 0:f2716e543d97 1938 buildJoinRequest(ftype);
dudmuck 0:f2716e543d97 1939 LMIC.osjob.func = FUNC_ADDR(jreqDone);
dudmuck 0:f2716e543d97 1940 } else {
dudmuck 0:f2716e543d97 1941 if( LMIC.seqnoDn >= 0xFFFFFF80 ) {
dudmuck 0:f2716e543d97 1942 // Imminent roll over - proactively reset MAC
dudmuck 0:f2716e543d97 1943 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
dudmuck 0:f2716e543d97 1944 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1945 e_.info = LMIC.seqnoDn,
dudmuck 0:f2716e543d97 1946 e_.info2 = 0));
dudmuck 0:f2716e543d97 1947 // Device has to react! NWK will not roll over and just stop sending.
dudmuck 0:f2716e543d97 1948 // Thus, we have N frames to detect a possible lock up.
dudmuck 0:f2716e543d97 1949 reset:
dudmuck 0:f2716e543d97 1950 os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset));
dudmuck 0:f2716e543d97 1951 return;
dudmuck 0:f2716e543d97 1952 }
dudmuck 0:f2716e543d97 1953 if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) {
dudmuck 0:f2716e543d97 1954 // Roll over of up seq counter
dudmuck 0:f2716e543d97 1955 EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER,
dudmuck 0:f2716e543d97 1956 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1957 e_.info2 = LMIC.seqnoUp));
dudmuck 0:f2716e543d97 1958 // Do not run RESET event callback from here!
dudmuck 0:f2716e543d97 1959 // App code might do some stuff after send unaware of RESET.
dudmuck 0:f2716e543d97 1960 goto reset;
dudmuck 0:f2716e543d97 1961 }
dudmuck 0:f2716e543d97 1962 buildDataFrame();
dudmuck 0:f2716e543d97 1963 LMIC.osjob.func = FUNC_ADDR(updataDone);
dudmuck 0:f2716e543d97 1964 }
dudmuck 0:f2716e543d97 1965 LMIC.rps = setCr(updr2rps(txdr), (cr_t)LMIC.errcr);
dudmuck 0:f2716e543d97 1966 LMIC.dndr = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1
dudmuck 0:f2716e543d97 1967 LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL;
dudmuck 0:f2716e543d97 1968 updateTx(txbeg);
dudmuck 0:f2716e543d97 1969 os_radio(RADIO_TX);
dudmuck 0:f2716e543d97 1970 return;
dudmuck 0:f2716e543d97 1971 }
dudmuck 0:f2716e543d97 1972 // Cannot yet TX
dudmuck 0:f2716e543d97 1973 if( (LMIC.opmode & OP_TRACK) == 0 )
dudmuck 0:f2716e543d97 1974 goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX
dudmuck 0:f2716e543d97 1975 // Consider RX tasks
dudmuck 0:f2716e543d97 1976 if( txbeg == 0 ) // zero indicates no TX pending
dudmuck 0:f2716e543d97 1977 txbeg += 1; // TX delayed by one tick (insignificant amount of time)
dudmuck 0:f2716e543d97 1978 } else {
dudmuck 0:f2716e543d97 1979 // No TX pending - no scheduled RX
dudmuck 0:f2716e543d97 1980 if( (LMIC.opmode & OP_TRACK) == 0 )
dudmuck 0:f2716e543d97 1981 return;
dudmuck 0:f2716e543d97 1982 }
dudmuck 0:f2716e543d97 1983
dudmuck 0:f2716e543d97 1984 // Are we pingable?
dudmuck 0:f2716e543d97 1985 checkrx:
dudmuck 0:f2716e543d97 1986 if( (LMIC.opmode & OP_PINGINI) != 0 ) {
dudmuck 0:f2716e543d97 1987 // One more RX slot in this beacon period?
dudmuck 0:f2716e543d97 1988 if( rxschedNext(&LMIC.ping, now+RX_RAMPUP) ) {
dudmuck 0:f2716e543d97 1989 if( txbeg != 0 && (txbeg - LMIC.ping.rxtime) < 0 )
dudmuck 0:f2716e543d97 1990 goto txdelay;
dudmuck 0:f2716e543d97 1991 LMIC.rxsyms = LMIC.ping.rxsyms;
dudmuck 0:f2716e543d97 1992 LMIC.rxtime = LMIC.ping.rxtime;
dudmuck 0:f2716e543d97 1993 LMIC.freq = LMIC.ping.freq;
dudmuck 0:f2716e543d97 1994 LMIC.rps = dndr2rps(LMIC.ping.dr);
dudmuck 0:f2716e543d97 1995 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1996 ASSERT(LMIC.rxtime - now+RX_RAMPUP >= 0 );
dudmuck 0:f2716e543d97 1997 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, FUNC_ADDR(startRxPing));
dudmuck 0:f2716e543d97 1998 return;
dudmuck 0:f2716e543d97 1999 }
dudmuck 0:f2716e543d97 2000 // no - just wait for the beacon
dudmuck 0:f2716e543d97 2001 }
dudmuck 0:f2716e543d97 2002
dudmuck 0:f2716e543d97 2003 if( txbeg != 0 && (txbeg - rxtime) < 0 )
dudmuck 0:f2716e543d97 2004 goto txdelay;
dudmuck 0:f2716e543d97 2005
dudmuck 0:f2716e543d97 2006 setBcnRxParams();
dudmuck 0:f2716e543d97 2007 LMIC.rxsyms = LMIC.bcnRxsyms;
dudmuck 0:f2716e543d97 2008 LMIC.rxtime = LMIC.bcnRxtime;
dudmuck 0:f2716e543d97 2009 if( now - rxtime >= 0 ) {
dudmuck 0:f2716e543d97 2010 LMIC.osjob.func = FUNC_ADDR(processBeacon);
dudmuck 0:f2716e543d97 2011 os_radio(RADIO_RX);
dudmuck 0:f2716e543d97 2012 return;
dudmuck 0:f2716e543d97 2013 }
dudmuck 0:f2716e543d97 2014 os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn));
dudmuck 0:f2716e543d97 2015 return;
dudmuck 0:f2716e543d97 2016
dudmuck 0:f2716e543d97 2017 txdelay:
dudmuck 0:f2716e543d97 2018 EV(devCond, INFO, (e_.reason = EV::devCond_t::TX_DELAY,
dudmuck 0:f2716e543d97 2019 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 2020 e_.info = osticks2ms(txbeg-now),
dudmuck 0:f2716e543d97 2021 e_.info2 = LMIC.seqnoUp-1));
dudmuck 0:f2716e543d97 2022 os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate));
dudmuck 0:f2716e543d97 2023 }
dudmuck 0:f2716e543d97 2024
dudmuck 0:f2716e543d97 2025
dudmuck 0:f2716e543d97 2026 void LMIC_setAdrMode (bit_t enabled) {
dudmuck 0:f2716e543d97 2027 LMIC.adrEnabled = enabled ? FCT_ADREN : 0;
dudmuck 0:f2716e543d97 2028 }
dudmuck 0:f2716e543d97 2029
dudmuck 0:f2716e543d97 2030
dudmuck 0:f2716e543d97 2031 // Should we have/need an ext. API like this?
dudmuck 0:f2716e543d97 2032 void LMIC_setDrTxpow (dr_t dr, s1_t txpow) {
dudmuck 0:f2716e543d97 2033 setDrTxpow(DRCHG_SET, dr, txpow);
dudmuck 0:f2716e543d97 2034 }
dudmuck 0:f2716e543d97 2035
dudmuck 0:f2716e543d97 2036
dudmuck 0:f2716e543d97 2037 void LMIC_shutdown (void) {
dudmuck 0:f2716e543d97 2038 os_clearCallback(&LMIC.osjob);
dudmuck 0:f2716e543d97 2039 os_radio(RADIO_RST);
dudmuck 0:f2716e543d97 2040 LMIC.opmode |= OP_SHUTDOWN;
dudmuck 0:f2716e543d97 2041 }
dudmuck 0:f2716e543d97 2042
dudmuck 0:f2716e543d97 2043
dudmuck 0:f2716e543d97 2044 void LMIC_reset (void) {
dudmuck 0:f2716e543d97 2045 EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
dudmuck 0:f2716e543d97 2046 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 2047 e_.info = EV_RESET));
dudmuck 0:f2716e543d97 2048 os_radio(RADIO_RST);
dudmuck 0:f2716e543d97 2049 os_clearCallback(&LMIC.osjob);
dudmuck 0:f2716e543d97 2050
dudmuck 0:f2716e543d97 2051 os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC));
dudmuck 0:f2716e543d97 2052 LMIC.devaddr = 0;
dudmuck 0:f2716e543d97 2053 LMIC.devNonce = os_getRndU2();
dudmuck 0:f2716e543d97 2054 LMIC.opmode = OP_NONE;
dudmuck 0:f2716e543d97 2055 LMIC.errcr = CR_4_5;
dudmuck 0:f2716e543d97 2056 LMIC.adrEnabled = FCT_ADREN;
dudmuck 0:f2716e543d97 2057 LMIC.dn2Dr = DR_DNW2; // we need this for 2nd DN window of join accept
dudmuck 0:f2716e543d97 2058 LMIC.dn2Freq = FREQ_DNW2; // ditto
dudmuck 0:f2716e543d97 2059 LMIC.ping.freq = FREQ_PING; // defaults for ping
dudmuck 0:f2716e543d97 2060 LMIC.ping.dr = DR_PING; // ditto
dudmuck 0:f2716e543d97 2061 LMIC.ping.intvExp = 0xFF;
dudmuck 0:f2716e543d97 2062 #if defined(CFG_us915)
dudmuck 0:f2716e543d97 2063 initDefaultChannels();
dudmuck 0:f2716e543d97 2064 #endif
dudmuck 0:f2716e543d97 2065 DO_DEVDB(LMIC.devaddr, devaddr);
dudmuck 0:f2716e543d97 2066 DO_DEVDB(LMIC.devNonce, devNonce);
dudmuck 0:f2716e543d97 2067 DO_DEVDB(LMIC.dn2Dr, dn2Dr);
dudmuck 0:f2716e543d97 2068 DO_DEVDB(LMIC.dn2Freq, dn2Freq);
dudmuck 0:f2716e543d97 2069 DO_DEVDB(LMIC.ping.freq, pingFreq);
dudmuck 0:f2716e543d97 2070 DO_DEVDB(LMIC.ping.dr, pingDr);
dudmuck 0:f2716e543d97 2071 DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
dudmuck 0:f2716e543d97 2072 }
dudmuck 0:f2716e543d97 2073
dudmuck 0:f2716e543d97 2074
dudmuck 0:f2716e543d97 2075 void LMIC_init (void) {
dudmuck 0:f2716e543d97 2076 LMIC.opmode = OP_SHUTDOWN;
dudmuck 0:f2716e543d97 2077 }
dudmuck 0:f2716e543d97 2078
dudmuck 0:f2716e543d97 2079
dudmuck 0:f2716e543d97 2080 void LMIC_clrTxData (void) {
dudmuck 0:f2716e543d97 2081 LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND|OP_POLL);
dudmuck 0:f2716e543d97 2082 LMIC.pendTxLen = 0;
dudmuck 0:f2716e543d97 2083 if( (LMIC.opmode & (OP_JOINING|OP_SCAN)) != 0 ) // do not interfere with JOINING
dudmuck 0:f2716e543d97 2084 return;
dudmuck 0:f2716e543d97 2085 os_clearCallback(&LMIC.osjob);
dudmuck 0:f2716e543d97 2086 os_radio(RADIO_RST);
dudmuck 0:f2716e543d97 2087 engineUpdate();
dudmuck 0:f2716e543d97 2088 }
dudmuck 0:f2716e543d97 2089
dudmuck 0:f2716e543d97 2090
dudmuck 0:f2716e543d97 2091 void LMIC_setTxData (void) {
dudmuck 0:f2716e543d97 2092 LMIC.opmode |= OP_TXDATA;
dudmuck 0:f2716e543d97 2093 if( (LMIC.opmode & OP_JOINING) == 0 )
dudmuck 0:f2716e543d97 2094 LMIC.txCnt = 0; // cancel any ongoing TX/RX retries
dudmuck 0:f2716e543d97 2095 engineUpdate();
dudmuck 0:f2716e543d97 2096 }
dudmuck 0:f2716e543d97 2097
dudmuck 0:f2716e543d97 2098
dudmuck 0:f2716e543d97 2099 //
dudmuck 0:f2716e543d97 2100 int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) {
dudmuck 0:f2716e543d97 2101 if( dlen > SIZEOFEXPR(LMIC.pendTxData) )
dudmuck 0:f2716e543d97 2102 return -2;
dudmuck 0:f2716e543d97 2103 if( data != (xref2u1_t)0 )
dudmuck 0:f2716e543d97 2104 os_copyMem(LMIC.pendTxData, data, dlen);
dudmuck 0:f2716e543d97 2105 LMIC.pendTxConf = confirmed;
dudmuck 0:f2716e543d97 2106 LMIC.pendTxPort = port;
dudmuck 0:f2716e543d97 2107 LMIC.pendTxLen = dlen;
dudmuck 0:f2716e543d97 2108 LMIC_setTxData();
dudmuck 0:f2716e543d97 2109 return 0;
dudmuck 0:f2716e543d97 2110 }
dudmuck 0:f2716e543d97 2111
dudmuck 0:f2716e543d97 2112
dudmuck 0:f2716e543d97 2113 // Send a payload-less message to signal device is alive
dudmuck 0:f2716e543d97 2114 void LMIC_sendAlive (void) {
dudmuck 0:f2716e543d97 2115 LMIC.opmode |= OP_POLL;
dudmuck 0:f2716e543d97 2116 engineUpdate();
dudmuck 0:f2716e543d97 2117 }
dudmuck 0:f2716e543d97 2118
dudmuck 0:f2716e543d97 2119
dudmuck 0:f2716e543d97 2120 // Check if other networks are around.
dudmuck 0:f2716e543d97 2121 void LMIC_tryRejoin (void) {
dudmuck 0:f2716e543d97 2122 LMIC.opmode |= OP_REJOIN;
dudmuck 0:f2716e543d97 2123 engineUpdate();
dudmuck 0:f2716e543d97 2124 }
dudmuck 0:f2716e543d97 2125
dudmuck 0:f2716e543d97 2126 //! \brief Setup given session keys
dudmuck 0:f2716e543d97 2127 //! and put the MAC in a state as if
dudmuck 0:f2716e543d97 2128 //! a join request/accept would have negotiated just these keys.
dudmuck 0:f2716e543d97 2129 //! It is crucial that the combinations `devaddr/nwkkey` and `devaddr/artkey`
dudmuck 0:f2716e543d97 2130 //! are unique within the network identified by `netid`.
dudmuck 0:f2716e543d97 2131 //! NOTE: on Harvard architectures when session keys are in flash:
dudmuck 0:f2716e543d97 2132 //! Caller has to fill in LMIC.{nwk,art}Key before and pass {nwk,art}Key are NULL
dudmuck 0:f2716e543d97 2133 //! \param netid a 24 bit number describing the network id this device is using
dudmuck 0:f2716e543d97 2134 //! \param devaddr the 32 bit session address of the device. It is strongly recommended
dudmuck 0:f2716e543d97 2135 //! to ensure that different devices use different numbers with high probability.
dudmuck 0:f2716e543d97 2136 //! \param nwkKey the 16 byte network session key used for message integrity.
dudmuck 0:f2716e543d97 2137 //! If NULL the caller has copied the key into `LMIC.nwkKey` before.
dudmuck 0:f2716e543d97 2138 //! \param artKey the 16 byte application router session key used for message confidentiality.
dudmuck 0:f2716e543d97 2139 //! If NULL the caller has copied the key into `LMIC.artKey` before.
dudmuck 0:f2716e543d97 2140 void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey) {
dudmuck 0:f2716e543d97 2141 LMIC.netid = netid;
dudmuck 0:f2716e543d97 2142 LMIC.devaddr = devaddr;
dudmuck 0:f2716e543d97 2143 if( nwkKey != (xref2u1_t)0 )
dudmuck 0:f2716e543d97 2144 os_copyMem(LMIC.nwkKey, nwkKey, 16);
dudmuck 0:f2716e543d97 2145 if( artKey != (xref2u1_t)0 )
dudmuck 0:f2716e543d97 2146 os_copyMem(LMIC.artKey, artKey, 16);
dudmuck 0:f2716e543d97 2147
dudmuck 0:f2716e543d97 2148 #if defined(CFG_eu868)
dudmuck 0:f2716e543d97 2149 initDefaultChannels(0);
dudmuck 0:f2716e543d97 2150 #endif
dudmuck 0:f2716e543d97 2151
dudmuck 0:f2716e543d97 2152 LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI);
dudmuck 0:f2716e543d97 2153 LMIC.opmode |= OP_NEXTCHNL;
dudmuck 0:f2716e543d97 2154 stateJustJoined();
dudmuck 0:f2716e543d97 2155 DO_DEVDB(LMIC.netid, netid);
dudmuck 0:f2716e543d97 2156 DO_DEVDB(LMIC.devaddr, devaddr);
dudmuck 0:f2716e543d97 2157 DO_DEVDB(LMIC.nwkKey, nwkkey);
dudmuck 0:f2716e543d97 2158 DO_DEVDB(LMIC.artKey, artkey);
dudmuck 0:f2716e543d97 2159 DO_DEVDB(LMIC.seqnoUp, seqnoUp);
dudmuck 0:f2716e543d97 2160 DO_DEVDB(LMIC.seqnoDn, seqnoDn);
dudmuck 0:f2716e543d97 2161 }
dudmuck 0:f2716e543d97 2162
dudmuck 0:f2716e543d97 2163 // Enable/disable link check validation.
dudmuck 0:f2716e543d97 2164 // LMIC sets the ADRACKREQ bit in UP frames if there were no DN frames
dudmuck 0:f2716e543d97 2165 // for a while. It expects the network to provide a DN message to prove
dudmuck 0:f2716e543d97 2166 // connectivity with a span of UP frames. If this no such prove is coming
dudmuck 0:f2716e543d97 2167 // then the datarate is lowered and a LINK_DEAD event is generated.
dudmuck 0:f2716e543d97 2168 // This mode can be disabled and no connectivity prove (ADRACKREQ) is requested
dudmuck 0:f2716e543d97 2169 // nor is the datarate changed.
dudmuck 0:f2716e543d97 2170 // This must be called only if a session is established (e.g. after EV_JOINED)
dudmuck 0:f2716e543d97 2171 void LMIC_setLinkCheckMode (bit_t enabled) {
dudmuck 0:f2716e543d97 2172 LMIC.adrChanged = 0;
dudmuck 0:f2716e543d97 2173 LMIC.adrAckReq = enabled ? LINK_CHECK_INIT : LINK_CHECK_OFF;
dudmuck 0:f2716e543d97 2174 }
dudmuck 0:f2716e543d97 2175
dudmuck 0:f2716e543d97 2176