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;