LoRaWAN MAC layer implementation

Dependents:   LoRaWAN-demo-72_tjm LoRaWAN-demo-72_jlc LoRaWAN-demo-elmo frdm_LoRa_Connect_Woodstream_Demo_tjm ... more

LoRAWAN-lib is a port of the GitHub LoRaMac-node LoRaWAN MAC layer implementation.

This library depends on the SX1276Lib or SX1272Lib radio drivers depending on the used mbed component shield.

This library depends also on some cryptographic helper functions as well as helper functions for the timers management. These can be found on the example projects under the system directory.

The example projects are:

  1. LoRaWAN-demo-72
  2. LoRaWAN-demo-76
  3. LoRaWAN-demo-NAMote72

The LoRaWAN specification specifies different ISM bands operating parameters. These are all implemented under the LoRaMac-board.h file.

In order to select which band to use, please change line 24 of board.h file provided on the examples projects as follows:


EU868

board.h

#define USE_BAND_868


US915

board.h

#define USE_BAND_915


US915 - Hybrid

board.h

#define USE_BAND_915_HYBRID


CN780

board.h

#define USE_BAND_780


EU433

board.h

#define USE_BAND_433

Files at this revision

API Documentation at this revision

Comitter:
mluis
Date:
Fri May 13 14:51:50 2016 +0000
Parent:
3:b9d87593a8ae
Child:
5:2477c924494a
Commit message:
Synchronized with https://github.com/Lora-net/LoRaMac-node git revision 55d16ca8949c09ee241c87b7600e2a8bc90d3743

Changed in this revision

LoRaMac-api-v3.cpp Show annotated file Show diff for this revision Revisions of this file
LoRaMac-api-v3.h Show annotated file Show diff for this revision Revisions of this file
LoRaMac-board.h Show annotated file Show diff for this revision Revisions of this file
LoRaMac.cpp Show annotated file Show diff for this revision Revisions of this file
LoRaMac.h Show annotated file Show diff for this revision Revisions of this file
--- a/LoRaMac-api-v3.cpp	Mon Mar 14 09:09:54 2016 +0000
+++ b/LoRaMac-api-v3.cpp	Fri May 13 14:51:50 2016 +0000
@@ -105,8 +105,7 @@
         {
             case MLME_JOIN:
             {
-                 // Status is OK, node has joined the network
-                LoRaMacEventInfo.Status = mlmeConfirm->Status;
+                // Status is OK, node has joined the network
                 LoRaMacEventFlags.Bits.Tx = 1;
                 LoRaMacEventFlags.Bits.Rx = 1;
                 LoRaMacEventFlags.Bits.JoinAccept = 1;
@@ -126,6 +125,7 @@
                 break;
         }
     }
