end node on synchronous star LoRa network.

Dependencies:   SX127x sx12xx_hal TSL2561

radio chip selection

Radio chip driver is not included, allowing choice of radio device.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
if you're using SX1280, then import sx1280 driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.

This project for use with LoRaWAN_singlechannel_gateway project.

Alternately gateway running on raspberry pi can be used as gateway.

LoRaWAN on single radio channel

Network description is at gateway project page. Synchronous star network.

Hardware Support

This project supports SX1276 and SX1272, sx126x kit, sx126x shield, and sx128x 2.4GHz. The ST board B-L072Z-LRWAN1 is also supported (TypeABZ module). When B-L072Z-LRWAN1 target is selected, TARGET_DISCO_L072CZ_LRWAN1 is defined by tools, allowing correct radio driver configuration for this platform. Alternately, any mbed board that can use LoRa radio shield board should work, but NUCLEO boards are tested.

End-node Unique ID

DevEUI is created from CPU serial number. AppEUI and AppKey are declared as software constants.

End-node Configuration

Data rate definition LORAMAC_DEFAULT_DATARATE configured in LoRaMac-definitions.h. See gateway project page for configuration of gateway.
LoRaWAN addressing is configured in Comissioning.h; only OTA mode is functional.
Header file board/lora_config.h, selects application layer options (i.e. sensors) to be compiled in.

Serial Interface

Serial port operates at 115200bps.
Application layer single_us915_main.cpp User button triggers uplink (i.e. blue button on nucleo board), or jumper enables continuously sends repeated uplink packets. The MAC layer holds each uplink request until the allocated timeslot.

commandargumentsdescription
?-print available commands
. (period)-print status (DevEUI, DevAddr, etc)
ullength integerset payload length of test uplink packets

sensor demo

Selected grove sensors may be plugged into SX1272 shield.
To enable, edit lora_config.h to define SENSORS.

Sensor connections on SX1272MB2xAS:

D8 D9: buttonRX TX: (unused)A3 A4: Rotary Angle Sensor
D6 D7: RGB LEDSCL SDA: digital light sensorA1 A2: Rotary Angle Sensor

Digital input pin, state reported via uplink: PC8
Digital output pin, controlled via downlink: PC6
PWM out: PB_10

Jumper enables auto-repeated transmit: PC10 and PC12 on NUCLEO board, located on end of morpho headers nearby JP4.

Revision:
30:1c35c4f56e50
Parent:
29:ad409c68c0a6
--- a/mac/LoRaMac.cpp	Fri Dec 07 17:57:41 2018 -0800
+++ b/mac/LoRaMac.cpp	Thu Dec 13 16:53:02 2018 -0800
@@ -168,9 +168,6 @@
 
 
 #ifdef SX128x_H 
-
-    #define LORA_MAX_NB_CHANNELS                        8
-
     //                             0    1   2  3  4  5  6  7
     const uint8_t Datarates[]  = { 12, 11, 10, 9, 8, 7, 6, 5 };
     #define SF_FROM_DR0     12  /* DR0 is sf12 */
@@ -186,6 +183,7 @@
     const int8_t TxPowers[] = { 12, 5 };
     #define LORAMAC_FIRST_CHANNEL           ( (uint32_t)2486.9e6 )
     #define LORAMAC_STEPWIDTH_CHANNEL       ( (uint32_t)300e3 )
+    #define LORA_MAX_NB_CHANNELS            8
 
     #define LORA_BANDWIDTH_KHZ           200
 
@@ -241,7 +239,7 @@
 
     #define LORAMAC_FIRST_CHANNEL           ( (uint32_t)433.32e6 )
     #define LORAMAC_STEPWIDTH_CHANNEL       ( (uint32_t)200e3 )
-    #define LORA_MAX_NB_CHANNELS                        7
+    #define LORA_MAX_NB_CHANNELS            7
 
     /* end USE_BAND_433 */
 #else
@@ -253,67 +251,24 @@
 
 #ifdef SX128x_H 
 
