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 Nov 16 23:52:45 2015 +0000
Revision:
9:83ae7f34e88c
Parent:
8:0faa1bb768b5
Child:
10:6c0830baf10f
correct join behavior after link dead (joining again)

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