IBM LoRa MAC in C (LMiC) mbed library port

Dependents:   lora-temperature LoRaWAN-lmic-app_HS LoRaWAN-lmic-app_huynh

LoRa WAN in C for sx1276 shield

Currently version 1.5


LoRaWAN network configuration for end-device

The following three pieces of information uniquely identifies end-device to network to allow over-the-air activation. These are stored in the end-device prior to join procedure.

AppEUI

Uniquely identifies application provider of end-device.

Least-significant byte first, 8 bytes, use reverse memcpy() to keep same order as shown on lora server.

example C code

static const u1_t APPEUI[8]  = { 0x01, 0x00, 0x01, 0x00, 0x00, 0x0C, 0x25, 0x00 };

This is copied into LMIC by os_getArtEui() callback function in application.

DevEUI

End-device ID, unique to each end-node.

Least-significant byte first, 8 bytes, use reverse memcpy() to keep same order as shown on lora server.

example C code

static const u1_t DEVEUI[8]  = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x25, 0x00 }; 

This is copied into LMIC by os_getDevEui() callback function in application.

AppKey (aka DevKey)

128-bit (16byte) AES key.

example C code

static const u1_t DEVKEY[16] = { 0xe4, 0x72, 0x71, 0xc5, 0xf5, 0x30, 0xa9, 0x9f, 0xcf, 0xc4, 0x0e, 0xab, 0xea, 0xd7, 0x19, 0x42 };

This is copied into LMIC by os_getDevKey() callback function in application.

Using over-the air activation, the end-device (LMIC) performs a join procedure every time it starts for first time, or has lost session context information. When join procedure has successfully completed, the end-device will have a network session key (NwkSKey) and an application session key (AppSKey), which are used for encryption and message integrity check.


US915 configuration with http://us01-iot.semtech.com/

  • log in to server
  • click on Applications
  • find your application and click it
  • go to configure motes
  • to create a mote, you may enter a new DevEUI
    • you may copy-paste the 16byte application key from an already existing mote, if you desire.
CHNL_HYBRID125KHz500KHz
defined valuechannelschannel
00 to 764
18 to 1565
216 to 2366
324 to 3167
432 to 3968
540 to 4769
648 to 5570
756 to 6371
undef0 to 6364 to 71
Committer:
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 }