+    LoRaMacEventInfo.Status = mlmeConfirm->Status;
 
     if( LoRaMacFlags.Bits.McpsInd != 1 )
     {
--- a/LoRaMac-api-v3.h	Mon Mar 14 09:09:54 2016 +0000
+++ b/LoRaMac-api-v3.h	Fri May 13 14:51:50 2016 +0000
@@ -41,15 +41,23 @@
 #define BEACON_INTERVAL                             128000000
 
 /*!
- * Class A&B receive delay in us
+ * Class A&B receive delay 1 in us
  */
 #define RECEIVE_DELAY1                              1000000
+
+/*!
+ * Class A&B receive delay 2 in us
+ */
 #define RECEIVE_DELAY2                              2000000
 
 /*!
- * Join accept receive delay in us
+ * Join accept receive delay 1 in us
  */
 #define JOIN_ACCEPT_DELAY1                          5000000
+
+/*!
+ * Join accept receive delay 2 in us
+ */
 #define JOIN_ACCEPT_DELAY2                          6000000
 
 /*!
@@ -75,19 +83,19 @@
 /*!
  * Number of seconds after the start of the second reception window without
  * receiving an acknowledge.
- * AckTimeout = ACK_TIMEOUT + Random( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND )
+ * AckTimeout = \ref ACK_TIMEOUT + Random( -\ref ACK_TIMEOUT_RND, \ref ACK_TIMEOUT_RND )
  */
 #define ACK_TIMEOUT                                 2000000
 
 /*!
  * Random number of seconds after the start of the second reception window without
  * receiving an acknowledge
- * AckTimeout = ACK_TIMEOUT + Random( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND )
+ * AckTimeout = \ref ACK_TIMEOUT + Random( -\ref ACK_TIMEOUT_RND, \ref ACK_TIMEOUT_RND )
  */
 #define ACK_TIMEOUT_RND                             1000000
 
 /*!
- * Check the Mac layer state every MAC_STATE_CHECK_TIMEOUT
+ * Check the Mac layer state every MAC_STATE_CHECK_TIMEOUT in us
  */
 #define MAC_STATE_CHECK_TIMEOUT                     1000000
 
@@ -97,14 +105,18 @@
 #define MAX_ACK_RETRIES                             8
 
 /*!
- * RSSI free threshold
+ * RSSI free threshold [dBm]
  */
-#define RSSI_FREE_TH                                ( int8_t )( -90 ) // [dBm]
+#define RSSI_FREE_TH                                ( int8_t )( -90 )
 
 /*!
- * Frame direction definition
+ * Frame direction definition for up-link communications
  */
 #define UP_LINK                                     0
+
+/*!
+ * Frame direction definition for down-link communications
+ */
 #define DOWN_LINK                                   1
 
 /*!
--- a/LoRaMac-board.h	Mon Mar 14 09:09:54 2016 +0000
+++ b/LoRaMac-board.h	Fri May 13 14:51:50 2016 +0000
@@ -33,12 +33,22 @@
 /*!
  * Minimal datarate that can be used by the node
  */
-#define LORAMAC_MIN_DATARATE                        DR_0
+#define LORAMAC_TX_MIN_DATARATE                     DR_0
 
 /*!
  * Minimal datarate that can be used by the node
  */
-#define LORAMAC_MAX_DATARATE                        DR_7
+#define LORAMAC_TX_MAX_DATARATE                     DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_RX_MIN_DATARATE                     DR_0
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_RX_MAX_DATARATE                     DR_7
 
 /*!
  * Default datarate used by the node
@@ -111,9 +121,19 @@
  */
 // Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
 #define LC1                { 433175000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-#define LC2                { 433375000, { ( ( DR_7 << 4 ) | DR_0 ) }, 0 }
+#define LC2                { 433375000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
 #define LC3                { 433575000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
 
+/*!
+ * LoRaMac duty cycle for the join procedure
+ */
+#define JOIN_DC            1000
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define JOIN_CHANNELS      ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
 #elif defined( USE_BAND_780 )
 
 /*!
@@ -124,12 +144,22 @@
 /*!
  * Minimal datarate that can be used by the node
  */
-#define LORAMAC_MIN_DATARATE                        DR_0
+#define LORAMAC_TX_MIN_DATARATE                     DR_0
 
 /*!
  * Minimal datarate that can be used by the node
  */
-#define LORAMAC_MAX_DATARATE                        DR_7
+#define LORAMAC_TX_MAX_DATARATE                     DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_RX_MIN_DATARATE                     DR_0
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_RX_MAX_DATARATE                     DR_7
 
 /*!
  * Default datarate used by the node
@@ -202,9 +232,19 @@
  */
 // Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
 #define LC1                { 779500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
-#define LC2                { 779700000, { ( ( DR_7 << 4 ) | DR_0 ) }, 0 }
+#define LC2                { 779700000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
 #define LC3                { 779900000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
 
+/*!
+ * LoRaMac duty cycle for the join procedure
+ */
+#define JOIN_DC            1000
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define JOIN_CHANNELS      ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
 #elif defined( USE_BAND_868 )
 
 /*!
@@ -215,12 +255,22 @@
 /*!
  * Minimal datarate that can be used by the node
  */
-#define LORAMAC_MIN_DATARATE                        DR_0
+#define LORAMAC_TX_MIN_DATARATE                     DR_0
 
 /*!
  * Minimal datarate that can be used by the node
  */
-#define LORAMAC_MAX_DATARATE                        DR_7
+#define LORAMAC_TX_MAX_DATARATE                     DR_7
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_RX_MIN_DATARATE                     DR_0
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_RX_MAX_DATARATE                     DR_7
 
 /*!
  * Default datarate used by the node
@@ -309,9 +359,19 @@
  */
 // Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
 #define LC1                { 868100000, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
-#define LC2                { 868300000, { ( ( DR_6 << 4 ) | DR_0 ) }, 1 }
+#define LC2                { 868300000, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
 #define LC3                { 868500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
 
+/*!
+ * LoRaMac duty cycle for the join procedure
+ */
+#define JOIN_DC            1000
+
+/*!
+ * LoRaMac channels which are allowed for the join procedure
+ */
+#define JOIN_CHANNELS      ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) )
+
 #elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
 
 /*!
@@ -322,12 +382,22 @@
 /*!
  * Minimal datarate that can be used by the node
  */
-#define LORAMAC_MIN_DATARATE                        DR_0
+#define LORAMAC_TX_MIN_DATARATE                     DR_0
 
 /*!
  * Minimal datarate that can be used by the node
  */
-#define LORAMAC_MAX_DATARATE                        DR_4
+#define LORAMAC_TX_MAX_DATARATE                     DR_4
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_RX_MIN_DATARATE                     DR_8
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_RX_MAX_DATARATE                     DR_13
 
 /*!
  * Default datarate used by the node
--- a/LoRaMac.cpp	Mon Mar 14 09:09:54 2016 +0000
+++ b/LoRaMac.cpp	Fri May 13 14:51:50 2016 +0000
@@ -34,6 +34,13 @@
 #define LORA_MAC_COMMAND_MAX_LENGTH                 15
 
 /*!
+ * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength
+ * in RxWindowSetup function.
+ * Maximum PHYPayload = MaxPayloadOfDatarate/MaxPayloadOfDatarateRepeater + LORA_MAC_FRMPAYLOAD_OVERHEAD
+ */
+#define LORA_MAC_FRMPAYLOAD_OVERHEAD                13 // MHDR(1) + FHDR(7) + Port(1) + MIC(4)
+
+/*!
  * Device IEEE EUI
  */
 static uint8_t *LoRaMacDevEui;
@@ -362,7 +369,6 @@
  * Contains the channels which remain to be applied.
  */
 static uint16_t ChannelsMaskRemaining[6];
-
 #else
     #error "Please define a frequency band in the compiler options."
 #endif
@@ -430,6 +436,8 @@
  */
 static uint8_t Channel;
 
+static uint8_t LastTxChannel;
+
 /*!
  * LoRaMac internal states
  */
@@ -442,6 +450,7 @@
     MAC_ACK_RETRY     = 0x00000008,
     MAC_TX_DELAYED    = 0x00000010,
     MAC_TX_CONFIG     = 0x00000020,
+    MAC_RX_ABORT      = 0x00000040,
 };
 
 /*!
@@ -672,16 +681,46 @@
  */
 static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen );
 
