this is avaiable project
Fork of LoRaMacLib by
mac/LoRaMac.cpp
- Committer:
- pzheng
- Date:
- 2015-11-30
- Revision:
- 9:b3ddbad8c5e3
- Parent:
- 8:19a3b4a82c69
- Child:
- 11:29686c1ac910
File content as of revision 9:b3ddbad8c5e3:
/* / _____) _ | | ( (____ _____ ____ _| |_ _____ ____| |__ \____ \| ___ | (_ _) ___ |/ ___) _ \ _____) ) ____| | | || |_| ____( (___| | | | (______/|_____)_|_|_| \__)_____)\____)_| |_| (C)2013 Semtech Description: LoRa MAC layer implementation License: Revised BSD License, see LICENSE.TXT file include in the project Maintainer: Miguel Luis and Gregory Cristian */ #include "mbed.h" #include "board.h" #include "utilities.h" #include "sx1276-hal.h" #include "LoRaMac.h" #include "LoRaMacCrypto.h" /*! * Maximum PHY layer payload size */ #define LORAMAC_PHY_MAXPAYLOAD 64 /*! * Device IEEE EUI */ static uint8_t *LoRaMacDevEui; /*! * Application IEEE EUI */ static uint8_t *LoRaMacAppEui; /*! * AES encryption/decryption cipher application key */ static uint8_t *LoRaMacAppKey; /*! * AES encryption/decryption cipher network session key */ static uint8_t LoRaMacNwkSKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*! * AES encryption/decryption cipher application session key */ static uint8_t LoRaMacAppSKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*! * Device nonce is a random value extracted by issuing a sequence of RSSI * measurements */ static uint16_t LoRaMacDevNonce; /*! * Network ID ( 3 bytes ) */ static uint32_t LoRaMacNetID; /*! * Mote Address */ static uint32_t LoRaMacDevAddr; /*! * Mutlicast channels linked list */ static MulticastParams_t *MulticastChannels = NULL; /*! * Actual device class */ static DeviceClass_t LoRaMacDeviceClass; /*! * Indicates if the node is connected to a private or public network */ static bool PublicNetwork; /*! * Buffer containing the data to be sent or received. */ static uint8_t LoRaMacBuffer[LORAMAC_PHY_MAXPAYLOAD]; /*! * Length of packet in LoRaMacBuffer */ static uint16_t LoRaMacBufferPktLen = 0; /*! * Buffer containing the upper layer data. */ static uint8_t LoRaMacPayload[LORAMAC_PHY_MAXPAYLOAD]; static uint8_t LoRaMacRxPayload[LORAMAC_PHY_MAXPAYLOAD]; /*! * LoRaMAC frame counter. Each time a packet is sent the counter is incremented. * Only the 16 LSB bits are sent */ static uint32_t UpLinkCounter = 1; /*! * LoRaMAC frame counter. Each time a packet is received the counter is incremented. * Only the 16 LSB bits are received */ static uint32_t DownLinkCounter = 0; /*! * IsPacketCounterFixed enables the MIC field tests by fixing the * UpLinkCounter value */ static bool IsUpLinkCounterFixed = false; /*! * Used for replying the uplink * Added by pzheng 20151111 */ static bool UpLinkTestOn = false; /*! * Used for test purposes. Disables the opening of the reception windows. */ static bool IsRxWindowsEnabled = true; /*! * Indicates if the MAC layer has already joined a network. */ static bool IsLoRaMacNetworkJoined = false; /*! * LoRaMac ADR control status */ static bool AdrCtrlOn = false; /*! * Counts the number of missed ADR acknowledgements */ static uint32_t AdrAckCounter = 0; /*! * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates * if the nodes needs to manage the server acknowledgement. */ static bool NodeAckRequested = false; /*! * If the server has sent a FRAME_TYPE_DATA_CONFIRMED_DOWN this variable indicates * if the ACK bit must be set for the next transmission */ static bool SrvAckRequested = false; /*! * Indicates if the MAC layer wants to send MAC commands */ static bool MacCommandsInNextTx = false; /*! * Contains the current MacCommandsBuffer index */ static uint8_t MacCommandsBufferIndex = 0; /*! * Buffer containing the MAC layer commands */ static uint8_t MacCommandsBuffer[15]; /*! * Data rates table definition */ const uint8_t Datarates[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; /*! * Tx output powers table definition */ const int8_t TxPowers[] = { 20, 14, 11, 8, 5, 2 }; #if defined( USE_BAND_868 ) /*! * LoRaMac bands */ static Band_t Bands[LORA_MAX_NB_BANDS] = { BAND0, BAND1, BAND2, BAND3, BAND4, }; /*! * LoRaMAC channels */ static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] = { LC1, LC2, LC3, LC4, LC5, LC6, /* LC7, LC8, LC9,*/ }; #else /*! * LoRaMac bands */ static Band_t Bands[LORA_MAX_NB_BANDS] = { BAND0, }; /*! * LoRaMAC channels */ static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] = { LC1, LC2, LC3, LC4, LC5, LC6, LC7, LC8, // LC9, }; #endif /*! * LoRaMAC 2nd reception window settings */ static Rx2ChannelParams_t Rx2Channel = RX_WND_2_CHANNEL; /*! * Datarate offset between uplink and downlink on first window */ static uint8_t Rx1DrOffset = 0; /*! * Mask indicating which channels are enabled */ static uint16_t ChannelsMask; /*! * Channels Tx output power */ static int8_t ChannelsTxPower = LORAMAC_DEFAULT_TX_POWER; /*! * Channels datarate */ static int8_t ChannelsDatarate = LORAMAC_DEFAULT_DATARATE; /*! * Channels defualt datarate */ static int8_t ChannelsDefaultDatarate = LORAMAC_DEFAULT_DATARATE; /*! * Number of uplink messages repetitions [1:15] (unconfirmed messages only) */ static uint8_t ChannelsNbRep = 1; // /*! * Uplink messages repetitions counter */ static uint8_t ChannelsNbRepCounter = 0; /*! * Maximum duty cycle * \remark Possibility to shutdown the device. Everything else not implemented. */ static uint8_t MaxDCycle = 0; /*! * Agregated duty cycle management */ static uint16_t AggregatedDCycle; static uint64_t AggregatedLastTxDoneTime; static uint64_t AggregatedTimeOff; /*! * Enables/Disables duty cycle management (Test only) */ static bool DutyCycleOn; /*! * Current channel index */ static uint8_t Channel; /*! * LoRaMac internal states */ enum LoRaMacState_e { MAC_IDLE = 0x00000000, MAC_TX_RUNNING = 0x00000001, MAC_RX = 0x00000002, MAC_ACK_REQ = 0x00000004, MAC_ACK_RETRY = 0x00000008, MAC_CHANNEL_CHECK = 0x00000010, }; /*! * LoRaMac internal state */ uint32_t LoRaMacState = MAC_IDLE; /*! * LoRaMac timer used to check the LoRaMacState (runs every second) */ Ticker MacStateCheckTimer; /*! * LoRaMac upper layer event functions */ static LoRaMacEvent_t *LoRaMacEvents; /*! * LoRaMac notification event flags */ LoRaMacEventFlags_t LoRaMacEventFlags; /*! * LoRaMac notification event info */ LoRaMacEventInfo_t LoRaMacEventInfo; /*! * Radio events function pointer */ //static RadioEvents_t RadioEvents; /*! * LoRaMac channel check timer */ Ticker ChannelCheckTimer; /*! * LoRaMac duty cycle delayed Tx timer */ Ticker TxDelayedTimer; /*! * LoRaMac reception windows timers */ Ticker RxWindowTimer1; Ticker RxWindowTimer2; /*! * LoRaMac reception windows delay from end of Tx */ static uint32_t ReceiveDelay1; static uint32_t ReceiveDelay2; static uint32_t JoinAcceptDelay1; static uint32_t JoinAcceptDelay2; /*! * LoRaMac reception windows delay * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME * join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME */ static uint32_t RxWindow1Delay; static uint32_t RxWindow2Delay; /*! * LoRaMac maximum time a reception window stays open */ static uint32_t MaxRxWindow; /*! * Acknowledge timeout timer. Used for packet retransmissions. */ Ticker AckTimeoutTimer; /*! * Number of trials to get a frame acknowledged */ static uint8_t AckTimeoutRetries = 1; /*! * Number of trials to get a frame acknowledged */ static uint8_t AckTimeoutRetriesCounter = 1; /*! * Indicates if the AckTimeout timer has expired or not */ static bool AckTimeoutRetry = false; /*! * Last transmission time on air */ TimerTime_t TxTimeOnAir = 0; /*! * Function to be executed on Radio Tx Done event */ static void OnRadioTxDone( void ); /*! * Function to be executed on Radio Rx Done event */ static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); /*! * Function executed on Radio Tx Timeout event */ static void OnRadioTxTimeout( void ); /*! * Function executed on Radio Rx error event */ static void OnRadioRxError( void ); /*! * Function executed on Radio Rx Timeout event */ static void OnRadioRxTimeout( void ); /*! * Function executed on Resend Frame timer event. */ static void OnMacStateCheckTimerEvent( void ); /*! * Function executed on duty cycle delayed Tx timer event */ static void OnTxDelayedTimerEvent( void ); /*! * Function executed on channel check timer event */ static void OnChannelCheckTimerEvent( void ); /*! * Function executed on first Rx window timer event */ static void OnRxWindow1TimerEvent( void ); /*! * Function executed on second Rx window timer event */ static void OnRxWindow2TimerEvent( void ); /*! * Function executed on AckTimeout timer event */ static void OnAckTimeoutTimerEvent( void ); SX1276MB1xAS Radio( OnRadioTxDone, OnRadioTxTimeout, OnRadioRxDone, OnRadioRxTimeout, OnRadioRxError, NULL, NULL ); /*! * Searches and set the next random available channel * * \retval status Function status [0: OK, 1: Unable to find a free channel] */ static uint8_t LoRaMacSetNextChannel( void ) { uint8_t i = 0; uint8_t j = 0; uint8_t channelNext = Channel; uint8_t nbEnabledChannels = 0; uint8_t enabledChannels[LORA_MAX_NB_CHANNELS]; uint64_t curTime = TimerGetCurrentTime( ); memset( enabledChannels, 0, LORA_MAX_NB_CHANNELS ); // Update Aggregated duty cycle if( AggregatedTimeOff < ( curTime - AggregatedLastTxDoneTime ) ) { AggregatedTimeOff = 0; } // Update bands Time OFF uint64_t minTime = 0xFFFFFFFFFFFFFFFF; for( i = 0; i < LORA_MAX_NB_BANDS; i++ ) { if( DutyCycleOn == true ) { if( Bands[i].TimeOff < ( curTime - Bands[i].LastTxDoneTime ) ) { Bands[i].TimeOff = 0; } if( Bands[i].TimeOff != 0 ) { minTime = MIN( Bands[i].TimeOff, minTime ); } } else { minTime = 0; Bands[i].TimeOff = 0; } } // Search how many channels are enabled for( i = 0; i < LORA_MAX_NB_CHANNELS; i++ ) { if( ( ( 1 << i ) & ChannelsMask ) != 0 ) { if( Channels[i].Frequency == 0 ) { // Check if the channel is enabled continue; } if( ( ( Channels[i].DrRange.Fields.Min <= ChannelsDatarate ) && ( ChannelsDatarate <= Channels[i].DrRange.Fields.Max ) ) == false ) { // Check if the current channel selection supports the given datarate continue; } if( Bands[Channels[i].Band].TimeOff > 0 ) { // Check if the band is available for transmission continue; } if( AggregatedTimeOff > 0 ) { // Check if there is time available for transmission continue; } enabledChannels[nbEnabledChannels++] = i; } } if( nbEnabledChannels > 0 ) { for( i = 0, j = randr( 0, nbEnabledChannels - 1 ); i < LORA_MAX_NB_CHANNELS; i++ ) { channelNext = enabledChannels[j]; j = ( j + 1 ) % nbEnabledChannels; if( Radio.IsChannelFree( MODEM_LORA, Channels[channelNext].Frequency, RSSI_FREE_TH ) == true ) { // Free channel found Channel = channelNext; LoRaMacState &= ~MAC_CHANNEL_CHECK; ChannelCheckTimer.detach( ); return 0; } } } // No free channel found. // Check again if( ( LoRaMacState & MAC_CHANNEL_CHECK ) == 0 ) { ChannelCheckTimer.attach_us( OnChannelCheckTimerEvent, minTime ); LoRaMacState |= MAC_CHANNEL_CHECK; } return 1; } /* * TODO: Add documentation */ void OnChannelCheckTimerEvent( void ) { ChannelCheckTimer.detach( ); LoRaMacState &= ~MAC_CHANNEL_CHECK; if( LoRaMacSetNextChannel( ) == 0 ) { if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) { LoRaMacSendFrameOnChannel( Channels[Channel] ); } } } /*! * Adds a new MAC command to be sent. * * \Remark MAC layer internal function * * \param [in] cmd MAC command to be added * [MOTE_MAC_LINK_CHECK_REQ, * MOTE_MAC_LINK_ADR_ANS, * MOTE_MAC_DUTY_CYCLE_ANS, * MOTE_MAC_RX2_PARAM_SET_ANS, * MOTE_MAC_DEV_STATUS_ANS * MOTE_MAC_NEW_CHANNEL_ANS] * \param [in] p1 1st parameter ( optional depends on the command ) * \param [in] p2 2nd parameter ( optional depends on the command ) * * \retval status Function status [0: OK, 1: Unknown command, 2: Buffer full] */ static uint8_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ) { //Added by pzheng 20151112 debug( "_LoRaMacMoteCmd = 0x%x\n\r", cmd ); MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; switch( cmd ) { case MOTE_MAC_LINK_CHECK_REQ: // No payload for this command break; case MOTE_MAC_LINK_ADR_ANS: // Margin MacCommandsBuffer[MacCommandsBufferIndex++] = p1; break; case MOTE_MAC_DUTY_CYCLE_ANS: // No payload for this answer break; case MOTE_MAC_RX_PARAM_SETUP_ANS: // Status: Datarate ACK, Channel ACK MacCommandsBuffer[MacCommandsBufferIndex++] = p1; break; case MOTE_MAC_DEV_STATUS_ANS: // 1st byte Battery // 2nd byte Margin MacCommandsBuffer[MacCommandsBufferIndex++] = p1; MacCommandsBuffer[MacCommandsBufferIndex++] = p2; break; case MOTE_MAC_NEW_CHANNEL_ANS: // Status: Datarate range OK, Channel frequency OK MacCommandsBuffer[MacCommandsBufferIndex++] = p1; break; case MOTE_MAC_RX_TIMING_SETUP_ANS: // No payload for this answer break; default: return 1; } if( MacCommandsBufferIndex <= 15 ) { MacCommandsInNextTx = true; return 0; } else { return 2; } } // TODO: Add Documentation static void LoRaMacNotify( LoRaMacEventFlags_t *flags, LoRaMacEventInfo_t *info ) { if( ( LoRaMacEvents != NULL ) && ( LoRaMacEvents->MacEvent != NULL ) ) { LoRaMacEvents->MacEvent( flags, info ); } flags->Value = 0; } void LoRaMacInit( LoRaMacEvent_t *events ) { LoRaMacEvents = events; LoRaMacEventFlags.Value = 0; LoRaMacEventInfo.TxAckReceived = false; LoRaMacEventInfo.TxNbRetries = 0; LoRaMacEventInfo.TxDatarate = 7; LoRaMacEventInfo.RxPort = 1; LoRaMacEventInfo.RxBuffer = NULL; LoRaMacEventInfo.RxBufferSize = 0; LoRaMacEventInfo.RxRssi = 0; LoRaMacEventInfo.RxSnr = 0; LoRaMacEventInfo.Energy = 0; LoRaMacEventInfo.DemodMargin = 0; LoRaMacEventInfo.NbGateways = 0; LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; LoRaMacDeviceClass = CLASS_A; UpLinkCounter = 1; DownLinkCounter = 0; IsLoRaMacNetworkJoined = false; LoRaMacState = MAC_IDLE; //#if defined( USE_BAND_868 ) ChannelsMask = LC( 1 ) + LC( 2 ) + LC( 3 ) + LC( 4 ) + LC( 5 ) + LC( 6 ) + LC( 7 ) + LC( 8 ) + LC( 9 ); //#else // ChannelsMask = LC( 1 ) + LC( 2 ) + LC( 3 ); //#endif ChannelsTxPower = LORAMAC_DEFAULT_TX_POWER; ChannelsDefaultDatarate = ChannelsDatarate = LORAMAC_DEFAULT_DATARATE; ChannelsNbRep = 1; ChannelsNbRepCounter = 0; MaxDCycle = 0; AggregatedDCycle = 1; AggregatedLastTxDoneTime = 0; AggregatedTimeOff = 0; DutyCycleOn = true; UpLinkTestOn = false; MaxRxWindow = MAX_RX_WINDOW; ReceiveDelay1 = RECEIVE_DELAY1; ReceiveDelay2 = RECEIVE_DELAY2; JoinAcceptDelay1 = JOIN_ACCEPT_DELAY1; JoinAcceptDelay2 = JOIN_ACCEPT_DELAY2; // MacStateCheckTimer.attach_us( &OnMacStateCheckTimerEvent, MAC_STATE_CHECK_TIMEOUT ); // Random seed initialization srand1( RAND_SEED ); // Initialize channel index. Channel = LORA_MAX_NB_CHANNELS; PublicNetwork = true; LoRaMacSetPublicNetwork( PublicNetwork ); Radio.Sleep( ); } void LoRaMacSetAdrOn( bool enable ) { AdrCtrlOn = enable; } //Added by pzheng 20151111 void LoRaMacSetUpLinkTestOn( bool enable ) { UpLinkTestOn = enable; } //Added by pzheng 20151111 bool LoRaMacGetUpLinkTestOn( ) { return(bool)UpLinkTestOn; } void LoRaMacInitNwkIds( uint32_t netID, uint32_t devAddr, uint8_t *nwkSKey, uint8_t *appSKey ) { LoRaMacNetID = netID; LoRaMacDevAddr = devAddr; LoRaMacMemCpy( nwkSKey, LoRaMacNwkSKey, 16 ); LoRaMacMemCpy( appSKey, LoRaMacAppSKey, 16 ); IsLoRaMacNetworkJoined = true; } void LoRaMacMulticastChannelAdd( MulticastParams_t *channelParam ) { // Reset downlink counter channelParam->DownLinkCounter = 0; if( MulticastChannels == NULL ) { MulticastChannels = channelParam; } else { MulticastParams_t *cur = MulticastChannels; while( cur->Next != NULL ) { cur = cur->Next; } cur->Next = channelParam; } } void LoRaMacMulticastChannelRemove( MulticastParams_t *channelParam ) { MulticastParams_t *cur = NULL; // Remove the front element if( MulticastChannels == channelParam ) { if( MulticastChannels != NULL ) { cur = MulticastChannels; MulticastChannels = MulticastChannels->Next; cur->Next = NULL; // Last node in the list if( cur == MulticastChannels ) { MulticastChannels = NULL; } } return; } // Remove last element if( channelParam->Next == NULL ) { if( MulticastChannels != NULL ) { cur = MulticastChannels; MulticastParams_t *last = NULL; while( cur->Next != NULL ) { last = cur; cur = cur->Next; } if( last != NULL ) { last->Next = NULL; } // Last node in the list if( cur == last ) { MulticastChannels = NULL; } } return; } // Remove a middle element cur = MulticastChannels; while( cur != NULL ) { if( cur->Next == channelParam ) { break; } cur = cur->Next; } if( cur != NULL ) { MulticastParams_t *tmp = cur ->Next; cur->Next = tmp->Next; tmp->Next = NULL; } } uint8_t LoRaMacJoinReq( uint8_t *devEui, uint8_t *appEui, uint8_t *appKey ) { LoRaMacHeader_t macHdr; LoRaMacDevEui = devEui; LoRaMacAppEui = appEui; LoRaMacAppKey = appKey; macHdr.Value = 0; macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; IsLoRaMacNetworkJoined = false; return LoRaMacSend( &macHdr, NULL, 0, NULL, 0 ); } uint8_t LoRaMacLinkCheckReq( void ) { return AddMacCommand( MOTE_MAC_LINK_CHECK_REQ, 0, 0 ); } uint8_t LoRaMacSendFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) { LoRaMacHeader_t macHdr; macHdr.Value = 0; macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP; return LoRaMacSend( &macHdr, NULL, fPort, fBuffer, fBufferSize ); } uint8_t LoRaMacSendConfirmedFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize, uint8_t retries ) { LoRaMacHeader_t macHdr; if( AdrCtrlOn == false ) { ChannelsDatarate = ChannelsDefaultDatarate; } AckTimeoutRetries = retries; AckTimeoutRetriesCounter = 1; macHdr.Value = 0; macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP; return LoRaMacSend( &macHdr, NULL, fPort, fBuffer, fBufferSize ); } uint8_t LoRaMacSend( LoRaMacHeader_t *macHdr, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) { LoRaMacFrameCtrl_t fCtrl; fCtrl.Value = 0; fCtrl.Bits.FOptsLen = 0; fCtrl.Bits.FPending = 0; fCtrl.Bits.Ack = false; fCtrl.Bits.AdrAckReq = false; fCtrl.Bits.Adr = AdrCtrlOn; if( LoRaMacSetNextChannel( ) == 0 ) { return LoRaMacSendOnChannel( Channels[Channel], macHdr, &fCtrl, fOpts, fPort, fBuffer, fBufferSize ); } return 5; } uint8_t LoRaMacPrepareFrame( ChannelParams_t channel, LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) { uint16_t i; uint8_t pktHeaderLen = 0; uint32_t mic = 0; LoRaMacBufferPktLen = 0; NodeAckRequested = false; if( fBuffer == NULL ) { fBufferSize = 0; } LoRaMacBuffer[pktHeaderLen++] = macHdr->Value; switch( macHdr->Bits.MType ) { case FRAME_TYPE_JOIN_REQ: RxWindow1Delay = JoinAcceptDelay1 - RADIO_WAKEUP_TIME; RxWindow2Delay = JoinAcceptDelay2 - RADIO_WAKEUP_TIME; LoRaMacBufferPktLen = pktHeaderLen; LoRaMacMemCpy( LoRaMacAppEui, LoRaMacBuffer + LoRaMacBufferPktLen, 8 ); LoRaMacBufferPktLen += 8; LoRaMacMemCpy( LoRaMacDevEui, LoRaMacBuffer + LoRaMacBufferPktLen, 8 ); LoRaMacBufferPktLen += 8; LoRaMacDevNonce = Radio.Random( ); LoRaMacBuffer[LoRaMacBufferPktLen++] = LoRaMacDevNonce & 0xFF; LoRaMacBuffer[LoRaMacBufferPktLen++] = ( LoRaMacDevNonce >> 8 ) & 0xFF; LoRaMacJoinComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen & 0xFF, LoRaMacAppKey, &mic ); LoRaMacBuffer[LoRaMacBufferPktLen++] = mic & 0xFF; LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 8 ) & 0xFF; LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 16 ) & 0xFF; LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 24 ) & 0xFF; break; case FRAME_TYPE_DATA_CONFIRMED_UP: NodeAckRequested = true; //Intentional falltrough case FRAME_TYPE_DATA_UNCONFIRMED_UP: if( IsLoRaMacNetworkJoined == false ) { return 2; // No network has been joined yet } RxWindow1Delay = ReceiveDelay1 - RADIO_WAKEUP_TIME; RxWindow2Delay = ReceiveDelay2 - RADIO_WAKEUP_TIME; if( fOpts == NULL ) { fCtrl->Bits.FOptsLen = 0; } if( SrvAckRequested == true ) { SrvAckRequested = false; fCtrl->Bits.Ack = 1; } if( fCtrl->Bits.Adr == true ) { if( ChannelsDatarate == LORAMAC_MIN_DATARATE ) { AdrAckCounter = 0; fCtrl->Bits.AdrAckReq = false; } else { if( AdrAckCounter > ADR_ACK_LIMIT ) { fCtrl->Bits.AdrAckReq = true; } else { fCtrl->Bits.AdrAckReq = false; } if( AdrAckCounter > ( ADR_ACK_LIMIT + ADR_ACK_DELAY ) ) { AdrAckCounter = 0; if( ChannelsDatarate > LORAMAC_MIN_DATARATE ) { ChannelsDatarate--; } else { // Re-enable default channels LC1, LC2, LC3 ChannelsMask = ChannelsMask | ( LC( 1 ) + LC( 2 ) + LC( 3 ) ); } } } } LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr ) & 0xFF; LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 8 ) & 0xFF; LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 16 ) & 0xFF; LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 24 ) & 0xFF; LoRaMacBuffer[pktHeaderLen++] = fCtrl->Value; LoRaMacBuffer[pktHeaderLen++] = UpLinkCounter & 0xFF; LoRaMacBuffer[pktHeaderLen++] = ( UpLinkCounter >> 8 ) & 0xFF; if( fOpts != NULL ) { for( i = 0; i < fCtrl->Bits.FOptsLen; i++ ) { LoRaMacBuffer[pktHeaderLen++] = fOpts[i]; } } if( ( MacCommandsBufferIndex + fCtrl->Bits.FOptsLen ) <= 15 ) { if( MacCommandsInNextTx == true ) { fCtrl->Bits.FOptsLen += MacCommandsBufferIndex; // Update FCtrl field with new value of OptionsLength LoRaMacBuffer[0x05] = fCtrl->Value; for( i = 0; i < MacCommandsBufferIndex; i++ ) { LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i]; } } MacCommandsInNextTx = false; MacCommandsBufferIndex = 0; } if( ( pktHeaderLen + fBufferSize ) > LORAMAC_PHY_MAXPAYLOAD ) { return 3; } if( fBuffer != NULL ) { LoRaMacBuffer[pktHeaderLen] = fPort; if( fPort == 0 ) { LoRaMacPayloadEncrypt( (uint8_t*)fBuffer, fBufferSize, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload ); } else { LoRaMacPayloadEncrypt( (uint8_t*)fBuffer, fBufferSize, LoRaMacAppSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload ); } LoRaMacMemCpy( LoRaMacPayload, LoRaMacBuffer + pktHeaderLen + 1, fBufferSize ); } LoRaMacBufferPktLen = pktHeaderLen + 1 + fBufferSize; LoRaMacComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &mic ); if( ( LoRaMacBufferPktLen + LORAMAC_MFR_LEN ) > LORAMAC_PHY_MAXPAYLOAD ) { return 3; } LoRaMacBuffer[LoRaMacBufferPktLen + 0] = mic & 0xFF; LoRaMacBuffer[LoRaMacBufferPktLen + 1] = ( mic >> 8 ) & 0xFF; LoRaMacBuffer[LoRaMacBufferPktLen + 2] = ( mic >> 16 ) & 0xFF; LoRaMacBuffer[LoRaMacBufferPktLen + 3] = ( mic >> 24 ) & 0xFF; LoRaMacBufferPktLen += LORAMAC_MFR_LEN; break; default: return 4; } return 0; } uint8_t LoRaMacSendFrameOnChannel( ChannelParams_t channel ) { LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; LoRaMacEventInfo.TxDatarate = Datarates[ChannelsDatarate]; Radio.SetChannel( channel.Frequency ); if( ChannelsDatarate == DR_7 ) { // High Speed FSK channel Radio.SetTxConfig( MODEM_FSK, TxPowers[ChannelsTxPower], 25e3, 0, Datarates[ChannelsDatarate] * 1e3, 0, 5, false, true, 0, 0, false, 3e6 ); TxTimeOnAir = Radio.TimeOnAir( MODEM_FSK, LoRaMacBufferPktLen ); } else if( ChannelsDatarate == DR_6 ) { // High speed LoRa channel Radio.SetTxConfig( MODEM_LORA, TxPowers[ChannelsTxPower], 0, 1, Datarates[ChannelsDatarate], 1, 8, false, true, 0, 0, false, 3e6 ); TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen ); } else { // Normal LoRa channel Radio.SetTxConfig( MODEM_LORA, TxPowers[ChannelsTxPower], 0, 0, Datarates[ChannelsDatarate], 1, 8, false, true, 0, 0, false, 3e6 ); TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen ); } if( MaxDCycle == 255 ) { return 6; } if( MaxDCycle == 0 ) { AggregatedTimeOff = 0; } LoRaMacState |= MAC_TX_RUNNING; // Starts the MAC layer status check timer MacStateCheckTimer.attach_us( &OnMacStateCheckTimerEvent, MAC_STATE_CHECK_TIMEOUT ); if( MAX( Bands[channel.Band].TimeOff, AggregatedTimeOff ) > ( TimerGetCurrentTime( ) ) ) { // Schedule transmission TxDelayedTimer.attach_us( &OnTxDelayedTimerEvent, MAX( Bands[channel.Band].TimeOff, AggregatedTimeOff ) ); } else { // Send now Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen ); } return 0; } void OnTxDelayedTimerEvent( void ) { TxDelayedTimer.detach( ); Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen ); } uint8_t LoRaMacSendOnChannel( ChannelParams_t channel, LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) { uint8_t status = 0; if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) { return 1; // MAC is busy transmitting a previous frame } status = LoRaMacPrepareFrame( channel, macHdr, fCtrl, fOpts, fPort, fBuffer, fBufferSize ); if( status != 0 ) { return status; } LoRaMacEventInfo.TxNbRetries = 0; LoRaMacEventInfo.TxAckReceived = false; return LoRaMacSendFrameOnChannel( channel ); } static void LoRaMacProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize ) { while( macIndex < commandsSize ) { //Added by pzheng 20151112 debug( "_LoRaMacProcessMacCommands = 0x%x\n\r", payload[macIndex]); // Decode Frame MAC commands switch( payload[macIndex++] ) { case SRV_MAC_LINK_CHECK_ANS: LoRaMacEventFlags.Bits.LinkCheck = 1; LoRaMacEventInfo.DemodMargin = payload[macIndex++]; LoRaMacEventInfo.NbGateways = payload[macIndex++]; break; case SRV_MAC_LINK_ADR_REQ: { uint8_t i; uint8_t status = 0x07; uint16_t chMask = 0; int8_t txPower = 0; int8_t datarate = 0; uint8_t nbRep = 0; uint8_t chMaskCntl = 0; datarate = payload[macIndex++]; txPower = datarate & 0x0F; datarate = ( datarate >> 4 ) & 0x0F; if( ( AdrCtrlOn == false ) && ( ( ChannelsDatarate != datarate ) || ( ChannelsTxPower != txPower ) ) ) { // ADR disabled don't handle ADR requests if server tries to change datarate or txpower // Answer the server with fail status // Power ACK = 0 // Data rate ACK = 0 // Channel mask = 0 AddMacCommand( MOTE_MAC_LINK_ADR_ANS, 0, 0 ); break; } chMask = payload[macIndex++]; chMask |= payload[macIndex++] << 8; nbRep = payload[macIndex++]; chMaskCntl = ( nbRep >> 4 ) & 0x07; nbRep &= 0x0F; if( nbRep == 0 ) { nbRep = 1; } if( ( chMaskCntl == 0 ) && ( chMask == 0 ) ) { status &= 0xFE; // Channel mask KO } else { for( i = 0; i < LORA_MAX_NB_CHANNELS; i++ ) { if( chMaskCntl == 6 ) { if( Channels[i].Frequency != 0 ) { chMask |= 1 << i; } } else { if( ( ( chMask & ( 1 << i ) ) != 0 ) && ( Channels[i].Frequency == 0 ) ) {// Trying to enable an undefined channel status &= 0xFE; // Channel mask KO } } } } if( ( ( datarate < LORAMAC_MIN_DATARATE ) || ( datarate > LORAMAC_MAX_DATARATE ) ) == true ) { status &= 0xFD; // Datarate KO } // // Remark MaxTxPower = 0 and MinTxPower = 5 // if( ( ( LORAMAC_MAX_TX_POWER <= txPower ) && ( txPower <= LORAMAC_MIN_TX_POWER ) ) == false ) { status &= 0xFB; // TxPower KO } if( ( status & 0x07 ) == 0x07 ) { ChannelsDatarate = datarate; ChannelsTxPower = txPower; ChannelsMask = chMask; ChannelsNbRep = nbRep; } AddMacCommand( MOTE_MAC_LINK_ADR_ANS, status, 0 ); } break; case SRV_MAC_DUTY_CYCLE_REQ: MaxDCycle = payload[macIndex++]; AggregatedDCycle = 1 << MaxDCycle; AddMacCommand( MOTE_MAC_DUTY_CYCLE_ANS, 0, 0 ); break; case SRV_MAC_RX_PARAM_SETUP_REQ: { uint8_t status = 0x07; int8_t datarate = 0; int8_t drOffset = 0; uint32_t freq = 0; drOffset = payload[macIndex++]; datarate = drOffset & 0x0F; drOffset = ( drOffset >> 4 ) & 0x0F; freq = payload[macIndex++]; freq |= payload[macIndex++] << 8; freq |= payload[macIndex++] << 16; freq *= 100; if( Radio.CheckRfFrequency( freq ) == false ) { status &= 0xFE; // Channel frequency KO } if( ( ( datarate < LORAMAC_MIN_DATARATE ) || ( datarate > LORAMAC_MAX_DATARATE ) ) == true ) { status &= 0xFD; // Datarate KO } if( ( ( drOffset < 0 ) || ( drOffset > 5 ) ) == true ) { status &= 0xFB; // Rx1DrOffset range KO } if( ( status & 0x07 ) == 0x07 ) { Rx2Channel.Datarate = datarate; Rx2Channel.Frequency = freq; Rx1DrOffset = drOffset; } AddMacCommand( MOTE_MAC_RX_PARAM_SETUP_ANS, status, 0 ); } break; case SRV_MAC_DEV_STATUS_REQ: AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, BoardMeasureBatterieLevel( ), LoRaMacEventInfo.RxSnr ); break; case SRV_MAC_NEW_CHANNEL_REQ: { uint8_t status = 0x03; int8_t channelIndex = 0; ChannelParams_t chParam; channelIndex = payload[macIndex++]; chParam.Frequency = payload[macIndex++]; chParam.Frequency |= payload[macIndex++] << 8; chParam.Frequency |= payload[macIndex++] << 16; chParam.Frequency *= 100; chParam.DrRange.Value = payload[macIndex++]; if( ( channelIndex < 3 ) || ( channelIndex > LORA_MAX_NB_CHANNELS ) ) { status &= 0xFE; // Channel frequency KO } if( Radio.CheckRfFrequency( chParam.Frequency ) == false ) { status &= 0xFE; // Channel frequency KO } if( ( chParam.DrRange.Fields.Min > chParam.DrRange.Fields.Max ) || ( ( ( LORAMAC_MIN_DATARATE <= chParam.DrRange.Fields.Min ) && ( chParam.DrRange.Fields.Min <= LORAMAC_MAX_DATARATE ) ) == false ) || ( ( ( LORAMAC_MIN_DATARATE <= chParam.DrRange.Fields.Max ) && ( chParam.DrRange.Fields.Max <= LORAMAC_MAX_DATARATE ) ) == false ) ) { status &= 0xFD; // Datarate range KO } if( ( status & 0x03 ) == 0x03 ) { LoRaMacSetChannel( channelIndex, chParam ); } AddMacCommand( MOTE_MAC_NEW_CHANNEL_ANS, status, 0 ); } break; case SRV_MAC_RX_TIMING_SETUP_REQ: { uint8_t delay = payload[macIndex++] & 0x0F; if( delay == 0 ) { delay++; } ReceiveDelay1 = delay * 1e6; ReceiveDelay2 = ReceiveDelay1 + 1e6; AddMacCommand( MOTE_MAC_RX_TIMING_SETUP_ANS, 0, 0 ); } break; default: // Unknown command. ABORT MAC commands processing return; } } } /*! * Function to be executed on Tx Done event */ static void OnRadioTxDone( void ) { uint64_t curTime = TimerGetCurrentTime( ); if( LoRaMacDeviceClass != CLASS_C ) { Radio.Sleep( ); } else { 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 Agregated Time OFF AggregatedLastTxDoneTime = curTime; AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir ); if( IsRxWindowsEnabled == true ) { RxWindowTimer1.attach_us( &OnRxWindow1TimerEvent, RxWindow1Delay ); if( LoRaMacDeviceClass != CLASS_C ) { RxWindowTimer2.attach_us( &OnRxWindow2TimerEvent, RxWindow2Delay ); } } else { LoRaMacEventFlags.Bits.Tx = 1; LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; } if( NodeAckRequested == false ) { ChannelsNbRepCounter++; } } /*! * Function to be executed on Rx Done event */ static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) { LoRaMacHeader_t macHdr; LoRaMacFrameCtrl_t fCtrl; uint8_t pktHeaderLen = 0; uint32_t address = 0; uint16_t sequenceCounter = 0; int32_t sequence = 0; uint8_t appPayloadStartIndex = 0; uint8_t port = 0xFF; uint8_t frameLen = 0; uint32_t mic = 0; uint32_t micRx = 0; MulticastParams_t *curMulticastParams = NULL; uint8_t *nwkSKey = LoRaMacNwkSKey; uint8_t *appSKey = LoRaMacAppSKey; uint32_t downLinkCounter = 0; bool isMicOk = false; if( LoRaMacDeviceClass != CLASS_C ) { Radio.Sleep( ); } else { if( LoRaMacEventFlags.Bits.RxSlot == 0 ) { OnRxWindow2TimerEvent( ); } } RxWindowTimer2.detach( ); // Timer is removed anyway in functions above macHdr.Value = payload[pktHeaderLen++]; switch( macHdr.Bits.MType ) { case FRAME_TYPE_JOIN_ACCEPT: if( IsLoRaMacNetworkJoined == true ) { break; } LoRaMacJoinDecrypt( payload + 1, size - 1, LoRaMacAppKey, LoRaMacRxPayload + 1 ); LoRaMacRxPayload[0] = macHdr.Value; LoRaMacJoinComputeMic( LoRaMacRxPayload, size - LORAMAC_MFR_LEN, LoRaMacAppKey, &mic ); micRx |= LoRaMacRxPayload[size - LORAMAC_MFR_LEN]; micRx |= ( LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 1] << 8 ); micRx |= ( LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 2] << 16 ); micRx |= ( LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 3] << 24 ); if( micRx == mic ) { LoRaMacEventFlags.Bits.Rx = 1; LoRaMacEventInfo.RxSnr = snr; LoRaMacEventInfo.RxRssi = rssi; LoRaMacJoinComputeSKeys( LoRaMacAppKey, LoRaMacRxPayload + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey ); LoRaMacNetID = LoRaMacRxPayload[4]; LoRaMacNetID |= ( LoRaMacRxPayload[5] << 8 ); LoRaMacNetID |= ( LoRaMacRxPayload[6] << 16 ); LoRaMacDevAddr = LoRaMacRxPayload[7]; LoRaMacDevAddr |= ( LoRaMacRxPayload[8] << 8 ); LoRaMacDevAddr |= ( LoRaMacRxPayload[9] << 16 ); LoRaMacDevAddr |= ( LoRaMacRxPayload[10] << 24 ); // DLSettings Rx1DrOffset = ( LoRaMacRxPayload[11] >> 4 ) & 0x07; Rx2Channel.Datarate = LoRaMacRxPayload[11] & 0x0F; // RxDelay ReceiveDelay1 = ( LoRaMacRxPayload[12] & 0x0F ); if( ReceiveDelay1 == 0 ) { ReceiveDelay1 = 1; } ReceiveDelay1 *= 1e6; ReceiveDelay2 = ReceiveDelay1 + 1e6; //CFList if( ( size - 1 ) > 16 ) { ChannelParams_t param; param.DrRange.Value = ( DR_5 << 4 ) | DR_0; for( uint8_t i = 3, j = 0; i < ( 5 + 3 ); i++, j += 3 ) { param.Frequency = ( LoRaMacRxPayload[13 + j] | ( LoRaMacRxPayload[14 + j] << 8 ) | ( LoRaMacRxPayload[15 + j] << 16 ) ) * 100; LoRaMacSetChannel( i, param ); } } LoRaMacEventFlags.Bits.JoinAccept = 1; IsLoRaMacNetworkJoined = true; ChannelsDatarate = ChannelsDefaultDatarate; LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; } else { LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; } LoRaMacEventFlags.Bits.Tx = 1; break; case FRAME_TYPE_DATA_CONFIRMED_DOWN: case FRAME_TYPE_DATA_UNCONFIRMED_DOWN: { address = payload[pktHeaderLen++]; address |= ( payload[pktHeaderLen++] << 8 ); address |= ( payload[pktHeaderLen++] << 16 ); address |= ( payload[pktHeaderLen++] << 24 ); if( address != LoRaMacDevAddr ) { curMulticastParams = MulticastChannels; while( curMulticastParams != NULL ) { if( address == curMulticastParams->Address ) { LoRaMacEventFlags.Bits.Multicast = 1; nwkSKey = curMulticastParams->NwkSKey; appSKey = curMulticastParams->AppSKey; downLinkCounter = curMulticastParams->DownLinkCounter; break; } curMulticastParams = curMulticastParams->Next; } if( LoRaMacEventFlags.Bits.Multicast == 0 ) { // We are not the destination of this frame. LoRaMacEventFlags.Bits.Tx = 1; LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL; LoRaMacState &= ~MAC_TX_RUNNING; return; } } else { LoRaMacEventFlags.Bits.Multicast = 0; nwkSKey = LoRaMacNwkSKey; appSKey = LoRaMacAppSKey; downLinkCounter = DownLinkCounter; } if( LoRaMacDeviceClass != CLASS_A ) { LoRaMacState |= MAC_RX; // Starts the MAC layer status check timer MacStateCheckTimer.attach_us( &OnMacStateCheckTimerEvent , MAC_STATE_CHECK_TIMEOUT ); } fCtrl.Value = payload[pktHeaderLen++]; sequenceCounter |= payload[pktHeaderLen++]; sequenceCounter |= payload[pktHeaderLen++] << 8; appPayloadStartIndex = 8 + fCtrl.Bits.FOptsLen; micRx |= payload[size - LORAMAC_MFR_LEN]; micRx |= ( payload[size - LORAMAC_MFR_LEN + 1] << 8 ); micRx |= ( payload[size - LORAMAC_MFR_LEN + 2] << 16 ); micRx |= ( payload[size - LORAMAC_MFR_LEN + 3] << 24 ); sequence = ( int32_t )sequenceCounter - ( int32_t )( downLinkCounter & 0xFFFF ); if( sequence < 0 ) { // sequence reset or roll over happened downLinkCounter = ( downLinkCounter & 0xFFFF0000 ) | ( sequenceCounter + ( uint32_t )0x10000 ); LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic ); if( micRx == mic ) { isMicOk = true; } else { isMicOk = false; // sequence reset if( LoRaMacEventFlags.Bits.Multicast == 1 ) { curMulticastParams->DownLinkCounter = downLinkCounter = sequenceCounter; } else { DownLinkCounter = downLinkCounter = sequenceCounter; } LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic ); } } else { downLinkCounter = ( downLinkCounter & 0xFFFF0000 ) | sequenceCounter; LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic ); } if( ( isMicOk == true ) || ( micRx == mic ) ) { LoRaMacEventFlags.Bits.Rx = 1; LoRaMacEventInfo.RxSnr = snr; LoRaMacEventInfo.RxRssi = rssi; LoRaMacEventInfo.RxBufferSize = 0; AdrAckCounter = 0; if( LoRaMacEventFlags.Bits.Multicast == 1 ) { curMulticastParams->DownLinkCounter = downLinkCounter; } else { DownLinkCounter = downLinkCounter; } if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) { SrvAckRequested = true; } else { SrvAckRequested = false; } // Check if the frame is an acknowledgement if( fCtrl.Bits.Ack == 1 ) { LoRaMacEventInfo.TxAckReceived = true; // Stop the AckTimeout timer as no more retransmissions // are needed. AckTimeoutTimer.detach( ); } else { LoRaMacEventInfo.TxAckReceived = false; if( AckTimeoutRetriesCounter > AckTimeoutRetries ) { // Stop the AckTimeout timer as no more retransmissions // are needed. AckTimeoutTimer.detach( ); } } if( fCtrl.Bits.FOptsLen > 0 ) { // Decode Options field MAC commands LoRaMacProcessMacCommands( payload, 8, appPayloadStartIndex ); } if( ( ( size - 4 ) - appPayloadStartIndex ) > 0 ) { port = payload[appPayloadStartIndex++]; frameLen = ( size - 4 ) - appPayloadStartIndex; if( port == 0 ) { LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, frameLen, nwkSKey, address, DOWN_LINK, downLinkCounter, LoRaMacRxPayload ); // Decode frame payload MAC commands LoRaMacProcessMacCommands( LoRaMacRxPayload, 0, frameLen ); } else { LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, frameLen, appSKey, address, DOWN_LINK, downLinkCounter, LoRaMacRxPayload ); LoRaMacEventFlags.Bits.RxData = 1; LoRaMacEventInfo.RxPort = port; LoRaMacEventInfo.RxBuffer = LoRaMacRxPayload; LoRaMacEventInfo.RxBufferSize = frameLen; } } LoRaMacEventFlags.Bits.Tx = 1; LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; } else { LoRaMacEventInfo.TxAckReceived = false; LoRaMacEventFlags.Bits.Tx = 1; LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL; LoRaMacState &= ~MAC_TX_RUNNING; } } break; case FRAME_TYPE_PROPRIETARY: //Intentional falltrough default: LoRaMacEventFlags.Bits.Tx = 1; LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; LoRaMacState &= ~MAC_TX_RUNNING; break; } } /*! * Function executed on Radio Tx Timeout event */ static void OnRadioTxTimeout( void ) { if( LoRaMacDeviceClass != CLASS_C ) { Radio.Sleep( ); } else { OnRxWindow2TimerEvent( ); } LoRaMacEventFlags.Bits.Tx = 1; LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; } /*! * Function executed on Radio Rx Timeout event */ static void OnRadioRxTimeout( void ) { if( LoRaMacDeviceClass != CLASS_C ) { Radio.Sleep( ); } if( LoRaMacEventFlags.Bits.RxSlot == 1 ) { LoRaMacEventFlags.Bits.Tx = 1; LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; } } /*! * Function executed on Radio Rx Error event */ static void OnRadioRxError( void ) { if( LoRaMacDeviceClass != CLASS_C ) { Radio.Sleep( ); } if( LoRaMacEventFlags.Bits.RxSlot == 1 ) { LoRaMacEventFlags.Bits.Tx = 1; LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; } } /*! * Initializes and opens the reception window * * \param [IN] freq window channel frequency * \param [IN] datarate window channel datarate * \param [IN] bandwidth window channel bandwidth * \param [IN] timeout window channel timeout */ void LoRaMacRxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous ) { if( Radio.GetStatus( ) == IDLE ) { Radio.SetChannel( freq ); if( datarate == DR_7 ) { Radio.SetRxConfig( MODEM_FSK, 50e3, Datarates[datarate] * 1e3, 0, 83.333e3, 5, 0, false, 0, true, 0, 0, false, rxContinuous ); } else { Radio.SetRxConfig( MODEM_LORA, bandwidth, Datarates[datarate], 1, 0, 8, timeout, false, 0, false, 0, 0, true, rxContinuous ); } if( rxContinuous == false ) { Radio.Rx( MaxRxWindow ); } else { Radio.Rx( 0 ); // Continuous mode } } } /*! * Function executed on first Rx window timer event */ static void OnRxWindow1TimerEvent( void ) { uint16_t symbTimeout = 20; // DR_2, DR_1, DR_0 int8_t datarate = 0; uint32_t bandwidth = 0; // LoRa 125 kHz RxWindowTimer1.detach( ); datarate = ChannelsDatarate - Rx1DrOffset; if( datarate < 0 ) { datarate = DR_0; } // 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 symbTimeout = 80; } if( datarate == DR_6 ) {// LoRa 250 kHz bandwidth = 1; } LoRaMacEventFlags.Bits.RxSlot = 0; LoRaMacRxWindowSetup( Channels[Channel].Frequency, datarate, bandwidth, symbTimeout, false ); } /*! * Function executed on second Rx window timer event */ static void OnRxWindow2TimerEvent( void ) { RxWindowTimer2.detach( ); if( NodeAckRequested == true ) { AckTimeoutTimer.attach_us( &OnAckTimeoutTimerEvent, ACK_TIMEOUT + randr( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND ) ); } uint16_t symbTimeout = 20; // DR_2, DR_1, DR_0 uint32_t bandwidth = 0; // LoRa 125 kHz // 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 symbTimeout = 80; } if( Rx2Channel.Datarate == DR_6 ) {// LoRa 250 kHz bandwidth = 1; } LoRaMacEventFlags.Bits.RxSlot = 1; if( LoRaMacDeviceClass != CLASS_C ) { LoRaMacRxWindowSetup( Rx2Channel.Frequency, Rx2Channel.Datarate, bandwidth, symbTimeout, false ); } else { LoRaMacRxWindowSetup( Rx2Channel.Frequency, Rx2Channel.Datarate, bandwidth, symbTimeout, true ); } } /*! * Function executed on MacStateCheck timer event */ static void OnMacStateCheckTimerEvent( void ) { MacStateCheckTimer.detach( ); if( LoRaMacEventFlags.Bits.Tx == 1 ) { if( NodeAckRequested == false ) { if( LoRaMacEventFlags.Bits.JoinAccept == true ) { // Join messages aren't repeated automatically ChannelsNbRepCounter = ChannelsNbRep; UpLinkCounter = 0; } if( ChannelsNbRepCounter >= ChannelsNbRep ) { ChannelsNbRepCounter = 0; LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; AdrAckCounter++; if( IsUpLinkCounterFixed == false ) { UpLinkCounter++; } LoRaMacState &= ~MAC_TX_RUNNING; } else { LoRaMacEventFlags.Bits.Tx = 0; // Sends the same frame again if( LoRaMacSetNextChannel( ) == 0 ) { LoRaMacSendFrameOnChannel( Channels[Channel] ); } } } if( LoRaMacEventFlags.Bits.Rx == 1 ) { if( ( LoRaMacEventInfo.TxAckReceived == true ) || ( AckTimeoutRetriesCounter > AckTimeoutRetries ) ) { AckTimeoutRetry = false; if( IsUpLinkCounterFixed == false ) { UpLinkCounter++; } LoRaMacEventInfo.TxNbRetries = AckTimeoutRetriesCounter; LoRaMacState &= ~MAC_TX_RUNNING; } } if( ( AckTimeoutRetry == true ) && ( ( LoRaMacState & MAC_CHANNEL_CHECK ) == 0 ) ) { AckTimeoutRetry = false; if( ( AckTimeoutRetriesCounter < AckTimeoutRetries ) && ( AckTimeoutRetriesCounter <= MAX_ACK_RETRIES ) ) { AckTimeoutRetriesCounter++; if( ( AckTimeoutRetriesCounter % 2 ) == 1 ) { ChannelsDatarate = MAX( ChannelsDatarate - 1, LORAMAC_MIN_DATARATE ); } LoRaMacEventFlags.Bits.Tx = 0; // Sends the same frame again if( LoRaMacSetNextChannel( ) == 0 ) { LoRaMacSendFrameOnChannel( Channels[Channel] ); } } else { // Re-enable default channels LC1, LC2, LC3 ChannelsMask = ChannelsMask | ( LC( 1 ) + LC( 2 ) + LC( 3 ) ); LoRaMacState &= ~MAC_TX_RUNNING; LoRaMacEventInfo.TxAckReceived = false; LoRaMacEventInfo.TxNbRetries = AckTimeoutRetriesCounter; if( IsUpLinkCounterFixed == false ) { UpLinkCounter++; } LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; } } } // Handle reception for Class B and Class C if( ( LoRaMacState & MAC_RX ) == MAC_RX ) { LoRaMacState &= ~MAC_RX; } if( LoRaMacState == MAC_IDLE ) { LoRaMacNotify( &LoRaMacEventFlags, &LoRaMacEventInfo ); } else { // Operation not finished restart timer MacStateCheckTimer.attach_us( &OnMacStateCheckTimerEvent, MAC_STATE_CHECK_TIMEOUT ); } } static void OnAckTimeoutTimerEvent( void ) { AckTimeoutTimer.detach( ); AckTimeoutRetry = true; LoRaMacState &= ~MAC_ACK_REQ; } /*! * ============================================================================ * = LoRaMac test functions = * ============================================================================ */ void LoRaMacSetDeviceClass( DeviceClass_t deviceClass ) { LoRaMacDeviceClass = deviceClass; } void LoRaMacSetPublicNetwork( bool enable ) { PublicNetwork = enable; Radio.SetModem( MODEM_LORA ); if( PublicNetwork == true ) { // Change LoRa modem SyncWord Radio.Write( REG_LR_SYNCWORD, LORA_MAC_PUBLIC_SYNCWORD ); } else { // Change LoRa modem SyncWord Radio.Write( REG_LR_SYNCWORD, LORA_MAC_PRIVATE_SYNCWORD ); } } void LoRaMacSetDutyCycleOn( bool enable ) { DutyCycleOn = enable; } void LoRaMacSetChannel( uint8_t id, ChannelParams_t params ) { params.Band = 0; Channels[id] = params; // Activate the newly created channel ChannelsMask |= 1 << id; #if defined( USE_BAND_868 ) if( ( Channels[id].Frequency >= 865000000 ) && ( Channels[id].Frequency <= 868000000 ) ) { if( Channels[id].Band != BAND_G1_0 ) { Channels[id].Band = BAND_G1_0; } } else if( ( Channels[id].Frequency > 868000000 ) && ( Channels[id].Frequency <= 868600000 ) ) { if( Channels[id].Band != BAND_G1_1 ) { Channels[id].Band = BAND_G1_1; } } else if( ( Channels[id].Frequency >= 868700000 ) && ( Channels[id].Frequency <= 869200000 ) ) { if( Channels[id].Band != BAND_G1_2 ) { Channels[id].Band = BAND_G1_2; } } else if( ( Channels[id].Frequency >= 869400000 ) && ( Channels[id].Frequency <= 869650000 ) ) { if( Channels[id].Band != BAND_G1_3 ) { Channels[id].Band = BAND_G1_3; } } else if( ( Channels[id].Frequency >= 869700000 ) && ( Channels[id].Frequency <= 870000000 ) ) { if( Channels[id].Band != BAND_G1_4 ) { Channels[id].Band = BAND_G1_4; } } else { Channels[id].Frequency = 0; Channels[id].DrRange.Value = 0; } #endif // Check if it is a valid channel if( Channels[id].Frequency == 0 ) { ChannelsMask &= ~( 1 << id ); } } void LoRaMacSetRx2Channel( Rx2ChannelParams_t param ) { Rx2Channel = param; } void LoRaMacSetChannelsTxPower( int8_t txPower ) { ChannelsTxPower = txPower; } void LoRaMacSetChannelsDatarate( int8_t datarate ) { ChannelsDefaultDatarate = ChannelsDatarate = datarate; } void LoRaMacSetChannelsMask( uint16_t mask ) { ChannelsMask = mask; } void LoRaMacSetChannelsNbRep( uint8_t nbRep ) { if( nbRep < 1 ) { nbRep = 1; } if( nbRep > 15 ) { nbRep = 15; } ChannelsNbRep = nbRep; } void LoRaMacSetMaxRxWindow( uint32_t delay ) { MaxRxWindow = delay; } void LoRaMacSetReceiveDelay1( uint32_t delay ) { ReceiveDelay1 = delay; } void LoRaMacSetReceiveDelay2( uint32_t delay ) { ReceiveDelay2 = delay; } void LoRaMacSetJoinAcceptDelay1( uint32_t delay ) { JoinAcceptDelay1 = delay; } void LoRaMacSetJoinAcceptDelay2( uint32_t delay ) { JoinAcceptDelay2 = delay; } void LoRaMacTestRxWindowsOn( bool enable ) { IsRxWindowsEnabled = enable; } uint32_t LoRaMacGetUpLinkCounter( void ) { return UpLinkCounter; } uint32_t LoRaMacGetDownLinkCounter( void ) { return DownLinkCounter; } void LoRaMacSetMicTest( uint16_t upLinkCounter ) { UpLinkCounter = upLinkCounter; IsUpLinkCounterFixed = true; }