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:
Mon Sep 21 17:59:42 2015 +0000
Revision:
7:9095e54e381f
Parent:
5:e4ba433f0ac1
Child:
8:0faa1bb768b5
CHNL_HYBRID defined with value 0 to 7 to select block of 8 channels to be used of the 64 available

Who changed what in which revision?

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