+/*!
+ * \brief Counts the number of bits in a mask.
+ *
+ * \param [IN] mask A mask from which the function counts the active bits.
+ * \param [IN] nbBits The number of bits to check.
+ *
+ * \retval Number of enabled bits in the mask.
+ */
+static uint8_t CountBits( uint16_t mask, uint8_t nbBits );
+
 #if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
 /*!
  * \brief Counts the number of enabled 125 kHz channels in the channel mask.
  *        This function can only be applied to US915 band.
  *
- * \param channelsMask Pointer to the first element of the channel mask
+ * \param [IN] channelsMask Pointer to the first element of the channel mask
  *
  * \retval Number of enabled channels in the channel mask
  */
 static uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask );
+
+#if defined( USE_BAND_915_HYBRID )
+/*!
+ * \brief Validates the correctness of the channel mask for US915, hybrid mode.
+ *
+ * \param [IN] mask Block definition to set.
+ * \param [OUT] channelsMask Pointer to the first element of the channel mask
+ */
+static void ReenableChannels( uint16_t mask, uint16_t* channelMask );
+
+/*!
+ * \brief Validates the correctness of the channel mask for US915, hybrid mode.
+ *
+ * \param [IN] channelsMask Pointer to the first element of the channel mask
+ *
+ * \retval [true: channel mask correct, false: channel mask not correct]
+ */
+static bool ValidateChannelMask( uint16_t* channelMask );
+#endif
+
 #endif
 
 /*!
@@ -764,6 +803,13 @@
  */
 static LoRaMacStatus_t ScheduleTx( void );
 
