LMiC LoRa Semtech + Nucleo

Dependents:   LoRaWAN-lmic-app

Fork of LMiC by Pascal Nysten

Committer:
mluis
Date:
Thu Jan 22 12:50:49 2015 +0000
Revision:
0:62d1edcc13d1
Child:
1:d3b7bde3995c
Porting of IBM LoRa MAC in C (LMiC) to the mbed platform.

Who changed what in which revision?

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