/* / _____) _ | | ( (____ _____ ____ _| |_ _____ ____| |__ \____ \| ___ | (_ _) ___ |/ ___) _ \ _____) ) ____| | | || |_| ____( (___| | | | (______/|_____)_|_|_| \__)_____)\____)_| |_| (C)2013 Semtech Description: LoRaMac classA device implementation License: Revised BSD License, see LICENSE.TXT file include in the project Maintainer: Miguel Luis, Gregory Cristian and Wael Guibene */ /****************************************************************************** * @file lora.c * @author MCD Application Team * @version V1.1.2 * @date 08-September-2017 * @brief lora API to drive the lora state Machine ****************************************************************************** * @attention * *

© Copyright (c) 2017 STMicroelectronics International N.V. * All rights reserved.

* * Redistribution and use in source and binary forms, with or without * modification, are permitted, provided that the following conditions are met: * * 1. Redistribution of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of other * contributors to this software may be used to endorse or promote products * derived from this software without specific written permission. * 4. This software, including modifications and/or derivative works of this * software, must execute solely and exclusively on microcontroller or * microprocessor devices manufactured by or for STMicroelectronics. * 5. Redistribution and use of this software other than as permitted under * this license is void and will automatically terminate your rights under * this license. * * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "hw.h" #include "timeServer.h" #include "LoRaMac.h" #include "lora.h" /*! * Join requests trials duty cycle. */ #define OVER_THE_AIR_ACTIVATION_DUTYCYCLE 10000 // 10 [s] value in ms #if defined( REGION_EU868 ) #include "LoRaMacTest.h" /*! * LoRaWAN ETSI duty cycle control enable/disable * * \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes */ #define LORAWAN_DUTYCYCLE_ON true #define USE_SEMTECH_DEFAULT_CHANNEL_LINEUP 0 #if( USE_SEMTECH_DEFAULT_CHANNEL_LINEUP == 1 ) #define LC4 { 867100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } #define LC5 { 867300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } #define LC6 { 867500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } #define LC7 { 867700000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } #define LC8 { 867900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } #define LC9 { 868800000, 0, { ( ( DR_7 << 4 ) | DR_7 ) }, 2 } #define LC10 { 868300000, 0, { ( ( DR_6 << 4 ) | DR_6 ) }, 1 } #endif #endif static uint8_t DevEui[] = LORAWAN_DEVICE_EUI; static uint8_t AppEui[] = LORAWAN_APPLICATION_EUI; static uint8_t AppKey[] = LORAWAN_APPLICATION_KEY; #if( OVER_THE_AIR_ACTIVATION == 0 ) static uint8_t NwkSKey[] = LORAWAN_NWKSKEY; static uint8_t AppSKey[] = LORAWAN_APPSKEY; /*! * Device address */ static uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS; #endif /*! * User application data buffer size */ #define LORAWAN_APP_DATA_BUFF_SIZE 64 /*! * User application data */ static uint8_t AppDataBuff[LORAWAN_APP_DATA_BUFF_SIZE]; /*! * User application data structure */ static lora_AppData_t AppData={ AppDataBuff, 0 ,0 }; /*! * Indicates if the node is sending confirmed or unconfirmed messages */ static FunctionalState IsTxConfirmed ; /*! * Defines the LoRa parameters at Init */ static LoRaParam_t* LoRaParamInit; /*! * Timer to handle the application data transmission duty cycle */ static TimerEvent_t TxNextPacketTimer; static DeviceState_t DeviceState = DEVICE_STATE_INIT ; /*! * Timer to handle the state of LED1 */ static LoRaMacPrimitives_t LoRaMacPrimitives; static LoRaMacCallback_t LoRaMacCallbacks; static MibRequestConfirm_t mibReq; static LoRaMainCallback_t *LoRaMainCallbacks; /*! * Indicates if a new packet can be sent */ static bool NextTx = true; /*! * LoRaWAN compliance tests support data */ struct ComplianceTest_s { bool Running; uint8_t State; FunctionalState IsTxConfirmed; uint8_t AppPort; uint8_t AppDataSize; uint8_t *AppDataBuffer; uint16_t DownLinkCounter; bool LinkCheck; uint8_t DemodMargin; uint8_t NbGateways; }ComplianceTest; /*! * \brief Prepares the payload of the frame */ static void PrepareTxFrame( void) { if( ComplianceTest.Running == true ) { if( ComplianceTest.LinkCheck == true ) { ComplianceTest.LinkCheck = false; AppData.BuffSize = 3; AppData.Buff[0] = 5; AppData.Buff[1] = ComplianceTest.DemodMargin; AppData.Buff[2] = ComplianceTest.NbGateways; ComplianceTest.State = 1; } else { switch( ComplianceTest.State ) { case 4: ComplianceTest.State = 1; break; case 1: AppData.BuffSize = 2; AppData.Buff[0] = ComplianceTest.DownLinkCounter >> 8; AppData.Buff[1] = ComplianceTest.DownLinkCounter; break; } } } else { LoRaMainCallbacks->LoraTxData(&AppData, &IsTxConfirmed); } } /*! * \brief Prepares the payload of the frame * * \retval [0: frame could be send, 1: error] */ static bool SendFrame( void ) { McpsReq_t mcpsReq; LoRaMacTxInfo_t txInfo; if( LoRaMacQueryTxPossible( AppData.BuffSize, &txInfo ) != LORAMAC_STATUS_OK ) { // Send empty frame in order to flush MAC commands mcpsReq.Type = MCPS_UNCONFIRMED; mcpsReq.Req.Unconfirmed.fBuffer = NULL; mcpsReq.Req.Unconfirmed.fBufferSize = 0; mcpsReq.Req.Unconfirmed.Datarate = LoRaParamInit->TxDatarate; } else { if( IsTxConfirmed == DISABLE ) { mcpsReq.Type = MCPS_UNCONFIRMED; mcpsReq.Req.Unconfirmed.fPort = AppData.Port; mcpsReq.Req.Unconfirmed.fBuffer = AppData.Buff; mcpsReq.Req.Unconfirmed.fBufferSize = AppData.BuffSize; mcpsReq.Req.Unconfirmed.Datarate = LoRaParamInit->TxDatarate; } else { mcpsReq.Type = MCPS_CONFIRMED; mcpsReq.Req.Confirmed.fPort = AppData.Port; mcpsReq.Req.Confirmed.fBuffer = AppData.Buff; mcpsReq.Req.Confirmed.fBufferSize = AppData.BuffSize; mcpsReq.Req.Confirmed.NbTrials = 8; mcpsReq.Req.Confirmed.Datarate = LoRaParamInit->TxDatarate; } } if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK ) { return false; } return true; } void OnSendEvent( void ) { MibRequestConfirm_t _mibReq; LoRaMacStatus_t status; _mibReq.Type = MIB_NETWORK_JOINED; status = LoRaMacMibGetRequestConfirm( &_mibReq ); if( status == LORAMAC_STATUS_OK ) { if( _mibReq.Param.IsNetworkJoined == true ) { DeviceState = DEVICE_STATE_SEND; NextTx = true; } else { DeviceState = DEVICE_STATE_JOIN; } } } /*! * \brief Function executed on TxNextPacket Timeout event */ static void OnTxNextPacketTimerEvent( void ) { TimerStop( &TxNextPacketTimer ); OnSendEvent(); } /*! * \brief MCPS-Confirm event function * * \param [IN] McpsConfirm - Pointer to the confirm structure, * containing confirm attributes. */ static void McpsConfirm( McpsConfirm_t *mcpsConfirm ) { if( mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) { switch( mcpsConfirm->McpsRequest ) { case MCPS_UNCONFIRMED: { // Check Datarate // Check TxPower break; } case MCPS_CONFIRMED: { // Check Datarate // Check TxPower // Check AckReceived // Check NbTrials break; } case MCPS_PROPRIETARY: { break; } default: break; } } NextTx = true; } /*! * \brief MCPS-Indication event function * * \param [IN] mcpsIndication - Pointer to the indication structure, * containing indication attributes. */ static void McpsIndication( McpsIndication_t *mcpsIndication ) { if( mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK ) { return; } switch( mcpsIndication->McpsIndication ) { case MCPS_UNCONFIRMED: { break; } case MCPS_CONFIRMED: { break; } case MCPS_PROPRIETARY: { break; } case MCPS_MULTICAST: { break; } default: break; } // Check Multicast // Check Port // Check Datarate // Check FramePending // Check Buffer // Check BufferSize // Check Rssi // Check Snr // Check RxSlot if( ComplianceTest.Running == true ) { ComplianceTest.DownLinkCounter++; } if( mcpsIndication->RxData == true ) { switch( mcpsIndication->Port ) { case 224: if( ComplianceTest.Running == false ) { // Check compliance test enable command (i) if( ( mcpsIndication->BufferSize == 4 ) && ( mcpsIndication->Buffer[0] == 0x01 ) && ( mcpsIndication->Buffer[1] == 0x01 ) && ( mcpsIndication->Buffer[2] == 0x01 ) && ( mcpsIndication->Buffer[3] == 0x01 ) ) { IsTxConfirmed = DISABLE; AppData.Port = 224; AppData.BuffSize = 2; ComplianceTest.DownLinkCounter = 0; ComplianceTest.LinkCheck = false; ComplianceTest.DemodMargin = 0; ComplianceTest.NbGateways = 0; ComplianceTest.Running = true; ComplianceTest.State = 1; MibRequestConfirm_t _mibReq; _mibReq.Type = MIB_ADR; _mibReq.Param.AdrEnable = true; LoRaMacMibSetRequestConfirm( &_mibReq ); #if defined( REGION_EU868 ) LoRaMacTestSetDutyCycleOn( false ); #endif } } else { ComplianceTest.State = mcpsIndication->Buffer[0]; switch( ComplianceTest.State ) { case 0: // Check compliance test disable command (ii) ComplianceTest.DownLinkCounter = 0; ComplianceTest.Running = false; MibRequestConfirm_t _mibReq; _mibReq.Type = MIB_ADR; _mibReq.Param.AdrEnable = LoRaParamInit->AdrEnable; LoRaMacMibSetRequestConfirm( &_mibReq ); #if defined( REGION_EU868 ) LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON ); #endif break; case 1: // (iii, iv) AppData.BuffSize = 2; break; case 2: // Enable confirmed messages (v) IsTxConfirmed = ENABLE; ComplianceTest.State = 1; break; case 3: // Disable confirmed messages (vi) IsTxConfirmed = DISABLE; ComplianceTest.State = 1; break; case 4: // (vii) AppData.BuffSize = mcpsIndication->BufferSize; AppData.Buff[0] = 4; for( uint8_t i = 1; i < AppData.BuffSize; i++ ) { AppData.Buff[i] = mcpsIndication->Buffer[i] + 1; } break; case 5: // (viii) { MlmeReq_t mlmeReq; mlmeReq.Type = MLME_LINK_CHECK; LoRaMacMlmeRequest( &mlmeReq ); } break; case 6: // (ix) { MlmeReq_t mlmeReq; // Disable TestMode and revert back to normal operation ComplianceTest.DownLinkCounter = 0; ComplianceTest.Running = false; MibRequestConfirm_t mibReq; mibReq.Type = MIB_ADR; mibReq.Param.AdrEnable = LoRaParamInit->AdrEnable; LoRaMacMibSetRequestConfirm( &mibReq ); mlmeReq.Type = MLME_JOIN; mlmeReq.Req.Join.DevEui = DevEui; mlmeReq.Req.Join.AppEui = AppEui; mlmeReq.Req.Join.AppKey = AppKey; mlmeReq.Req.Join.NbTrials = 3; LoRaMacMlmeRequest( &mlmeReq ); DeviceState = DEVICE_STATE_SLEEP; } break; case 7: // (x) { if( mcpsIndication->BufferSize == 3 ) { MlmeReq_t mlmeReq; mlmeReq.Type = MLME_TXCW; mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] ); LoRaMacMlmeRequest( &mlmeReq ); } else if( mcpsIndication->BufferSize == 7 ) { MlmeReq_t mlmeReq; mlmeReq.Type = MLME_TXCW_1; mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] ); mlmeReq.Req.TxCw.Frequency = ( uint32_t )( ( mcpsIndication->Buffer[3] << 16 ) | ( mcpsIndication->Buffer[4] << 8 ) | mcpsIndication->Buffer[5] ) * 100; mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[6]; LoRaMacMlmeRequest( &mlmeReq ); } ComplianceTest.State = 1; } break; default: break; } } break; default: AppData.Port = mcpsIndication->Port; AppData.BuffSize = mcpsIndication->BufferSize; memcpy1( AppData.Buff, mcpsIndication->Buffer, AppData.BuffSize ); LoRaMainCallbacks->LoraRxData( &AppData ); break; } } } /*! * \brief MLME-Confirm event function * * \param [IN] MlmeConfirm - Pointer to the confirm structure, * containing confirm attributes. */ static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm ) { switch( mlmeConfirm->MlmeRequest ) { case MLME_JOIN: { if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) { // Status is OK, node has joined the network DeviceState = DEVICE_STATE_JOINED; } else { // Join was not successful. Try to join again DeviceState = DEVICE_STATE_JOIN; } break; } case MLME_LINK_CHECK: { if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) { // Check DemodMargin // Check NbGateways if( ComplianceTest.Running == true ) { ComplianceTest.LinkCheck = true; ComplianceTest.DemodMargin = mlmeConfirm->DemodMargin; ComplianceTest.NbGateways = mlmeConfirm->NbGateways; } } break; } default: break; } NextTx = true; } /** * lora Init */ void lora_Init (LoRaMainCallback_t *callbacks, LoRaParam_t* LoRaParam ) { /* init the DeviceState*/ DeviceState= DEVICE_STATE_INIT; /* init the Tx Duty Cycle*/ LoRaParamInit = LoRaParam; /* init the main call backs*/ LoRaMainCallbacks = callbacks; #if (STATIC_DEVICE_EUI != 1) LoRaMainCallbacks->BoardGetUniqueId( DevEui ); #endif #if( OVER_THE_AIR_ACTIVATION != 0 ) PRINTF("OTAA\n\r"); PRINTF("DevEui= %02X", DevEui[0]) ;for(int i=1; i<8 ; i++) {PRINTF("-%02X", DevEui[i]); }; PRINTF("\n\r"); PRINTF("AppEui= %02X", AppEui[0]) ;for(int i=1; i<8 ; i++) {PRINTF("-%02X", AppEui[i]); }; PRINTF("\n\r"); PRINTF("AppKey= %02X", AppKey[0]) ;for(int i=1; i<16; i++) {PRINTF(" %02X", AppKey[i]); }; PRINTF("\n\n\r"); #else #if (STATIC_DEVICE_ADDRESS != 1) // Random seed initialization srand1( LoRaMainCallbacks->BoardGetRandomSeed( ) ); // Choose a random device address DevAddr = randr( 0, 0x01FFFFFF ); #endif PRINTF("ABP\n\r"); PRINTF("DevEui= %02X", DevEui[0]) ;for(int i=1; i<8 ; i++) {PRINTF("-%02X", DevEui[i]); }; PRINTF("\n\r"); PRINTF("DevAdd= %08X\n\r", DevAddr) ; PRINTF("NwkSKey= %02X", NwkSKey[0]) ;for(int i=1; i<16 ; i++) {PRINTF(" %02X", NwkSKey[i]); }; PRINTF("\n\r"); PRINTF("AppSKey= %02X", AppSKey[0]) ;for(int i=1; i<16 ; i++) {PRINTF(" %02X", AppSKey[i]); }; PRINTF("\n\r"); #endif } /** * lora class A state machine */ void lora_fsm( void) { switch( DeviceState ) { case DEVICE_STATE_INIT: { LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm; LoRaMacPrimitives.MacMcpsIndication = McpsIndication; LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm; LoRaMacCallbacks.GetBatteryLevel = LoRaMainCallbacks->BoardGetBatteryLevel; #if defined( REGION_AS923 ) LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LORAMAC_REGION_AS923 ); #elif defined( REGION_AU915 ) LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LORAMAC_REGION_AU915 ); #elif defined( REGION_CN470 ) LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LORAMAC_REGION_CN470 ); #elif defined( REGION_CN779 ) LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LORAMAC_REGION_CN779 ); #elif defined( REGION_EU433 ) LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LORAMAC_REGION_EU433 ); #elif defined( REGION_IN865 ) LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LORAMAC_REGION_IN865 ); #elif defined( REGION_EU868 ) LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LORAMAC_REGION_EU868 ); #elif defined( REGION_KR920 ) LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LORAMAC_REGION_KR920 ); #elif defined( REGION_US915 ) LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LORAMAC_REGION_US915 ); #elif defined( REGION_US915_HYBRID ) LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LORAMAC_REGION_US915_HYBRID ); #else #error "Please define a region in the compiler options." #endif TimerInit( &TxNextPacketTimer, OnTxNextPacketTimerEvent ); mibReq.Type = MIB_ADR; mibReq.Param.AdrEnable = LoRaParamInit->AdrEnable; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_PUBLIC_NETWORK; mibReq.Param.EnablePublicNetwork = LoRaParamInit->EnablePublicNetwork; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_DEVICE_CLASS; mibReq.Param.Class= LoRaParamInit->Class; LoRaMacMibSetRequestConfirm( &mibReq ); #if defined( REGION_EU868 ) LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON ); #if( USE_SEMTECH_DEFAULT_CHANNEL_LINEUP == 1 ) LoRaMacChannelAdd( 3, ( ChannelParams_t )LC4 ); LoRaMacChannelAdd( 4, ( ChannelParams_t )LC5 ); LoRaMacChannelAdd( 5, ( ChannelParams_t )LC6 ); LoRaMacChannelAdd( 6, ( ChannelParams_t )LC7 ); LoRaMacChannelAdd( 7, ( ChannelParams_t )LC8 ); LoRaMacChannelAdd( 8, ( ChannelParams_t )LC9 ); LoRaMacChannelAdd( 9, ( ChannelParams_t )LC10 ); mibReq.Type = MIB_RX2_DEFAULT_CHANNEL; mibReq.Param.Rx2DefaultChannel = ( Rx2ChannelParams_t ){ 869525000, DR_3 }; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_RX2_CHANNEL; mibReq.Param.Rx2Channel = ( Rx2ChannelParams_t ){ 869525000, DR_3 }; LoRaMacMibSetRequestConfirm( &mibReq ); #endif #endif DeviceState = DEVICE_STATE_JOIN; break; } case DEVICE_STATE_JOIN: { #if( OVER_THE_AIR_ACTIVATION != 0 ) MlmeReq_t mlmeReq; mlmeReq.Type = MLME_JOIN; mlmeReq.Req.Join.DevEui = DevEui; mlmeReq.Req.Join.AppEui = AppEui; mlmeReq.Req.Join.AppKey = AppKey; mlmeReq.Req.Join.NbTrials = LoRaParamInit->NbTrials; if( NextTx == true ) { LoRaMacMlmeRequest( &mlmeReq ); } DeviceState = DEVICE_STATE_SLEEP; #else mibReq.Type = MIB_NET_ID; mibReq.Param.NetID = LORAWAN_NETWORK_ID; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_DEV_ADDR; mibReq.Param.DevAddr = DevAddr; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_NWK_SKEY; mibReq.Param.NwkSKey = NwkSKey; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_APP_SKEY; mibReq.Param.AppSKey = AppSKey; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_NETWORK_JOINED; mibReq.Param.IsNetworkJoined = true; LoRaMacMibSetRequestConfirm( &mibReq ); DeviceState = DEVICE_STATE_SEND; #endif break; } case DEVICE_STATE_JOINED: { PRINTF("JOINED\n\r"); DeviceState = DEVICE_STATE_SEND; break; } case DEVICE_STATE_SEND: { if( NextTx == true ) { PrepareTxFrame( ); NextTx = SendFrame( ); } if( ComplianceTest.Running == true ) { // Schedule next packet transmission as soon as possible TimerSetValue( &TxNextPacketTimer, 5000); /* 5s */ TimerStart( &TxNextPacketTimer ); } else if (LoRaParamInit->TxEvent == TX_ON_TIMER ) { // Schedule next packet transmission TimerSetValue( &TxNextPacketTimer, LoRaParamInit->TxDutyCycleTime ); TimerStart( &TxNextPacketTimer ); } DeviceState = DEVICE_STATE_SLEEP; break; } case DEVICE_STATE_SLEEP: { // Wake up through events break; } default: { DeviceState = DEVICE_STATE_INIT; break; } } } DeviceState_t lora_getDeviceState( void ) { return DeviceState; } /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/