+/*
+ * \brief Calculates the back-off time for the band of a channel.
+ *
+ * \param [IN] channel     The last Tx channel index
+ */
+static void CalculateBackOff( uint8_t channel );
+
 /*!
  * \brief LoRaMAC layer prepared frame buffer transmission with channel specification
  *
@@ -789,19 +835,12 @@
         OnRxWindow2TimerEvent( );
     }
 
-    // Update Band Time OFF
-    Bands[Channels[Channel].Band].LastTxDoneTime = curTime;
-    if( DutyCycleOn == true )
-    {
-        Bands[Channels[Channel].Band].TimeOff = TxTimeOnAir * Bands[Channels[Channel].Band].DCycle - TxTimeOnAir;
-    }
-    else
-    {
-        Bands[Channels[Channel].Band].TimeOff = 0;
-    }
-    // Update Aggregated Time OFF
+    // Store last Tx channel
+    LastTxChannel = Channel;
+    // Update last tx done time for the current channel
+    Bands[Channels[LastTxChannel].Band].LastTxDoneTime = curTime;
+    // Update Aggregated last tx done time
     AggregatedLastTxDoneTime = curTime;
-    AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir );
 
     if( IsRxWindowsEnabled == true )
     {
@@ -840,7 +879,7 @@
 
 static void PrepareRxDoneAbort( void )
 {
-    LoRaMacState &= ~MAC_TX_RUNNING;
+    LoRaMacState |= MAC_RX_ABORT;
 
     if( NodeAckRequested )
     {
@@ -864,6 +903,7 @@
 {
     LoRaMacHeader_t macHdr;
     LoRaMacFrameCtrl_t fCtrl;
+    bool skipIndication = false;
 
     uint8_t pktHeaderLen = 0;
     uint32_t address = 0;
@@ -1061,6 +1101,15 @@
                     }
                 }
 
+                // Check for a the maximum allowed counter difference
+                if( sequenceCounterDiff >= MAX_FCNT_GAP )
+                {
+                    McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS;
+                    McpsIndication.DownLinkCounter = downLinkCounter;
+                    PrepareRxDoneAbort( );
+                    return;
+                }
+
                 if( isMicOk == true )
                 {
                     McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
@@ -1095,19 +1144,27 @@
                         {
                             SrvAckRequested = true;
                             McpsIndication.McpsIndication = MCPS_CONFIRMED;
+
+                            if( ( DownLinkCounter == downLinkCounter ) &&
+                                ( DownLinkCounter != 0 ) )
+                            {
+                                // Duplicated confirmed downlink. Skip indication.
+                                skipIndication = true;
+                            }
                         }
                         else
                         {
                             SrvAckRequested = false;
                             McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
-                        }
-                        if( ( DownLinkCounter == downLinkCounter ) &&
-                            ( DownLinkCounter != 0 ) )
-                        {
-                            McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED;
-                            McpsIndication.DownLinkCounter = downLinkCounter;
-                            PrepareRxDoneAbort( );
-                            return;
+
+                            if( ( DownLinkCounter == downLinkCounter ) &&
+                                ( DownLinkCounter != 0 ) )
+                            {
+                                McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED;
+                                McpsIndication.DownLinkCounter = downLinkCounter;
+                                PrepareRxDoneAbort( );
+                                return;
+                            }
                         }
                         DownLinkCounter = downLinkCounter;
                     }
@@ -1169,12 +1226,18 @@
                                                    downLinkCounter,
                                                    LoRaMacRxPayload );
 
-                            McpsIndication.Buffer = LoRaMacRxPayload;
-                            McpsIndication.BufferSize = frameLen;
-                            McpsIndication.RxData = true;
+                            if( skipIndication == false )
+                            {
+                                McpsIndication.Buffer = LoRaMacRxPayload;
+                                McpsIndication.BufferSize = frameLen;
+                                McpsIndication.RxData = true;
+                            }
                         }
                     }
-                    LoRaMacFlags.Bits.McpsInd = 1;
+                    if( skipIndication == false )
+                    {
+                        LoRaMacFlags.Bits.McpsInd = 1;
+                    }
                 }
                 else
                 {
@@ -1281,6 +1344,12 @@
 
     if( LoRaMacFlags.Bits.MacDone == 1 )
     {
+        if( ( LoRaMacState & MAC_RX_ABORT ) == MAC_RX_ABORT )
+        {
+            LoRaMacState &= ~MAC_RX_ABORT;
+            LoRaMacState &= ~MAC_TX_RUNNING;
+        }
+
         if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) || ( ( LoRaMacFlags.Bits.McpsReq == 1 ) ) )
         {
             if( ( McpsConfirm.Status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) ||
@@ -1357,7 +1426,7 @@
 
                 if( ( AckTimeoutRetriesCounter % 2 ) == 1 )
                 {
-                    ChannelsDatarate = MAX( ChannelsDatarate - 1, LORAMAC_MIN_DATARATE );
+                    ChannelsDatarate = MAX( ChannelsDatarate - 1, LORAMAC_TX_MIN_DATARATE );
                 }
                 LoRaMacFlags.Bits.MacDone = 0;
                 // Sends the same frame again
@@ -1378,12 +1447,7 @@
                 ChannelsMask[5] = 0x0000;
 #elif defined( USE_BAND_915_HYBRID )
                 // Re-enable default channels
-                ChannelsMask[0] = 0x00FF;
-                ChannelsMask[1] = 0x0000;
-                ChannelsMask[2] = 0x0000;
-                ChannelsMask[3] = 0x0000;
-                ChannelsMask[4] = 0x0001;
-                ChannelsMask[5] = 0x0000;
+                ReenableChannels( ChannelsMask[4], ChannelsMask );
 #else
     #error "Please define a frequency band in the compiler options."
 #endif
@@ -1464,13 +1528,18 @@
     }
 
     // For higher datarates, we increase the number of symbols generating a Rx Timeout
-    if( datarate >= DR_3 )
-    { // DR_6, DR_5, DR_4, DR_3
+    if( ( datarate == DR_3 ) || ( datarate == DR_4 ) )
+    { // DR_4, DR_3
         symbTimeout = 8;
     }
-    if( datarate == DR_6 )
+    else if( datarate == DR_5 )
+    {
+        symbTimeout = 10;
+    }
+    else if( datarate == DR_6 )
     {// LoRa 250 kHz
         bandwidth  = 1;
+        symbTimeout = 14;
     }
     RxWindowSetup( Channels[Channel].Frequency, datarate, bandwidth, symbTimeout, false );
 #elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
@@ -1480,9 +1549,35 @@
         datarate = DR_0;
     }
     // For higher datarates, we increase the number of symbols generating a Rx Timeout
-    if( datarate > DR_0 )
-    { // DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13
-        symbTimeout = 8;
+    switch( datarate )
+    {
+        case DR_0:       // SF10 - BW125
+            symbTimeout = 5;
+            break;
+
+        case DR_1:       // SF9  - BW125
+        case DR_2:       // SF8  - BW125
+        case DR_8:       // SF12 - BW500
+        case DR_9:       // SF11 - BW500
+        case DR_10:      // SF10 - BW500
+            symbTimeout = 8;
+            break;
+
+        case DR_3:       // SF7  - BW125
+        case DR_11:      // SF9  - BW500
+            symbTimeout = 10;
+            break;
+
+        case DR_4:       // SF8  - BW500
+        case DR_12:      // SF8  - BW500
+            symbTimeout = 14;
+            break;
+
+        case DR_13:      // SF7  - BW500
+            symbTimeout = 16;
+            break;
+        default:
+            break;
     }
     if( datarate >= DR_4 )
     {// LoRa 500 kHz
@@ -1504,19 +1599,50 @@
 
 #if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
     // For higher datarates, we increase the number of symbols generating a Rx Timeout
-    if( Rx2Channel.Datarate >= DR_3 )
-    { // DR_6, DR_5, DR_4, DR_3
+    if( ( Rx2Channel.Datarate == DR_3 ) || ( Rx2Channel.Datarate == DR_4 ) )
+    { // DR_4, DR_3
         symbTimeout = 8;
     }
-    if( Rx2Channel.Datarate == DR_6 )
+    else if( Rx2Channel.Datarate == DR_5 )
+    {
+        symbTimeout = 10;
+    }
+    else if( Rx2Channel.Datarate == DR_6 )
     {// LoRa 250 kHz
         bandwidth  = 1;
+        symbTimeout = 14;
     }
 #elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
     // For higher datarates, we increase the number of symbols generating a Rx Timeout
-    if( Rx2Channel.Datarate > DR_0 )
-    { // DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13
-        symbTimeout = 8;
+    switch( Rx2Channel.Datarate )
+    {
+        case DR_0:       // SF10 - BW125
+            symbTimeout = 5;
+            break;
+
+        case DR_1:       // SF9  - BW125
+        case DR_2:       // SF8  - BW125
+        case DR_8:       // SF12 - BW500
+        case DR_9:       // SF11 - BW500
+        case DR_10:      // SF10 - BW500
+            symbTimeout = 8;
+            break;
+
+        case DR_3:       // SF7  - BW125
+        case DR_11:      // SF9  - BW500
+            symbTimeout = 10;
+            break;
+
+        case DR_4:       // SF8  - BW500
+        case DR_12:      // SF8  - BW500
+            symbTimeout = 14;
+            break;
+
+        case DR_13:      // SF7  - BW500
+            symbTimeout = 16;
+            break;
+        default:
+            break;
     }
     if( Rx2Channel.Datarate >= DR_4 )
     {// LoRa 500 kHz
@@ -1555,7 +1681,6 @@
     uint8_t nbEnabledChannels = 0;
     uint8_t delayTx = 0;
     uint8_t enabledChannels[LORA_MAX_NB_CHANNELS];
-    TimerTime_t curTime = TimerGetCurrentTime( );
     TimerTime_t nextTxDelay = ( TimerTime_t )( -1 );
 
     memset1( enabledChannels, 0, LORA_MAX_NB_CHANNELS );
@@ -1570,16 +1695,7 @@
         ChannelsMaskRemaining[4] = ChannelsMask[4];
     }
 #else
-    uint8_t chanCnt = 0;
-    for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ )
-    {
-        if( ChannelsMask[k] != 0 )
-        {
-            chanCnt++;
-            break;
-        }
-    }
-    if( chanCnt == 0 )
+    if( CountBits( ChannelsMask[0], 16 ) == 0 )
     {
         // Re-enable default channels, if no channel is enabled
         ChannelsMask[0] = ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
@@ -1587,7 +1703,7 @@
 #endif
 
     // Update Aggregated duty cycle
-    if( AggregatedTimeOff < ( curTime - AggregatedLastTxDoneTime ) )
+    if( AggregatedTimeOff <= TimerGetElapsedTime( AggregatedLastTxDoneTime ) )
     {
         AggregatedTimeOff = 0;
 
@@ -1596,14 +1712,14 @@
         {
             if( DutyCycleOn == true )
             {
-                if( Bands[i].TimeOff < ( curTime - Bands[i].LastTxDoneTime ) )
+                if( Bands[i].TimeOff <= TimerGetElapsedTime( Bands[i].LastTxDoneTime ) )
                 {
                     Bands[i].TimeOff = 0;
                 }
                 if( Bands[i].TimeOff != 0 )
                 {
                     nextTxDelay = MIN( Bands[i].TimeOff -
-                                       ( curTime - Bands[i].LastTxDoneTime ),
+                                       TimerGetElapsedTime( Bands[i].LastTxDoneTime ),
                                        nextTxDelay );
                 }
             }
@@ -1629,6 +1745,15 @@
                     { // Check if the channel is enabled
                         continue;
                     }
+#if defined( USE_BAND_868 ) || defined( USE_BAND_433 ) || defined( USE_BAND_780 )
+                    if( IsLoRaMacNetworkJoined == false )
+                    {
+                        if( ( JOIN_CHANNELS & ( 1 << j ) ) == 0 )
+                        {
+                            continue;
+                        }
+                    }
+#endif
                     if( ( ( Channels[i + j].DrRange.Fields.Min <= ChannelsDatarate ) &&
                           ( ChannelsDatarate <= Channels[i + j].DrRange.Fields.Max ) ) == false )
                     { // Check if the current channel selection supports the given datarate
@@ -1647,7 +1772,7 @@
     else
     {
         delayTx++;
-        nextTxDelay = AggregatedTimeOff - ( curTime - AggregatedLastTxDoneTime );
+        nextTxDelay = AggregatedTimeOff - TimerGetElapsedTime( AggregatedLastTxDoneTime );
     }
 
     if( nbEnabledChannels > 0 )
@@ -1722,11 +1847,11 @@
 
         if( RepeaterSupport == true )
         {
-            Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateRepeater[datarate] );
+            Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateRepeater[datarate] + LORA_MAC_FRMPAYLOAD_OVERHEAD );
         }
         else
         {
-            Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarate[datarate] );
+            Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarate[datarate] + LORA_MAC_FRMPAYLOAD_OVERHEAD );
         }
 
         if( rxContinuous == false )
@@ -1766,6 +1891,20 @@
     return false;
 }
 
+static uint8_t CountBits( uint16_t mask, uint8_t nbBits )
+{
+    uint8_t nbActiveBits = 0;
+
+    for( uint8_t j = 0; j < nbBits; j++ )
+    {
+        if( ( mask & ( 1 << j ) ) == ( 1 << j ) )
+        {
+            nbActiveBits++;
+        }
+    }
+    return nbActiveBits;
+}
+
 #if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
 static uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask )
 {
@@ -1773,17 +1912,75 @@
 
     for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS - 8; i += 16, k++ )
     {
-        for( uint8_t j = 0; j < 16; j++ )
-        {// Verify if the channel is active
-            if( ( channelsMask[k] & ( 1 << j ) ) == ( 1 << j ) )
-            {
-                nb125kHzChannels++;
-            }
-        }
+        nb125kHzChannels += CountBits( channelsMask[k], 16 );
     }
 
     return nb125kHzChannels;
 }
+
+#if defined( USE_BAND_915_HYBRID )
+static void ReenableChannels( uint16_t mask, uint16_t* channelMask )
+{
+    uint16_t blockMask = mask;
+
+    for( uint8_t i = 0, j = 0; i < 4; i++, j += 2 )
+    {
+        channelMask[i] = 0;
+        if( ( blockMask & ( 1 << j ) ) != 0 )
+        {
+            channelMask[i] |= 0x00FF;
+        }
+        if( ( blockMask & ( 1 << ( j + 1 ) ) ) != 0 )
+        {
+            channelMask[i] |= 0xFF00;
+        }
+    }
+    channelMask[4] = blockMask;
+    channelMask[5] = 0x0000;
+}
+
+static bool ValidateChannelMask( uint16_t* channelMask )
+{
+    bool chanMaskState = false;
+    uint16_t block1 = 0;
+    uint16_t block2 = 0;
+    uint8_t index = 0;
+
+    for( uint8_t i = 0; i < 4; i++ )
+    {
+        block1 = channelMask[i] & 0x00FF;
+        block2 = channelMask[i] & 0xFF00;
+
+        if( ( CountBits( block1, 16 ) > 5 ) && ( chanMaskState == false ) )
+        {
+            channelMask[i] &= block1;
+            channelMask[4] = 1 << ( i * 2 );
+            chanMaskState = true;
+            index = i;
+        }
+        else if( ( CountBits( block2, 16 ) > 5 ) && ( chanMaskState == false ) )
+        {
+            channelMask[i] &= block2;
+            channelMask[4] = 1 << ( i * 2 + 1 );
+            chanMaskState = true;
+            index = i;
+        }
+    }
+
+    // Do only change the channel mask, if we have found a valid block.
+    if( chanMaskState == true )
+    {
+        for( uint8_t i = 0; i < 4; i++ )
+        {
+            if( i != index )
+            {
+                channelMask[i] = 0;
+            }
+        }
+    }
+    return chanMaskState;
+}
+#endif
 #endif
 
 static int8_t LimitTxPower( int8_t txPower )
@@ -1838,7 +2035,7 @@
 
     if( adrEnabled == true )
     {
-        if( datarate == LORAMAC_MIN_DATARATE )
+        if( datarate == LORAMAC_TX_MIN_DATARATE )
         {
             AdrAckCounter = 0;
             adrAckReq = false;
@@ -1858,11 +2055,11 @@
                 if( ( ( AdrAckCounter - ADR_ACK_DELAY ) % ADR_ACK_LIMIT ) == 0 )
                 {
 #if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
-                    if( datarate > LORAMAC_MIN_DATARATE )
+                    if( datarate > LORAMAC_TX_MIN_DATARATE )
                     {
                         datarate--;
                     }
-                    if( datarate == LORAMAC_MIN_DATARATE )
+                    if( datarate == LORAMAC_TX_MIN_DATARATE )
                     {
                         if( updateChannelMask == true )
                         {
@@ -1872,15 +2069,15 @@
                         }
                     }
 #elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-                    if( ( datarate > LORAMAC_MIN_DATARATE ) && ( datarate == DR_8 ) )
+                    if( ( datarate > LORAMAC_TX_MIN_DATARATE ) && ( datarate == DR_8 ) )
                     {
                         datarate = DR_4;
                     }
-                    else if( datarate > LORAMAC_MIN_DATARATE )
+                    else if( datarate > LORAMAC_TX_MIN_DATARATE )
                     {
                         datarate--;
                     }
-                    if( datarate == LORAMAC_MIN_DATARATE )
+                    if( datarate == LORAMAC_TX_MIN_DATARATE )
                     {
                         if( updateChannelMask == true )
                         {
@@ -1894,12 +2091,7 @@
                             ChannelsMask[5] = 0x0000;
 #else // defined( USE_BAND_915_HYBRID )
                             // Re-enable default channels
-                            ChannelsMask[0] = 0x00FF;
-                            ChannelsMask[1] = 0x0000;
-                            ChannelsMask[2] = 0x0000;
-                            ChannelsMask[3] = 0x0000;
-                            ChannelsMask[4] = 0x0001;
-                            ChannelsMask[5] = 0x0000;
+                            ReenableChannels( ChannelsMask[4], ChannelsMask );
 #endif
                         }
                     }
@@ -2124,11 +2316,18 @@
                         {
                             status &= 0xFE; // Channel mask KO
                         }
+
+#if defined( USE_BAND_915_HYBRID )
+                        if( ValidateChannelMask( channelsMask ) == false )
+                        {
+                            status &= 0xFE; // Channel mask KO
+                        }
+#endif
                     }
 #else
     #error "Please define a frequency band in the compiler options."
 #endif
-                    if( ValueInRange( datarate, LORAMAC_MIN_DATARATE, LORAMAC_MAX_DATARATE ) == false )
+                    if( ValueInRange( datarate, LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) == false )
                     {
                         status &= 0xFD; // Datarate KO
                     }
@@ -2144,21 +2343,14 @@
                     {
                         ChannelsDatarate = datarate;
                         ChannelsTxPower = txPower;
-#if defined( USE_BAND_915_HYBRID )
-                        ChannelsMask[0] = channelsMask[0] & 0x00FF;
-                        ChannelsMask[1] = channelsMask[1] & 0x0000;
-                        ChannelsMask[2] = channelsMask[2] & 0x0000;
-                        ChannelsMask[3] = channelsMask[3] & 0x0000;
-                        ChannelsMask[4] = channelsMask[4] & 0x0001;
-                        ChannelsMask[5] = channelsMask[5] & 0x0000;
-#else
+
                         ChannelsMask[0] = channelsMask[0];
                         ChannelsMask[1] = channelsMask[1];
                         ChannelsMask[2] = channelsMask[2];
                         ChannelsMask[3] = channelsMask[3];
                         ChannelsMask[4] = channelsMask[4];
                         ChannelsMask[5] = channelsMask[5];
-#endif
+
                         ChannelsNbRep = nbRep;
                     }
                     AddMacCommand( MOTE_MAC_LINK_ADR_ANS, status, 0 );
@@ -2190,7 +2382,7 @@
                         status &= 0xFE; // Channel frequency KO
                     }
 
-                    if( ValueInRange( datarate, LORAMAC_MIN_DATARATE, LORAMAC_MAX_DATARATE ) == false )
+                    if( ValueInRange( datarate, LORAMAC_RX_MIN_DATARATE, LORAMAC_RX_MAX_DATARATE ) == false )
                     {
                         status &= 0xFD; // Datarate KO
                     }
@@ -2358,6 +2550,8 @@
         AggregatedTimeOff = 0;
     }
 
+    CalculateBackOff( LastTxChannel );
+
     // Select channel
     while( SetNextChannel( &dutyCycleTimeOff ) == false )
     {
@@ -2387,6 +2581,30 @@
     }
 }
 
+static void CalculateBackOff( uint8_t channel )
+{
+    uint16_t dutyCycle = Bands[Channels[channel].Band].DCycle;
+
+    if( IsLoRaMacNetworkJoined == false )
+    {
+#if defined( USE_BAND_868 ) || defined( USE_BAND_433 ) || defined( USE_BAND_780 )
+        dutyCycle = JOIN_DC;
+#endif
+    }
+
+    // Update Band Time OFF
+    if( DutyCycleOn == true )
+    {
+        Bands[Channels[channel].Band].TimeOff = TxTimeOnAir * dutyCycle - TxTimeOnAir;
+    }
+    else
+    {
+        Bands[Channels[channel].Band].TimeOff = 0;
+    }
+    // Update Aggregated Time OFF
+    AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir );
+}
+
 LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize )
 {
     uint16_t i;
@@ -2537,15 +2755,16 @@
 LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel )
 {
     int8_t datarate = Datarates[ChannelsDatarate];
+    int8_t txPowerIndex = 0;
     int8_t txPower = 0;
 
-    ChannelsTxPower = LimitTxPower( ChannelsTxPower );
-    txPower = TxPowers[ChannelsTxPower];
+    txPowerIndex = LimitTxPower( ChannelsTxPower );
+    txPower = TxPowers[txPowerIndex];
 
     MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
     McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
     McpsConfirm.Datarate = ChannelsDatarate;
-    McpsConfirm.TxPower = ChannelsTxPower;
+    McpsConfirm.TxPower = txPowerIndex;
 
     Radio.SetChannel( channel.Frequency );
 
@@ -2876,6 +3095,11 @@
             mibGet->Param.JoinAcceptDelay2 = JoinAcceptDelay2;
             break;
         }
+        case MIB_CHANNELS_DEFAULT_DATARATE:
+        {
+            mibGet->Param.ChannelsDefaultDatarate = ChannelsDefaultDatarate;
+            break;
+        }
         case MIB_CHANNELS_DATARATE:
         {
             mibGet->Param.ChannelsDatarate = ChannelsDatarate;
@@ -3015,19 +3239,32 @@
             if( mibSet->Param.ChannelsMask )
             {
 #if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
-                if( ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) < 6 ) &&
-                    ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) > 0 ) )
+                bool chanMaskState = true;
+
+#if defined( USE_BAND_915_HYBRID )
+                chanMaskState = ValidateChannelMask( mibSet->Param.ChannelsMask );
+#endif
+                if( chanMaskState == true )
                 {
-                    status = LORAMAC_STATUS_PARAMETER_INVALID;
+                    if( ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) < 6 ) &&
+                        ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) > 0 ) )
+                    {
+                        status = LORAMAC_STATUS_PARAMETER_INVALID;
+                    }
+                    else
+                    {
+                        memcpy1( ( uint8_t* ) ChannelsMask,
+                                 ( uint8_t* ) mibSet->Param.ChannelsMask, sizeof( ChannelsMask ) );
+                        for ( uint8_t i = 0; i < sizeof( ChannelsMask ) / 2; i++ )
+                        {
+                            // Disable channels which are no longer available
+                            ChannelsMaskRemaining[i] &= ChannelsMask[i];
+                        }
+                    }
                 }
                 else
                 {
-                    memcpy1( ( uint8_t* ) ChannelsMask,
-                             ( uint8_t* ) mibSet->Param.ChannelsMask, sizeof( ChannelsMask ) );
-                    for ( uint8_t i = 0; i < sizeof( ChannelsMask ) / 2; i++ )
-                    {
-                        ChannelsMaskRemaining[i] &= ChannelsMask[i];
-                    }
+                    status = LORAMAC_STATUS_PARAMETER_INVALID;
                 }
 #else
                 memcpy1( ( uint8_t* ) ChannelsMask,
@@ -3078,10 +3315,23 @@
             JoinAcceptDelay2 = mibSet->Param.JoinAcceptDelay2;
             break;
         }
+        case MIB_CHANNELS_DEFAULT_DATARATE:
+        {
+            if( ValueInRange( mibSet->Param.ChannelsDefaultDatarate,
+                              LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) )
+            {
+                ChannelsDefaultDatarate = mibSet->Param.ChannelsDefaultDatarate;
+            }
+            else
+            {
+                status = LORAMAC_STATUS_PARAMETER_INVALID;
+            }
+            break;
+        }
         case MIB_CHANNELS_DATARATE:
         {
             if( ValueInRange( mibSet->Param.ChannelsDatarate,
-                              LORAMAC_MIN_DATARATE, LORAMAC_MAX_DATARATE ) )
+                              LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) )
             {
                 ChannelsDatarate = mibSet->Param.ChannelsDatarate;
             }
@@ -3116,6 +3366,16 @@
             }
             break;
         }
+        case MIB_UPLINK_COUNTER:
+        {
+            UpLinkCounter = mibSet->Param.UpLinkCounter;
+            break;
+        }
+        case MIB_DOWNLINK_COUNTER:
+        {
+            DownLinkCounter = mibSet->Param.DownLinkCounter;
+            break;
+        }
         default:
             status = LORAMAC_STATUS_SERVICE_UNKNOWN;
             break;
@@ -3148,10 +3408,10 @@
     }
     // Validate the datarate
     if( ( params.DrRange.Fields.Min > params.DrRange.Fields.Max ) ||
-        ( ValueInRange( params.DrRange.Fields.Min, LORAMAC_MIN_DATARATE,
-                        LORAMAC_MAX_DATARATE ) == false ) ||
-        ( ValueInRange( params.DrRange.Fields.Max, LORAMAC_MIN_DATARATE,
-                        LORAMAC_MAX_DATARATE ) == false ) )
+        ( ValueInRange( params.DrRange.Fields.Min, LORAMAC_TX_MIN_DATARATE,
+                        LORAMAC_TX_MAX_DATARATE ) == false ) ||
+        ( ValueInRange( params.DrRange.Fields.Max, LORAMAC_TX_MIN_DATARATE,
+                        LORAMAC_TX_MAX_DATARATE ) == false ) )
     {
         datarateInvalid = true;
     }
@@ -3164,11 +3424,11 @@
             frequencyInvalid = true;
         }
 
-        if( params.DrRange.Fields.Min > LORAMAC_DEFAULT_DATARATE )
+        if( params.DrRange.Fields.Min > ChannelsDefaultDatarate )
         {
             datarateInvalid = true;
         }
-        if( ValueInRange( params.DrRange.Fields.Max, DR_5, LORAMAC_MAX_DATARATE ) == false )
+        if( ValueInRange( params.DrRange.Fields.Max, DR_5, LORAMAC_TX_MAX_DATARATE ) == false )
         {
             datarateInvalid = true;
         }
@@ -3243,7 +3503,7 @@
         }
     }
 
-    if( id < 3 )
+    if( ( id < 3 ) || ( id >= LORA_MAX_NB_CHANNELS ) )
     {
         return LORAMAC_STATUS_PARAMETER_INVALID;
     }
@@ -3251,7 +3511,7 @@
     {
         // Remove the channel from the list of channels
         Channels[id] = ( ChannelParams_t ){ 0, { 0 }, 0 };
-        
+
         // Disable the channel as it doesn't exist anymore
         if( DisableChannelInMask( id, ChannelsMask ) == false )
         {
@@ -3501,7 +3761,7 @@
     {
         if( AdrCtrlOn == false )
         {
-            if( ValueInRange( datarate, LORAMAC_MIN_DATARATE, LORAMAC_MAX_DATARATE ) == true )
+            if( ValueInRange( datarate, LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) == true )
             {
                 ChannelsDatarate = datarate;
             }
--- a/LoRaMac.h	Mon Mar 14 09:09:54 2016 +0000
+++ b/LoRaMac.h	Fri May 13 14:51:50 2016 +0000
@@ -480,6 +480,9 @@
      * Byte-access to the bits
      */
     uint8_t Value;
