Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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_HYBRID | 125KHz | 500KHz |
|---|---|---|
| defined value | channels | channel |
| 0 | 0 to 7 | 64 |
| 1 | 8 to 15 | 65 |
| 2 | 16 to 23 | 66 |
| 3 | 24 to 31 | 67 |
| 4 | 32 to 39 | 68 |
| 5 | 40 to 47 | 69 |
| 6 | 48 to 55 | 70 |
| 7 | 56 to 63 | 71 |
| undef | 0 to 63 | 64 to 71 |
Revision 1:d3b7bde3995c, committed 2015-03-31
- Comitter:
- mluis
- Date:
- Tue Mar 31 13:36:56 2015 +0000
- Parent:
- 0:62d1edcc13d1
- Child:
- 2:974cafbfb159
- Child:
- 3:519c71d29a06
- Commit message:
- Updated LMiC to release v1.4 (http://www.zurich.ibm.com/pdf/lrsc/lmic-release-v1.4.zip)
Changed in this revision
--- a/aes.cpp Thu Jan 22 12:50:49 2015 +0000
+++ b/aes.cpp Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -181,23 +181,23 @@
#define msbf4_write(p,v) (p)[0]=(v)>>24,(p)[1]=(v)>>16,(p)[2]=(v)>>8,(p)[3]=(v)
#define swapmsbf(x) ( (x&0xFF)<<24 | (x&0xFF00)<<8 | (x&0xFF0000)>>8 | (x>>24) )
-#define u1(v) ((u1_t)(v))
+#define u1(v) ((u1_t)(v))
#define AES_key4(r1,r2,r3,r0,i) r1 = ki[i+1]; \
r2 = ki[i+2]; \
r3 = ki[i+3]; \
r0 = ki[i]
-#define AES_expr4(r1,r2,r3,r0,i) r1 ^= AES_E4[u1(i)]; \
- r2 ^= AES_E3[u1(i>>8)]; \
- r3 ^= AES_E2[u1(i>>16)]; \
- r0 ^= AES_E1[ (i>>24)]
+#define AES_expr4(r1,r2,r3,r0,i) r1 ^= AES_E4[u1(i)]; \
+ r2 ^= AES_E3[u1(i>>8)]; \
+ r3 ^= AES_E2[u1(i>>16)]; \
+ r0 ^= AES_E1[ (i>>24)]
#define AES_expr(a,r0,r1,r2,r3,i) a = ki[i]; \
- a ^= (AES_S[ r0>>24 ]<<24); \
- a ^= (AES_S[u1(r1>>16)]<<16); \
- a ^= (AES_S[u1(r2>> 8)]<< 8); \
- a ^= AES_S[u1(r3) ]
+ a ^= (AES_S[ r0>>24 ]<<24); \
+ a ^= (AES_S[u1(r1>>16)]<<16); \
+ a ^= (AES_S[u1(r2>> 8)]<< 8); \
+ a ^= AES_S[u1(r3) ]
// global area for passing parameters (aux, key) and for storing round keys
u4_t AESAUX[16/sizeof(u4_t)];
@@ -205,59 +205,59 @@
// generate 1+10 roundkeys for encryption with 128-bit key
// read 128-bit key from AESKEY in MSBF, generate roundkey words in place
-static void aesroundkeys (void) {
+static void aesroundkeys () {
int i;
u4_t b;
for( i=0; i<4; i++) {
- AESKEY[i] = swapmsbf(AESKEY[i]);
+ AESKEY[i] = swapmsbf(AESKEY[i]);
}
b = AESKEY[3];
for( ; i<44; i++ ) {
- if( i%4==0 ) {
+ if( i%4==0 ) {
// b = SubWord(RotWord(b)) xor Rcon[i/4]
- b = (AES_S[u1(b >> 16)] << 24) ^
- (AES_S[u1(b >> 8)] << 16) ^
- (AES_S[u1(b) ] << 8) ^
- (AES_S[ b >> 24 ] ) ^
+ b = (AES_S[u1(b >> 16)] << 24) ^
+ (AES_S[u1(b >> 8)] << 16) ^
+ (AES_S[u1(b) ] << 8) ^
+ (AES_S[ b >> 24 ] ) ^
AES_RCON[(i-4)/4];
- }
- AESKEY[i] = b ^= AESKEY[i-4];
+ }
+ AESKEY[i] = b ^= AESKEY[i-4];
}
}
u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) {
- aesroundkeys();
+ aesroundkeys();
- if( mode & AES_MICNOAUX ) {
- AESAUX[0] = AESAUX[1] = AESAUX[2] = AESAUX[3] = 0;
- } else {
- AESAUX[0] = swapmsbf(AESAUX[0]);
- AESAUX[1] = swapmsbf(AESAUX[1]);
- AESAUX[2] = swapmsbf(AESAUX[2]);
- AESAUX[3] = swapmsbf(AESAUX[3]);
- }
+ if( mode & AES_MICNOAUX ) {
+ AESAUX[0] = AESAUX[1] = AESAUX[2] = AESAUX[3] = 0;
+ } else {
+ AESAUX[0] = swapmsbf(AESAUX[0]);
+ AESAUX[1] = swapmsbf(AESAUX[1]);
+ AESAUX[2] = swapmsbf(AESAUX[2]);
+ AESAUX[3] = swapmsbf(AESAUX[3]);
+ }
- while( (signed char)len > 0 ) {
- u4_t a0, a1, a2, a3;
- u4_t t0, t1, t2, t3;
- u4_t *ki, *ke;
+ while( (signed char)len > 0 ) {
+ u4_t a0, a1, a2, a3;
+ u4_t t0, t1, t2, t3;
+ u4_t *ki, *ke;
- // load input block
- if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block
- a0 = AESAUX[0];
- a1 = AESAUX[1];
- a2 = AESAUX[2];
- a3 = AESAUX[3];
+ // load input block
+ if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block
+ a0 = AESAUX[0];
+ a1 = AESAUX[1];
+ a2 = AESAUX[2];
+ a3 = AESAUX[3];
}
else if( (mode & AES_MIC) && len <= 16 ) { // last MIC block
a0 = a1 = a2 = a3 = 0; // load null block
mode |= ((len == 16) ? 1 : 2) << 4; // set MICSUB: CMAC subkey K1 or K2
} else
LOADDATA: { // load data block (partially)
- for(t0=0; t0<16; t0++) {
+ for(t0=0; t0<16; t0++) {
t1 = (t1<<8) | ((t0<len) ? buf[t0] : (t0==len) ? 0x80 : 0x00);
if((t0&3)==3) {
a0 = a1;
@@ -266,74 +266,74 @@
a3 = t1;
}
}
- if( mode & AES_MIC ) {
- a0 ^= AESAUX[0];
- a1 ^= AESAUX[1];
- a2 ^= AESAUX[2];
- a3 ^= AESAUX[3];
- }
+ if( mode & AES_MIC ) {
+ a0 ^= AESAUX[0];
+ a1 ^= AESAUX[1];
+ a2 ^= AESAUX[2];
+ a3 ^= AESAUX[3];
+ }
}
- // perform AES encryption on block in a0-a3
- ki = AESKEY;
- ke = ki + 8*4;
- a0 ^= ki[0];
- a1 ^= ki[1];
- a2 ^= ki[2];
- a3 ^= ki[3];
- do {
- AES_key4 (t1,t2,t3,t0,4);
- AES_expr4(t1,t2,t3,t0,a0);
- AES_expr4(t2,t3,t0,t1,a1);
- AES_expr4(t3,t0,t1,t2,a2);
- AES_expr4(t0,t1,t2,t3,a3);
+ // perform AES encryption on block in a0-a3
+ ki = AESKEY;
+ ke = ki + 8*4;
+ a0 ^= ki[0];
+ a1 ^= ki[1];
+ a2 ^= ki[2];
+ a3 ^= ki[3];
+ do {
+ AES_key4 (t1,t2,t3,t0,4);
+ AES_expr4(t1,t2,t3,t0,a0);
+ AES_expr4(t2,t3,t0,t1,a1);
+ AES_expr4(t3,t0,t1,t2,a2);
+ AES_expr4(t0,t1,t2,t3,a3);
- AES_key4 (a1,a2,a3,a0,8);
- AES_expr4(a1,a2,a3,a0,t0);
- AES_expr4(a2,a3,a0,a1,t1);
- AES_expr4(a3,a0,a1,a2,t2);
- AES_expr4(a0,a1,a2,a3,t3);
- } while( (ki+=8) < ke );
+ AES_key4 (a1,a2,a3,a0,8);
+ AES_expr4(a1,a2,a3,a0,t0);
+ AES_expr4(a2,a3,a0,a1,t1);
+ AES_expr4(a3,a0,a1,a2,t2);
+ AES_expr4(a0,a1,a2,a3,t3);
+ } while( (ki+=8) < ke );
- AES_key4 (t1,t2,t3,t0,4);
- AES_expr4(t1,t2,t3,t0,a0);
- AES_expr4(t2,t3,t0,t1,a1);
- AES_expr4(t3,t0,t1,t2,a2);
- AES_expr4(t0,t1,t2,t3,a3);
+ AES_key4 (t1,t2,t3,t0,4);
+ AES_expr4(t1,t2,t3,t0,a0);
+ AES_expr4(t2,t3,t0,t1,a1);
+ AES_expr4(t3,t0,t1,t2,a2);
+ AES_expr4(t0,t1,t2,t3,a3);
- AES_expr(a0,t0,t1,t2,t3,8);
- AES_expr(a1,t1,t2,t3,t0,9);
- AES_expr(a2,t2,t3,t0,t1,10);
- AES_expr(a3,t3,t0,t1,t2,11);
- // result of AES encryption in a0-a3
+ AES_expr(a0,t0,t1,t2,t3,8);
+ AES_expr(a1,t1,t2,t3,t0,9);
+ AES_expr(a2,t2,t3,t0,t1,10);
+ AES_expr(a3,t3,t0,t1,t2,11);
+ // result of AES encryption in a0-a3
- if( mode & AES_MIC ) {
- if( (t1 = ((mode & AES_MICSUB) >> 4)) != 0 ) { // last block
- do {
- // compute CMAC subkey K1 and K2
- t0 = a0 >> 31; // save MSB
- a0 = (a0 << 1) | (a1 >> 31);
- a1 = (a1 << 1) | (a2 >> 31);
- a2 = (a2 << 1) | (a3 >> 31);
- a3 = (a3 << 1);
- if( t0 ) a3 ^= 0x87;
- } while( --t1 );
+ if( mode & AES_MIC ) {
+ if( (t1 = (mode & AES_MICSUB) >> 4) != 0 ) { // last block
+ do {
+ // compute CMAC subkey K1 and K2
+ t0 = a0 >> 31; // save MSB
+ a0 = (a0 << 1) | (a1 >> 31);
+ a1 = (a1 << 1) | (a2 >> 31);
+ a2 = (a2 << 1) | (a3 >> 31);
+ a3 = (a3 << 1);
+ if( t0 ) a3 ^= 0x87;
+ } while( --t1 );
- AESAUX[0] ^= a0;
- AESAUX[1] ^= a1;
- AESAUX[2] ^= a2;
- AESAUX[3] ^= a3;
+ AESAUX[0] ^= a0;
+ AESAUX[1] ^= a1;
+ AESAUX[2] ^= a2;
+ AESAUX[3] ^= a3;
mode &= ~AES_MICSUB;
- goto LOADDATA;
- } else {
+ goto LOADDATA;
+ } else {
// save cipher block as new iv
AESAUX[0] = a0;
AESAUX[1] = a1;
AESAUX[2] = a2;
AESAUX[3] = a3;
}
- } else { // CIPHER
- if( mode & AES_CTR ) { // xor block (partially)
+ } else { // CIPHER
+ if( mode & AES_CTR ) { // xor block (partially)
t0 = (len > 16) ? 16: len;
for(t1=0; t1<t0; t1++) {
buf[t1] ^= (a0>>24);
@@ -344,16 +344,16 @@
a2 = a3;
}
}
- // update counter
- AESAUX[3]++;
- } else { // ECB
+ // update counter
+ AESAUX[3]++;
+ } else { // ECB
// store block
msbf4_write(buf+0, a0);
- msbf4_write(buf+4, a1);
- msbf4_write(buf+8, a2);
- msbf4_write(buf+12, a3);
- }
- }
+ msbf4_write(buf+4, a1);
+ msbf4_write(buf+8, a2);
+ msbf4_write(buf+12, a3);
+ }
+ }
// update block state
if( (mode & AES_MIC)==0 || (mode & AES_MICNOAUX) ) {
@@ -361,7 +361,7 @@
len -= 16;
}
mode |= AES_MICNOAUX;
- }
- return AESAUX[0];
+ }
+ return AESAUX[0];
}
--- a/hal.h Thu Jan 22 12:50:49 2015 +0000 +++ b/hal.h Tue Mar 31 13:36:56 2015 +0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014 IBM Corporation. + * Copyright (c) 2014-2015 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -80,36 +80,4 @@ */ void hal_failed (void); -////////////////////////////////////////////////////////////////////// -#ifdef CFG_DEBUG -void debug_init (void); -void debug_led (u1_t val); -void debug_char (u1_t c); -void debug_hex (u1_t b); -void debug_buf (const u1_t* buf, u2_t len); -void debug_uint (u4_t v); -void debug_str (const u1_t* str); -void debug_event (int ev); -void debug_val (const u1_t* label, u4_t val); -#define DEBUG_INIT() debug_init() -#define DEBUG_LED(v) debug_led(v) -#define DEBUG_CHAR(c) debug_char(c) -#define DEBUG_HEX(b) debug_hex(b) -#define DEBUG_BUF(b,l) debug_buf(b,l) -#define DEBUG_UINT(v) debug_uint(v) -#define DEBUG_STR(s) debug_str(s) -#define DEBUG_EVENT(e) debug_event(e) -#define DEBUG_VAL(l,v) debug_val(l, v) -#else // CFG_DEBUG -#define DEBUG_INIT() -#define DEBUG_LED(v) -#define DEBUG_CHAR(c) -#define DEBUG_HEX(b) -#define DEBUG_BUF(b,l) -#define DEBUG_UINT(v) -#define DEBUG_STR(s) -#define DEBUG_EVENT(e) -#define DEBUG_VAL(l,v) -#endif // CFG_DEBUG - #endif // _hal_hpp_
--- a/hal/debug.h Thu Jan 22 12:50:49 2015 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/* Copyright (c) 2012 mbed.org, MIT License
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
- * and associated documentation files (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge, publish, distribute,
- * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all copies or
- * substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
- * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef DEBUG_H
-#define DEBUG_H
-
-/** @file debug.h */
-
-#ifndef NDEBUG
-
-#include <stdarg.h>
-#include <stdio.h>
-
-/** Output a debug message
- *
- * @param format printf-style format string, followed by variables
- */
-static inline void debug(const char *format, ...) {
- va_list args;
- va_start(args, format);
- vfprintf(stderr, format, args);
- va_end(args);
-}
-
-/** Conditionally output a debug message
- *
- * @param condition output only if condition is true
- * @param format printf-style format string, followed by variables
- */
-static inline void debug_if(bool condition, const char *format, ...) {
- if(condition) {
- va_list args;
- va_start(args, format);
- vfprintf(stderr, format, args);
- va_end(args);
- }
-}
-
-#else
-
-static inline void debug(const char *format, ...) {}
-static inline void debug(bool condition, const char *format, ...) {}
-
-#endif
-
-#endif
--- a/hal/hal.cpp Thu Jan 22 12:50:49 2015 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,270 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * IBM Zurich Research Lab - initial API, implementation and documentation
- * Semtech Apps Team - Modified to support the MBED sx1276 driver
- * library.
- * Possibility to use original or Semtech's MBED
- * radio driver. The selection is done by setting
- * USE_SMTC_RADIO_DRIVER preprocessing directive
- * in lmic.h
- *******************************************************************************/
-#include "mbed.h"
-#include "lmic.h"
-#include "mbed_debug.h"
-
-#if USE_SMTC_RADIO_DRIVER
-
-#else
-
-extern void radio_irq_handler( u1_t dio );
-
-static DigitalOut nss( D10 );
-static SPI spi( D11, D12, D13 ); // ( mosi, miso, sclk )
-
-static DigitalInOut rst( A0 );
-static DigitalOut rxtx( A4 );
-
-static InterruptIn dio0( D2 );
-static InterruptIn dio1( D3 );
-static InterruptIn dio2( D4 );
-
-static void dio0Irq( void )
-{
- radio_irq_handler( 0 );
-}
-
-static void dio1Irq( void )
-{
- radio_irq_handler( 1 );
-}
-static void dio2Irq( void )
-{
- radio_irq_handler( 2 );
-}
-
-#endif
-
-static u1_t irqlevel = 0;
-static u4_t ticks = 0;
-
-static Timer timer;
-static Ticker ticker;
-
-static void reset_timer( void )
-{
- ticks += timer.read_us( ) >> 6;
- timer.reset( );
-}
-
-void hal_init( void )
-{
- __disable_irq( );
- irqlevel = 0;
-
-#if USE_SMTC_RADIO_DRIVER
-
-#else
- // configure input lines
- dio0.mode( PullDown );
- dio0.rise( dio0Irq );
- dio0.enable_irq( );
- dio1.mode( PullDown );
- dio1.rise( dio1Irq );
- dio1.enable_irq( );
- dio2.mode( PullDown );
- dio2.rise( dio2Irq );
- dio2.enable_irq( );
- // configure reset line
- rst.input( );
- // configure spi
- spi.frequency( 8000000 );
- spi.format( 8, 0 );
- nss = 1;
-#endif
- // configure timer
- timer.start( );
- ticker.attach_us( reset_timer, 10000000 ); // reset timer every 10sec
- __enable_irq( );
-}
-
-#if USE_SMTC_RADIO_DRIVER
-
-#else
-
-void hal_pin_rxtx( u1_t val )
-{
- rxtx = !val;
-}
-
-void hal_pin_nss( u1_t val )
-{
- nss = val;
-}
-
-void hal_pin_rst( u1_t val )
-{
- if( val == 0 || val == 1 )
- { // drive pin
- rst.output( );
- rst = val;
- }
- else
- { // keep pin floating
- rst.input( );
- }
-}
-
-u1_t hal_spi( u1_t out )
-{
- return spi.write( out );
-}
-
-#endif
-
-void hal_disableIRQs( void )
-{
- __disable_irq( );
- irqlevel++;
-}
-
-void hal_enableIRQs( void )
-{
- if( --irqlevel == 0 )
- {
- __enable_irq( );
- }
-}
-
-void hal_sleep( void )
-{
- // NOP
-}
-
-u4_t hal_ticks( void )
-{
- hal_disableIRQs( );
- int t = ticks + ( timer.read_us( ) >> 6 );
- hal_enableIRQs( );
- return t;
-}
-
-static u2_t deltaticks( u4_t time )
-{
- u4_t t = hal_ticks( );
- s4_t d = time - t;
- if( d <= 0 )
- {
- return 0; // in the past
- }
- if( ( d >> 16 ) != 0 )
- {
- return 0xFFFF; // far ahead
- }
- return ( u2_t )d;
-}
-
-void hal_waitUntil( u4_t time )
-{
- while( deltaticks( time ) != 0 ); // busy wait until timestamp is reached
-}
-
-u1_t hal_checkTimer( u4_t time )
-{
- return ( deltaticks( time ) < 2 );
-}
-
-void hal_failed( void )
-{
- while( 1 );
-}
-
-//////////////////////////////////////////////////////////////////////
-// DEBUG CODE BELOW (use CFG_DEBUG)
-//////////////////////////////////////////////////////////////////////
-#ifdef CFG_DEBUG
-
-void debug_init( void )
-{
- // print banner
- debug( "\r\n============== DEBUG STARTED ==============\r\n" );
-}
-
-void debug_char( u1_t c )
-{
- debug( "%c", c );
-}
-
-void debug_hex( u1_t b )
-{
- debug( "%02X", b );
-}
-
-void debug_buf( const u1_t* buf, u2_t len )
-{
- while( len-- )
- {
- debug_hex( *buf++ );
- debug_char( ' ' );
- }
- debug_char( '\r' );
- debug_char( '\n' );
-}
-
-void debug_uint( u4_t v )
-{
- for( s1_t n = 24; n >= 0; n -= 8 )
- {
- debug_hex( v >> n );
- }
-}
-
-void debug_str( const u1_t* str )
-{
- while( *str )
- {
- debug_char( *str++ );
- }
-}
-
-void debug_val( const u1_t* label, u4_t val )
-{
- debug_str( label );
- debug_uint( val );
- debug_char( '\r' );
- debug_char( '\n' );
-}
-
-void debug_led( u1_t val )
-{
- debug_val( "LED = ", val );
-}
-
-void debug_event( int ev )
-{
- static const u1_t* evnames[] =
- {
- [EV_SCAN_TIMEOUT] = "SCAN_TIMEOUT",
- [EV_BEACON_FOUND] = "BEACON_FOUND",
- [EV_BEACON_MISSED] = "BEACON_MISSED",
- [EV_BEACON_TRACKED] = "BEACON_TRACKED",
- [EV_JOINING] = "JOINING",
- [EV_JOINED] = "JOINED",
- [EV_RFU1] = "RFU1",
- [EV_JOIN_FAILED] = "JOIN_FAILED",
- [EV_REJOIN_FAILED] = "REJOIN_FAILED",
- [EV_TXCOMPLETE] = "TXCOMPLETE",
- [EV_LOST_TSYNC] = "LOST_TSYNC",
- [EV_RESET] = "RESET",
- [EV_RXCOMPLETE] = "RXCOMPLETE",
- [EV_LINK_DEAD] = "LINK_DEAD",
- [EV_LINK_ALIVE] = "LINK_ALIVE",
- };
- debug( "%s\r\n", evnames[ev] );
-}
-#endif // CFG_DEBUG
--- a/lmic.cpp Thu Jan 22 12:50:49 2015 +0000
+++ b/lmic.cpp Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -9,10 +9,13 @@
* IBM Zurich Research Lab - initial API, implementation and documentation
*******************************************************************************/
+//! \file
#include "lmic.h"
+#if !defined(MINRX_SYMS)
+#define MINRX_SYMS 5
+#endif // !defined(MINRX_SYMS)
#define PAMBL_SYMS 8
-#define MINRX_SYMS 5
#define PAMBL_FSK 5
#define PRERX_FSK 1
#define RXLEN_FSK (1+5+2)
@@ -29,6 +32,15 @@
#define BCN_GUARD_osticks ms2osticks(BCN_GUARD_ms)
#define BCN_WINDOW_osticks ms2osticks(BCN_WINDOW_ms)
#define AIRTIME_BCN_osticks us2osticks(AIRTIME_BCN)
+#if defined(CFG_eu868)
+#define DNW2_SAFETY_ZONE ms2osticks(3000)
+#endif
+#if defined(CFG_us915)
+#define DNW2_SAFETY_ZONE ms2osticks(750)
+#endif
+
+// Special APIs - for development or testing
+#define isTESTMODE() 0
DEFINE_LMIC;
DECL_ON_LMIC_EVENT;
@@ -42,36 +54,36 @@
// ================================================================================
// BEG OS - default implementations for certain OS suport functions
-#if !HAS_os_calls
+#if !defined(HAS_os_calls)
-#ifndef os_rlsbf2
+#if !defined(os_rlsbf2)
u2_t os_rlsbf2 (xref2cu1_t buf) {
return (u2_t)(buf[0] | (buf[1]<<8));
}
#endif
-#ifndef os_rlsbf4
+#if !defined(os_rlsbf4)
u4_t os_rlsbf4 (xref2cu1_t buf) {
return (u4_t)(buf[0] | (buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24));
}
#endif
-#ifndef os_rmsbf4
+#if !defined(os_rmsbf4)
u4_t os_rmsbf4 (xref2cu1_t buf) {
return (u4_t)(buf[3] | (buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24));
}
#endif
-#ifndef os_wlsbf2
+#if !defined(os_wlsbf2)
void os_wlsbf2 (xref2u1_t buf, u2_t v) {
buf[0] = v;
buf[1] = v>>8;
}
#endif
-#ifndef os_wlsbf4
+#if !defined(os_wlsbf4)
void os_wlsbf4 (xref2u1_t buf, u4_t v) {
buf[0] = v;
buf[1] = v>>8;
@@ -80,7 +92,7 @@
}
#endif
-#ifndef os_wmsbf4
+#if !defined(os_wmsbf4)
void os_wmsbf4 (xref2u1_t buf, u4_t v) {
buf[3] = v;
buf[2] = v>>8;
@@ -89,54 +101,27 @@
}
#endif
-#ifndef os_getBattLevel
+#if !defined(os_getBattLevel)
u1_t os_getBattLevel (void) {
return MCMD_DEVS_BATT_NOINFO;
}
#endif
-#ifndef os_crc16
-static const u2_t CRC_TABLE[] = {
- 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
- 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
- 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
- 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
- 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
- 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
- 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
- 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
- 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
- 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
- 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
- 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
- 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
- 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
- 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
- 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
- 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
- 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
- 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
- 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
- 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
- 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
- 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
- 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
- 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
- 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
- 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
- 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
- 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
- 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
- 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
- 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
-};
+#if !defined(os_crc16)
+// New CRC-16 CCITT(XMODEM) checksum for beacons:
u2_t os_crc16 (xref2u1_t data, uint len) {
- u2_t fcs = 0;
- for( uint u=0; u<len; u++ ) {
- u1_t b = data[u];
- fcs = (CRC_TABLE[(fcs ^ b) & 0xFF] ^ ((fcs >> 8) & 0xFF));
+ u2_t remainder = 0;
+ u2_t polynomial = 0x1021;
+ for( uint i = 0; i < len; i++ ) {
+ remainder ^= data[i] << 8;
+ for( u1_t bit = 8; bit > 0; bit--) {
+ if( (remainder & 0x8000) )
+ remainder = (remainder << 1) ^ polynomial;
+ else
+ remainder <<= 1;
+ }
}
- return fcs;
+ return remainder;
}
#endif
@@ -193,7 +178,7 @@
static void aes_cipher (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t payload, int len) {
if( len <= 0 )
- return;
+ return;
os_clearMem(AESaux, 16);
AESaux[0] = AESaux[15] = 1; // mode=cipher / dir=down / block counter=1
AESaux[5] = dndir?1:0;
@@ -225,7 +210,7 @@
// ================================================================================
// BEG LORA
-#if CFG_eu868 // ========================================
+#if defined(CFG_eu868) // ========================================
#define maxFrameLen(dr) ((dr)<=DR_SF9 ? maxFrameLens[(dr)] : 0xFF)
const u1_t maxFrameLens [] = { 64,64,64,123 };
@@ -248,7 +233,7 @@
};
#define pow2dBm(mcmd_ladr_p1) (TXPOWLEVELS[(mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT])
-#elif CFG_us915 // ========================================
+#elif defined(CFG_us915) // ========================================
#define maxFrameLen(dr) ((dr)<=DR_SF11CR ? maxFrameLens[(dr)] : 0xFF)
const u1_t maxFrameLens [] = { 24,66,142,255,255,255,255,255, 66,142 };
@@ -296,21 +281,18 @@
u1_t bw = getBw(rps); // 0,1,2 = 125,250,500kHz
u1_t sf = getSf(rps); // 0=FSK, 1..6 = SF7..12
if( sf == FSK ) {
- // MLu - 12012015
- return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8
- * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000;
- //return (plen+/*preamble*/5+/*syncword*/2+/*len*/1+/*crc*/2) * /*bits/byte*/8
- // * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000;
+ return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8
+ * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000;
}
u1_t sfx = 4*(sf+(7-SF7));
u1_t q = sfx - (sf >= SF11 ? 8 : 0);
int tmp = 8*plen - sfx + 28 + (getNocrc(rps)?0:16) - (getIh(rps)?20:0);
if( tmp > 0 ) {
- tmp = (tmp + q - 1) / q;
- tmp *= getCr(rps)+5;
- tmp += 8;
+ tmp = (tmp + q - 1) / q;
+ tmp *= getCr(rps)+5;
+ tmp += 8;
} else {
- tmp = 8;
+ tmp = 8;
}
tmp = (tmp<<2) + /*preamble*/49 /* 4 * (8 + 4.25) */;
// bw = 125000 = 15625 * 2^3
@@ -325,9 +307,9 @@
sfx = sf+(7-SF7) - (3+2) - bw;
int div = 15625;
if( sfx > 4 ) {
- // prevent 32bit signed int overflow in last step
- div >>= sfx-4;
- sfx = 4;
+ // prevent 32bit signed int overflow in last step
+ div >>= sfx-4;
+ sfx = 4;
}
// Need 32bit arithmetic for this last step
return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div;
@@ -337,8 +319,6 @@
extern inline int s12rssi (s1_t v);
extern inline float s12snr (s1_t v);
extern inline s1_t snr2s1 (double v);
-extern inline int getRssi (rxqu_t*rxq);
-extern inline void setRssi (rxqu_t*rxq, int v);
extern inline rps_t updr2rps (dr_t dr);
extern inline rps_t dndr2rps (dr_t dr);
@@ -374,23 +354,23 @@
// normal frames - 1st try / no retry
0,
// confirmed frames
- 0,0,0,0,0,1,1,1,2
+ 0,0,1,0,1,0,1,0,0
};
// Table below defines the size of one symbol as
// symtime = 256us * 2^T(sf,bw)
// 256us is called one symunit.
-// SF:
-// BW: |__7___8___9__10__11__12
-// 125kHz | 2 3 4 5 6 7
-// 250kHz | 1 2 3 4 5 6
-// 500kHz | 0 1 2 3 4 5
+// SF:
+// BW: |__7___8___9__10__11__12
+// 125kHz | 2 3 4 5 6 7
+// 250kHz | 1 2 3 4 5 6
+// 500kHz | 0 1 2 3 4 5
//
// Times for half symbol per DR
// Per DR table to minimize rounding errors
static const ostime_t DR2HSYM_osticks[] = {
-#if CFG_eu868
+#if defined(CFG_eu868)
#define dr2hsym(dr) (DR2HSYM_osticks[(dr)])
us2osticksRound(128<<7), // DR_SF12
us2osticksRound(128<<6), // DR_SF11
@@ -400,7 +380,7 @@
us2osticksRound(128<<2), // DR_SF7
us2osticksRound(128<<1), // DR_SF7B
us2osticksRound(80) // FSK -- not used (time for 1/2 byte)
-#elif CFG_us915
+#elif defined(CFG_us915)
#define dr2hsym(dr) (DR2HSYM_osticks[(dr)&7]) // map DR_SFnCR -> 0-6
us2osticksRound(128<<5), // DR_SF10 DR_SF12CR
us2osticksRound(128<<4), // DR_SF9 DR_SF11CR
@@ -415,13 +395,13 @@
static ostime_t calcRxWindow (u1_t secs, dr_t dr) {
ostime_t rxoff, err;
if( secs==0 ) {
- // aka 128 secs (next becaon)
- rxoff = LMIC.drift;
- err = LMIC.lastDriftDiff;
+ // aka 128 secs (next becaon)
+ rxoff = LMIC.drift;
+ err = LMIC.lastDriftDiff;
} else {
- // scheduled RX window within secs into current beacon period
- rxoff = (LMIC.drift * (ostime_t)secs) >> BCN_INTV_exp;
- err = (LMIC.lastDriftDiff * (ostime_t)secs) >> BCN_INTV_exp;
+ // scheduled RX window within secs into current beacon period
+ rxoff = (LMIC.drift * (ostime_t)secs) >> BCN_INTV_exp;
+ err = (LMIC.lastDriftDiff * (ostime_t)secs) >> BCN_INTV_exp;
}
u1_t rxsyms = MINRX_SYMS;
err += (ostime_t)LMIC.maxDriftDiff * LMIC.missedBcns;
@@ -434,10 +414,10 @@
// Setup beacon RX parameters assuming we have an error of ms (aka +/-(ms/2))
static void calcBcnRxWindowFromMillis (u1_t ms, bit_t ini) {
if( ini ) {
- LMIC.drift = 0;
- LMIC.maxDriftDiff = 0;
- LMIC.missedBcns = 0;
- LMIC.bcninfo.flags |= BCN_NODRIFT|BCN_NODDIFF;
+ LMIC.drift = 0;
+ LMIC.maxDriftDiff = 0;
+ LMIC.missedBcns = 0;
+ LMIC.bcninfo.flags |= BCN_NODRIFT|BCN_NODDIFF;
}
ostime_t hsym = dr2hsym(DR_BCN);
LMIC.bcnRxsyms = MINRX_SYMS + ms2osticksCeil(ms) / hsym;
@@ -455,8 +435,8 @@
u1_t intvExp = rxsched->intvExp;
ostime_t off = os_rlsbf2(LMIC.frame) & (0x0FFF >> (7 - intvExp)); // random offset (slot units)
rxsched->rxbase = (LMIC.bcninfo.txtime +
- BCN_RESERVE_osticks +
- ms2osticks(BCN_SLOT_SPAN_ms * off)); // random offset osticks
+ BCN_RESERVE_osticks +
+ ms2osticks(BCN_SLOT_SPAN_ms * off)); // random offset osticks
rxsched->slot = 0;
rxsched->rxtime = rxsched->rxbase - calcRxWindow(/*secs BCN_RESERVE*/2+(1<<intvExp),rxsched->dr);
rxsched->rxsyms = LMIC.rxsyms;
@@ -466,16 +446,16 @@
static bit_t rxschedNext (xref2rxsched_t rxsched, ostime_t cando) {
again:
if( rxsched->rxtime - cando >= 0 )
- return 1;
+ return 1;
u1_t slot;
if( (slot=rxsched->slot) >= 128 )
- return 0;
+ return 0;
u1_t intv = 1<<rxsched->intvExp;
if( (rxsched->slot = (slot += (intv))) >= 128 )
- return 0;
+ return 0;
rxsched->rxtime = rxsched->rxbase
- + ((BCN_WINDOW_osticks * (ostime_t)slot) >> BCN_INTV_exp)
- - calcRxWindow(/*secs BCN_RESERVE*/2+slot+intv,rxsched->dr);
+ + ((BCN_WINDOW_osticks * (ostime_t)slot) >> BCN_INTV_exp)
+ - calcRxWindow(/*secs BCN_RESERVE*/2+slot+intv,rxsched->dr);
rxsched->rxsyms = LMIC.rxsyms;
goto again;
}
@@ -485,9 +465,9 @@
u2_t r = os_getRndU2();
ostime_t delay = r;
if( delay > OSTICKS_PER_SEC )
- delay = r % (u2_t)OSTICKS_PER_SEC;
+ delay = r % (u2_t)OSTICKS_PER_SEC;
if( secSpan > 0 )
- delay += ((u1_t)r % secSpan) * OSTICKS_PER_SEC;
+ delay += ((u1_t)r % secSpan) * OSTICKS_PER_SEC;
return delay;
}
@@ -495,36 +475,39 @@
static void txDelay (ostime_t reftime, u1_t secSpan) {
reftime += rndDelay(secSpan);
if( LMIC.globalDutyRate == 0 || (reftime - LMIC.globalDutyAvail) > 0 ) {
- LMIC.globalDutyAvail = reftime;
- LMIC.opmode |= OP_RNDTX;
+ LMIC.globalDutyAvail = reftime;
+ LMIC.opmode |= OP_RNDTX;
}
}
static void setDrJoin (u1_t reason, u1_t dr) {
EV(drChange, INFO, (e_.reason = reason,
- e_.deveui = MAIN::CDEV->getEui(),
- e_.dr = dr|DR_PAGE,
- e_.txpow = LMIC.adrTxPow,
- e_.prevdr = LMIC.datarate|DR_PAGE,
- e_.prevtxpow = LMIC.adrTxPow));
+ e_.deveui = MAIN::CDEV->getEui(),
+ e_.dr = dr|DR_PAGE,
+ e_.txpow = LMIC.adrTxPow,
+ e_.prevdr = LMIC.datarate|DR_PAGE,
+ e_.prevtxpow = LMIC.adrTxPow));
LMIC.datarate = dr;
- DO_DEVDB(updateDatarate, dr);
+ DO_DEVDB(LMIC.datarate,datarate);
}
static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
EV(drChange, INFO, (e_.reason = reason,
- e_.deveui = MAIN::CDEV->getEui(),
- e_.dr = dr|DR_PAGE,
- e_.txpow = pow,
- e_.prevdr = LMIC.datarate|DR_PAGE,
- e_.prevtxpow = LMIC.adrTxPow));
+ e_.deveui = MAIN::CDEV->getEui(),
+ e_.dr = dr|DR_PAGE,
+ e_.txpow = pow,
+ e_.prevdr = LMIC.datarate|DR_PAGE,
+ e_.prevtxpow = LMIC.adrTxPow));
+
if( pow != KEEP_TXPOW )
- LMIC.adrTxPow = pow;
- LMIC.datarate = dr;
- DO_DEVDB(updateDatarate, dr);
- LMIC.opmode |= OP_NEXTCHNL;
+ LMIC.adrTxPow = pow;
+ if( LMIC.datarate != dr ) {
+ LMIC.datarate = dr;
+ DO_DEVDB(LMIC.datarate,datarate);
+ LMIC.opmode |= OP_NEXTCHNL;
+ }
}
@@ -540,74 +523,107 @@
// App may call LMIC_enableTracking() explicitely before
// Otherwise tracking is implicitly enabled here
if( (LMIC.opmode & (OP_TRACK|OP_SCAN)) == 0 && LMIC.bcninfoTries == 0 )
- LMIC_enableTracking(0);
+ LMIC_enableTracking(0);
}
-#if CFG_eu868
+#if defined(CFG_eu868)
// ================================================================================
//
// BEG: EU868 related stuff
//
-enum { BAND_MILLI=0, BAND_CENTI=1, BAND_DECI=2 };
+enum { NUM_DEFAULT_CHANNELS=6 };
static const u4_t iniChannelFreq[12] = {
// Join frequencies and duty cycle limit (0.1%)
- EU868_F1|BAND_MILLI, EU868_F2|BAND_MILLI, EU868_F3|BAND_MILLI,
- EU868_J4|BAND_MILLI, EU868_J5|BAND_MILLI, EU868_J6|BAND_MILLI,
+ EU868_F1|BAND_MILLI, EU868_J4|BAND_MILLI,
+ EU868_F2|BAND_MILLI, EU868_J5|BAND_MILLI,
+ EU868_F3|BAND_MILLI, EU868_J6|BAND_MILLI,
// Default operational frequencies
EU868_F1|BAND_CENTI, EU868_F2|BAND_CENTI, EU868_F3|BAND_CENTI,
EU868_F4|BAND_MILLI, EU868_F5|BAND_MILLI, EU868_F6|BAND_DECI
};
static void initDefaultChannels (bit_t join) {
- LMIC.channelMap = 0x3F;
+ os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
+ os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
+ os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
+
+ LMIC.channelMap = 0x1F;
u1_t su = join ? 0 : 6;
for( u1_t fu=0; fu<6; fu++,su++ ) {
- LMIC.channelFreq[fu] = iniChannelFreq[su];
- LMIC.channelDrs[fu] = DR_SF12 | (DR_SF7<<4);
+ LMIC.channelFreq[fu] = iniChannelFreq[su];
+ LMIC.channelDrMap[fu] = DR_RANGE_MAP(DR_SF12,DR_SF7);
}
- if( !join )
- LMIC.channelDrs[1] = DR_SF12 | (DR_FSK<<4);
+ if( !join ) {
+ LMIC.channelDrMap[5] = DR_RANGE_MAP(DR_SF12,DR_SF7);
+ LMIC.channelDrMap[1] = DR_RANGE_MAP(DR_SF12,DR_FSK);
+ }
- LMIC.bands[BAND_MILLI].txcap = 1000; // 0.1%
- LMIC.bands[BAND_MILLI].txpow = 14;
- LMIC.bands[BAND_CENTI].txcap = 100; // 1%
- LMIC.bands[BAND_CENTI].txpow = 14;
- LMIC.bands[BAND_DECI ].txcap = 10; // 10%
- LMIC.bands[BAND_DECI ].txpow = 27;
+ LMIC.bands[BAND_MILLI].txcap = 1000; // 0.1%
+ LMIC.bands[BAND_MILLI].txpow = 14;
+ LMIC.bands[BAND_MILLI].lastchnl = os_getRndU1() % MAX_CHANNELS;
+ LMIC.bands[BAND_CENTI].txcap = 100; // 1%
+ LMIC.bands[BAND_CENTI].txpow = 14;
+ LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS;
+ LMIC.bands[BAND_DECI ].txcap = 10; // 10%
+ LMIC.bands[BAND_DECI ].txpow = 27;
+ LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS;
LMIC.bands[BAND_MILLI].avail =
LMIC.bands[BAND_CENTI].avail =
LMIC.bands[BAND_DECI ].avail = os_getTime();
}
+bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap) {
+ if( bandidx > BAND_AUX ) return 0;
+ band_t* b = &LMIC.bands[bandidx];
+ b->txpow = txpow;
+ b->txcap = txcap;
+ b->avail = os_getTime();
+ b->lastchnl = os_getRndU1() % MAX_CHANNELS;
+ return 1;
+}
+
+bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
+ if( chidx >= MAX_CHANNELS )
+ return 0;
+ if( band == -1 ) {
+ if( freq >= 869400000 && freq <= 869650000 )
+ freq |= BAND_DECI; // 10% 27dBm
+ else if( (freq >= 868000000 && freq <= 868600000) ||
+ (freq >= 869700000 && freq <= 870000000) )
+ freq |= BAND_CENTI; // 1% 14dBm
+ else
+ freq |= BAND_MILLI; // 0.1% 14dBm
+ } else {
+ if( band > BAND_AUX ) return 0;
+ freq = (freq&~3) | band;
+ }
+ LMIC.channelFreq [chidx] = freq;
+ LMIC.channelDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF12,DR_SF7) : drmap;
+ LMIC.channelMap |= 1<<chidx; // enabled right away
+ return 1;
+}
+
+void LMIC_disableChannel (u1_t channel) {
+ LMIC.channelFreq[channel] = 0;
+ LMIC.channelDrMap[channel] = 0;
+ LMIC.channelMap &= ~(1<<channel);
+}
+
static u4_t convFreq (xref2u1_t ptr) {
u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100;
- if( freq >= EU868_FREQ_MIN && freq <= EU868_FREQ_MAX )
- freq = 0;
+ if( freq < EU868_FREQ_MIN || freq > EU868_FREQ_MAX )
+ freq = 0;
return freq;
}
-static bit_t setupChannel (u1_t chidx, u4_t freq, int drs) {
- if( chidx >= MAX_CHANNELS )
- return 0;
- if( freq >= 869400000 && freq <= 869650000 )
- freq |= BAND_DECI; // 10% 27dBm
- else if( (freq >= 868000000 && freq <= 868600000) ||
- (freq >= 869700000 && freq <= 870000000) )
- freq |= BAND_CENTI; // 1% 14dBm
- //else
- // freq |= BAND_MILLI; // 0.1% 14dBm
- LMIC.channelFreq[chidx] = freq;
- LMIC.channelDrs [chidx] = drs < 0 ? (DR_SF12|(DR_SF7<<4)) : drs;
- return 1;
-}
-
static u1_t mapChannels (u1_t chpage, u2_t chmap) {
- if( chpage != 0 || (chmap & ~((u2_t)(1<<MAX_CHANNELS)-1)) != 0 )
- return 0; // illegal input
+ // Bad page, disable all channel, enable non-existent
+ if( chpage != 0 || chmap==0 || (chmap & ~LMIC.channelMap) != 0 )
+ return 0; // illegal input
for( u1_t chnl=0; chnl<MAX_CHANNELS; chnl++ ) {
- if( (chmap & (1<<chnl)) != 0 && LMIC.channelFreq[chnl] == 0 )
- chmap &= ~(1<<chnl); // ignore - channel is not defined
+ if( (chmap & (1<<chnl)) != 0 && LMIC.channelFreq[chnl] == 0 )
+ chmap &= ~(1<<chnl); // ignore - channel is not defined
}
LMIC.channelMap = chmap;
return 1;
@@ -623,68 +639,39 @@
LMIC.freq = freq & ~(u4_t)3;
LMIC.txpow = band->txpow;
band->avail = txbeg + airtime * band->txcap;
- // Update obsolete avail's to prevent rollover
- // (needed for devices that send rarely - e.g. once/twice a day)
- for( u1_t b=0; b<MAX_BANDS; b++ ) {
- if( LMIC.bands[b].avail - txbeg < 0 )
- LMIC.bands[b].avail = txbeg;
- }
if( LMIC.globalDutyRate != 0 )
- LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
+ LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
}
static ostime_t nextTx (ostime_t now) {
- // If we do not find a suitable channel stop sending (misconfigured device)
- ostime_t mintime = now + /*10h*/36000*OSTICKS_PER_SEC;
- s1_t bmask = 0;
- for( u1_t b=0; b<MAX_BANDS; b++ ) {
- xref2band_t band = &LMIC.bands[b];
- if( band->txcap == 0 ) // band not setup
- continue;
- if( now - band->avail >= 0 ) {
- bmask = (bmask < 0 ? 0 : bmask) | (1<<b);
- }
- else if( mintime - band->avail > 0 ) {
- mintime = band->avail;
- }
- }
- if( bmask == 0 )
- return mintime;
- u1_t chnl, ccnt;
- u2_t cset, mask;
- chnl = cset = ccnt = 0;
- mask = 1;
- while( mask && mask <= LMIC.channelMap ) {
- // Channel is enabled AND and in a ready band and can handle the datarate
- if( (mask & LMIC.channelMap) != 0 && (bmask & (1 << (LMIC.channelFreq[chnl] & 0x3))) != 0) {
- u1_t drs = LMIC.channelDrs[chnl];
- if( !(isSlowerDR((dr_t)LMIC.datarate, (dr_t)(drs & 0xF)) || // lowest datarate
- isFasterDR((dr_t)LMIC.datarate, (dr_t)(drs >> 4))) ) { // fastest datarate
- cset |= mask;
- ccnt++;
+ u1_t bmap=0xF;
+ do {
+ ostime_t mintime = now + /*10h*/36000*OSTICKS_PER_SEC;
+ u1_t band=0;
+ for( u1_t bi=0; bi<4; bi++ ) {
+ if( (bmap & (1<<bi)) && mintime - LMIC.bands[bi].avail > 0 )
+ mintime = LMIC.bands[band = bi].avail;
}
- }
- mask <<= 1;
- chnl++;
- }
- if( ccnt == 0 ) // No eligible channel - misconfigured device?
- return mintime;
- ccnt = os_getRndU2() % ccnt;
- mask = 1;
- chnl = 0;
- do {
- if( (cset & mask) != 0 ) {
- if( ccnt==0 ) {
- LMIC.txChnl = chnl;
- return now;
+ // Find next channel in given band
+ u1_t chnl = LMIC.bands[band].lastchnl;
+ for( u1_t ci=0; ci<MAX_CHANNELS; ci++ ) {
+ if( (chnl = (chnl+1)) >= MAX_CHANNELS )
+ chnl -= MAX_CHANNELS;
+ if( (LMIC.channelMap & (1<<chnl)) != 0 && // channel enabled
+ (LMIC.channelDrMap[chnl] & (1<<(LMIC.datarate&0xF))) != 0 &&
+ band == (LMIC.channelFreq[chnl] & 0x3) ) { // in selected band
+ LMIC.txChnl = LMIC.bands[band].lastchnl = chnl;
+ return mintime;
+ }
}
- ccnt--;
- }
- mask <<= 1;
- chnl++;
+ if( (bmap &= ~(1<<band)) == 0 ) {
+ // No feasible channel found!
+ return mintime;
+ }
} while(1);
}
+
static void setBcnRxParams (void) {
LMIC.dataLen = 0;
LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3;
@@ -694,84 +681,96 @@
#define setRx1Params() /*LMIC.freq/rps remain unchanged*/
static void initJoinLoop (void) {
- LMIC.txChnl = os_getRndU1() % 3;
+ LMIC.txChnl = os_getRndU1() % 6;
LMIC.adrTxPow = 14;
setDrJoin(DRCHG_SET, DR_SF7);
initDefaultChannels(1);
ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
- LMIC.txend = LMIC.bands[BAND_MILLI].avail;
+ LMIC.txend = LMIC.bands[BAND_MILLI].avail + rndDelay(8);
}
static ostime_t nextJoinState (void) {
+ u1_t failed = 0;
+
// Try 869.x and then 864.x with same DR
// If both fail try next lower datarate
+ if( ++LMIC.txChnl == 6 )
+ LMIC.txChnl = 0;
+ if( (++LMIC.txCnt & 1) == 0 ) {
+ // Lower DR every 2nd try (having tried 868.x and 864.x with the same DR)
+ if( LMIC.datarate == DR_SF12 )
+ failed = 1; // we have tried all DR - signal EV_JOIN_FAILED
+ else
+ setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate));
+ }
+ // Clear NEXTCHNL because join state engine controls channel hopping
LMIC.opmode &= ~OP_NEXTCHNL;
- LMIC.txend = LMIC.bands[BAND_MILLI].avail;
- if( LMIC.txChnl < 3 ) {
- LMIC.txChnl += 3;
- } else {
- if( LMIC.datarate == DR_SF12 ) {
- setDrJoin(DRCHG_NOJACC, DR_SF7);
- // We tried one round of FSK..SF12
- // Now pick new random channel and insert a random delay and try another round.
- LMIC.txChnl = os_getRndU1() % 3;
- if( LMIC.txCnt < 5 )
- LMIC.txCnt += 1;
- // Delay by 7,15,31,63,127,255 secs
- return rndDelay((4<<LMIC.txCnt)-1) | 1; // |1 - trigger EV_JOIN_FAILED event
- }
- LMIC.txChnl = LMIC.txChnl==5 ? 0 : LMIC.txChnl-2;
- setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate));
- }
- // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it)
- return 3*OSTICKS_PER_SEC;
+ // Move txend to randomize synchronized concurrent joins.
+ // Duty cycle is based on txend.
+ ostime_t time = os_getTime();
+ if( time - LMIC.bands[BAND_MILLI].avail < 0 )
+ time = LMIC.bands[BAND_MILLI].avail;
+ LMIC.txend = time +
+ (isTESTMODE()
+ // Avoid collision with JOIN ACCEPT @ SF12 being sent by GW (but we missed it)
+ ? DNW2_SAFETY_ZONE
+ // Otherwise: randomize join (street lamp case):
+ // SF12:255, SF11:127, .., SF7:8secs
+ : DNW2_SAFETY_ZONE+rndDelay(255>>LMIC.datarate));
+ // 1 - triggers EV_JOIN_FAILED event
+ return failed;
}
//
// END: EU868 related stuff
//
// ================================================================================
-#elif CFG_us915
+#elif defined(CFG_us915)
// ================================================================================
//
// BEG: US915 related stuff
//
+
static void initDefaultChannels (void) {
for( u1_t i=0; i<4; i++ )
- LMIC.channelMap[i] = 0xFFFF;
+ LMIC.channelMap[i] = 0xFFFF;
LMIC.channelMap[4] = 0x00FF;
}
static u4_t convFreq (xref2u1_t ptr) {
u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100;
if( freq >= US915_FREQ_MIN && freq <= US915_FREQ_MAX )
- freq = 0;
+ freq = 0;
return freq;
}
-static bit_t setupChannel (u1_t chidx, u4_t freq, int drs) {
+bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
if( chidx < 72 || chidx >= 72+MAX_XCHANNELS )
- return 0; // channels 0..71 are hardwired
+ return 0; // channels 0..71 are hardwired
chidx -= 72;
LMIC.xchFreq[chidx] = freq;
- LMIC.xchDrs[chidx] = drs < 0 ? (DR_SF10|(DR_SF8C<<4)) : drs;
+ LMIC.xchDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF10,DR_SF8C) : drmap;
LMIC.channelMap[chidx>>4] |= (1<<(chidx&0xF));
return 1;
}
+void LMIC_disableChannel (u1_t channel) {
+ if( channel < 72+MAX_XCHANNELS )
+ LMIC.channelMap[channel/4] &= ~(1<<(channel&0xF));
+}
static u1_t mapChannels (u1_t chpage, u2_t chmap) {
if( chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF ) {
- u2_t en125 = chpage == MCMD_LADR_CHP_125ON ? 0xFFFF : 0x0000;
- for( u1_t u=0; u<4; u++ )
- LMIC.channelMap[u] = en125;
- LMIC.channelMap[64/16] = chmap;
+ u2_t en125 = chpage == MCMD_LADR_CHP_125ON ? 0xFFFF : 0x0000;
+ for( u1_t u=0; u<4; u++ )
+ LMIC.channelMap[u] = en125;
+ LMIC.channelMap[64/16] = chmap;
} else {
- if( chpage >= (72+MAX_XCHANNELS+15)/16 )
- return 0;
- LMIC.channelMap[chpage] = chmap;
+ if( chpage >= (72+MAX_XCHANNELS+15)/16 )
+ return 0;
+ LMIC.channelMap[chpage] = chmap;
}
return 1;
}
@@ -779,77 +778,68 @@
static void updateTx (ostime_t txbeg) {
u1_t chnl = LMIC.txChnl;
if( chnl < 64 ) {
- LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP;
- LMIC.txpow = 30;
- return;
+ LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP;
+ LMIC.txpow = 30;
+ return;
}
LMIC.txpow = 26;
if( chnl < 64+8 ) {
- LMIC.freq = US915_500kHz_UPFBASE + (chnl-64)*US915_500kHz_UPFSTEP;
+ LMIC.freq = US915_500kHz_UPFBASE + (chnl-64)*US915_500kHz_UPFSTEP;
} else {
- ASSERT(chnl < 64+8+MAX_XCHANNELS);
- LMIC.freq = LMIC.xchFreq[chnl-72];
+ ASSERT(chnl < 64+8+MAX_XCHANNELS);
+ LMIC.freq = LMIC.xchFreq[chnl-72];
}
// Update global duty cycle stats
if( LMIC.globalDutyRate != 0 ) {
- ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
- LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
+ ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
+ LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
}
}
// US does not have duty cycling - return now as earliest TX time
#define nextTx(now) (_nextTx(),(now))
static void _nextTx (void) {
- u1_t chnl = LMIC.txChnl;
- u1_t cnt = 0;
- bit_t bw500 = (LMIC.datarate >= DR_SF8C);
-
- if( (LMIC.chRnd & 0xF) == 0 ) {
- chnl += LMIC.chRnd>>4;
- LMIC.chRnd = os_getRndU1();
+ if( LMIC.chRnd==0 )
+ LMIC.chRnd = os_getRndU1() & 0x3F;
+ if( LMIC.datarate >= DR_SF8C ) { // 500kHz
+ u1_t map = LMIC.channelMap[64/16]&0xFF;
+ for( u1_t i=0; i<8; i++ ) {
+ if( (map & (1<<(++LMIC.chRnd & 7))) != 0 ) {
+ LMIC.txChnl = 64 + (LMIC.chRnd & 7);
+ return;
+ }
+ }
+ } else { // 125kHz
+ for( u1_t i=0; i<64; i++ ) {
+ u1_t chnl = ++LMIC.chRnd & 0x3F;
+ if( (LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0xF))) != 0 ) {
+ LMIC.txChnl = chnl;
+ return;
+ }
+ }
}
- LMIC.chRnd--;
- again:
- chnl += 1;
- if( bw500 ) {
- // Only channels 64..71
- chnl = 64+(chnl&0x7);
- // At least one 500kHz channel must be enabled
- // - otherwise MAC should not have used such a datarate
- ASSERT((LMIC.channelMap[64/16]&0xFF)!=0x00);
- if( ++cnt == 8 )
- return; // no appropriate channel enabled stay where we are
- } else {
- // At least one 125kHz channel must be enabled
- // - otherwise MAC should not have used DR_SF10-7
- ASSERT((LMIC.channelMap[0]|LMIC.channelMap[1]|LMIC.channelMap[2]|LMIC.channelMap[3])!=0x0000);
- chnl = chnl&0x3F;
- if( ++cnt == 64 )
- return; // no appropriate channel enabled stay where we are
- }
- if( (LMIC.channelMap[(chnl >> 4)] & (1<<(chnl&0xF))) == 0 )
- goto again; // not enabled
- LMIC.txChnl = chnl;
+ // No feasible channel found! Keep old one.
}
static void setBcnRxParams (void) {
LMIC.dataLen = 0;
- LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl*US915_500kHz_DNFSTEP;
+ LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl * US915_500kHz_DNFSTEP;
LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN),1),LEN_BCN);
}
-#define setRx1Params(void) { \
+#define setRx1Params() { \
LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP; \
- if( /* TX datarate */LMIC.rxsyms < DR_SF8C ) \
- LMIC.rxsyms += DR_SF10CR - DR_SF10; \
- else if( LMIC.rxsyms == DR_SF8C ) \
- LMIC.rxsyms = DR_SF7CR; \
- LMIC.rps = dndr2rps(LMIC.rxsyms); \
+ if( /* TX datarate */LMIC.dndr < DR_SF8C ) \
+ LMIC.dndr += DR_SF10CR - DR_SF10; \
+ else if( LMIC.dndr == DR_SF8C ) \
+ LMIC.dndr = DR_SF7CR; \
+ LMIC.rps = dndr2rps(LMIC.dndr); \
}
static void initJoinLoop (void) {
- LMIC.txChnl = os_getRndU1() & 0x3F;
+ LMIC.chRnd = 0;
+ LMIC.txChnl = 0;
LMIC.adrTxPow = 20;
ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
LMIC.txend = os_getTime();
@@ -861,23 +851,29 @@
// SF7/8/9/10 on a random channel 0..63
// SF8C on a random channel 64..71
//
- LMIC.opmode &= ~OP_NEXTCHNL;
- LMIC.txend = os_getTime();
+ u1_t failed = 0;
if( LMIC.datarate != DR_SF8C ) {
- LMIC.txChnl = 64+(LMIC.txChnl&7);
- LMIC.datarate = DR_SF8C;
+ LMIC.txChnl = 64+(LMIC.txChnl&7);
+ setDrJoin(DRCHG_SET, DR_SF8C);
} else {
- LMIC.txChnl = os_getRndU1() & 0x3F;
- LMIC.datarate = DR_SF7 - (LMIC.txCnt++ & 3);
- if( (LMIC.txCnt & 3) == 0 ) {
- // Tried SF10/SF8C
- // Delay by 7,15,31,63,127,255 secs
- return rndDelay((4<<(LMIC.txCnt>>2))-1) | 1; // |1 - trigger EV_JOIN_FAILED event
+ LMIC.txChnl = os_getRndU1() & 0x3F;
+ s1_t dr = DR_SF7 - ++LMIC.txCnt;
+ if( dr < DR_SF10 ) {
+ dr = DR_SF10;
+ failed = 1; // All DR exhausted - signal failed
+ }
+ setDrJoin(DRCHG_SET, dr);
}
- }
- // Always wait 500ms in case there was a TX and we just missed it.
- // Sending immediately would not be picked up because GW is still busy txing missed frame.
- return (OSTICKS_PER_SEC/2) & ~1;
+ LMIC.opmode &= ~OP_NEXTCHNL;
+ LMIC.txend = os_getTime() +
+ (isTESTMODE()
+ // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it - GW is still busy)
+ ? DNW2_SAFETY_ZONE
+ // Otherwise: randomize join (street lamp case):
+ // SF10:16, SF9=8,..SF8C:1secs
+ : rndDelay(16>>LMIC.datarate));
+ // 1 - triggers EV_JOIN_FAILED event
+ return failed;
}
//
@@ -896,13 +892,20 @@
static void reportEvent (ev_t ev) {
EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = ev));
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = ev));
ON_LMIC_EVENT(ev);
engineUpdate();
}
+static void runReset (xref2osjob_t osjob) {
+ // Disable session
+ LMIC_reset();
+ LMIC_startJoining();
+ reportEvent(EV_RESET);
+}
+
static void stateJustJoined (void) {
LMIC.seqnoDn = LMIC.seqnoUp = 0;
LMIC.rejoinCnt = 0;
@@ -927,23 +930,30 @@
static int decodeBeacon (void) {
ASSERT(LMIC.dataLen == LEN_BCN); // implicit header RX guarantees this
xref2u1_t d = LMIC.frame;
- if( os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1) )
- return 0; // first (common) part fails CRC check
+ if(
+#if CFG_eu868
+ d[OFF_BCN_CRC1] != (u1_t)os_crc16(d,OFF_BCN_CRC1)
+#elif CFG_us915
+ os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d,OFF_BCN_CRC1)
+#endif
+ )
+ return 0; // first (common) part fails CRC check
// First set of fields is ok
u4_t bcnnetid = os_rlsbf4(&d[OFF_BCN_NETID]) & 0xFFFFFF;
if( bcnnetid != LMIC.netid )
- return -1; // not the beacon we're looking for
+ return -1; // not the beacon we're looking for
LMIC.bcninfo.flags &= ~(BCN_PARTIAL|BCN_FULL);
// Match - update bcninfo structure
- os_copyMem((xref2u1_t)&LMIC.bcninfo.rxq, (xref2u1_t)&LMIC.rxq, SIZEOFEXPR(LMIC.rxq));
+ LMIC.bcninfo.snr = LMIC.snr;
+ LMIC.bcninfo.rssi = LMIC.rssi;
LMIC.bcninfo.txtime = LMIC.rxtime - AIRTIME_BCN_osticks;
LMIC.bcninfo.time = os_rlsbf4(&d[OFF_BCN_TIME]);
LMIC.bcninfo.flags |= BCN_PARTIAL;
// Check 2nd set
if( os_rlsbf2(&d[OFF_BCN_CRC2]) != os_crc16(d,OFF_BCN_CRC2) )
- return 1;
+ return 1;
// Second set of fields is ok
LMIC.bcninfo.lat = (s4_t)os_rlsbf4(&d[OFF_BCN_LAT-1]) >> 8; // read as signed 24-bit
LMIC.bcninfo.lon = (s4_t)os_rlsbf4(&d[OFF_BCN_LON-1]) >> 8; // ditto
@@ -959,16 +969,16 @@
u1_t ftype = hdr & HDR_FTYPE;
int dlen = LMIC.dataLen;
if( dlen < OFF_DAT_OPTS+4 ||
- (hdr & HDR_MAJOR) != HDR_MAJOR_V1 ||
- (ftype != HDR_FTYPE_DADN && ftype != HDR_FTYPE_DCDN) ) {
- // Basic sanity checks failed
- EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]),
- e_.info2 = hdr + (dlen<<8)));
+ (hdr & HDR_MAJOR) != HDR_MAJOR_V1 ||
+ (ftype != HDR_FTYPE_DADN && ftype != HDR_FTYPE_DCDN) ) {
+ // Basic sanity checks failed
+ EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]),
+ e_.info2 = hdr + (dlen<<8)));
norx:
- LMIC.dataLen = 0;
- return 0;
+ LMIC.dataLen = 0;
+ return 0;
}
// Validate exact frame length
// Note: device address was already read+evaluated in order to arrive here.
@@ -981,252 +991,261 @@
int pend = dlen-4; // MIC
if( addr != LMIC.devaddr ) {
- EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = addr,
- e_.info2 = LMIC.devaddr));
- goto norx;
+ EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = addr,
+ e_.info2 = LMIC.devaddr));
+ goto norx;
}
if( poff > pend ) {
- EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16)));
- goto norx;
+ EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16)));
+ goto norx;
}
int port = -1;
int replayConf = 0;
if( pend > poff )
- port = d[poff++];
+ port = d[poff++];
seqno = LMIC.seqnoDn + (u2_t)(seqno - LMIC.seqnoDn);
if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) {
- EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_MIC,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = Base::lsbf4(&d[pend]),
- e_.info2 = seqno));
- goto norx;
- }
- if( seqno < LMIC.seqnoDn ) {
- if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) {
- EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = LMIC.seqnoDn,
- e_.info2 = seqno));
+ EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC,
+ e_.eui1 = MAIN::CDEV->getEui(),
+ e_.info1 = Base::lsbf4(&d[pend]),
+ e_.info2 = seqno,
+ e_.info3 = LMIC.devaddr));
goto norx;
}
- if( seqno != LMIC.seqnoDn-1 || !LMIC.dnConf || ftype != HDR_FTYPE_DCDN ) {
- EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_OBSOLETE,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = LMIC.seqnoDn,
- e_.info2 = seqno));
- goto norx;
- }
- // Replay of previous sequence number allowed only if
- // previous frame and repeated both requested confirmation
- replayConf = 1;
+ if( seqno < LMIC.seqnoDn ) {
+ if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) {
+ EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = LMIC.seqnoDn,
+ e_.info2 = seqno));
+ goto norx;
+ }
+ if( seqno != LMIC.seqnoDn-1 || !LMIC.dnConf || ftype != HDR_FTYPE_DCDN ) {
+ EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_OBSOLETE,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = LMIC.seqnoDn,
+ e_.info2 = seqno));
+ goto norx;
+ }
+ // Replay of previous sequence number allowed only if
+ // previous frame and repeated both requested confirmation
+ replayConf = 1;
}
else {
- if( seqno > LMIC.seqnoDn ) {
- EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = LMIC.seqnoDn,
- e_.info2 = seqno));
- }
- LMIC.seqnoDn = seqno+1; // next number to be expected
- DO_DEVDB(updateSeqnoDn, LMIC.seqnoDn);
- // DN frame requested confirmation - provide ACK once with next UP frame
- LMIC.dnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0);
+ if( seqno > LMIC.seqnoDn ) {
+ EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = LMIC.seqnoDn,
+ e_.info2 = seqno));
+ }
+ LMIC.seqnoDn = seqno+1; // next number to be expected
+ DO_DEVDB(LMIC.seqnoDn,seqnoDn);
+ // DN frame requested confirmation - provide ACK once with next UP frame
+ LMIC.dnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0);
}
if( LMIC.dnConf || (fct & FCT_MORE) )
- LMIC.opmode |= OP_POLL;
+ LMIC.opmode |= OP_POLL;
// We heard from network
- LMIC.adrChanged = 0;
- LMIC.adrAckReq = LINK_CHECK_INIT;
+ LMIC.adrChanged = LMIC.rejoinCnt = 0;
+ if( LMIC.adrAckReq != LINK_CHECK_OFF )
+ LMIC.adrAckReq = LINK_CHECK_INIT;
// Process OPTS
- int m = LMIC.rxq.rssi - RSSI_OFF - getSensitivity(LMIC.rps);
+ int m = LMIC.rssi - RSSI_OFF - getSensitivity(LMIC.rps);
LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m;
xref2u1_t opts = &d[OFF_DAT_OPTS];
int oidx = 0;
while( oidx < olen ) {
- switch( opts[oidx] ) {
- case MCMD_LCHK_ANS: {
- //int gwmargin = opts[oidx+1];
- //int ngws = opts[oidx+2];
- oidx += 3;
- continue;
- }
- case MCMD_LADR_REQ: {
- u1_t p1 = opts[oidx+1]; // txpow + DR
- u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channel
- u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK; // channel page
- u1_t uprpt = opts[oidx+4] & MCMD_LADR_REPEAT_MASK; // up repeat count
- oidx += 5;
+ switch( opts[oidx] ) {
+ case MCMD_LCHK_ANS: {
+ //int gwmargin = opts[oidx+1];
+ //int ngws = opts[oidx+2];
+ oidx += 3;
+ continue;
+ }
+ case MCMD_LADR_REQ: {
+ u1_t p1 = opts[oidx+1]; // txpow + DR
+ u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels
+ u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK; // channel page
+ u1_t uprpt = opts[oidx+4] & MCMD_LADR_REPEAT_MASK; // up repeat count
+ oidx += 5;
- LMIC.ladrAns = 0x80 | // Include an answer into next frame up
- MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK;
- if( !mapChannels(chpage, chmap) )
- LMIC.ladrAns &= ~MCMD_LADR_ANS_CHACK;
- dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT);
- if( !validDR(dr) ) {
- LMIC.ladrAns &= ~MCMD_LADR_ANS_DRACK;
- dr = (dr_t)LMIC.datarate;
- EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = Base::lsbf4(&d[pend]),
- e_.info2 = Base::msbf4(&opts[oidx-4])));
+ LMIC.ladrAns = 0x80 | // Include an answer into next frame up
+ MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK;
+ if( !mapChannels(chpage, chmap) )
+ LMIC.ladrAns &= ~MCMD_LADR_ANS_CHACK;
+ dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT);
+ if( !validDR(dr) ) {
+ LMIC.ladrAns &= ~MCMD_LADR_ANS_DRACK;
+ EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = Base::lsbf4(&d[pend]),
+ e_.info2 = Base::msbf4(&opts[oidx-4])));
+ }
+ if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) {
+ // Nothing went wrong - use settings
+ LMIC.upRepeat = uprpt;
+ setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1));
+ }
+ LMIC.adrChanged = 1; // Trigger an ACK to NWK
+ continue;
}
- LMIC.upRepeat = uprpt;
- setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1));
- LMIC.adrChanged = 1; // Trigger an ACK to NWK
- continue;
- }
- case MCMD_DEVS_REQ: {
- LMIC.devsAns = 1;
- oidx += 1;
- continue;
- }
- case MCMD_DN2P_SET: {
- dr_t dr = (dr_t)(opts[oidx+1] & 0x0F);
- u4_t freq = convFreq(&opts[oidx+2]);
- oidx += 5;
- LMIC.dn2Ans = 0x80; // answer pending
- if( validDR(dr) )
- LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK;
- if( freq != 0 )
- LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK;
- if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK) ) {
- LMIC.dn2Dr = dr;
- LMIC.dn2Freq = freq;
- DO_DEVDB(updateDn2,LMIC.dn2Dr,LMIC.dn2Freq);
+ case MCMD_DEVS_REQ: {
+ LMIC.devsAns = 1;
+ oidx += 1;
+ continue;
+ }
+ case MCMD_DN2P_SET: {
+ dr_t dr = (dr_t)(opts[oidx+1] & 0x0F);
+ u4_t freq = convFreq(&opts[oidx+2]);
+ oidx += 5;
+ LMIC.dn2Ans = 0x80; // answer pending
+ if( validDR(dr) )
+ LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK;
+ if( freq != 0 )
+ LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK;
+ if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK) ) {
+ LMIC.dn2Dr = dr;
+ LMIC.dn2Freq = freq;
+ DO_DEVDB(LMIC.dn2Dr,dn2Dr);
+ DO_DEVDB(LMIC.dn2Freq,dn2Freq);
+ }
+ continue;
}
- continue;
- }
- case MCMD_DCAP_REQ: {
- u1_t cap = opts[oidx+1];
- oidx += 2;
- // A value cap=0xFF means device is OFF unless enabled again manually
- // We just set duty cap to 0xF which is 0.003% -- pretty much off.
- // We don't check 0xF0 bits if cap!=0xFF
- LMIC.globalDutyRate = cap & 0xF;
- LMIC.globalDutyAvail = os_getTime();
- DO_DEVDB(updateDutyCap,cap);
- LMIC.dutyCapAns = 1;
- continue;
- }
- case MCMD_SNCH_REQ: {
- u1_t chidx = opts[oidx+1]; // channel
- u4_t freq = convFreq(&opts[oidx+2]); // freq
- u1_t drs = opts[oidx+5]; // datarate span
- LMIC.snchAns = 0x80;
- if( freq != 0 && setupChannel(chidx,freq,drs) )
- LMIC.snchAns |= MCMD_SNCH_ANS_DRACK|MCMD_SNCH_ANS_FQACK;
- oidx += 6;
- }
- case MCMD_PING_SET: {
- u4_t freq = convFreq(&opts[oidx+1]);
- oidx += 4;
- u1_t flags = 0x80;
- if( freq != 0 ) {
- flags |= MCMD_PING_ANS_FQACK;
- LMIC.ping.freq = freq;
- DO_DEVDB(updateClassB,LMIC.ping.intvExp,freq,LMIC.ping.dr);
+ case MCMD_DCAP_REQ: {
+ u1_t cap = opts[oidx+1];
+ oidx += 2;
+ // A value cap=0xFF means device is OFF unless enabled again manually.
+ if( cap==0xFF )
+ LMIC.opmode |= OP_SHUTDOWN; // stop any sending
+ LMIC.globalDutyRate = cap & 0xF;
+ LMIC.globalDutyAvail = os_getTime();
+ DO_DEVDB(cap,dutyCap);
+ LMIC.dutyCapAns = 1;
+ continue;
+ }
+ case MCMD_SNCH_REQ: {
+ u1_t chidx = opts[oidx+1]; // channel
+ u4_t freq = convFreq(&opts[oidx+2]); // freq
+ u1_t drs = opts[oidx+5]; // datarate span
+ LMIC.snchAns = 0x80;
+ if( freq != 0 && LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(drs&0xF,drs>>4), -1) )
+ LMIC.snchAns |= MCMD_SNCH_ANS_DRACK|MCMD_SNCH_ANS_FQACK;
+ oidx += 6;
+ continue;
}
- LMIC.pingSetAns = flags;
- continue;
- }
- case MCMD_BCNI_ANS: {
- // Ignore if tracking already enabled
- if( (LMIC.opmode & OP_TRACK) == 0 ) {
- LMIC.bcnChnl = opts[oidx+3];
- // Enable tracking - bcninfoTries
- LMIC.opmode |= OP_TRACK;
- // Cleared later in txComplete handling - triggers EV_BEACON_FOUND
- ASSERT(LMIC.bcninfoTries!=0);
- // Setup RX parameters
- LMIC.bcninfo.txtime = (LMIC.rxtime
- - calcAirTime(LMIC.rps, LMIC.dataLen)
- + ms2osticks(os_rlsbf2(&opts[oidx+1]) * 10)
- + ms2osticksCeil(5)
- - BCN_INTV_osticks);
- LMIC.bcninfo.flags = 0; // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared)
- calcBcnRxWindowFromMillis(10,1); // error of +/-5 ms
+ case MCMD_PING_SET: {
+ u4_t freq = convFreq(&opts[oidx+1]);
+ oidx += 4;
+ u1_t flags = 0x80;
+ if( freq != 0 ) {
+ flags |= MCMD_PING_ANS_FQACK;
+ LMIC.ping.freq = freq;
+ DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
+ DO_DEVDB(LMIC.ping.freq, pingFreq);
+ DO_DEVDB(LMIC.ping.dr, pingDr);
+ }
+ LMIC.pingSetAns = flags;
+ continue;
+ }
+ case MCMD_BCNI_ANS: {
+ // Ignore if tracking already enabled
+ if( (LMIC.opmode & OP_TRACK) == 0 ) {
+ LMIC.bcnChnl = opts[oidx+3];
+ // Enable tracking - bcninfoTries
+ LMIC.opmode |= OP_TRACK;
+ // Cleared later in txComplete handling - triggers EV_BEACON_FOUND
+ ASSERT(LMIC.bcninfoTries!=0);
+ // Setup RX parameters
+ LMIC.bcninfo.txtime = (LMIC.rxtime
+ + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BCNI_TUNIT)
+ + ms2osticksCeil(MCMD_BCNI_TUNIT/2)
+ - BCN_INTV_osticks);
+ LMIC.bcninfo.flags = 0; // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared)
+ calcBcnRxWindowFromMillis(MCMD_BCNI_TUNIT,1); // error of +/-N ms
- EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BCNI_ANS,
- e_.eui = MAIN::CDEV->getEui(),
- e_.lostmic = Base::lsbf4(&d[pend]),
- e_.info = (LMIC.missedBcns |
- (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks
- - LMIC.bcnRxtime) << 8)),
- e_.time = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks)));
+ EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BCNI_ANS,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.lostmic = Base::lsbf4(&d[pend]),
+ e_.info = (LMIC.missedBcns |
+ (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks
+ - LMIC.bcnRxtime) << 8)),
+ e_.time = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks)));
+ }
+ oidx += 4;
+ continue;
}
- oidx += 4;
- continue;
- }
- }
- EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = Base::lsbf4(&d[pend]),
- e_.info2 = Base::msbf4(&opts[oidx])));
- break;
+ }
+ EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = Base::lsbf4(&d[pend]),
+ e_.info2 = Base::msbf4(&opts[oidx])));
+ break;
}
if( oidx != olen ) {
- EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = 0x1000000 + (oidx) + (olen<<8)));
+ EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = 0x1000000 + (oidx) + (olen<<8)));
}
if( !replayConf ) {
- // Handle payload only if not a replay
- // Decrypt payload - if any
- if( port >= 0 && pend-poff > 0 )
- aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff);
+ // Handle payload only if not a replay
+ // Decrypt payload - if any
+ if( port >= 0 && pend-poff > 0 )
+ aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff);
- EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(),
- e_.devaddr = LMIC.devaddr,
- e_.seqno = seqno,
- e_.flags = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN,
- e_.mic = Base::lsbf4(&d[pend]),
- e_.hdr = d[LORA::OFF_DAT_HDR],
- e_.fct = d[LORA::OFF_DAT_FCT],
- e_.port = port,
- e_.plen = dlen,
- e_.olen = olen,
- memcpy(e_.opts, opts, olen)));
+ EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(),
+ e_.devaddr = LMIC.devaddr,
+ e_.seqno = seqno,
+ e_.flags = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN,
+ e_.mic = Base::lsbf4(&d[pend]),
+ e_.hdr = d[LORA::OFF_DAT_HDR],
+ e_.fct = d[LORA::OFF_DAT_FCT],
+ e_.port = port,
+ e_.plen = dlen,
+ e_.opts.length = olen,
+ memcpy(&e_.opts[0], opts, olen)));
} else {
- EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = Base::lsbf4(&d[pend]),
- e_.info2 = seqno));
+ EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = Base::lsbf4(&d[pend]),
+ e_.info2 = seqno));
}
if( // NWK acks but we don't have a frame pending
- (ackup && LMIC.txCnt == 0) ||
- // We sent up confirmed and we got a response in DNW1/DNW2
- // BUT it did not carry an ACK - this should never happen
- // Do not resend and assume frame was not ACKed.
- (!ackup && LMIC.txCnt != 0) ) {
- EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = seqno,
- e_.info2 = ackup));
+ (ackup && LMIC.txCnt == 0) ||
+ // We sent up confirmed and we got a response in DNW1/DNW2
+ // BUT it did not carry an ACK - this should never happen
+ // Do not resend and assume frame was not ACKed.
+ (!ackup && LMIC.txCnt != 0) ) {
+ EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = seqno,
+ e_.info2 = ackup));
}
if( LMIC.txCnt != 0 ) // we requested an ACK
- LMIC.txrxFlags |= ackup ? TXRX_ACK : TXRX_NACK;
+ LMIC.txrxFlags |= ackup ? TXRX_ACK : TXRX_NACK;
if( port < 0 ) {
- LMIC.txrxFlags |= TXRX_NOPORT;
- LMIC.dataBeg = LMIC.dataLen = 0;
+ LMIC.txrxFlags |= TXRX_NOPORT;
+ LMIC.dataBeg = poff;
+ LMIC.dataLen = 0;
} else {
- LMIC.dataBeg = poff;
- LMIC.dataLen = pend-poff;
+ LMIC.txrxFlags |= TXRX_PORT;
+ LMIC.dataBeg = poff;
+ LMIC.dataLen = pend-poff;
}
return 1;
}
@@ -1263,27 +1282,25 @@
// Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime
static void txDone (ostime_t delay, osjobcb_t func) {
- // NOTE: LMIC.rxsyms carries the modified DR for TX (LMIC.datarate is the base DR - [CONFIRM mode])
- u1_t dr = LMIC.rxsyms; // rxschedInit - would overwrite rxsyms
if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) {
- rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer!
- LMIC.opmode |= OP_PINGINI;
+ rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer!
+ LMIC.opmode |= OP_PINGINI;
}
// Change RX frequency / rps (US only) before we increment txChnl
setRx1Params();
// LMIC.rxsyms carries the TX datarate (can be != LMIC.datarate [confirm retries etc.])
// Setup receive - LMIC.rxtime is preloaded with 1.5 symbols offset to tune
// into the middle of the 8 symbols preamble.
-#if CFG_eu868
+#if defined(CFG_eu868)
if( /* TX datarate */LMIC.rxsyms == DR_FSK ) {
- LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160);
- LMIC.rxsyms = RXLEN_FSK;
+ LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160);
+ LMIC.rxsyms = RXLEN_FSK;
}
else
#endif
{
- LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(dr);
- LMIC.rxsyms = MINRX_SYMS;
+ LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(LMIC.dndr);
+ LMIC.rxsyms = MINRX_SYMS;
}
os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
}
@@ -1305,86 +1322,90 @@
if( LMIC.dataLen == 0 ) {
nojoinframe:
- if( (LMIC.opmode & OP_OTA) != 0 ) {
- if( (LMIC.opmode & OP_JOINING) == 0 ) {
- ASSERT((LMIC.opmode & OP_REJOIN) != 0);
- // REJOIN attempt for roaming
- LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND);
- if( LMIC.rejoinCnt < 10 )
- LMIC.rejoinCnt++;
- reportEvent(EV_REJOIN_FAILED);
+ if( (LMIC.opmode & OP_JOINING) == 0 ) {
+ ASSERT((LMIC.opmode & OP_REJOIN) != 0);
+ // REJOIN attempt for roaming
+ LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND);
+ if( LMIC.rejoinCnt < 10 )
+ LMIC.rejoinCnt++;
+ reportEvent(EV_REJOIN_FAILED);
+ return 1;
+ }
+ LMIC.opmode &= ~OP_TXRXPEND;
+ ostime_t delay = nextJoinState();
+ EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = LMIC.datarate|DR_PAGE,
+ e_.info2 = osticks2ms(delay)));
+ // Build next JOIN REQUEST with next engineUpdate call
+ // Optionally, report join failed.
+ // Both after a random/chosen amount of ticks.
+ os_setTimedCallback(&LMIC.osjob, os_getTime()+delay,
+ (delay&1) != 0
+ ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed
+ : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
return 1;
}
- }
- LMIC.opmode &= ~OP_TXRXPEND;
- ostime_t delay = nextJoinState();
- EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = LMIC.datarate|DR_PAGE,
- e_.info2 = osticks2ms(delay)));
- // Build next JOIN REQUEST with next engineUpdate call
- // Optionally, report join failed.
- // Both after a random/chosen amount of ticks.
- os_setTimedCallback(&LMIC.osjob, os_getTime()+delay,
- (delay&1) != 0
- ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed
- : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
- return 1;
- }
u1_t hdr = LMIC.frame[0];
u1_t dlen = LMIC.dataLen;
u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt!
if( (dlen != LEN_JA && dlen != LEN_JAEXT)
- || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
- EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = dlen < 4 ? 0 : mic,
- e_.info2 = hdr + (dlen<<8)));
+ || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
+ EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = dlen < 4 ? 0 : mic,
+ e_.info2 = hdr + (dlen<<8)));
badframe:
- goto nojoinframe;
+ if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
+ return 0;
+ goto nojoinframe;
}
aes_encrypt(LMIC.frame+1, dlen-1);
if( !aes_verifyMic0(LMIC.frame, dlen-4) ) {
- EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC,
- e_.info = mic));
- goto badframe;
+ EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC,
+ e_.info = mic));
+ goto badframe;
}
u4_t addr = os_rlsbf4(LMIC.frame+OFF_JA_DEVADDR);
LMIC.devaddr = addr;
- LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_BCN_NETID-1]) >> 8;
+ LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_JA_NETID]) & 0xFFFFFF;
-#if CFG_eu868
+#if defined(CFG_eu868)
initDefaultChannels(0);
#endif
if( dlen > LEN_JA ) {
- dlen = OFF_CFLIST;
-#if CFG_eu868
- u1_t chidx=3;
-#elif CFG_us915
- u1_t chidx=72;
+ dlen = OFF_CFLIST;
+#if defined(CFG_eu868)
+ u1_t chidx=3;
+#elif defined(CFG_us915)
+ u1_t chidx=72;
#endif
- for( ; chidx<8; chidx++, dlen+=3 )
- setupChannel(chidx, os_rlsbf4(&LMIC.frame[dlen-1]) >> 8, -1);
+ for( ; chidx<8; chidx++, dlen+=3 )
+ LMIC_setupChannel(chidx, os_rlsbf4(&LMIC.frame[dlen-1]) >> 8, 0, -1);
}
// already incremented when JOIN REQ got sent off
aes_sessKeys(LMIC.devNonce-1, &LMIC.frame[OFF_JA_ARTNONCE], LMIC.nwkKey, LMIC.artKey);
- DO_DEVDB(updateJoinAcc, LMIC.devaddr, LMIC.nwkKey, LMIC.artKey);
+ DO_DEVDB(LMIC.netid, netid);
+ DO_DEVDB(LMIC.devaddr, devaddr);
+ DO_DEVDB(LMIC.nwkKey, nwkkey);
+ DO_DEVDB(LMIC.artKey, artkey);
EV(joininfo, INFO, (e_.arteui = MAIN::CDEV->getArtEui(),
- e_.deveui = MAIN::CDEV->getEui(),
- e_.devaddr = LMIC.devaddr,
- e_.oldaddr = oldaddr,
- e_.nonce = LMIC.devNonce-1,
- e_.mic = mic,
- e_.flags = ((LMIC.opmode & OP_REJOIN) != 0
- ? EV::joininfo_t::REJOIN_ACCEPT
- : EV::joininfo_t::ACCEPT)));
+ e_.deveui = MAIN::CDEV->getEui(),
+ e_.devaddr = LMIC.devaddr,
+ e_.oldaddr = oldaddr,
+ e_.nonce = LMIC.devNonce-1,
+ e_.mic = mic,
+ e_.reason = ((LMIC.opmode & OP_REJOIN) != 0
+ ? EV::joininfo_t::REJOIN_ACCEPT
+ : EV::joininfo_t::ACCEPT)));
ASSERT((LMIC.opmode & (OP_JOINING|OP_REJOIN))!=0);
if( (LMIC.opmode & OP_REJOIN) != 0 ) {
- LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt);
+ // Lower DR every try below current UP DR
+ LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt);
}
LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI) | OP_NEXTCHNL;
stateJustJoined();
@@ -1392,12 +1413,26 @@
return 1;
}
-static void processRx1Jacc (xref2osjob_t osjob) {
+
+static void processRx2Jacc (xref2osjob_t osjob) {
if( LMIC.dataLen == 0 )
- LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot
+ LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot
processJoinAccept();
}
+
+static void setupRx2Jacc (xref2osjob_t osjob) {
+ LMIC.osjob.func = FUNC_ADDR(processRx2Jacc);
+ setupRx2();
+}
+
+
+static void processRx1Jacc (xref2osjob_t osjob) {
+ if( LMIC.dataLen == 0 || !processJoinAccept() )
+ schedRx2(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc));
+}
+
+
static void setupRx1Jacc (xref2osjob_t osjob) {
setupRx1(FUNC_ADDR(processRx1Jacc));
}
@@ -1412,9 +1447,20 @@
// Fwd decl.
static bit_t processDnData(void);
+static void processRx2DnDataDelay (xref2osjob_t osjob) {
+ processDnData();
+}
+
static void processRx2DnData (xref2osjob_t osjob) {
- if( LMIC.dataLen == 0 )
- LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot
+ if( LMIC.dataLen == 0 ) {
+ LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot
+ // Delay callback processing to avoid up TX while gateway is txing our missed frame!
+ // Since DNW2 uses SF12 by default we wait 3 secs.
+ os_setTimedCallback(&LMIC.osjob,
+ (os_getTime() + DNW2_SAFETY_ZONE + rndDelay(2)),
+ processRx2DnDataDelay);
+ return;
+ }
processDnData();
}
@@ -1427,7 +1473,7 @@
static void processRx1DnData (xref2osjob_t osjob) {
if( LMIC.dataLen == 0 || !processDnData() )
- schedRx2(DELAY_DNW2_osticks, FUNC_ADDR(setupRx2DnData));
+ schedRx2(DELAY_DNW2_osticks, FUNC_ADDR(setupRx2DnData));
}
@@ -1451,77 +1497,80 @@
// Prioritize by importance
int end = OFF_DAT_OPTS;
if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE)) == (OP_TRACK|OP_PINGABLE) ) {
- // Indicate pingability in every UP frame
- LMIC.frame[end] = MCMD_PING_IND;
- LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4);
- end += 2;
+ // Indicate pingability in every UP frame
+ LMIC.frame[end] = MCMD_PING_IND;
+ LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4);
+ end += 2;
}
if( LMIC.dutyCapAns ) {
- LMIC.frame[end] = MCMD_DCAP_ANS;
- end += 1;
- LMIC.dutyCapAns = 0;
+ LMIC.frame[end] = MCMD_DCAP_ANS;
+ end += 1;
+ LMIC.dutyCapAns = 0;
}
if( LMIC.dn2Ans ) {
- LMIC.frame[end+0] = MCMD_DN2P_ANS;
- LMIC.frame[end+1] = LMIC.dn2Ans & ~MCMD_DN2P_ANS_RFU;
- end += 2;
- LMIC.dn2Ans = 0;
+ LMIC.frame[end+0] = MCMD_DN2P_ANS;
+ LMIC.frame[end+1] = LMIC.dn2Ans & ~MCMD_DN2P_ANS_RFU;
+ end += 2;
+ LMIC.dn2Ans = 0;
}
if( LMIC.devsAns ) { // answer to device status
- LMIC.frame[end+0] = MCMD_DEVS_ANS;
- LMIC.frame[end+1] = LMIC.margin;
- LMIC.frame[end+2] = os_getBattLevel();
- end += 3;
- LMIC.devsAns = 0;
+ LMIC.frame[end+0] = MCMD_DEVS_ANS;
+ LMIC.frame[end+1] = LMIC.margin;
+ LMIC.frame[end+2] = os_getBattLevel();
+ end += 3;
+ LMIC.devsAns = 0;
}
if( LMIC.ladrAns ) { // answer to ADR change
- LMIC.frame[end+0] = MCMD_LADR_ANS;
- LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU;
- end += 2;
- LMIC.ladrAns = 0;
+ LMIC.frame[end+0] = MCMD_LADR_ANS;
+ LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU;
+ end += 2;
+ LMIC.ladrAns = 0;
}
if( LMIC.bcninfoTries > 0 ) {
- LMIC.frame[end] = MCMD_BCNI_REQ;
- end += 1;
+ LMIC.frame[end] = MCMD_BCNI_REQ;
+ end += 1;
}
if( LMIC.adrChanged ) {
- LMIC.adrAckReq = LMIC.adrAckReq < 0 ? 0 : LMIC.adrAckReq;
- LMIC.adrChanged = 0;
+ if( LMIC.adrAckReq < 0 )
+ LMIC.adrAckReq = 0;
+ LMIC.adrChanged = 0;
}
if( LMIC.pingSetAns != 0 ) {
- LMIC.frame[end+0] = MCMD_PING_ANS;
- LMIC.frame[end+1] = LMIC.pingSetAns & ~MCMD_PING_ANS_RFU;
- end += 2;
- LMIC.pingSetAns = 0;
+ LMIC.frame[end+0] = MCMD_PING_ANS;
+ LMIC.frame[end+1] = LMIC.pingSetAns & ~MCMD_PING_ANS_RFU;
+ end += 2;
+ LMIC.pingSetAns = 0;
}
if( LMIC.snchAns ) {
- LMIC.frame[end+0] = MCMD_SNCH_ANS;
- LMIC.frame[end+1] = LMIC.snchAns;
- end += 2;
- LMIC.snchAns = 0;
+ LMIC.frame[end+0] = MCMD_SNCH_ANS;
+ LMIC.frame[end+1] = LMIC.snchAns & ~MCMD_SNCH_ANS_RFU;
+ end += 2;
+ LMIC.snchAns = 0;
}
ASSERT(end <= OFF_DAT_OPTS+16);
u1_t flen = end + (txdata ? 5+dlen : 4);
if( flen > MAX_LEN_FRAME ) {
- // Options and payload too big - delay payload
- txdata = 0;
- flen = end+4;
+ // Options and payload too big - delay payload
+ txdata = 0;
+ flen = end+4;
}
LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DAUP | HDR_MAJOR_V1;
LMIC.frame[OFF_DAT_FCT] = (LMIC.dnConf | LMIC.adrEnabled
- | (LMIC.adrAckReq >= 0 ? FCT_ADRARQ : 0)
- | (end-OFF_DAT_OPTS));
+ | (LMIC.adrAckReq >= 0 ? FCT_ADRARQ : 0)
+ | (end-OFF_DAT_OPTS));
os_wlsbf4(LMIC.frame+OFF_DAT_ADDR, LMIC.devaddr);
if( LMIC.txCnt == 0 ) {
- LMIC.seqnoUp += 1;
- DO_DEVDB(updateSeqnoUp, LMIC.seqnoUp);
+ LMIC.seqnoUp += 1;
+ DO_DEVDB(LMIC.seqnoUp,seqnoUp);
} else {
- EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = LMIC.seqnoUp-1,
- e_.info2 = (LMIC.txCnt+1) | (DRADJUST[LMIC.txCnt+1] << 8) | ((LMIC.datarate|DR_PAGE)<<16)));
+ EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = LMIC.seqnoUp-1,
+ e_.info2 = ((LMIC.txCnt+1) |
+ (DRADJUST[LMIC.txCnt+1] << 8) |
+ ((LMIC.datarate|DR_PAGE)<<16))));
}
os_wlsbf2(LMIC.frame+OFF_DAT_SEQNO, LMIC.seqnoUp-1);
@@ -1529,30 +1578,30 @@
LMIC.dnConf = 0;
if( txdata ) {
- if( LMIC.pendTxConf ) {
- // Confirmed only makes sense if we have a payload (or at least a port)
- LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DCUP | HDR_MAJOR_V1;
- LMIC.txCnt += 1;
- }
- LMIC.frame[end] = LMIC.pendTxPort;
- os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen);
- aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey,
- LMIC.devaddr, LMIC.seqnoUp-1,
- /*up*/0, LMIC.frame+end+1, dlen);
+ if( LMIC.pendTxConf ) {
+ // Confirmed only makes sense if we have a payload (or at least a port)
+ LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DCUP | HDR_MAJOR_V1;
+ if( LMIC.txCnt == 0 ) LMIC.txCnt = 1;
+ }
+ LMIC.frame[end] = LMIC.pendTxPort;
+ os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen);
+ aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey,
+ LMIC.devaddr, LMIC.seqnoUp-1,
+ /*up*/0, LMIC.frame+end+1, dlen);
}
aes_appendMic(LMIC.nwkKey, LMIC.devaddr, LMIC.seqnoUp-1, /*up*/0, LMIC.frame, flen-4);
EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(),
- e_.devaddr = LMIC.devaddr,
- e_.seqno = LMIC.seqnoUp-1,
- e_.flags = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP),
- e_.mic = Base::lsbf4(&LMIC.frame[flen-4]),
- e_.hdr = LMIC.frame[LORA::OFF_DAT_HDR],
- e_.fct = LMIC.frame[LORA::OFF_DAT_FCT],
- e_.port = LMIC.pendTxPort,
- e_.plen = txdata ? dlen : 0,
- e_.olen = end-LORA::OFF_DAT_OPTS,
- memcpy(e_.opts, LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS)));
+ e_.devaddr = LMIC.devaddr,
+ e_.seqno = LMIC.seqnoUp-1,
+ e_.flags = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP),
+ e_.mic = Base::lsbf4(&LMIC.frame[flen-4]),
+ e_.hdr = LMIC.frame[LORA::OFF_DAT_HDR],
+ e_.fct = LMIC.frame[LORA::OFF_DAT_FCT],
+ e_.port = LMIC.pendTxPort,
+ e_.plen = txdata ? dlen : 0,
+ e_.opts.length = end-LORA::OFF_DAT_OPTS,
+ memcpy(&e_.opts[0], LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS)));
LMIC.dataLen = flen;
}
@@ -1563,17 +1612,17 @@
os_radio(RADIO_RST);
os_clearCallback(&LMIC.osjob);
if( LMIC.dataLen == 0 ) {
- // Nothing received - timeout
- LMIC.opmode &= ~(OP_SCAN | OP_TRACK);
- reportEvent(EV_SCAN_TIMEOUT);
- return;
+ // Nothing received - timeout
+ LMIC.opmode &= ~(OP_SCAN | OP_TRACK);
+ reportEvent(EV_SCAN_TIMEOUT);
+ return;
}
if( decodeBeacon() <= 0 ) {
- // Something is wrong with the beacon - continue scan
- LMIC.dataLen = 0;
- os_radio(RADIO_RXON);
- os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx));
- return;
+ // Something is wrong with the beacon - continue scan
+ LMIC.dataLen = 0;
+ os_radio(RADIO_RXON);
+ os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx));
+ return;
}
// Found our 1st beacon
// We don't have a previous beacon to calc some drift - assume
@@ -1593,7 +1642,7 @@
static void startScan (void) {
ASSERT(LMIC.devaddr!=0 && (LMIC.opmode & OP_JOINING)==0);
if( (LMIC.opmode & OP_SHUTDOWN) != 0 )
- return;
+ return;
// Cancel onging TX/RX transaction
LMIC.txCnt = LMIC.dnConf = LMIC.bcninfo.flags = 0;
LMIC.opmode = (LMIC.opmode | OP_SCAN) & ~(OP_TXRXPEND);
@@ -1606,11 +1655,11 @@
bit_t LMIC_enableTracking (u1_t tryBcnInfo) {
if( (LMIC.opmode & (OP_SCAN|OP_TRACK|OP_SHUTDOWN)) != 0 )
- return 0; // already in progress or failed to enable
+ return 0; // already in progress or failed to enable
// If BCN info requested from NWK then app has to take are
// of sending data up so that MCMD_BCNI_REQ can be attached.
if( (LMIC.bcninfoTries = tryBcnInfo) == 0 )
- startScan();
+ startScan();
return 1; // enabled
}
@@ -1639,16 +1688,16 @@
aes_appendMic0(d, OFF_JR_MIC);
EV(joininfo,INFO,(e_.deveui = MAIN::CDEV->getEui(),
- e_.arteui = MAIN::CDEV->getArtEui(),
- e_.nonce = LMIC.devNonce,
- e_.oldaddr = LMIC.devaddr,
- e_.mic = Base::lsbf4(&d[LORA::OFF_JR_MIC]),
- e_.flags = ((LMIC.opmode & OP_REJOIN) != 0
- ? EV::joininfo_t::REJOIN_REQUEST
- : EV::joininfo_t::REQUEST)));
+ e_.arteui = MAIN::CDEV->getArtEui(),
+ e_.nonce = LMIC.devNonce,
+ e_.oldaddr = LMIC.devaddr,
+ e_.mic = Base::lsbf4(&d[LORA::OFF_JR_MIC]),
+ e_.reason = ((LMIC.opmode & OP_REJOIN) != 0
+ ? EV::joininfo_t::REJOIN_REQUEST
+ : EV::joininfo_t::REQUEST)));
LMIC.dataLen = LEN_JR;
LMIC.devNonce++;
- DO_DEVDB(updateDevNonce, LMIC.devNonce);
+ DO_DEVDB(LMIC.devNonce,devNonce);
}
static void startJoining (xref2osjob_t osjob) {
@@ -1658,19 +1707,21 @@
// Start join procedure if not already joined.
bit_t LMIC_startJoining (void) {
if( LMIC.devaddr == 0 ) {
- // There should be no TX/RX going on
- ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0);
- // Cancel scanning
- LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL);
- // Setup state
- LMIC.rejoinCnt = LMIC.txCnt = 0;
- initJoinLoop();
- LMIC.opmode |= OP_OTA|OP_JOINING;
- // reportEvent will call engineUpdate which then starts sending JOIN REQUESTS
- os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining));
- return 1;
+ // There should be no TX/RX going on
+ ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0);
+ // Lift any previous duty limitation
+ LMIC.globalDutyRate = 0;
+ // Cancel scanning
+ LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL);
+ // Setup state
+ LMIC.rejoinCnt = LMIC.txCnt = LMIC.pendTxConf = 0;
+ initJoinLoop();
+ LMIC.opmode |= OP_JOINING;
+ // reportEvent will call engineUpdate which then starts sending JOIN REQUESTS
+ os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining));
+ return 1;
}
- return 0;
+ return 0; // already joined
}
@@ -1682,11 +1733,11 @@
static void processPingRx (xref2osjob_t osjob) {
if( LMIC.dataLen != 0 ) {
- LMIC.txrxFlags = TXRX_PING;
- if( decodeFrame() ) {
- reportEvent(EV_RXCOMPLETE);
- return;
- }
+ LMIC.txrxFlags = TXRX_PING;
+ if( decodeFrame() ) {
+ reportEvent(EV_RXCOMPLETE);
+ return;
+ }
}
// Pick next ping slot
engineUpdate();
@@ -1698,66 +1749,61 @@
if( LMIC.dataLen == 0 ) {
norx:
- if( LMIC.txCnt != 0 ) {
- if( LMIC.txCnt <= TXCONF_ATTEMPTS ) {
- // Schedule another retransmission
- txDelay(LMIC.rxtime, RETRY_PERIOD_secs);
- LMIC.opmode &= ~OP_TXRXPEND;
- engineUpdate();
- return 1;
+ if( LMIC.txCnt != 0 ) {
+ if( LMIC.txCnt < TXCONF_ATTEMPTS ) {
+ LMIC.txCnt += 1;
+ setDrTxpow(DRCHG_NOACK, lowerDR(LMIC.datarate, DRADJUST[LMIC.txCnt]), KEEP_TXPOW);
+ // Schedule another retransmission
+ txDelay(LMIC.rxtime, RETRY_PERIOD_secs);
+ LMIC.opmode &= ~OP_TXRXPEND;
+ engineUpdate();
+ return 1;
+ }
+ LMIC.txrxFlags = TXRX_NACK | TXRX_NOPORT;
+ } else {
+ // Nothing received - implies no port
+ LMIC.txrxFlags = TXRX_NOPORT;
}
- LMIC.txrxFlags = TXRX_NACK | TXRX_NOPORT;
- } else {
- // Nothing received - implies no port
- LMIC.txrxFlags = TXRX_NOPORT;
- }
- LMIC.adrAckReq += 1;
- LMIC.dataBeg = LMIC.dataLen = 0;
+ if( LMIC.adrAckReq != LINK_CHECK_OFF )
+ LMIC.adrAckReq += 1;
+ LMIC.dataBeg = LMIC.dataLen = 0;
txcomplete:
- LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
- if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) {
- LMIC.opmode &= ~OP_LINKDEAD;
- reportEvent(EV_LINK_ALIVE);
- }
- reportEvent(EV_TXCOMPLETE);
- // If we haven't heard from NWK in a while although we asked for a sign
- // assume link is dead - notify application and keep going
- if( LMIC.adrAckReq > LINK_CHECK_DEAD ) {
- // We haven't heard from NWK for some time although
- // We asked for a response for some time - assume we're disconnected. Lower DR one notch.
- EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = LMIC.adrAckReq));
- setDrTxpow(DRCHG_NOADRACK, decDR((dr_t)LMIC.datarate), KEEP_TXPOW);
- LMIC.adrAckReq = LINK_CHECK_CONT;
- if( (LMIC.opmode & OP_OTA) != 0 ) {
+ LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
+ if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) {
+ LMIC.opmode &= ~OP_LINKDEAD;
+ reportEvent(EV_LINK_ALIVE);
+ }
+ reportEvent(EV_TXCOMPLETE);
+ // If we haven't heard from NWK in a while although we asked for a sign
+ // assume link is dead - notify application and keep going
+ if( LMIC.adrAckReq > LINK_CHECK_DEAD ) {
+ // We haven't heard from NWK for some time although we
+ // asked for a response for some time - assume we're disconnected. Lower DR one notch.
+ EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = LMIC.adrAckReq));
+ setDrTxpow(DRCHG_NOADRACK, decDR((dr_t)LMIC.datarate), KEEP_TXPOW);
+ LMIC.adrAckReq = LINK_CHECK_CONT;
LMIC.opmode |= OP_REJOIN|OP_LINKDEAD;
- }
- else {
- LMIC.opmode |= OP_LINKDEAD;
+ reportEvent(EV_LINK_DEAD);
}
- reportEvent(EV_LINK_DEAD);
- }
- // If this falls to zero the NWK did not answer our MCMD_BCNI_REQ commands - try full scan
- if( LMIC.bcninfoTries > 0 ) {
- if( (LMIC.opmode & OP_TRACK) != 0 ) {
- reportEvent(EV_BEACON_FOUND);
- LMIC.bcninfoTries = 0;
+ // If this falls to zero the NWK did not answer our MCMD_BCNI_REQ commands - try full scan
+ if( LMIC.bcninfoTries > 0 ) {
+ if( (LMIC.opmode & OP_TRACK) != 0 ) {
+ reportEvent(EV_BEACON_FOUND);
+ LMIC.bcninfoTries = 0;
+ }
+ else if( --LMIC.bcninfoTries == 0 ) {
+ startScan(); // NWK did not answer - try scan
+ }
}
- else if( --LMIC.bcninfoTries == 0 ) {
- startScan(); // NWK did not answer - try scan
- }
- }
- return 1;
+ return 1;
}
if( !decodeFrame() ) {
- if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
- return 0;
- goto norx;
+ if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
+ return 0;
+ goto norx;
}
- // If we received a frame reset counter
- LMIC.adrAckReq = LINK_CHECK_INIT;
- LMIC.rejoinCnt = 0;
goto txcomplete;
}
@@ -1768,55 +1814,57 @@
ev_t ev;
if( LMIC.dataLen != 0 && decodeBeacon() >= 1 ) {
- ev = EV_BEACON_TRACKED;
- if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) {
- // We don't have a previous beacon to calc some drift - assume
- // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
- calcBcnRxWindowFromMillis(13,0);
- goto rev;
- }
- // We have a previous BEACON to calculate some drift
- s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx);
- if( LMIC.missedBcns > 0 ) {
- drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1);
- }
- if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) {
- s2_t diff = LMIC.drift - drift;
- if( diff < 0 ) diff = -diff;
- LMIC.lastDriftDiff = diff;
- if( LMIC.maxDriftDiff < diff )
- LMIC.maxDriftDiff = diff;
- LMIC.bcninfo.flags &= ~BCN_NODDIFF;
- }
- LMIC.drift = drift;
- LMIC.missedBcns = LMIC.rejoinCnt = 0;
- LMIC.bcninfo.flags &= ~BCN_NODRIFT;
- EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT,
- e_.info = drift,
- e_.info2 = /*occasion BEACON*/0));
- ASSERT((LMIC.bcninfo.flags & (BCN_PARTIAL|BCN_FULL)) != 0);
+ ev = EV_BEACON_TRACKED;
+ if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) {
+ // We don't have a previous beacon to calc some drift - assume
+ // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
+ calcBcnRxWindowFromMillis(13,0);
+ goto rev;
+ }
+ // We have a previous BEACON to calculate some drift
+ s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx);
+ if( LMIC.missedBcns > 0 ) {
+ drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1);
+ }
+ if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) {
+ s2_t diff = LMIC.drift - drift;
+ if( diff < 0 ) diff = -diff;
+ LMIC.lastDriftDiff = diff;
+ if( LMIC.maxDriftDiff < diff )
+ LMIC.maxDriftDiff = diff;
+ LMIC.bcninfo.flags &= ~BCN_NODDIFF;
+ }
+ LMIC.drift = drift;
+ LMIC.missedBcns = LMIC.rejoinCnt = 0;
+ LMIC.bcninfo.flags &= ~BCN_NODRIFT;
+ EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = drift,
+ e_.info2 = /*occasion BEACON*/0));
+ ASSERT((LMIC.bcninfo.flags & (BCN_PARTIAL|BCN_FULL)) != 0);
} else {
- ev = EV_BEACON_MISSED;
- LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift;
- LMIC.bcninfo.time += BCN_INTV_sec;
- LMIC.missedBcns++;
- // Delay any possible TX after surmised beacon - it's there although we missed it
- txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4);
- if( (LMIC.opmode & OP_OTA) != 0 ) {
- if( LMIC.missedBcns > MAX_MISSED_BCNS )
- LMIC.opmode |= OP_REJOIN; // try if we can roam to another network
- }
- if( LMIC.bcnRxsyms > MAX_RXSYMS ) {
- LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN);
- reportEvent(EV_LOST_TSYNC);
- return;
- }
+ ev = EV_BEACON_MISSED;
+ LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift;
+ LMIC.bcninfo.time += BCN_INTV_sec;
+ LMIC.missedBcns++;
+ // Delay any possible TX after surmised beacon - it's there although we missed it
+ txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4);
+ if( LMIC.missedBcns > MAX_MISSED_BCNS )
+ LMIC.opmode |= OP_REJOIN; // try if we can roam to another network
+ if( LMIC.bcnRxsyms > MAX_RXSYMS ) {
+ LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN);
+ reportEvent(EV_LOST_TSYNC);
+ return;
+ }
}
LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - calcRxWindow(0,DR_BCN);
LMIC.bcnRxsyms = LMIC.rxsyms;
rev:
+#if CFG_us915
+ LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7;
+#endif
if( (LMIC.opmode & OP_PINGINI) != 0 )
- rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer!
+ rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer!
reportEvent(ev);
}
@@ -1837,11 +1885,11 @@
static void engineUpdate (void) {
// Check for ongoing state: scan or TX/RX transaction
if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 )
- return;
+ return;
if( LMIC.devaddr == 0 && (LMIC.opmode & OP_JOINING) == 0 ) {
- LMIC_startJoining();
- return;
+ LMIC_startJoining();
+ return;
}
ostime_t now = os_getTime();
@@ -1849,123 +1897,131 @@
ostime_t txbeg = 0;
if( (LMIC.opmode & OP_TRACK) != 0 ) {
- // We are tracking a beacon
- ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
- rxtime = LMIC.bcnRxtime - RX_RAMPUP;
+ // We are tracking a beacon
+ ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
+ rxtime = LMIC.bcnRxtime - RX_RAMPUP;
}
if( (LMIC.opmode & (OP_JOINING|OP_REJOIN|OP_TXDATA|OP_POLL)) != 0 ) {
- // Need to TX some data...
- // Assuming txChnl points to channel which first becomes available again.
- bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0);
- // Find next suitable channel and return availability time
- if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) {
- txbeg = LMIC.txend = nextTx(now);
- LMIC.opmode &= ~OP_NEXTCHNL;
- } else {
- txbeg = LMIC.txend;
- }
- // Delayed TX or waiting for duty cycle?
- if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 )
- txbeg = LMIC.globalDutyAvail;
- // If we're tracking a beacon...
- // then make sure TX-RX transaction is complete before beacon
- if( (LMIC.opmode & OP_TRACK) != 0 &&
- txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) {
- // Not enough time to complete TX-RX before beacon - postpone after beacon.
- // In order to avoid clustering of postponed TX right after beacon randomize start!
- txDelay(rxtime + BCN_RESERVE_osticks, 16);
- txbeg = 0;
- goto checkrx;
- }
- // Earliest possible time vs overhead to setup radio
- if( txbeg - (now + TX_RAMPUP) < 0 ) {
- // We could send right now!
- dr_t txdr = (dr_t)LMIC.datarate;
- if( jacc ) {
- u1_t ftype;
- if( (LMIC.opmode & OP_REJOIN) != 0 ) {
- txdr = lowerDR(txdr, LMIC.rejoinCnt);
- ftype = HDR_FTYPE_REJOIN;
+ // Need to TX some data...
+ // Assuming txChnl points to channel which first becomes available again.
+ bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0);
+ // Find next suitable channel and return availability time
+ if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) {
+ txbeg = LMIC.txend = nextTx(now);
+ LMIC.opmode &= ~OP_NEXTCHNL;
} else {
- ftype = HDR_FTYPE_JREQ;
+ txbeg = LMIC.txend;
+ }
+ // Delayed TX or waiting for duty cycle?
+ if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 )
+ txbeg = LMIC.globalDutyAvail;
+ // If we're tracking a beacon...
+ // then make sure TX-RX transaction is complete before beacon
+ if( (LMIC.opmode & OP_TRACK) != 0 &&
+ txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) {
+ // Not enough time to complete TX-RX before beacon - postpone after beacon.
+ // In order to avoid clustering of postponed TX right after beacon randomize start!
+ txDelay(rxtime + BCN_RESERVE_osticks, 16);
+ txbeg = 0;
+ goto checkrx;
}
- buildJoinRequest(ftype);
- LMIC.osjob.func = FUNC_ADDR(jreqDone);
- } else {
- if( LMIC.seqnoUp == 0xFFFFFFFF ) {
- // Roll over of up seq counter
- EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER,
- e_.eui = MAIN::CDEV->getEui()));
- // Rerun join procedure - start with current datarate
- LMIC.devaddr = 0;
- LMIC_startJoining();
- reportEvent(EV_RESET);
+ // Earliest possible time vs overhead to setup radio
+ if( txbeg - (now + TX_RAMPUP) < 0 ) {
+ // We could send right now!
+ dr_t txdr = (dr_t)LMIC.datarate;
+ if( jacc ) {
+ u1_t ftype;
+ if( (LMIC.opmode & OP_REJOIN) != 0 ) {
+ txdr = lowerDR(txdr, LMIC.rejoinCnt);
+ ftype = HDR_FTYPE_REJOIN;
+ } else {
+ ftype = HDR_FTYPE_JREQ;
+ }
+ buildJoinRequest(ftype);
+ LMIC.osjob.func = FUNC_ADDR(jreqDone);
+ } else {
+ if( LMIC.seqnoDn >= 0xFFFFFF80 ) {
+ // Imminent roll over - proactively reset MAC
+ EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = LMIC.seqnoDn,
+ e_.info2 = 0));
+ // Device has to react! NWK will not roll over and just stop sending.
+ // Thus, we have N frames to detect a possible lock up.
+ reset:
+ os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset));
+ return;
+ }
+ if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) {
+ // Roll over of up seq counter
+ EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER,
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info2 = LMIC.seqnoUp));
+ // Do not run RESET event callback from here!
+ // App code might do some stuff after send unaware of RESET.
+ goto reset;
+ }
+ buildDataFrame();
+ LMIC.osjob.func = FUNC_ADDR(updataDone);
+ }
+ LMIC.rps = setCr(updr2rps(txdr), (cr_t)LMIC.errcr);
+ LMIC.dndr = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1
+ LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL;
+ updateTx(txbeg);
+ os_radio(RADIO_TX);
return;
}
- buildDataFrame();
- // NOTE: a channel decision for LMIC.txChnl above will never be rendered invalid
- // by chosing a slower datarate (it could be invalided ONLY by a faster datarate).
- txdr = lowerDR(txdr, DRADJUST[LMIC.txCnt]);
- LMIC.osjob.func = FUNC_ADDR(updataDone);
- }
- LMIC.rps = setCr(updr2rps(txdr), (cr_t)LMIC.errcr);
- LMIC.rxsyms = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1
- LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL;
- updateTx(txbeg);
- os_radio(RADIO_TX);
- return;
- }
- // Cannot yet TX
- if( (LMIC.opmode & OP_TRACK) == 0 )
- goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX
- // Consider RX tasks
- if( txbeg == 0 ) // zero indicates no TX pending
- txbeg += 1; // TX delayed by one tick (insignificant amount of time)
+ // Cannot yet TX
+ if( (LMIC.opmode & OP_TRACK) == 0 )
+ goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX
+ // Consider RX tasks
+ if( txbeg == 0 ) // zero indicates no TX pending
+ txbeg += 1; // TX delayed by one tick (insignificant amount of time)
} else {
- // No TX pending - no scheduled RX
- if( (LMIC.opmode & OP_TRACK) == 0 )
- return;
+ // No TX pending - no scheduled RX
+ if( (LMIC.opmode & OP_TRACK) == 0 )
+ return;
}
// Are we pingable?
checkrx:
if( (LMIC.opmode & OP_PINGINI) != 0 ) {
- // One more RX slot in this beacon period?
- if( rxschedNext(&LMIC.ping, now+RX_RAMPUP) ) {
- if( txbeg != 0 && (txbeg - LMIC.ping.rxtime) < 0 )
- goto txdelay;
- LMIC.rxsyms = LMIC.ping.rxsyms;
- LMIC.rxtime = LMIC.ping.rxtime;
- LMIC.freq = LMIC.ping.freq;
- LMIC.rps = dndr2rps(LMIC.ping.dr);
- LMIC.dataLen = 0;
- ASSERT(LMIC.rxtime - now+RX_RAMPUP >= 0 );
- os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, FUNC_ADDR(startRxPing));
- return;
- }
- // no - just wait for the beacon
+ // One more RX slot in this beacon period?
+ if( rxschedNext(&LMIC.ping, now+RX_RAMPUP) ) {
+ if( txbeg != 0 && (txbeg - LMIC.ping.rxtime) < 0 )
+ goto txdelay;
+ LMIC.rxsyms = LMIC.ping.rxsyms;
+ LMIC.rxtime = LMIC.ping.rxtime;
+ LMIC.freq = LMIC.ping.freq;
+ LMIC.rps = dndr2rps(LMIC.ping.dr);
+ LMIC.dataLen = 0;
+ ASSERT(LMIC.rxtime - now+RX_RAMPUP >= 0 );
+ os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, FUNC_ADDR(startRxPing));
+ return;
+ }
+ // no - just wait for the beacon
}
if( txbeg != 0 && (txbeg - rxtime) < 0 )
- goto txdelay;
+ goto txdelay;
setBcnRxParams();
LMIC.rxsyms = LMIC.bcnRxsyms;
LMIC.rxtime = LMIC.bcnRxtime;
if( now - rxtime >= 0 ) {
- LMIC.osjob.func = FUNC_ADDR(processBeacon);
- os_radio(RADIO_RX);
- return;
+ LMIC.osjob.func = FUNC_ADDR(processBeacon);
+ os_radio(RADIO_RX);
+ return;
}
os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn));
return;
txdelay:
EV(devCond, INFO, (e_.reason = EV::devCond_t::TX_DELAY,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = osticks2ms(txbeg-now),
- e_.info2 = LMIC.seqnoUp-1));
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = osticks2ms(txbeg-now),
+ e_.info2 = LMIC.seqnoUp-1));
os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate));
}
@@ -1990,22 +2046,32 @@
void LMIC_reset (void) {
EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
- e_.eui = MAIN::CDEV->getEui(),
- e_.info = EV_RESET));
+ e_.eui = MAIN::CDEV->getEui(),
+ e_.info = EV_RESET));
os_radio(RADIO_RST);
os_clearCallback(&LMIC.osjob);
os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC));
- LMIC.devaddr = 0;
- LMIC.devNonce = os_getRndU2();
- LMIC.opmode = OP_NONE;
- LMIC.errcr = CR_4_5;
- LMIC.adrEnabled = FCT_ADREN;
- LMIC.dn2Dr = DR_DNW2; // we need this for 2ns DN window of join accept
- LMIC.dn2Freq = FREQ_DNW2; // ditto
-#if CFG_us915
+ LMIC.devaddr = 0;
+ LMIC.devNonce = os_getRndU2();
+ LMIC.opmode = OP_NONE;
+ LMIC.errcr = CR_4_5;
+ LMIC.adrEnabled = FCT_ADREN;
+ LMIC.dn2Dr = DR_DNW2; // we need this for 2nd DN window of join accept
+ LMIC.dn2Freq = FREQ_DNW2; // ditto
+ LMIC.ping.freq = FREQ_PING; // defaults for ping
+ LMIC.ping.dr = DR_PING; // ditto
+ LMIC.ping.intvExp = 0xFF;
+#if defined(CFG_us915)
initDefaultChannels();
#endif
+ DO_DEVDB(LMIC.devaddr, devaddr);
+ DO_DEVDB(LMIC.devNonce, devNonce);
+ DO_DEVDB(LMIC.dn2Dr, dn2Dr);
+ DO_DEVDB(LMIC.dn2Freq, dn2Freq);
+ DO_DEVDB(LMIC.ping.freq, pingFreq);
+ DO_DEVDB(LMIC.ping.dr, pingDr);
+ DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
}
@@ -2018,7 +2084,7 @@
LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND|OP_POLL);
LMIC.pendTxLen = 0;
if( (LMIC.opmode & (OP_JOINING|OP_SCAN)) != 0 ) // do not interfere with JOINING
- return;
+ return;
os_clearCallback(&LMIC.osjob);
os_radio(RADIO_RST);
engineUpdate();
@@ -2028,7 +2094,7 @@
void LMIC_setTxData (void) {
LMIC.opmode |= OP_TXDATA;
if( (LMIC.opmode & OP_JOINING) == 0 )
- LMIC.txCnt = 0; // cancel any ongoing TX/RX retries
+ LMIC.txCnt = 0; // cancel any ongoing TX/RX retries
engineUpdate();
}
@@ -2036,9 +2102,9 @@
//
int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) {
if( dlen > SIZEOFEXPR(LMIC.pendTxData) )
- return -2;
+ return -2;
if( data != (xref2u1_t)0 )
- os_copyMem(LMIC.pendTxData, data, dlen);
+ os_copyMem(LMIC.pendTxData, data, dlen);
LMIC.pendTxConf = confirmed;
LMIC.pendTxPort = port;
LMIC.pendTxLen = dlen;
@@ -2060,19 +2126,48 @@
engineUpdate();
}
-void LMIC_startABP(u4_t netid, devaddr_t devaddr, u1_t* nwkKey, u1_t* artKey)
-{
+//! \brief Setup given session keys
+//! and put the MAC in a state as if
+//! a join request/accept would have negotiated just these keys.
+//! It is crucial that the combinations `devaddr/nwkkey` and `devaddr/artkey`
+//! are unique within the network identified by `netid`.
+//! NOTE: on Harvard architectures when session keys are in flash:
+//! Caller has to fill in LMIC.{nwk,art}Key before and pass {nwk,art}Key are NULL
+//! \param netid a 24 bit number describing the network id this device is using
+//! \param devaddr the 32 bit session address of the device. It is strongly recommended
+//! to ensure that different devices use different numbers with high probability.
+//! \param nwkKey the 16 byte network session key used for message integrity.
+//! If NULL the caller has copied the key into `LMIC.nwkKey` before.
+//! \param artKey the 16 byte application router session key used for message confidentiality.
+//! If NULL the caller has copied the key into `LMIC.artKey` before.
+void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey) {
LMIC.netid = netid;
LMIC.devaddr = devaddr;
- memcpy(LMIC.nwkKey, nwkKey, 16);
- memcpy(LMIC.artKey, artKey, 16);
+ if( nwkKey != (xref2u1_t)0 )
+ os_copyMem(LMIC.nwkKey, nwkKey, 16);
+ if( artKey != (xref2u1_t)0 )
+ os_copyMem(LMIC.artKey, artKey, 16);
-#if CFG_eu868
+#if defined(CFG_eu868)
initDefaultChannels(0);
#endif
-
- LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI) | OP_NEXTCHNL;
+
+ LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI);
+ LMIC.opmode |= OP_NEXTCHNL;
stateJustJoined();
- //engineUpdate();
- reportEvent(EV_JOINED);
}
+
+// Enable/disable link check validation.
+// LMIC sets the ADRACKREQ bit in UP frames if there were no DN frames
+// for a while. It expects the network to provide a DN message to prove
+// connectivity with a span of UP frames. If this no such prove is coming
+// then the datarate is lowered and a LINK_DEAD event is generated.
+// This mode can be disabled and no connectivity prove (ADRACKREQ) is requested
+// nor is the datarate changed.
+// This must be called only if a session is established (e.g. after EV_JOINED)
+void LMIC_setLinkCheckMode (bit_t enabled) {
+ LMIC.adrChanged = 0;
+ LMIC.adrAckReq = enabled ? LINK_CHECK_INIT : LINK_CHECK_OFF;
+}
+
+
--- a/lmic.h Thu Jan 22 12:50:49 2015 +0000
+++ b/lmic.h Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -9,32 +9,39 @@
* IBM Zurich Research Lab - initial API, implementation and documentation
*******************************************************************************/
-#define NEW_CHNL 1
+//! @file
+//! @brief LMIC API
#ifndef _lmic_h_
#define _lmic_h_
-#define CFG_DEBUG 1
-#define CFG_eu868 1
-//#define CFG_us915 0
+// MBED compiler options
+#define CFG_eu868 1
+//#define CFG_us915 0
-#define USE_SMTC_RADIO_DRIVER 1
+#define USE_SMTC_RADIO_DRIVER 1
-//#define CFG_sx1272_radio 0
-#define CFG_sx1276_radio 1
+//#define CFG_sx1272_radio 0
+#define CFG_sx1276_radio 1
+// End MBED compiler options
-#include "mbed.h"
#include "oslmic.h"
#include "lorabase.h"
-enum { MAX_FRAME_LEN = 64 }; // Library cap on max frame length
-enum { TXCONF_ATTEMPTS = 8 }; // transmit attempts for confirmed frames
+// LMIC version
+#define LMIC_VERSION_MAJOR 1
+#define LMIC_VERSION_MINOR 4
+#define LMIC_VERSION_BUILD 1426605786
+
+enum { MAX_FRAME_LEN = 64 }; //!< Library cap on max frame length
+enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames
enum { MAX_MISSED_BCNS = 20 }; // threshold for triggering rejoin requests
enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this
-enum { LINK_CHECK_CONT = 6 }; // continue with this after reported dead link
-enum { LINK_CHECK_DEAD = 12 }; // after this UP frames and no response from NWK assume link is dead
-enum { LINK_CHECK_INIT = -12 }; // UP frame count until we inc datarate
+enum { LINK_CHECK_CONT = 6 , // continue with this after reported dead link
+ LINK_CHECK_DEAD = 12 , // after this UP frames and no response from NWK assume link is dead
+ LINK_CHECK_INIT = -12 , // UP frame count until we inc datarate
+ LINK_CHECK_OFF =-128 }; // link check disabled
enum { TIME_RESYNC = 6*128 }; // secs
enum { TXRX_GUARD_ms = 6000 }; // msecs - don't start TX-RX transaction before beacon
@@ -42,20 +49,22 @@
enum { TXRX_BCNEXT_secs = 2 }; // secs - earliest start after beacon time
enum { RETRY_PERIOD_secs = 3 }; // secs - random period for retrying a confirmed send
-#if CFG_eu868 // EU868 spectrum ====================================================
+#if defined(CFG_eu868) // EU868 spectrum ====================================================
-enum { MAX_CHANNELS = 8 }; // library may not support all 16 channels
-enum { MAX_BANDS = 4 };
+enum { MAX_CHANNELS = 16 }; //!< Max supported channels
+enum { MAX_BANDS = 4 };
enum { LIMIT_CHANNELS = (1<<4) }; // EU868 will never have more channels
+//! \internal
struct band_t {
- u2_t txcap; // duty cycle limitation: 1/txcap
- s1_t txpow; // maximum TX power
- ostime_t avail; // channel is blocked until this time
+ u2_t txcap; // duty cycle limitation: 1/txcap
+ s1_t txpow; // maximum TX power
+ u1_t lastchnl; // last used channel
+ ostime_t avail; // channel is blocked until this time
};
-TYPEDEF_xref2band_t;
+TYPEDEF_xref2band_t; //!< \internal
-#elif CFG_us915 // US915 spectrum =================================================
+#elif defined(CFG_us915) // US915 spectrum =================================================
enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable
enum { MAX_TXPOW_125kHz = 30 };
@@ -67,6 +76,7 @@
enum { KEEP_TXPOW = -128 };
+//! \internal
struct rxsched_t {
u1_t dr;
u1_t intvExp; // 0..7
@@ -76,30 +86,32 @@
ostime_t rxtime; // start of next spot
u4_t freq;
};
-TYPEDEF_xref2rxsched_t;
+TYPEDEF_xref2rxsched_t; //!< \internal
-// Information extracted from (last) beacon
-enum { BCN_NONE = 0x00,
- BCN_PARTIAL = 0x01,
- BCN_FULL = 0x02,
- BCN_NODRIFT = 0x04, // no drift value measured
- BCN_NODDIFF = 0x08 }; // no differential drift measured yet
+//! Parsing and tracking states of beacons.
+enum { BCN_NONE = 0x00, //!< No beacon received
+ BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous)
+ BCN_FULL = 0x02, //!< Full beacon decoded
+ BCN_NODRIFT = 0x04, //!< No drift value measured yet
+ BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet
+//! Information about the last and previous beacons.
struct bcninfo_t {
- rxqu_t rxq;
- ostime_t txtime; // time when beacon was sent
- u1_t flags;
- u4_t time; // GPS time in seconds
- // This part is valid only if flags==BCN_FULL
- u1_t info; // info field
- s4_t lat;
- s4_t lon;
+ ostime_t txtime; //!< Time when the beacon was sent
+ s1_t rssi; //!< Adjusted RSSI value of last received beacon
+ s1_t snr; //!< Scaled SNR value of last received beacon
+ u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values.
+ u4_t time; //!< GPS time in seconds of last beacon (received or surrogate)
+ //
+ u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set)
+ s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set)
+ s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set)
};
// purpose of receive window - lmic_t.rxState
enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 };
// Netid values / lmic_t.netid
-enum { NETID_NONE=(int)~0U, NETID_MASK=0xFFFFFF };
+enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF };
// MAC operation modes (lmic_t.opmode).
enum { OP_NONE = 0x0000,
OP_SCAN = 0x0001, // radio scan to find a beacon
@@ -115,21 +127,22 @@
OP_PINGABLE = 0x0400, // we're pingable
OP_NEXTCHNL = 0x0800, // find a new channel
OP_LINKDEAD = 0x1000, // link was reported as dead
- OP_OTA = 0x2000, // Over the Air Activation in use
+ OP_TESTMODE = 0x2000, // developer test mode
};
// TX-RX transaction flags - report back to user
enum { TXRX_ACK = 0x80, // confirmed UP frame was acked
TXRX_NACK = 0x40, // confirmed UP frame was not acked
TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port
+ TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port
TXRX_DNW1 = 0x01, // received in 1st DN slot
TXRX_DNW2 = 0x02, // received in 2dn DN slot
TXRX_PING = 0x04 }; // received in a scheduled RX slot
// Event types for event callback
enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND,
- EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING,
- EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED,
- EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET,
- EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE };
+ EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING,
+ EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED,
+ EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET,
+ EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE };
typedef enum _ev_t ev_t;
@@ -137,25 +150,27 @@
// Radio settings TX/RX (also accessed by HAL)
ostime_t txend;
ostime_t rxtime;
- rxqu_t rxq;
u4_t freq;
+ s1_t rssi;
+ s1_t snr;
rps_t rps;
u1_t rxsyms;
+ u1_t dndr;
s1_t txpow; // dBm
osjob_t osjob;
// Channel scheduling
-#if CFG_eu868
+#if defined(CFG_eu868)
band_t bands[MAX_BANDS];
u4_t channelFreq[MAX_CHANNELS];
- u1_t channelDrs[MAX_CHANNELS];
+ u2_t channelDrMap[MAX_CHANNELS];
u2_t channelMap;
-#elif CFG_us915
- u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater)
- u1_t xchDrs[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto
+#elif defined(CFG_us915)
+ u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater)
+ u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto
u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits
- u1_t chRnd; // channel randomizer
+ u2_t chRnd; // channel randomizer
#endif
u1_t txChnl; // channel for next TX
u1_t globalDutyRate; // max rate: 1/2^k
@@ -218,8 +233,18 @@
ostime_t bcnRxtime;
bcninfo_t bcninfo; // Last received beacon info
};
-DECLARE_LMIC;
+//! \var struct lmic_t LMIC
+//! The state of LMIC MAC layer is encapsulated in this variable.
+DECLARE_LMIC; //!< \internal
+//! Construct a bit map of allowed datarates from drlo to drhi (both included).
+#define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi))))
+#if defined(CFG_eu868)
+enum { BAND_MILLI=0, BAND_CENTI=1, BAND_DECI=2, BAND_AUX=3 };
+bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap);
+#endif
+bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band);
+void LMIC_disableChannel (u1_t channel);
void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow
void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off)
@@ -239,6 +264,11 @@
void LMIC_stopPingable (void);
void LMIC_setPingable (u1_t intvExp);
void LMIC_tryRejoin (void);
-void LMIC_startABP (u4_t netid, devaddr_t devaddr, u1_t* nwkKey, u1_t* artKey);
+
+void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
+void LMIC_setLinkCheckMode (bit_t enabled);
+
+// Special APIs - for development or testing
+// !!!See implementation for caveats!!!
#endif // _lmic_h_
--- a/lorabase.h Thu Jan 22 12:50:49 2015 +0000
+++ b/lorabase.h Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -54,7 +54,7 @@
enum { BCN_GUARD_us = 3000000 };
enum { BCN_SLOT_SPAN_us = 30000 };
-#if CFG_eu868 // ==============================================
+#if defined(CFG_eu868) // ==============================================
enum _dr_eu868_t { DR_SF12=0, DR_SF11, DR_SF10, DR_SF9, DR_SF8, DR_SF7, DR_SF7B, DR_FSK, DR_NONE };
enum { DR_DFLTMIN = DR_SF7 };
@@ -84,17 +84,30 @@
enum { DR_PING = SF9 }; // default ping DR
enum { CHNL_DNW2 = 5 };
enum { FREQ_DNW2 = EU868_F6 };
-enum { DR_DNW2 = DR_SF9 };
+enum { DR_DNW2 = DR_SF9 }; // changed from LoRaWAN specification.
+ // Default value is DR_SF12
enum { CHNL_BCN = 5 };
enum { FREQ_BCN = EU868_F6 };
enum { DR_BCN = DR_SF9 };
-enum { AIRTIME_BCN = 185344 }; // micros
+enum { AIRTIME_BCN = 144384 }; // micros
-#elif CFG_us915 // =========================================
+enum {
+ // Beacon frame format EU SF9
+ OFF_BCN_NETID = 0,
+ OFF_BCN_TIME = 3,
+ OFF_BCN_CRC1 = 7,
+ OFF_BCN_INFO = 8,
+ OFF_BCN_LAT = 9,
+ OFF_BCN_LON = 12,
+ OFF_BCN_CRC2 = 15,
+ LEN_BCN = 17
+};
+
+#elif defined(CFG_us915) // =========================================
enum _dr_us915_t { DR_SF10=0, DR_SF9, DR_SF8, DR_SF7, DR_SF8C, DR_NONE,
- // Devices behind a router:
- DR_SF12CR=8, DR_SF11CR, DR_SF10CR, DR_SF9CR, DR_SF8CR, DR_SF7CR };
+ // Devices behind a router:
+ DR_SF12CR=8, DR_SF11CR, DR_SF10CR, DR_SF9CR, DR_SF8CR, DR_SF7CR };
enum { DR_DFLTMIN = DR_SF8C };
enum { DR_PAGE = DR_PAGE_US915 };
@@ -109,33 +122,31 @@
enum { US915_FREQ_MIN = 902000000,
US915_FREQ_MAX = 928000000 };
-enum { CHNL_PING = 2 };
+enum { CHNL_PING = 0 }; // used only for default init of state (follows beacon - rotating)
enum { FREQ_PING = US915_500kHz_DNFBASE + CHNL_PING*US915_500kHz_DNFSTEP }; // default ping freq
enum { DR_PING = DR_SF10CR }; // default ping DR
-enum { CHNL_DNW2 = 1 };
+enum { CHNL_DNW2 = 0 };
enum { FREQ_DNW2 = US915_500kHz_DNFBASE + CHNL_DNW2*US915_500kHz_DNFSTEP };
-enum { DR_DNW2 = DR_SF10CR };
-enum { CHNL_BCN = 0 };
-enum { FREQ_BCN = US915_500kHz_DNFBASE + CHNL_BCN*US915_500kHz_DNFSTEP };
+enum { DR_DNW2 = DR_SF12CR };
+enum { CHNL_BCN = 0 }; // used only for default init of state (rotating beacon scheme)
enum { DR_BCN = DR_SF10CR };
-enum { AIRTIME_BCN = 82432 }; // micros
+enum { AIRTIME_BCN = 72192 }; // micros
+
+enum {
+ // Beacon frame format US SF10
+ OFF_BCN_NETID = 0,
+ OFF_BCN_TIME = 3,
+ OFF_BCN_CRC1 = 7,
+ OFF_BCN_INFO = 9,
+ OFF_BCN_LAT = 10,
+ OFF_BCN_LON = 13,
+ OFF_BCN_RFU1 = 16,
+ OFF_BCN_CRC2 = 17,
+ LEN_BCN = 19
+};
#endif // ===================================================
-
-enum {
- // Beacon frame format
- OFF_BCN_RFU = 0,
- OFF_BCN_NETID = 4,
- OFF_BCN_CMAP = 7,
- OFF_BCN_TIME = 9,
- OFF_BCN_CRC1 = 13,
- OFF_BCN_INFO = 15,
- OFF_BCN_LAT = 16,
- OFF_BCN_LON = 19,
- OFF_BCN_CRC2 = 22,
- LEN_BCN = 24
-};
enum {
// Join Request frame format
OFF_JR_HDR = 0,
@@ -152,7 +163,9 @@
OFF_JA_NETID = 4,
OFF_JA_DEVADDR = 7,
OFF_JA_RFU = 11,
- OFF_CFLIST = 14,
+ OFF_JA_DLSET = 11,
+ OFF_JA_RXDLY = 12,
+ OFF_CFLIST = 13,
LEN_JA = 17,
LEN_JAEXT = 17+16
};
@@ -191,10 +204,14 @@
FCT_ADREN = 0x80,
FCT_ADRARQ = 0x40,
FCT_ACK = 0x20,
- FCT_MORE = 0x10,
+ FCT_MORE = 0x10, // also in DN direction: Class B indicator
FCT_OPTLEN = 0x0F,
};
enum {
+ // In UP direction: signals class B enabled
+ FCT_CLASSB = FCT_MORE
+};
+enum {
NWKID_MASK = (int)0xFE000000,
NWKID_BITS = 7
};
@@ -225,10 +242,13 @@
MCMD_SNCH_REQ = 0x07, // set new channel : u1:chidx, u3:freq, u1:DRrange
// Class B
MCMD_PING_SET = 0x11, // set ping freq : u3: freq
- MCMD_BCNI_ANS = 0x12, // next beacon start : u2: delay(10ms), u1:channel
+ MCMD_BCNI_ANS = 0x12, // next beacon start : u2: delay(in TUNIT millis), u1:channel
};
enum {
+ MCMD_BCNI_TUNIT = 30 // time unit of delay value in millis
+};
+enum {
MCMD_LADR_ANS_RFU = 0xF8, // RFU bits
MCMD_LADR_ANS_POWACK = 0x04, // 0=not supported power level
MCMD_LADR_ANS_DRACK = 0x02, // 0=unknown data rate
@@ -272,7 +292,7 @@
MCMD_LADR_POW_MASK = 0x0F,
MCMD_LADR_DR_SHIFT = 4,
MCMD_LADR_POW_SHIFT = 0,
-#if CFG_eu868
+#if defined(CFG_eu868)
MCMD_LADR_SF12 = DR_SF12<<4,
MCMD_LADR_SF11 = DR_SF11<<4,
MCMD_LADR_SF10 = DR_SF10<<4,
@@ -288,7 +308,7 @@
MCMD_LADR_8dBm = 3,
MCMD_LADR_5dBm = 4,
MCMD_LADR_2dBm = 5,
-#elif CFG_us915
+#elif defined(CFG_us915)
MCMD_LADR_SF10 = DR_SF10<<4,
MCMD_LADR_SF9 = DR_SF9 <<4,
MCMD_LADR_SF8 = DR_SF8 <<4,
@@ -318,15 +338,8 @@
// Device address
typedef u4_t devaddr_t;
-
// RX quality (device)
enum { RSSI_OFF=64, SNR_SCALEUP=4 };
-struct rxqu_t {
- s1_t rssi; // offset by RSSI_OFF, max physical RSSI range -198..+63
- s1_t snr; // scaled by SNR_SCALEUP, max physical SNR range -32..+31.75
-};
-TYPEDEF_xref2rxqu_t;
-
inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); }
inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); }
--- a/oslmic.cpp Thu Jan 22 12:50:49 2015 +0000
+++ b/oslmic.cpp Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -17,14 +17,14 @@
osjob_t* runnablejobs;
} OS;
-void os_init (void) {
+void os_init () {
memset(&OS, 0x00, sizeof(OS));
hal_init();
radio_init();
LMIC_init();
}
-ostime_t os_getTime (void) {
+ostime_t os_getTime () {
return hal_ticks();
}
@@ -72,7 +72,7 @@
job->next = NULL;
// insert into schedule
for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) {
- if(time < (*pnext)->deadline) {
+ if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!)
// enqueue before next element and stop
job->next = *pnext;
break;
@@ -83,15 +83,15 @@
}
// execute jobs from timer and from run queue
-void os_runloop (void) {
+void os_runloop () {
while(1) {
osjob_t* j = NULL;
hal_disableIRQs();
- // check for runnable jobs
- if(OS.runnablejobs) {
+ // check for runnable jobs
+ if(OS.runnablejobs) {
j = OS.runnablejobs;
OS.runnablejobs = j->next;
- } else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs
+ } else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs
j = OS.scheduledjobs;
OS.scheduledjobs = j->next;
} else { // nothing pending
@@ -99,9 +99,7 @@
}
hal_enableIRQs();
if(j) { // run job callback
- if(j->func) {
- j->func(j);
- }
+ j->func(j);
}
}
}
--- a/oslmic.h Thu Jan 22 12:50:49 2015 +0000 +++ b/oslmic.h Tue Mar 31 13:36:56 2015 +0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014 IBM Corporation. + * Copyright (c) 2014-2015 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -9,6 +9,7 @@ * IBM Zurich Research Lab - initial API, implementation and documentation *******************************************************************************/ +//! \file #ifndef _oslmic_h_ #define _oslmic_h_ @@ -33,12 +34,11 @@ typedef unsigned int uint; typedef const char* str_t; -#define CFG_noassert 1 #include <string.h> #include "hal.h" #define EV(a,b,c) /**/ -#define DO_DEVDB(meth,...) /**/ -#if !CFG_noassert +#define DO_DEVDB(field1,field2) /**/ +#if !defined(CFG_noassert) #define ASSERT(cond) if(!(cond)) hal_failed() #else #define ASSERT(cond) /**/ @@ -49,14 +49,12 @@ typedef struct osjob_t osjob_t; typedef struct band_t band_t; -typedef struct rxqu_t rxqu_t; typedef struct chnldef_t chnldef_t; typedef struct rxsched_t rxsched_t; typedef struct bcninfo_t bcninfo_t; typedef const u1_t* xref2cu1_t; typedef u1_t* xref2u1_t; #define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t -#define TYPEDEF_xref2rxqu_t typedef rxqu_t* xref2rxqu_t #define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t #define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t #define TYPEDEF_xref2band_t typedef band_t* xref2band_t @@ -79,7 +77,7 @@ #define DEFINE_LMIC struct lmic_t LMIC #define DECLARE_LMIC extern struct lmic_t LMIC -void radio_init(void); +void radio_init (void); void radio_irq_handler (u1_t dio); void os_init (void); void os_runloop (void); @@ -126,7 +124,7 @@ TYPEDEF_xref2osjob_t; -#if !HAS_os_calls +#ifndef HAS_os_calls #ifndef os_getDevKey void os_getDevKey (xref2u1_t buf);
--- a/radio.cpp Thu Jan 22 12:50:49 2015 +0000
+++ b/radio.cpp Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -14,15 +14,16 @@
* USE_SMTC_RADIO_DRIVER preprocessing directive
* in lmic.h
*******************************************************************************/
+
#include "lmic.h"
#if USE_SMTC_RADIO_DRIVER
#include "sx1276-hal.h"
/*!
- * Syncword for lora networks (nibbles swapped)
+ * Syncword for lora networks
*/
-#define LORA_MAC_SYNCWORD 0x34
+#define LORA_MAC_SYNCWORD 0x34
/*
* Callback functions prototypes
@@ -81,7 +82,7 @@
{
ostime_t now = os_getTime( );
// save exact tx time
- LMIC.txend = now - us2osticks( 43 ); // TXDONE FIXUP
+ LMIC.txend = now - us2osticks( RADIO_WAKEUP_TIME ); // TXDONE FIXUP
// go from stanby to sleep
Radio.Sleep( );
@@ -93,14 +94,18 @@
{
ostime_t now = os_getTime( );
// save exact rx time
- LMIC.rxtime = now - LORA_RXDONE_FIXUP[getSf( LMIC.rps )];
+ if( getBw( LMIC.rps ) == BW125 )
+ {
+ now -= LORA_RXDONE_FIXUP[getSf( LMIC.rps )];
+ }
+ LMIC.rxtime = now;
// read the PDU and inform the MAC that we received something
LMIC.dataLen = size;
// now read the FIFO
memcpy( LMIC.frame, payload, size );
// read rx quality parameters
- LMIC.rxq.snr = snr; // SNR [dB] * 4
- LMIC.rxq.rssi = rssi; // RSSI [dBm] (-196...+63)
+ LMIC.snr = snr; // SNR [dB] * 4
+ LMIC.rssi = rssi; // RSSI [dBm] (-196...+63)
// go from stanby to sleep
Radio.Sleep( );
@@ -113,7 +118,8 @@
ostime_t now = os_getTime( );
// indicate error
-
+ LMIC.dataLen = 0;
+
// go from stanby to sleep
Radio.Sleep( );
// run os job (use preset func ptr)
@@ -230,7 +236,7 @@
else
{ // LoRa modem
- Radio.SetTxConfig( MODEM_LORA, LMIC.txpow, 0, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 8, getIh( LMIC.rps ) ? true : false, ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, false, 3e6 );
+ Radio.SetTxConfig( MODEM_LORA, LMIC.txpow, 0, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 8, getIh( LMIC.rps ) ? true : false, ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, false, 3e6 );
}
//starttx( ); // buf=LMIC.frame, len=LMIC.dataLen
@@ -249,8 +255,14 @@
}
else
{ // LoRa modem
-
- Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh(LMIC.rps), ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, true, false );
+ if( ( getSf( LMIC.rps ) <= SF9 ) && ( LMIC.rxsyms < 8 ) )
+ {
+ Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms + 3, getIh( LMIC.rps ) ? true : false, getIh( LMIC.rps ), ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, true, false );
+ }
+ else
+ {
+ Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh( LMIC.rps ), ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, true, false );
+ }
}
// now instruct the radio to receive
@@ -279,10 +291,10 @@
}
else
{ // LoRa modem
- Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh(LMIC.rps), ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, true, true );
+ Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh( LMIC.rps ), ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, true, true );
}
- //startrx( RXMODE_SCAN) ; // buf = LMIC.frame
+ //startrx( RXMODE_SCAN ); // buf = LMIC.frame
Radio.Rx( 0 );
break;
}
@@ -448,7 +460,7 @@
#define SX1276_MC3_AGCAUTO 0x04
// preamble for lora networks (nibbles swapped)
-#define LORA_MAC_PREAMBLE 0x34
+#define LORA_MAC_PREAMBLE 0x34
#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A
#ifdef CFG_sx1276_radio
@@ -539,6 +551,12 @@
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
#endif
+#define RADIO_DBG
+#if defined(RADIO_DBG)
+DigitalOut txStateIo( PB_8 );
+DigitalOut rxStateIo( PB_9 );
+#endif
+
static void writeReg (u1_t addr, u1_t data ) {
hal_pin_nss(0);
hal_spi(addr | 0x80);
@@ -576,7 +594,7 @@
writeReg(RegOpMode, (readReg(RegOpMode) & ~OPMODE_MASK) | mode);
}
-static void opmodeLora(void) {
+static void opmodeLora() {
u1_t u = OPMODE_LORA;
#ifdef CFG_sx1276_radio
u |= 0x8; // TBD: sx1276 high freq
@@ -584,7 +602,7 @@
writeReg(RegOpMode, u);
}
-static void opmodeFSK(void) {
+static void opmodeFSK() {
u1_t u = 0;
#ifdef CFG_sx1276_radio
u |= 0x8; // TBD: sx1276 high freq
@@ -593,79 +611,79 @@
}
// configure LoRa modem (cfg1, cfg2)
-static void configLoraModem (void) {
+static void configLoraModem () {
sf_t sf = getSf(LMIC.rps);
#ifdef CFG_sx1276_radio
- u1_t mc1 = 0, mc2 = 0, mc3 = 0;
+ u1_t mc1 = 0, mc2 = 0, mc3 = 0;
- switch (getBw(LMIC.rps)) {
- case BW125: mc1 |= SX1276_MC1_BW_125; break;
- case BW250: mc1 |= SX1276_MC1_BW_250; break;
- case BW500: mc1 |= SX1276_MC1_BW_500; break;
- default:
- ASSERT(0);
- }
- switch( getCr(LMIC.rps) ) {
- case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break;
- case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break;
- case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break;
- case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break;
- default:
- ASSERT(0);
- }
+ switch (getBw(LMIC.rps)) {
+ case BW125: mc1 |= SX1276_MC1_BW_125; break;
+ case BW250: mc1 |= SX1276_MC1_BW_250; break;
+ case BW500: mc1 |= SX1276_MC1_BW_500; break;
+ default:
+ ASSERT(0);
+ }
+ switch( getCr(LMIC.rps) ) {
+ case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break;
+ case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break;
+ case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break;
+ case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break;
+ default:
+ ASSERT(0);
+ }
- if (getIh(LMIC.rps)) {
- mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON;
- writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
- }
- // set ModemConfig1
- writeReg(LORARegModemConfig1, mc1);
+ if (getIh(LMIC.rps)) {
+ mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON;
+ writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
+ }
+ // set ModemConfig1
+ writeReg(LORARegModemConfig1, mc1);
- mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4));
- if (getNocrc(LMIC.rps) == 0) {
- mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON;
- }
- writeReg(LORARegModemConfig2, mc2);
-
- mc3 = SX1276_MC3_AGCAUTO;
- if (sf == SF11 || sf == SF12) {
- mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE;
- }
- writeReg(LORARegModemConfig3, mc3);
+ mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4));
+ if (getNocrc(LMIC.rps) == 0) {
+ mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON;
+ }
+ writeReg(LORARegModemConfig2, mc2);
+
+ mc3 = SX1276_MC3_AGCAUTO;
+ if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
+ mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE;
+ }
+ writeReg(LORARegModemConfig3, mc3);
#elif CFG_sx1272_radio
- u1_t mc1 = (getBw(LMIC.rps)<<6);
+ u1_t mc1 = (getBw(LMIC.rps)<<6);
- switch( getCr(LMIC.rps) ) {
- case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break;
- case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break;
- case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break;
- case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break;
- }
-
- if (sf == SF11 || sf == SF12) {
- mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE;
- }
-
- if (getNocrc(LMIC.rps) == 0) {
- mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON;
- }
-
- if (getIh(LMIC.rps)) {
- mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON;
- writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
- }
- // set ModemConfig1
- writeReg(LORARegModemConfig1, mc1);
-
- // set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi=00)
- writeReg(LORARegModemConfig2, (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04);
+ switch( getCr(LMIC.rps) ) {
+ case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break;
+ case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break;
+ case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break;
+ case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break;
+ }
+
+ if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
+ mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE;
+ }
+
+ if (getNocrc(LMIC.rps) == 0) {
+ mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON;
+ }
+
+ if (getIh(LMIC.rps)) {
+ mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON;
+ writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
+ }
+ // set ModemConfig1
+ writeReg(LORARegModemConfig1, mc1);
+
+ // set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi=00)
+ writeReg(LORARegModemConfig2, (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04);
#else
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
#endif /* CFG_sx1272_radio */
}
-static void configChannel (void) {
+static void configChannel () {
// set frequency: FQ = (FRF * 32 Mhz) / (2 ^ 19)
u8_t frf = ((u8_t)LMIC.freq << 19) / 32000000;
writeReg(RegFrfMsb, (u1_t)(frf>>16));
@@ -675,26 +693,26 @@
-static void configPower (void) {
+static void configPower () {
#ifdef CFG_sx1276_radio
// no boost used for now
s1_t pw = (s1_t)LMIC.txpow;
- if(pw > 14) {
- pw = 14;
- } else if(pw < -1) {
- pw = -1;
+ if(pw >= 17) {
+ pw = 15;
+ } else if(pw < 2) {
+ pw = 2;
}
// check board type for BOOST pin
- writeReg(RegPaConfig, (u1_t)(0x00|((pw+1)&0xf)));
+ writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf)));
writeReg(RegPaDac, readReg(RegPaDac)|0x4);
#elif CFG_sx1272_radio
// set PA config (2-17 dBm using PA_BOOST)
s1_t pw = (s1_t)LMIC.txpow;
if(pw > 17) {
- pw = 17;
+ pw = 17;
} else if(pw < 2) {
- pw = 2;
+ pw = 2;
}
writeReg(RegPaConfig, (u1_t)(0x80|(pw-2)));
#else
@@ -702,12 +720,16 @@
#endif /* CFG_sx1272_radio */
}
-static void txfsk (void) {
+static void txfsk () {
// select FSK modem (from sleep mode)
writeReg(RegOpMode, 0x10); // FSK, BT=0.5
ASSERT(readReg(RegOpMode) == 0x10);
// enter standby mode (required for FIFO loading))
opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+ txStateIo = 0;
+ rxStateIo = 0;
+#endif
// set bitrate
writeReg(FSKRegBitrateMsb, 0x02); // 50kbps
writeReg(FSKRegBitrateLsb, 0x80);
@@ -743,9 +765,12 @@
// now we actually start the transmission
opmode(OPMODE_TX);
+#if defined(RADIO_DBG)
+ txStateIo = 1;
+#endif
}
-static void txlora (void) {
+static void txlora () {
// select LoRa modem (from sleep mode)
//writeReg(RegOpMode, OPMODE_LORA);
opmodeLora();
@@ -753,6 +778,10 @@
// enter standby mode (required for FIFO loading))
opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+ txStateIo = 0;
+ rxStateIo = 0;
+#endif
// configure LoRa modem (cfg1, cfg2)
configLoraModem();
// configure frequency
@@ -786,7 +815,7 @@
}
// start transmitter (buf=LMIC.frame, len=LMIC.dataLen)
-static void starttx (void) {
+static void starttx () {
ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
if(getSf(LMIC.rps) == FSK) { // FSK modem
txfsk();
@@ -812,6 +841,10 @@
ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0);
// enter standby mode (warm up))
opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+ txStateIo = 0;
+ rxStateIo = 0;
+#endif
// don't use MAC settings at startup
if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan
writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1);
@@ -846,9 +879,15 @@
// now instruct the radio to receive
if (rxmode == RXMODE_SINGLE) { // single rx
hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
- opmode(OPMODE_RX_SINGLE);
+ opmode(OPMODE_RX_SINGLE);
+#if defined(RADIO_DBG)
+ rxStateIo = 1;
+#endif
} else { // continous rx (scan or rssi)
- opmode(OPMODE_RX);
+ opmode(OPMODE_RX);
+#if defined(RADIO_DBG)
+ rxStateIo = 1;
+#endif
}
}
@@ -861,6 +900,10 @@
ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0);
// enter standby mode (warm up))
opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+ txStateIo = 0;
+ rxStateIo = 0;
+#endif
// configure frequency
configChannel();
// set LNA gain
@@ -901,6 +944,9 @@
// now instruct the radio to receive
hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
opmode(OPMODE_RX); // no single rx mode available in FSK
+#if defined(RADIO_DBG)
+ rxStateIo = 1;
+#endif
}
static void startrx (u1_t rxmode) {
@@ -915,7 +961,7 @@
}
// get random seed from wideband noise rssi
-void radio_init (void) {
+void radio_init () {
hal_disableIRQs();
// manually reset radio
@@ -929,6 +975,10 @@
hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms
opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+ txStateIo = 0;
+ rxStateIo = 0;
+#endif
// some sanity checks, e.g., read version number
u1_t v = readReg(RegVersion);
#ifdef CFG_sx1276_radio
@@ -950,7 +1000,7 @@
}
randbuf[0] = 16; // set initial index
-#ifdef CFG_sx1276_radio
+#ifdef CFG_sx1276mb1_board
// chain calibration
writeReg(RegPaConfig, 0);
@@ -967,15 +1017,19 @@
// Launch Rx chain calibration for HF band
writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START);
while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; }
-#endif /* CFG_sx1276_radio */
+#endif /* CFG_sx1276mb1_board */
opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+ txStateIo = 0;
+ rxStateIo = 0;
+#endif
hal_enableIRQs();
}
// return next random byte derived from seed buffer
// (buf[0] holds index of next byte to be returned)
-u1_t radio_rand1 (void) {
+u1_t radio_rand1 () {
u1_t i = randbuf[0];
ASSERT( i != 0 );
if( i==16 ) {
@@ -987,7 +1041,7 @@
return v;
}
-u1_t radio_rssi (void) {
+u1_t radio_rssi () {
hal_disableIRQs();
u1_t r = readReg(LORARegRssiValue);
hal_enableIRQs();
@@ -1009,56 +1063,63 @@
void radio_irq_handler (u1_t dio) {
ostime_t now = os_getTime();
if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem
- u1_t flags = readReg(LORARegIrqFlags);
- if( flags & IRQ_LORA_TXDONE_MASK ) {
- // save exact tx time
- LMIC.txend = now - us2osticks(43); // TXDONE FIXUP
- } else if( flags & IRQ_LORA_RXDONE_MASK ) {
- // save exact rx time
- LMIC.rxtime = now - LORA_RXDONE_FIXUP[getSf(LMIC.rps)];
- // read the PDU and inform the MAC that we received something
- LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ?
- readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes);
- // set FIFO read address pointer
- writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr));
- // now read the FIFO
- readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
- // read rx quality parameters
- LMIC.rxq.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4
- LMIC.rxq.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
- } else if( flags & IRQ_LORA_RXTOUT_MASK ) {
- // indicate timeout
- LMIC.dataLen = 0;
- }
+ u1_t flags = readReg(LORARegIrqFlags);
+ if( flags & IRQ_LORA_TXDONE_MASK ) {
+ // save exact tx time
+ LMIC.txend = now - us2osticks(43); // TXDONE FIXUP
+ } else if( flags & IRQ_LORA_RXDONE_MASK ) {
+ // save exact rx time
+ if(getBw(LMIC.rps) == BW125) {
+ now -= LORA_RXDONE_FIXUP[getSf(LMIC.rps)];
+ }
+ LMIC.rxtime = now;
+ // read the PDU and inform the MAC that we received something
+ LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ?
+ readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes);
+ // set FIFO read address pointer
+ writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr));
+ // now read the FIFO
+ readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
+ // read rx quality parameters
+ LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4
+ LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
+ } else if( flags & IRQ_LORA_RXTOUT_MASK ) {
+ // indicate timeout
+ LMIC.dataLen = 0;
+ }
// mask all radio IRQs
writeReg(LORARegIrqFlagsMask, 0xFF);
// clear radio IRQ flags
writeReg(LORARegIrqFlags, 0xFF);
} else { // FSK modem
- u1_t flags1 = readReg(FSKRegIrqFlags1);
- u1_t flags2 = readReg(FSKRegIrqFlags2);
- if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) {
- // save exact tx time
- LMIC.txend = now;
+ u1_t flags1 = readReg(FSKRegIrqFlags1);
+ u1_t flags2 = readReg(FSKRegIrqFlags2);
+ if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) {
+ // save exact tx time
+ LMIC.txend = now;
} else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) {
- // save exact rx time
- LMIC.rxtime = now;
- // read the PDU and inform the MAC that we received something
- LMIC.dataLen = readReg(FSKRegPayloadLength);
- // now read the FIFO
- readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
- // read rx quality parameters
- LMIC.rxq.snr = 0; // determine snr
- LMIC.rxq.rssi = 0; // determine rssi
- } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) {
- // indicate timeout
- LMIC.dataLen = 0;
- } else {
+ // save exact rx time
+ LMIC.rxtime = now;
+ // read the PDU and inform the MAC that we received something
+ LMIC.dataLen = readReg(FSKRegPayloadLength);
+ // now read the FIFO
+ readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
+ // read rx quality parameters
+ LMIC.snr = 0; // determine snr
+ LMIC.rssi = 0; // determine rssi
+ } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) {
+ // indicate timeout
+ LMIC.dataLen = 0;
+ } else {
while(1);
}
}
// go from stanby to sleep
opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+ txStateIo = 0;
+ rxStateIo = 0;
+#endif
// run os job (use preset func ptr)
os_setCallback(&LMIC.osjob, LMIC.osjob.func);
}
@@ -1069,21 +1130,34 @@
case RADIO_RST:
// put radio to sleep
opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+ txStateIo = 0;
+ rxStateIo = 0;
+#endif
break;
case RADIO_TX:
- // transmit frame now
+ // transmit frame now
starttx(); // buf=LMIC.frame, len=LMIC.dataLen
+#if defined(RADIO_DBG)
+ txStateIo = 1;
+#endif
break;
case RADIO_RX:
- // receive frame now (exactly at rxtime)
+ // receive frame now (exactly at rxtime)
startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms
+#if defined(RADIO_DBG)
+ rxStateIo = 1;
+#endif
break;
case RADIO_RXON:
// start scanning for beacon now
startrx(RXMODE_SCAN); // buf=LMIC.frame
+#if defined(RADIO_DBG)
+ rxStateIo = 1;
+#endif
break;
}
hal_enableIRQs();