-    #define LATENCY_FROM_1600KHZ_SF12       17300
-    #define LATENCY_FROM_1600KHZ_SF11       16900
-    #define LATENCY_FROM_1600KHZ_SF10       4300
-    #define LATENCY_FROM_1600KHZ_SF9        4000
-    #define LATENCY_FROM_1600KHZ_SF8        1050
-    #define LATENCY_FROM_1600KHZ_SF7        1000
-    #define LATENCY_FROM_1600KHZ_SF6        504
-    #define LATENCY_FROM_1600KHZ_SF5        256
-
-    #define _LATENCY_FROM_SF(sf)    LATENCY_FROM_1600KHZ_SF ## sf
-    #define LATENCY_FROM_SF_(x)     _LATENCY_FROM_SF(x)
-
-    /* 6 byte implict packet */
-    #define BEACON_TOA_FROM_1600KHZ_SF12     66000
-    #define BEACON_TOA_FROM_1600KHZ_SF11     33100
-    #define BEACON_TOA_FROM_1600KHZ_SF10     16400
-    #define BEACON_TOA_FROM_1600KHZ_SF9      8260
-    #define BEACON_TOA_FROM_1600KHZ_SF8      5100
-    #define BEACON_TOA_FROM_1600KHZ_SF7      2560
-    #define BEACON_TOA_FROM_1600KHZ_SF6      1350
-    #define BEACON_TOA_FROM_1600KHZ_SF5      800
-
-    #define BEACON_RXDONE_LATENCY_us        (LATENCY_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (1600/LORA_BANDWIDTH_KHZ))
-
-    #define _BEACON_TOA_FROM_SF(sf)    BEACON_TOA_FROM_1600KHZ_SF ## sf
-    #define BEACON_TOA_FROM_SF_(x)     _BEACON_TOA_FROM_SF(x)
-    #define BEACON_TOA_us        (BEACON_TOA_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (1600/LORA_BANDWIDTH_KHZ))
+    #define FASTEST_SF      5
+    /* ratio of symbol period to time from end of packet to RxDone interrupt */
+//  sp       200KHz                         160    320   640   1280  2560  5120  10240 20480
+// usLatency 200KHz implicit-6byte           90    228   436   880   2020  4200  8800  19300
+                                    // SF:   5     6     7     8     9     10    11    12
+    const float rxLatencyFactorFromSF[8] = { 0.56, 0.72, 0.68, 0.69, 0.79, 0.82, 0.86, 0.94 };
 
 #elif defined(SX126x_H) || defined(SX127x_H)
 
-    /* 6 byte implict packet */
-    #define BEACON_TOA_FROM_500KHZ_SF12      198000
-    #define BEACON_TOA_FROM_500KHZ_SF11       97750
-    #define BEACON_TOA_FROM_500KHZ_SF10       49630
-    #define BEACON_TOA_FROM_500KHZ_SF9        24500
-    #define BEACON_TOA_FROM_500KHZ_SF8        12500
-    #define BEACON_TOA_FROM_500KHZ_SF7         6250
-    #define BEACON_TOA_FROM_500KHZ_SF6         3320
-    #define BEACON_TOA_FROM_500KHZ_SF5         1940
+    #define FASTEST_SF      5
+    /* ratio of symbol period to time from end of packet to RxDone interrupt */
+//  sp       500KHz                          64    128   256   512   1024  2048  4096  8192
+// usLatency 500KHz implicit-6byte                 92    184   376   780   1680  3680  7720
+                                    // SF:   5     6     7     8     9     10    11    12
+    const float rxLatencyFactorFromSF[8] = { 0.72, 0.72, 0.72, 0.73, 0.76, 0.82, 0.90, 0.94 };
 
-    #define LATENCY_FROM_500KHZ_SF12       7750
-    #define LATENCY_FROM_500KHZ_SF11       3600
-    #define LATENCY_FROM_500KHZ_SF10       1675
-    #define LATENCY_FROM_500KHZ_SF9        7875
-    #define LATENCY_FROM_500KHZ_SF8        385
-    #define LATENCY_FROM_500KHZ_SF7        195
-    #define LATENCY_FROM_500KHZ_SF6        103
-    #define LATENCY_FROM_500KHZ_SF5        48
-
-    #define _LATENCY_FROM_SF(sf)    LATENCY_FROM_500KHZ_SF ## sf
-    #define LATENCY_FROM_SF_(x)     _LATENCY_FROM_SF(x)
-
-    #define BEACON_RXDONE_LATENCY_us        (LATENCY_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (500/LORA_BANDWIDTH_KHZ))
-
-    #define _BEACON_TOA_FROM_SF(sf)    BEACON_TOA_FROM_500KHZ_SF ## sf
-    #define BEACON_TOA_FROM_SF_(x)     _BEACON_TOA_FROM_SF(x)
-    #define BEACON_TOA_us        (BEACON_TOA_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (500/LORA_BANDWIDTH_KHZ))
 #endif /* SX126x_H || SX127x_H */
 
