Fork of Semtech LoRaWAN stack
Fork of LoRaWAN-lib by
Diff: LoRaMac.cpp
- Branch:
- v4.2.0
- Revision:
- 8:4816c8449bf2
- Parent:
- 6:d7a34ded7c87
- Child:
- 9:ad93de20d720
--- a/LoRaMac.cpp Wed May 18 11:19:24 2016 +0000
+++ b/LoRaMac.cpp Mon Aug 22 20:30:06 2016 -0400
@@ -317,7 +317,7 @@
/*!
* Up/Down link data rates offset definition
*/
-const int8_t datarateOffsets[16][4] =
+const uint8_t datarateOffsets[16][4] =
{
{ DR_10, DR_9 , DR_8 , DR_8 }, // DR_0
{ DR_11, DR_10, DR_9 , DR_8 }, // DR_1
@@ -369,6 +369,39 @@
* Contains the channels which remain to be applied.
*/
static uint16_t ChannelsMaskRemaining[6];
+
+#if defined( USE_BAND_915 )
+/*!
+ * Last join request sub-band
+ */
+static int8_t LastJoinBlock;
+
+/*!
+ * Next join sub-band block
+ */
+static uint8_t NextJoinBlock;
+
+/*!
+ * Mask of 125 KHz sub-bands not used for join transmit
+ */
+static uint8_t JoinBlocksRemaining;
+
+/*!
+ * Mask of 500 KHz sub-bands not used for join transmit
+ */
+static uint8_t Join500KHzRemaining;
+
+#ifndef JOIN_BLOCK_ORDER
+#define JOIN_BLOCK_ORDER { -1, -1, -1, -1, -1, -1, -1, -1 }
+#endif
+
+/*!
+ * join sub-band block order
+ */
+static int8_t JoinBlock[8] = JOIN_BLOCK_ORDER;
+
+#endif
+
#else
#error "Please define a frequency band in the compiler options."
#endif
@@ -560,6 +593,50 @@
*/
LoRaMacFlags_t LoRaMacFlags;
+/* LoRaWAN 1.1 Section 9 Retransmission back-off
+ *
+ * Applies to uplink frames that:
+ * - Require acknowledgement or answer, and are
+ * retransmitted by the device if the answer is not received
+ * and
+ * - Can be triggered by an external event causing synchronization
+ *
+ * Transmission duty-cylce for such messages shall respect the local regulations
+ * and the following limits
+ * - Aggregated during the first hour 36 seconds
+ * - Aggregated during the next 10 hours 36 seconds
+ * - After the first 11 hours aggregated over 24h 8.7seconds
+ */
+#ifndef JOIN_RETRANSMISSION_DCYCLE1
+#define JOIN_RETRANSMISSION_DCYCLE1 { 3600, 1000000 } // (period, onAirTimeMax)
+#endif
+
+#ifndef JOIN_RETRANSMISSION_DCYCLE2
+#define JOIN_RETRANSMISSION_DCYCLE2 { 10*3600, 36000000 } // (period, onAirTimeMax)
+#endif
+
+#ifndef JOIN_RETRANSMISSION_DCYCLE3
+#define JOIN_RETRANSMISSION_DCYCLE3 { 24*3600, 8700000 } // (period, onAirTimeMax)
+#endif
+
+LoRaMacRetransmissionDCycle_t JoinReTransmitDCycle[JOIN_NB_RETRANSMISSION_DCYCLES] =
+{
+ JOIN_RETRANSMISSION_DCYCLE1,
+ JOIN_RETRANSMISSION_DCYCLE2,
+ JOIN_RETRANSMISSION_DCYCLE3
+};
+
+/*!
+ * Uptime of the last sent join request
+ */
+static TimerTime_t LastJoinTxTime;
+
+/*!
+ * Aggregated join request time on air
+ */
+static TimerTime_t JoinAggTimeOnAir;
+
+
/*!
* \brief Function to be executed on Radio Tx Done event
*/
@@ -748,13 +825,15 @@
*
* \param [IN] adrEnabled Specify whether ADR is on or off
*
- * \param [IN] updateChannelMask Set to true, if the channel masks shall be updated
+ * \param [IN] isTx Set to true if called by transmit, set to false if a dry run
*
* \param [OUT] datarateOut Reports the datarate which will be used next
*
+ * \param [OUT] txpowerOut Reports the tx power which will be used next
+ *
* \retval Returns the state of ADR ack request
*/
-static bool AdrNextDr( bool adrEnabled, bool updateChannelMask, int8_t* datarateOut );
+static bool AdrNextDr( bool adrEnabled, bool isTx, int8_t* datarateOut, int8_t* txpowerOut );
/*!
* \brief Disables channel in a specified channel mask
@@ -842,6 +921,13 @@
// Update Aggregated last tx done time
AggregatedLastTxDoneTime = curTime;
+ // Update join tx done and time on air
+ if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) )
+ {
+ JoinAggTimeOnAir += TxTimeOnAir;
+ LastJoinTxTime = curTime;
+ }
+
if( IsRxWindowsEnabled == true )
{
TimerSetValue( &RxWindowTimer1, RxWindow1Delay );
@@ -1019,7 +1105,9 @@
#endif
MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
IsLoRaMacNetworkJoined = true;
- ChannelsDatarate = ChannelsDefaultDatarate;
+
+ // Do not change the datarate
+ // ChannelsDatarate = ChannelsDefaultDatarate;
}
else
{
@@ -1191,11 +1279,6 @@
}
}
- if( fCtrl.Bits.FOptsLen > 0 )
- {
- // Decode Options field MAC commands
- ProcessMacCommands( payload, 8, appPayloadStartIndex, snr );
- }
if( ( ( size - 4 ) - appPayloadStartIndex ) > 0 )
{
port = payload[appPayloadStartIndex++];
@@ -1205,19 +1288,32 @@
if( port == 0 )
{
- LoRaMacPayloadDecrypt( payload + appPayloadStartIndex,
- frameLen,
- nwkSKey,
- address,
- DOWN_LINK,
- downLinkCounter,
- LoRaMacRxPayload );
-
- // Decode frame payload MAC commands
- ProcessMacCommands( LoRaMacRxPayload, 0, frameLen, snr );
+ if( fCtrl.Bits.FOptsLen == 0 )
+ {
+ LoRaMacPayloadDecrypt( payload + appPayloadStartIndex,
+ frameLen,
+ nwkSKey,
+ address,
+ DOWN_LINK,
+ downLinkCounter,
+ LoRaMacRxPayload );
+
+ // Decode frame payload MAC commands
+ ProcessMacCommands( LoRaMacRxPayload, 0, frameLen, snr );
+ }
+ else
+ {
+ skipIndication = true;
+ }
}
else
{
+ if( fCtrl.Bits.FOptsLen > 0 )
+ {
+ // Decode Options field MAC commands. Omit the fPort.
+ ProcessMacCommands( payload, 8, appPayloadStartIndex - 1, snr );
+ }
+
LoRaMacPayloadDecrypt( payload + appPayloadStartIndex,
frameLen,
appSKey,
@@ -1234,6 +1330,15 @@
}
}
}
+ else
+ {
+ if( fCtrl.Bits.FOptsLen > 0 )
+ {
+ // Decode Options field MAC commands
+ ProcessMacCommands( payload, 8, appPayloadStartIndex, snr );
+ }
+ }
+
if( skipIndication == false )
{
LoRaMacFlags.Bits.McpsInd = 1;
@@ -1676,6 +1781,91 @@
}
}
+#if defined( USE_BAND_915 )
+uint8_t GetNextJoinChannel( uint8_t* enabledChannels, uint8_t nbEnabledChannels )
+{
+ int8_t channel = -1;
+ uint8_t block;
+ uint8_t i;
+
+ // Use 125KHz channel
+ if( ChannelsDatarate < DR_4 )
+ {
+ block = JoinBlock[NextJoinBlock];
+ NextJoinBlock = (NextJoinBlock + 1) % 8;
+
+ // If next block is greater than max block then randomly select the next block
+ if( block >= 8 )
+ block = randr(0, 7);
+
+ // Start search for next join channel at the selected block
+ for(i = 0; ( i < 8 ) && ( channel == -1 ); i++)
+ {
+ uint8_t curBlock = (block + i) % 8;
+ uint8_t chMask;
+
+ // Cycle through all blocks before using a previously used block
+ if( ( JoinBlocksRemaining == 0 ) || ( ( JoinBlocksRemaining & ( 1 << curBlock ) ) != 0) )
+ {
+ chMask = ( ChannelsMaskRemaining[curBlock/2] >> ( curBlock & 1 ? 8 : 0 ) ) & 0xff;
+ if( chMask != 0)
+ {
+ channel = randr(0, 7);
+ for(uint8_t i = 0; ( i < 8 ); i++)
+ {
+ if( ( chMask & ( 1 << channel ) ) != 0 )
+ {
+ channel = channel + ( curBlock * 8 );
+ JoinBlocksRemaining &= ~(1 << curBlock);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // No next channel case should never happen since ScheduleTx has already
+ // checked for at least one enabled channel and if none re-enabled all channels.
+ // But if we find ourselves without a channel then randomly select one
+ if ( i >= 8 )
+ {
+ channel = randr( 0, 63 );
+ }
+ }
+ // Use 500 KHz channel
+ else
+ {
+ channel = randr( 0, 7 );
+ for(i = 0; i < 8; i++)
+ {
+ uint8_t curChannel = (channel + i) % 8;
+
+ if( ( Join500KHzRemaining == 0 ) || ( ( Join500KHzRemaining & ( 1 << curChannel ) ) != 0 ) )
+ {
+ channel = 64 + curChannel;
+ Join500KHzRemaining &= ~(1 << channel);
+ break;
+ }
+ }
+ }
+
+ for( i = 0; i < nbEnabledChannels; i++ )
+ {
+ if( enabledChannels[i] == channel )
+ break;
+ }
+
+ if( i == nbEnabledChannels )
+ {
+ channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+ }
+
+ LastJoinBlock = channel / 8;
+
+ return channel;
+}
+#endif
+
static bool SetNextChannel( TimerTime_t* time )
{
uint8_t nbEnabledChannels = 0;
@@ -1777,7 +1967,39 @@
if( nbEnabledChannels > 0 )
{
- Channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+#if defined( USE_BAND_915 )
+ if ( IsLoRaMacNetworkJoined == false )
+ {
+ Channel = GetNextJoinChannel(enabledChannels, nbEnabledChannels);
+ }
+ // Send first uplink on channel from same sub-band as the join
+ else if( ( UpLinkCounter == 1 ) && ( LastJoinBlock != -1 ) )
+ {
+ uint8_t i;
+ uint8_t blockFirstChannel = LastJoinBlock*8;
+
+ Channel = randr( blockFirstChannel, blockFirstChannel + 7 );
+
+ // Check channel is enabled
+ for( i = 0; i < nbEnabledChannels; i++ )
+ {
+ if( Channel == enabledChannels[i] )
+ break;
+ }
+
+ // If channel is not enabled fallback to selecting from the list of
+ // enabled channels
+ if (i == nbEnabledChannels)
+ {
+ Channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+ }
+ }
+ else
+#endif
+ {
+ Channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+ }
+
#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
if( Channel < ( LORA_MAX_NB_CHANNELS - 8 ) )
{
@@ -2028,82 +2250,75 @@
return true;
}
-static bool AdrNextDr( bool adrEnabled, bool updateChannelMask, int8_t* datarateOut )
+static bool AdrNextDr( bool adrEnabled, bool isTx, int8_t* datarateOut, int8_t* txpowerOut )
{
bool adrAckReq = false;
int8_t datarate = ChannelsDatarate;
+ int8_t txpower = ChannelsTxPower;
if( adrEnabled == true )
{
- if( datarate == LORAMAC_TX_MIN_DATARATE )
- {
- AdrAckCounter = 0;
- adrAckReq = false;
- }
- else
+ /* Request ADR Ack Request (including at lowest available datarate) */
+ adrAckReq = AdrAckCounter >= ADR_ACK_LIMIT ? true : false;
+
+ if( AdrAckCounter > (ADR_ACK_LIMIT + ADR_ACK_DELAY) )
{
- if( AdrAckCounter >= ADR_ACK_LIMIT )
- {
- adrAckReq = true;
- }
- else
- {
- adrAckReq = false;
- }
- if( AdrAckCounter >= ( ADR_ACK_LIMIT + ADR_ACK_DELAY ) )
- {
- if( ( ( AdrAckCounter - ADR_ACK_DELAY ) % ADR_ACK_LIMIT ) == 0 )
- {
+ // Step up power
+ txpower = LORAMAC_DEFAULT_TX_POWER;
+
+ // isTx=true when preparing to transmit the next frame
+ if(isTx == true)
+ AdrAckCounter = 0;
+
#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
- if( datarate > LORAMAC_TX_MIN_DATARATE )
- {
- datarate--;
- }
- if( datarate == LORAMAC_TX_MIN_DATARATE )
- {
- if( updateChannelMask == true )
- {
-
- // Re-enable default channels LC1, LC2, LC3
- ChannelsMask[0] = ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
- }
- }
+ if( datarate > LORAMAC_TX_MIN_DATARATE )
+ {
+ datarate--;
+ }
+ if( datarate == LORAMAC_TX_MIN_DATARATE )
+ {
+ if( isTx == true )
+ {
+ // Re-enable default channels LC1, LC2, LC3
+ ChannelsMask[0] = ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
+ }
+ }
#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
- if( ( datarate > LORAMAC_TX_MIN_DATARATE ) && ( datarate == DR_8 ) )
- {
- datarate = DR_4;
- }
- else if( datarate > LORAMAC_TX_MIN_DATARATE )
- {
- datarate--;
- }
- if( datarate == LORAMAC_TX_MIN_DATARATE )
- {
- if( updateChannelMask == true )
- {
+ if( datarate > LORAMAC_TX_MIN_DATARATE )
+ {
+ datarate--;
+ }
+ else if( isTx == true )
+ {
#if defined( USE_BAND_915 )
- // Re-enable default channels
- ChannelsMask[0] = 0xFFFF;
- ChannelsMask[1] = 0xFFFF;
- ChannelsMask[2] = 0xFFFF;
- ChannelsMask[3] = 0xFFFF;
- ChannelsMask[4] = 0x00FF;
- ChannelsMask[5] = 0x0000;
+ // Re-enable default channels
+ ChannelsMask[0] = 0xFFFF;
+ ChannelsMask[1] = 0xFFFF;
+ ChannelsMask[2] = 0xFFFF;
+ ChannelsMask[3] = 0xFFFF;
+ ChannelsMask[4] = 0x00FF;
+ ChannelsMask[5] = 0x0000;
#else // defined( USE_BAND_915_HYBRID )
- // Re-enable default channels
- ReenableChannels( ChannelsMask[4], ChannelsMask );
+ // Re-enable default channels
+ ChannelsMask[0] = 0x00FF;
+ ChannelsMask[1] = 0x0000;
+ ChannelsMask[2] = 0x0000;
+ ChannelsMask[3] = 0x0000;
+ ChannelsMask[4] = 0x0001;
+ ChannelsMask[5] = 0x0000;
#endif
- }
- }
+ }
#else
#error "Please define a frequency band in the compiler options."
#endif
- }
- }
}
}
- *datarateOut = datarate;
+ if(datarateOut != NULL)
+ *datarateOut = datarate;
+
+ if(txpowerOut != NULL)
+ *txpowerOut = txpower;
return adrAckReq;
}
@@ -2286,6 +2501,10 @@
}
}
}
+
+ // channel mask applied to 500 kHz channels
+ channelsMask[4] = chMask;
+ chMaskCntl = 4;
}
else if( chMaskCntl == 7 )
{
@@ -2294,13 +2513,18 @@
channelsMask[1] = 0x0000;
channelsMask[2] = 0x0000;
channelsMask[3] = 0x0000;
+
+ // channel mask applied to 500 kHz channels
+ channelsMask[4] = chMask;
+ chMaskCntl = 4;
}
else if( chMaskCntl == 5 )
{
// RFU
status &= 0xFE; // Channel mask KO
}
- else
+
+ if(status == 0xFF)
{
for( uint8_t i = 0; i < 16; i++ )
{
@@ -2660,7 +2884,7 @@
return LORAMAC_STATUS_NO_NETWORK_JOINED; // No network has been joined yet
}
- fCtrl->Bits.AdrAckReq = AdrNextDr( fCtrl->Bits.Adr, true, &ChannelsDatarate );
+ fCtrl->Bits.AdrAckReq = AdrNextDr( fCtrl->Bits.Adr, true, &ChannelsDatarate, &ChannelsTxPower );
if( ValidatePayloadLength( fBufferSize, ChannelsDatarate, MacCommandsBufferIndex ) == false )
{
@@ -2850,6 +3074,31 @@
IsLoRaMacNetworkJoined = false;
LoRaMacState = MAC_IDLE;
+ MulticastChannels = NULL;
+ IsUpLinkCounterFixed = false;
+ IsRxWindowsEnabled = true;
+ IsLoRaMacNetworkJoined = false;
+ AdrCtrlOn = false;
+ AdrAckCounter = 0;
+ NodeAckRequested = false;
+ SrvAckRequested = false;
+ MacCommandsInNextTx = false;
+ MacCommandsBufferIndex = 0;
+ AckTimeoutRetries = 1;
+ AckTimeoutRetriesCounter = 1;
+ AckTimeoutRetry = false;
+ TxTimeOnAir = 0;
+ RxSlot = 0;
+ //Rx2Channel = RX_WND_2_CHANNEL;
+ Rx1DrOffset = 0;
+ ChannelsTxPower = LORAMAC_DEFAULT_TX_POWER;
+ ChannelsDatarate = LORAMAC_DEFAULT_DATARATE;
+ ChannelsDefaultDatarate = LORAMAC_DEFAULT_DATARATE;
+ ChannelsNbRep = 1;
+ ChannelsNbRepCounter = 0;
+ MaxDCycle = 0;
+
+
#if defined( USE_BAND_433 )
ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
#elif defined( USE_BAND_780 )
@@ -2923,6 +3172,16 @@
JoinAcceptDelay1 = JOIN_ACCEPT_DELAY1;
JoinAcceptDelay2 = JOIN_ACCEPT_DELAY2;
+#if defined( USE_BAND_915 )
+ NextJoinBlock = 0;
+ LastJoinBlock = -1;
+ JoinBlocksRemaining = 0xff;
+ Join500KHzRemaining = 0xff;
+#endif
+
+ LastJoinTxTime = 0;
+ JoinAggTimeOnAir = 0;
+
TimerInit( &MacStateCheckTimer, OnMacStateCheckTimerEvent );
TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT );
@@ -2961,7 +3220,7 @@
return LORAMAC_STATUS_PARAMETER_INVALID;
}
- AdrNextDr( AdrCtrlOn, false, &datarate );
+ AdrNextDr( AdrCtrlOn, false, &datarate, NULL );
if( RepeaterSupport == true )
{
@@ -3176,6 +3435,14 @@
case MIB_NETWORK_JOINED:
{
IsLoRaMacNetworkJoined = mibSet->Param.IsNetworkJoined;
+#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+ if( IsLoRaMacNetworkJoined == false )
+ {
+ NextJoinBlock = 0;
+ JoinBlocksRemaining = 0xff;
+ Join500KHzRemaining = 0xff;
+ }
+#endif
break;
}
case MIB_ADR:
@@ -3628,10 +3895,23 @@
LoRaMacAppEui = mlmeRequest->Req.Join.AppEui;
LoRaMacAppKey = mlmeRequest->Req.Join.AppKey;
+ if( LoRaMacCalcJoinBackOff( ) != 0 )
+ {
+ return LORAMAC_STATUS_TX_DCYCLE_EXCEEDED;
+ }
+
macHdr.Value = 0;
macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ;
- IsLoRaMacNetworkJoined = false;
+#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+ if ( IsLoRaMacNetworkJoined == true )
+ {
+ NextJoinBlock = 0;
+ JoinBlocksRemaining = 0xff;
+ Join500KHzRemaining = 0xff;
+ IsLoRaMacNetworkJoined = false;
+ }
+#endif
#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
#if defined( USE_BAND_915 )
@@ -3774,6 +4054,67 @@
return status;
}
+TimerTime_t LoRaMacCalcJoinBackOff( )
+{
+ TimerTime_t timeOff = 0;
+ TimerTime_t uptime;
+ TimerTime_t currDCycleEndTime;
+ TimerTime_t prevDCycleEndTime;
+ TimerTime_t period;
+ TimerTime_t onAirTimeMax;
+ uint8_t dcycleNum;
+
+ // Check retransmit duty-cycle exists
+ if ( JOIN_NB_RETRANSMISSION_DCYCLES == 0 )
+ return 0;
+
+ uptime = TimerGetCurrentTime( ) / 1e6;
+
+ // Get uptime dutycycle
+ prevDCycleEndTime = 0;
+ currDCycleEndTime = 0;
+ for(dcycleNum = 0; dcycleNum < JOIN_NB_RETRANSMISSION_DCYCLES; dcycleNum++)
+ {
+ prevDCycleEndTime = currDCycleEndTime;
+ currDCycleEndTime += JoinReTransmitDCycle[dcycleNum].period;
+
+ bool isCurrentPeriod = uptime < currDCycleEndTime;
+
+ if( isCurrentPeriod || ( dcycleNum == ( JOIN_NB_RETRANSMISSION_DCYCLES-1 ) ) )
+ {
+ period = JoinReTransmitDCycle[dcycleNum].period;
+ onAirTimeMax = JoinReTransmitDCycle[dcycleNum].onAirTimeMax;
+ if( isCurrentPeriod == true )
+ break;
+ }
+ }
+
+ // Clear aggregate on air time if last join occured during a previous period
+ if( LastJoinTxTime < prevDCycleEndTime )
+ {
+ JoinAggTimeOnAir = 0;
+ }
+ // For last dutycyle, the current & previous period end time must be calculated
+ else if( dcycleNum == JOIN_NB_RETRANSMISSION_DCYCLES )
+ {
+ if( ( TimerGetElapsedTime( LastJoinTxTime )/1e6 ) >= period )
+ {
+ JoinAggTimeOnAir = 0;
+ }
+ }
+
+ if ( JoinAggTimeOnAir >= onAirTimeMax )
+ {
+ // current period elapsed time
+ TimerTime_t elapsedTime = ( uptime - prevDCycleEndTime ) % period;
+
+ // time off is remaining time until beginning of next period
+ timeOff = ( period - elapsedTime ) * 1e6;
+ }
+
+ return timeOff;
+}
+
void LoRaMacTestRxWindowsOn( bool enable )
{
IsRxWindowsEnabled = enable;