+    /*!
+     * Structure containing single access to bits
+     */
     struct sCtrlBits
     {
         /*!
@@ -541,6 +544,10 @@
      */
     LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED,
     /*!
+     * The node has lost MAX_FCNT_GAP or more frames.
+     */
+    LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS,
+    /*!
      * An address error occured
      */
     LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL,
@@ -559,6 +566,9 @@
      * Byte-access to the bits
      */
     uint8_t Value;
+    /*!
+     * Structure containing single access to bits
+     */
     struct sMacFlagBits
     {
         /*!
@@ -910,7 +920,7 @@
 }MlmeReqJoin_t;
 
 /*!
- *
+ * LoRaMAC MLME-Request structure
  */
 typedef struct sMlmeReq
 {
@@ -985,9 +995,10 @@
  * \ref MIB_JOIN_ACCEPT_DELAY_1      | YES | YES
  * \ref MIB_JOIN_ACCEPT_DELAY_2      | YES | YES
  * \ref MIB_CHANNELS_DATARATE        | YES | YES
+ * \ref MIB_CHANNELS_DEFAULT_DATARATE| YES | YES
  * \ref MIB_CHANNELS_TX_POWER        | YES | YES
- * \ref MIB_UPLINK_COUNTER           | YES | NO
- * \ref MIB_DOWNLINK_COUNTER         | YES | NO
+ * \ref MIB_UPLINK_COUNTER           | YES | YES
+ * \ref MIB_DOWNLINK_COUNTER         | YES | YES
  * \ref MIB_MULTICAST_CHANNEL        | YES | NO
  *
  * The following table provides links to the function implementations of the
@@ -1117,6 +1128,16 @@
      */
     MIB_JOIN_ACCEPT_DELAY_2,
     /*!
+     * Default Data rate of a channel
+     *
+     * LoRaWAN Specification V1.0, chapter 7
+     *
+     * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
+     *
+     * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13]
+     */
+    MIB_CHANNELS_DEFAULT_DATARATE,
+    /*!
      * Data rate of a channel
      *
      * LoRaWAN Specification V1.0, chapter 7
@@ -1276,6 +1297,12 @@
     /*!
      * Channels data rate
      *
+     * Related MIB type: \ref MIB_CHANNELS_DEFAULT_DATARATE
+     */
+    int8_t ChannelsDefaultDatarate;
+    /*!
+     * Channels data rate
+     *
      * Related MIB type: \ref MIB_CHANNELS_DATARATE
      */
     int8_t ChannelsDatarate;