-const us_timestamp_t beaconDuration = BEACON_TOA_us + BEACON_RXDONE_LATENCY_us;
-
 static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS];  // populated in init
 
 #define BEACON_GUARD_us         2000000     // pre-beacon start
@@ -385,11 +340,13 @@
     uint16_t nSymbsTimeout;
     unsigned SymbolTimeout_us;
     uint8_t num_missed;
+    uint8_t num_consecutive_ok;
 
     beacon_state_e state;
 
     uint16_t tx_slot_offset;
     uint16_t periodicity_slots;
+    uint32_t beaconStartToRxDone;
     uint16_t sendOpportunities;
 
     LowPowerTimeoutAbs _timeout_rx;
@@ -749,18 +706,6 @@
     RxWindowSetup(Channels[Channel].Frequency, RxWindowsParam.Datarate, RxWindowsParam.RxWindowTimeout, false);
     Radio::Rx( LoRaMacParams.MaxRxWindow );
     //mac_printf("rxwinTo:%u\r\n", RxWindowsParam.RxWindowTimeout);
-#ifdef SX128x_H
-    {
-        LoRaPktPar1_t LoRaPktPar1;                                                                                  
-        LoRaPktPar1.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR1, 1);
-        mac_printf("dnRxiq:");
-        if (LoRaPktPar1.bits.rxinvert_iq) {
-            mac_printf("IQ_STD\r\n");
-        } else {
-            mac_printf("IQ_INV\r\n");
-        }
-    }
-#endif /* SX128x_H */
 }
 
 static RxConfigParams_t ComputeRxWindowParameters( int8_t datarate, uint32_t rxError );
@@ -885,48 +830,44 @@
     _tx_timeout.attach_us(&send_callback, BeaconCtx.sendAt + err);
     BeaconCtx.lastSendAtErr = err;
 
-    if (LoRaMacFlags.Bits.uplink_pending)
-        LoRaMacFlags.Bits.send = 1;
+    if (LoRaMacFlags.Bits.uplink_pending) {
+        us_timestamp_t untilGuard = (BeaconCtx.rx_setup_at - BEACON_GUARD_us) - BeaconCtx._timeout_guard.read_us();
+        if (untilGuard > (RECEIVE_DELAY_us + TARGET_PRECESSION_us))
+            LoRaMacFlags.Bits.send = 1;
+    }
 }
 
 void OnRxBeaconSetup()
 {
-    Radio::Standby( );
-
+    BeaconCtx.guard = false;
     LoRaMacFlags.Bits.expecting_beacon = true;
 
-    Radio::LoRaModemConfig(LORA_BANDWIDTH_KHZ, Datarates[BEACON_CHANNEL_DR], 1);
-
-                        //    preambleLen, fixLen, crcOn, invIQ
-    Radio::LoRaPacketConfig(PREAMBLE_SYMBS, true, false, false);
-    Radio::SetFixedPayloadLength(BEACON_SIZE);
-    Radio::SetLoRaSymbolTimeout(BeaconCtx.nSymbsTimeout);
-
     Radio::Rx(2000);
 
     BeaconCtx.lastSendAtErr = 0;
-
-#ifdef SX128x_H
-    {
-        LoRaPktPar1_t LoRaPktPar1;                                                                                  
-        LoRaPktPar1.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR1, 1);
-        mac_printf("beaconRxiq:");
-        if (LoRaPktPar1.bits.rxinvert_iq) {
-            mac_printf("IQ_STD\r\n");
-        } else {
-            mac_printf("IQ_INV\r\n");
-        }
-    }
-#endif /* SX128x_H */
 }
 
 void guard_callback()
 {
     BeaconCtx.guard = true;
+    Radio::Standby( );
+    Radio::SetChannel( Channels[Channel].Frequency );
 
     _tx_timeout.detach();
     mac_printf("sendOpportunities:%u\r\n", BeaconCtx.sendOpportunities);
     BeaconCtx.sendOpportunities = 0;
+
+                        //    preambleLen, fixLen, crcOn, invIQ
+    Radio::LoRaPacketConfig(PREAMBLE_SYMBS, true, false, false);
+    Radio::LoRaModemConfig(LORA_BANDWIDTH_KHZ, Datarates[BEACON_CHANNEL_DR], 1);
+    Radio::SetFixedPayloadLength(BEACON_SIZE);
+
+#ifdef SX128x_H 
+    /* explicit to implicit header: does sx1280 really need this a 2nd time? */
+                        //    preambleLen, fixLen, crcOn, invIQ
+    Radio::LoRaPacketConfig(PREAMBLE_SYMBS, true, false, false);
+#endif /* SX128x_H */
+    Radio::SetLoRaSymbolTimeout(BeaconCtx.nSymbsTimeout);
 }
 
 static void us_to_nSymbTimeout(unsigned us)
