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:
Wed Oct 21 01:03:34 2015 +0000
Revision:
8:0faa1bb768b5
Parent:
7:9095e54e381f
Child:
9:83ae7f34e88c
parse channel mask in join accept, tx power table, channel search in nextJoinState()

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