@@ -970,10 +911,10 @@
 {
     static bool compensate_precession = false;
     int32_t compensation = 0;
-    us_timestamp_t ThisBeaconRx_us = Radio::irqAt + lpt_offset - beaconDuration;
+    us_timestamp_t ThisBeaconRx_us = Radio::irqAt + lpt_offset - BeaconCtx.beaconStartToRxDone;
 
+    BeaconCtx.num_consecutive_ok++;
     mac_printf("rx_beacon %llu ", Radio::irqAt);
-    BeaconCtx.guard = false;
     BeaconCtx.rx_precession_us = ThisBeaconRx_us - BeaconCtx.rx_setup_at;
     if (BeaconCtx.state != BEACON_STATE_FIRST_ACQ) {
         BeaconCtx.known_working_BeaconRxTimerError_us = BeaconCtx.last_BeaconRxTimerError_us;
@@ -989,7 +930,6 @@
             mac_printf("-->LOCKED ");
             BeaconCtx.state = BEACON_STATE_LOCKED;
             compensate_precession = true;
-            us_to_nSymbTimeout(BEACON_RX_TIMEOUT_LOCKED_us);
         }
     } else {
         /* ignore precession at first acquisition because it has slot resolution added */
@@ -1030,11 +970,6 @@
     BeaconCtx._timeout_rx.attach_us(&OnRxBeaconSetup, BeaconCtx.rx_setup_at);
     BeaconCtx._timeout_guard.attach_us(&guard_callback, BeaconCtx.rx_setup_at - BEACON_GUARD_us);
 
-    if (BeaconCtx.num_missed > 0) {
-        /* restore rx symbol timeout */
-        us_to_nSymbTimeout(BEACON_RX_TIMEOUT_LOCKED_us);
-    }
-
     BeaconCtx.num_missed = 0;
 
     MlmeIndication.MlmeIndication = MLME_BEACON;
@@ -1140,6 +1075,7 @@
 
             if( micRx == mic )
             {
+                uint32_t beaconDur;
                 LoRaMacJoinComputeSKeys( LoRaMacAppKey, _jaDecrypted + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey );
 
                 LoRaMacNetID = ( uint32_t )_jaDecrypted[4];
@@ -1180,6 +1116,16 @@
                 BeaconCtx.periodicity_slots = _jaDecrypted[17];
                 BeaconCtx.periodicity_slots |= _jaDecrypted[18] << 8;
 
+                beaconDur = _jaDecrypted[22];
+                beaconDur <<= 8;
+                beaconDur |= _jaDecrypted[21];
+                beaconDur <<= 8;
+                beaconDur |= _jaDecrypted[20];
+                beaconDur <<= 8;
+                beaconDur |= _jaDecrypted[19];
+                BeaconCtx.beaconStartToRxDone = beaconDur + (rxLatencyFactorFromSF[SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE)-FASTEST_SF] * BeaconCtx.symbol_period_us);
+
+
                 /* nowSlot: now vs previous beacon */
                 BeaconCtx.LastBeaconRx_us = BeaconCtx.rx_setup_at - BEACON_INTERVAL_us;
                 unsigned us_since_last_beacon = _rx_timeout.read_us() - BeaconCtx.LastBeaconRx_us;
@@ -1188,7 +1134,7 @@
                 while (useSlot < nowSlot)
                     useSlot += BeaconCtx.periodicity_slots;
 
-                mac_printf("useSlot:%u nowSlot:%u ", useSlot, nowSlot);
+                mac_printf("beaconDur:0x%x (%u) useSlot:%u nowSlot:%u ", beaconDur, BeaconCtx.beaconStartToRxDone, useSlot, nowSlot);
                 BeaconCtx.sendAt = BeaconCtx.LastBeaconRx_us + (useSlot * PING_SLOT_RESOLUTION_us);
                 mac_printf("sendIn:%u\r\n", BeaconCtx.sendAt - _tx_timeout.read_us());
                 _tx_timeout.attach_us(send_callback, BeaconCtx.sendAt);
@@ -1198,11 +1144,12 @@
                 BeaconCtx.guard = false;
                 BeaconCtx.num_missed = 0;
                 BeaconCtx.rx_precession_us = 0;
+                BeaconCtx.num_consecutive_ok = 0;
                 BeaconCtx.last_BeaconRxTimerError_us = -PPM_BEACON_INTERVAL;
                 BeaconCtx.known_working_BeaconRxTimerError_us = -PPM_BEACON_INTERVAL;
                 /* first beacon reception needs to open for 30ms timing resolution */
                 BeaconCtx.Precess_symbols = ceil(TARGET_PRECESSION_us / BeaconCtx.symbol_period_us);
-                BeaconCtx.SymbolTimeout_us = PING_SLOT_RESOLUTION_us + (PPM_BEACON_INTERVAL * 2);   // error unknown at start
+                BeaconCtx.SymbolTimeout_us = PING_SLOT_RESOLUTION_us + (PPM_BEACON_INTERVAL * 4);   // error unknown at start
                 BeaconCtx.nSymbsTimeout = BeaconCtx.SymbolTimeout_us / BeaconCtx.symbol_period_us;
                 if (BeaconCtx.nSymbsTimeout < MIN_SYMBOL_TIMEOUT)
                     BeaconCtx.nSymbsTimeout = MIN_SYMBOL_TIMEOUT;
@@ -1216,8 +1163,9 @@
             }
             else
             {
+                unsigned x;
                 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
-                mac_printf("join-mic-fail\r\n");
+                mac_printf("join-mic-fail size:%u\r\n", size);
                 JoinRequestTrials = MaxJoinRequestTrials; // stop trying
             }
             LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm );
@@ -1575,13 +1523,12 @@
     if (LoRaMacFlags.Bits.expecting_beacon) {
         float ourErrSecs = BeaconCtx.known_working_BeaconRxTimerError_us / 1000000.0;
 
-        BeaconCtx.guard = false;
         LoRaMacFlags.Bits.expecting_beacon = false;
 
         BeaconCtx.rx_setup_at += BEACON_INTERVAL_us + BeaconCtx.known_working_BeaconRxTimerError_us;
         BeaconCtx.rx_setup_at -= (PPM_BEACON_INTERVAL / 4);
-        us_to_nSymbTimeout(BeaconCtx.SymbolTimeout_us + (PPM_BEACON_INTERVAL / 2));
-        mac_printf("beacon timeout ourErr:%f SymbTo:%u\r\n", ourErrSecs, BeaconCtx.nSymbsTimeout);
+        us_to_nSymbTimeout(BeaconCtx.SymbolTimeout_us + PPM_BEACON_INTERVAL);
+        mac_printf("beacon timeout ourErr:%f SymbTo:%u(%uus)\r\n", ourErrSecs, BeaconCtx.nSymbsTimeout, BeaconCtx.SymbolTimeout_us);
 
         BeaconCtx._timeout_rx.attach_us(&OnRxBeaconSetup, BeaconCtx.rx_setup_at);
         BeaconCtx._timeout_guard.attach_us(&guard_callback, BeaconCtx.rx_setup_at - BEACON_GUARD_us);
@@ -1598,6 +1545,7 @@
         BeaconCtx.sendAt += BeaconCtx.tx_slot_offset * PING_SLOT_RESOLUTION_us;
         _tx_timeout.attach_us(&send_callback, BeaconCtx.sendAt);
 
+        BeaconCtx.num_consecutive_ok = 0;
     } else {
         if (LoRaMacFlags.Bits.MlmeReq && ( MlmeConfirm.MlmeRequest == MLME_JOIN )) {
             /* no join accept received: join retry */