This code uses Semtech's open source LoRaWAN stack ported to mbed environment.

Dependencies:   mbed LoRaWAN-lib SX1272Lib

Revision:
0:6155544d38b1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/main.cpp	Thu Jun 09 23:58:16 2016 +0000
@@ -0,0 +1,857 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2015 Semtech
+
+Description: LoRaMac classA device 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 "radio.h"
+
+#include "LoRaMac.h"
+#include "Comissioning.h"
+#include "SerialDisplay.h"
+
+//#include "mDot.h"
+
+/*!
+ * Join requests trials duty cycle.
+ */
+#define OVER_THE_AIR_ACTIVATION_DUTYCYCLE           10000000 // 10 [s] value in us
+
+/*!
+ * Defines the application data transmission duty cycle. 5s, value in [us].
+ */
+#define APP_TX_DUTYCYCLE                            5000000
+
+/*!
+ * Defines a random delay for application data transmission duty cycle. 1s,
+ * value in [us].
+ */
+#define APP_TX_DUTYCYCLE_RND                        1000000
+
+/*!
+ * Default datarate
+ */
+#define LORAWAN_DEFAULT_DATARATE                    DR_0
+
+/*!
+ * LoRaWAN confirmed messages
+ */
+#define LORAWAN_CONFIRMED_MSG_ON                    true
+
+/*!
+ * LoRaWAN Adaptive Data Rate
+ *
+ * \remark Please note that when ADR is enabled the end-device should be static
+ */
+#define LORAWAN_ADR_ON                              1
+
+#if defined( USE_BAND_868 )
+
+#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          1
+
+#if( USE_SEMTECH_DEFAULT_CHANNEL_LINEUP == 1 )
+
+#define LC4                { 867100000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC5                { 867300000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC6                { 867500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC7                { 867700000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC8                { 867900000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC9                { 868800000, { ( ( DR_7 << 4 ) | DR_7 ) }, 2 }
+#define LC10               { 868300000, { ( ( DR_6 << 4 ) | DR_6 ) }, 1 }
+
+#endif
+
+#endif
+
+/*!
+ * LoRaWAN application port
+ */
+#define LORAWAN_APP_PORT                            15
+
+/*!
+ * User application data buffer size
+ */
+#if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
+#define LORAWAN_APP_DATA_SIZE                       6
+
+#else
+#define LORAWAN_APP_DATA_SIZE                       1
+
+#endif
+
+#if( OVER_THE_AIR_ACTIVATION != 0 )
+
+static uint8_t DevEui[] = LORAWAN_DEVICE_EUI;
+//static uint8_t* DevEui;
+static uint8_t AppEui[] = LORAWAN_APPLICATION_EUI;
+static uint8_t AppKey[] = LORAWAN_APPLICATION_KEY;
+
+#else
+
+static uint8_t NwkSKey[] = LORAWAN_NWKSKEY;
+static uint8_t AppSKey[] = LORAWAN_APPSKEY;
+
+/*!
+ * Device address
+ */
+static uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS;
+
+#endif
+
+/*!
+ * Application port
+ */
+static uint8_t AppPort = LORAWAN_APP_PORT;
+
+/*!
+ * User application data size
+ */
+static uint8_t AppDataSize = LORAWAN_APP_DATA_SIZE;
+
+/*!
+ * User application data buffer size
+ */
+#define LORAWAN_APP_DATA_MAX_SIZE                           64
+
+/*!
+ * User application data
+ */
+static uint8_t AppData[LORAWAN_APP_DATA_MAX_SIZE];
+
+/*!
+ * Indicates if the node is sending confirmed or unconfirmed messages
+ */
+static uint8_t IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
+
+/*!
+ * Defines the application data transmission duty cycle
+ */
+static uint32_t TxDutyCycleTime;
+
+/*!
+ * Timer to handle the application data transmission duty cycle
+ */
+static TimerEvent_t TxNextPacketTimer;
+
+/*!
+ * Specifies the state of the application LED
+ */
+static bool AppLedStateOn = false;
+volatile bool Led3StateChanged = false;
+/*!
+ * Timer to handle the state of LED1
+ */
+static TimerEvent_t Led1Timer;
+volatile bool Led1State = false;
+volatile bool Led1StateChanged = false;
+/*!
+ * Timer to handle the state of LED2
+ */
+static TimerEvent_t Led2Timer;
+volatile bool Led2State = false;
+volatile bool Led2StateChanged = false;
+
+/*!
+ * Indicates if a new packet can be sent
+ */
+static bool NextTx = true;
+
+/*!
+ * Device states
+ */
+static enum eDevicState {
+    DEVICE_STATE_INIT,
+    DEVICE_STATE_JOIN,
+    DEVICE_STATE_SEND,
+    DEVICE_STATE_CYCLE,
+    DEVICE_STATE_SLEEP
+} DeviceState;
+
+/*!
+ * LoRaWAN compliance tests support data
+ */
+struct ComplianceTest_s {
+    bool Running;
+    uint8_t State;
+    bool IsTxConfirmed;
+    uint8_t AppPort;
+    uint8_t AppDataSize;
+    uint8_t *AppDataBuffer;
+    uint16_t DownLinkCounter;
+    bool LinkCheck;
+    uint8_t DemodMargin;
+    uint8_t NbGateways;
+} ComplianceTest;
+
+/*
+ * SerialDisplay managment variables
+ */
+
+/*!
+ * Indicates if the MAC layer network join status has changed.
+ */
+static bool IsNetworkJoinedStatusUpdate = false;
+
+/*!
+ * Strucure containing the Uplink status
+ */
+struct sLoRaMacUplinkStatus {
+    uint8_t Acked;
+    int8_t Datarate;
+    uint16_t UplinkCounter;
+    uint8_t Port;
+    uint8_t *Buffer;
+    uint8_t BufferSize;
+} LoRaMacUplinkStatus;
+volatile bool UplinkStatusUpdated = false;
+
+/*!
+ * Strucure containing the Downlink status
+ */
+struct sLoRaMacDownlinkStatus {
+    int16_t Rssi;
+    int8_t Snr;
+    uint16_t DownlinkCounter;
+    bool RxData;
+    uint8_t Port;
+    uint8_t *Buffer;
+    uint8_t BufferSize;
+} LoRaMacDownlinkStatus;
+volatile bool DownlinkStatusUpdated = false;
+
+void SerialDisplayRefresh( void )
+{
+    MibRequestConfirm_t mibReq;
+
+    SerialDisplayInit( );
+    SerialDisplayUpdateActivationMode( OVER_THE_AIR_ACTIVATION );
+
+#if( OVER_THE_AIR_ACTIVATION == 0 )
+    SerialDisplayUpdateNwkId( LORAWAN_NETWORK_ID );
+    SerialDisplayUpdateDevAddr( DevAddr );
+    SerialDisplayUpdateKey( 12, NwkSKey );
+    SerialDisplayUpdateKey( 13, AppSKey );
+#else
+    SerialDisplayUpdateEui( 5, DevEui );
+    SerialDisplayUpdateEui( 6, AppEui );
+    SerialDisplayUpdateKey( 7, AppKey );
+#endif
+
+    mibReq.Type = MIB_NETWORK_JOINED;
+    LoRaMacMibGetRequestConfirm( &mibReq );
+    SerialDisplayUpdateNetworkIsJoined( mibReq.Param.IsNetworkJoined );
+
+    SerialDisplayUpdateAdr( LORAWAN_ADR_ON );
+#if defined( USE_BAND_868 )
+    SerialDisplayUpdateDutyCycle( LORAWAN_DUTYCYCLE_ON );
+#else
+    SerialDisplayUpdateDutyCycle( false );
+#endif
+    SerialDisplayUpdatePublicNetwork( LORAWAN_PUBLIC_NETWORK );
+
+    SerialDisplayUpdateLedState( 3, AppLedStateOn );
+}
+
+void SerialRxProcess( void )
+{
+    if( SerialDisplayReadable( ) == true ) {
+        switch( SerialDisplayGetChar( ) ) {
+            case 'R':
+            case 'r':
+                // Refresh Serial screen
+                SerialDisplayRefresh( );
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+/*!
+ * \brief   Prepares the payload of the frame
+ */
+static void PrepareTxFrame( uint8_t port )
+{
+    switch( port ) {
+        case 15: {
+            AppData[0] = AppLedStateOn;
+            if( IsTxConfirmed == true ) {
+                AppData[1] = LoRaMacDownlinkStatus.DownlinkCounter >> 8;
+                AppData[2] = LoRaMacDownlinkStatus.DownlinkCounter;
+                AppData[3] = LoRaMacDownlinkStatus.Rssi >> 8;
+                AppData[4] = LoRaMacDownlinkStatus.Rssi;
+                AppData[5] = LoRaMacDownlinkStatus.Snr;
+            }
+        }
+        break;
+        case 224:
+            if( ComplianceTest.LinkCheck == true ) {
+                ComplianceTest.LinkCheck = false;
+                AppDataSize = 3;
+                AppData[0] = 5;
+                AppData[1] = ComplianceTest.DemodMargin;
+                AppData[2] = ComplianceTest.NbGateways;
+                ComplianceTest.State = 1;
+            } else {
+                switch( ComplianceTest.State ) {
+                    case 4:
+                        ComplianceTest.State = 1;
+                        break;
+                    case 1:
+                        AppDataSize = 2;
+                        AppData[0] = ComplianceTest.DownLinkCounter >> 8;
+                        AppData[1] = ComplianceTest.DownLinkCounter;
+                        break;
+                }
+            }
+            break;
+        default:
+            break;
+    }
+}
+
+/*!
+ * \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( AppDataSize, &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 = LORAWAN_DEFAULT_DATARATE;
+
+        LoRaMacUplinkStatus.Acked = false;
+        LoRaMacUplinkStatus.Port = 0;
+        LoRaMacUplinkStatus.Buffer = NULL;
+        LoRaMacUplinkStatus.BufferSize = 0;
+        SerialDisplayUpdateFrameType( false );
+    } else {
+        LoRaMacUplinkStatus.Acked = false;
+        LoRaMacUplinkStatus.Port = AppPort;
+        LoRaMacUplinkStatus.Buffer = AppData;
+        LoRaMacUplinkStatus.BufferSize = AppDataSize;
+        SerialDisplayUpdateFrameType( IsTxConfirmed );
+
+        if( IsTxConfirmed == false ) {
+            mcpsReq.Type = MCPS_UNCONFIRMED;
+            mcpsReq.Req.Unconfirmed.fPort = AppPort;
+            mcpsReq.Req.Unconfirmed.fBuffer = AppData;
+            mcpsReq.Req.Unconfirmed.fBufferSize = AppDataSize;
+            mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
+        } else {
+            mcpsReq.Type = MCPS_CONFIRMED;
+            mcpsReq.Req.Confirmed.fPort = AppPort;
+            mcpsReq.Req.Confirmed.fBuffer = AppData;
+            mcpsReq.Req.Confirmed.fBufferSize = AppDataSize;
+            mcpsReq.Req.Confirmed.NbTrials = 8;
+            mcpsReq.Req.Confirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
+        }
+    }
+
+    if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK ) {
+        return false;
+    }
+    return true;
+}
+
+/*!
+ * \brief Function executed on TxNextPacket Timeout event
+ */
+static void OnTxNextPacketTimerEvent( void )
+{
+    MibRequestConfirm_t mibReq;
+    LoRaMacStatus_t status;
+
+    TimerStop( &TxNextPacketTimer );
+
+    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 Led 1 Timeout event
+ */
+static void OnLed1TimerEvent( void )
+{
+    TimerStop( &Led1Timer );
+    // Switch LED 1 OFF
+    Led1State = false;
+    Led1StateChanged = true;
+}
+
+/*!
+ * \brief Function executed on Led 2 Timeout event
+ */
+static void OnLed2TimerEvent( void )
+{
+    TimerStop( &Led2Timer );
+    // Switch LED 2 OFF
+    Led2State = false;
+    Led2StateChanged = true;
+}
+
+/*!
+ * \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
+                LoRaMacUplinkStatus.Acked = mcpsConfirm->AckReceived;
+                break;
+            }
+            case MCPS_PROPRIETARY: {
+                break;
+            }
+            default:
+                break;
+        }
+        LoRaMacUplinkStatus.Datarate = mcpsConfirm->Datarate;
+        LoRaMacUplinkStatus.UplinkCounter = mcpsConfirm->UpLinkCounter;
+
+        UplinkStatusUpdated = true;
+    }
+    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
+    LoRaMacDownlinkStatus.Rssi = mcpsIndication->Rssi;
+    if( mcpsIndication->Snr & 0x80 ) { // The SNR sign bit is 1
+        // Invert and divide by 4
+        LoRaMacDownlinkStatus.Snr = ( ( ~mcpsIndication->Snr + 1 ) & 0xFF ) >> 2;
+        LoRaMacDownlinkStatus.Snr = -LoRaMacDownlinkStatus.Snr;
+    } else {
+        // Divide by 4
+        LoRaMacDownlinkStatus.Snr = ( mcpsIndication->Snr & 0xFF ) >> 2;
+    }
+    LoRaMacDownlinkStatus.DownlinkCounter++;
+    LoRaMacDownlinkStatus.RxData = mcpsIndication->RxData;
+    LoRaMacDownlinkStatus.Port = mcpsIndication->Port;
+    LoRaMacDownlinkStatus.Buffer = mcpsIndication->Buffer;
+    LoRaMacDownlinkStatus.BufferSize = mcpsIndication->BufferSize;
+
+    if( ComplianceTest.Running == true ) {
+        ComplianceTest.DownLinkCounter++;
+    }
+
+    if( mcpsIndication->RxData == true ) {
+        switch( mcpsIndication->Port ) {
+            case 1: // The application LED can be controlled on port 1 or 2
+            case 2:
+                if( mcpsIndication->BufferSize == 1 ) {
+                    AppLedStateOn = mcpsIndication->Buffer[0] & 0x01;
+                    Led3StateChanged = true;
+                }
+                break;
+            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 = false;
+                        AppPort = 224;
+                        AppDataSize = 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( USE_BAND_868 )
+                        LoRaMacTestSetDutyCycleOn( false );
+#endif
+                    }
+                } else {
+                    ComplianceTest.State = mcpsIndication->Buffer[0];
+                    switch( ComplianceTest.State ) {
+                        case 0: // Check compliance test disable command (ii)
+                            IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
+                            AppPort = LORAWAN_APP_PORT;
+                            AppDataSize = LORAWAN_APP_DATA_SIZE;
+                            ComplianceTest.DownLinkCounter = 0;
+                            ComplianceTest.Running = false;
+
+                            MibRequestConfirm_t mibReq;
+                            mibReq.Type = MIB_ADR;
+                            mibReq.Param.AdrEnable = LORAWAN_ADR_ON;
+                            LoRaMacMibSetRequestConfirm( &mibReq );
+#if defined( USE_BAND_868 )
+                            LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON );
+#endif
+                            break;
+                        case 1: // (iii, iv)
+                            AppDataSize = 2;
+                            break;
+                        case 2: // Enable confirmed messages (v)
+                            IsTxConfirmed = true;
+                            ComplianceTest.State = 1;
+                            break;
+                        case 3:  // Disable confirmed messages (vi)
+                            IsTxConfirmed = false;
+                            ComplianceTest.State = 1;
+                            break;
+                        case 4: // (vii)
+                            AppDataSize = mcpsIndication->BufferSize;
+
+                            AppData[0] = 4;
+                            for( uint8_t i = 1; i < AppDataSize; i++ ) {
+                                AppData[i] = mcpsIndication->Buffer[i] + 1;
+                            }
+                            break;
+                        case 5: { // (viii)
+                            MlmeReq_t mlmeReq;
+                            mlmeReq.Type = MLME_LINK_CHECK;
+                            LoRaMacMlmeRequest( &mlmeReq );
+                        }
+                        break;
+                        default:
+                            break;
+                    }
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    // Switch LED 2 ON for each received downlink
+    Led2State = true;
+    Led2StateChanged = true;
+    TimerStart( &Led2Timer );
+    DownlinkStatusUpdated = true;
+}
+
+/*!
+ * \brief   MLME-Confirm event function
+ *
+ * \param   [IN] mlmeConfirm - Pointer to the confirm structure,
+ *               containing confirm attributes.
+ */
+static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm )
+{
+    if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) {
+        switch( mlmeConfirm->MlmeRequest ) {
+            case MLME_JOIN: {
+                // Status is OK, node has joined the network
+                IsNetworkJoinedStatusUpdate = true;
+                break;
+            }
+            case MLME_LINK_CHECK: {
+                // Check DemodMargin
+                // Check NbGateways
+                if( ComplianceTest.Running == true ) {
+                    ComplianceTest.LinkCheck = true;
+                    ComplianceTest.DemodMargin = mlmeConfirm->DemodMargin;
+                    ComplianceTest.NbGateways = mlmeConfirm->NbGateways;
+                }
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    NextTx = true;
+    UplinkStatusUpdated = true;
+}
+
+/**
+ * Main application entry point.
+ */
+int main( void )
+{
+    // Mehrad: Using mDot library to grab the
+    // Device EUI using the functions exposed
+    // in libmDot.h
+    // mbed-rtos is a prerequisit for libmDot.
+    
+    /*
+    mDot* dot;
+    // get a mDot handle
+    dot = mDot::getInstance();
+    
+    // Get the device Id and assign it to DevEui static variable.
+    std::vector<uint8_t> devEuiVec = (*dot).getDeviceId();
+    DevEui = &devEuiVec[0];
+    
+    dot->killInstance();
+    */
+    
+    
+
+    LoRaMacPrimitives_t LoRaMacPrimitives;
+    LoRaMacCallback_t LoRaMacCallbacks;
+    MibRequestConfirm_t mibReq;
+
+    BoardInit( );
+    SerialDisplayInit( );
+
+    DeviceState = DEVICE_STATE_INIT;
+
+    while( 1 ) {
+        SerialRxProcess( );
+        if( IsNetworkJoinedStatusUpdate == true ) {
+            IsNetworkJoinedStatusUpdate = false;
+            mibReq.Type = MIB_NETWORK_JOINED;
+            LoRaMacMibGetRequestConfirm( &mibReq );
+            SerialDisplayUpdateNetworkIsJoined( mibReq.Param.IsNetworkJoined );
+        }
+        if( Led1StateChanged == true ) {
+            Led1StateChanged = false;
+            SerialDisplayUpdateLedState( 1, Led1State );
+        }
+        if( Led2StateChanged == true ) {
+            Led2StateChanged = false;
+            SerialDisplayUpdateLedState( 2, Led2State );
+        }
+        if( Led3StateChanged == true ) {
+            Led3StateChanged = false;
+            SerialDisplayUpdateLedState( 3, AppLedStateOn );
+        }
+        if( UplinkStatusUpdated == true ) {
+            UplinkStatusUpdated = false;
+            SerialDisplayUpdateUplink( LoRaMacUplinkStatus.Acked, LoRaMacUplinkStatus.Datarate, LoRaMacUplinkStatus.UplinkCounter, LoRaMacUplinkStatus.Port, LoRaMacUplinkStatus.Buffer, LoRaMacUplinkStatus.BufferSize );
+        }
+        if( DownlinkStatusUpdated == true ) {
+            DownlinkStatusUpdated = false;
+            SerialDisplayUpdateLedState( 2, Led2State );
+            SerialDisplayUpdateDownlink( LoRaMacDownlinkStatus.RxData, LoRaMacDownlinkStatus.Rssi, LoRaMacDownlinkStatus.Snr, LoRaMacDownlinkStatus.DownlinkCounter, LoRaMacDownlinkStatus.Port, LoRaMacDownlinkStatus.Buffer, LoRaMacDownlinkStatus.BufferSize );
+        }
+
+        switch( DeviceState ) {
+            case DEVICE_STATE_INIT: {
+                LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm;
+                LoRaMacPrimitives.MacMcpsIndication = McpsIndication;
+                LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm;
+                LoRaMacCallbacks.GetBatteryLevel = BoardGetBatteryLevel;
+                LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks );
+
+                TimerInit( &TxNextPacketTimer, OnTxNextPacketTimerEvent );
+
+                TimerInit( &Led1Timer, OnLed1TimerEvent );
+                TimerSetValue( &Led1Timer, 25000 );
+
+                TimerInit( &Led2Timer, OnLed2TimerEvent );
+                TimerSetValue( &Led2Timer, 25000 );
+
+                mibReq.Type = MIB_ADR;
+                mibReq.Param.AdrEnable = LORAWAN_ADR_ON;
+                LoRaMacMibSetRequestConfirm( &mibReq );
+
+                mibReq.Type = MIB_PUBLIC_NETWORK;
+                mibReq.Param.EnablePublicNetwork = LORAWAN_PUBLIC_NETWORK;
+                LoRaMacMibSetRequestConfirm( &mibReq );
+
+#if defined( USE_BAND_868 )
+                LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON );
+                SerialDisplayUpdateDutyCycle( 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 );
+#endif
+
+#endif
+                SerialDisplayUpdateActivationMode( OVER_THE_AIR_ACTIVATION );
+                SerialDisplayUpdateAdr( LORAWAN_ADR_ON );
+                SerialDisplayUpdatePublicNetwork( LORAWAN_PUBLIC_NETWORK );
+
+                LoRaMacDownlinkStatus.DownlinkCounter = 0;
+
+                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;
+
+                if( NextTx == true ) {
+                    LoRaMacMlmeRequest( &mlmeReq );
+                }
+
+                SerialDisplayUpdateEui( 5, DevEui );
+                SerialDisplayUpdateEui( 6, AppEui );
+                SerialDisplayUpdateKey( 7, AppKey );
+
+                // Schedule next packet transmission
+                TxDutyCycleTime = OVER_THE_AIR_ACTIVATION_DUTYCYCLE;
+                DeviceState = DEVICE_STATE_CYCLE;
+
+#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 );
+
+                SerialDisplayUpdateNwkId( LORAWAN_NETWORK_ID );
+                SerialDisplayUpdateDevAddr( DevAddr );
+                SerialDisplayUpdateKey( 12, NwkSKey );
+                SerialDisplayUpdateKey( 13, AppSKey );
+
+                DeviceState = DEVICE_STATE_SEND;
+#endif
+                IsNetworkJoinedStatusUpdate = true;
+                break;
+            }
+            case DEVICE_STATE_SEND: {
+                wait(10);
+                if( NextTx == true ) {
+                    SerialDisplayUpdateUplinkAcked( false );
+                    SerialDisplayUpdateDonwlinkRxData( false );
+                    PrepareTxFrame( AppPort );
+
+                    NextTx = SendFrame( );
+
+                    // Switch LED 1 ON
+                    Led1State = true;
+                    Led1StateChanged = true;
+                    TimerStart( &Led1Timer );
+                }
+                if( ComplianceTest.Running == true ) {
+                    // Schedule next packet transmission
+                    TxDutyCycleTime = 5000000; // 5000000 us
+                } else {
+                    // Schedule next packet transmission
+                    TxDutyCycleTime = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
+                }
+                DeviceState = DEVICE_STATE_CYCLE;
+                break;
+            }
+            case DEVICE_STATE_CYCLE: {
+                DeviceState = DEVICE_STATE_SLEEP;
+
+                // Schedule next packet transmission
+                TimerSetValue( &TxNextPacketTimer, TxDutyCycleTime );
+                TimerStart( &TxNextPacketTimer );
+                break;
+            }
+            case DEVICE_STATE_SLEEP: {
+                // Wake up through events
+                break;
+            }
+            default: {
+                DeviceState = DEVICE_STATE_INIT;
+                break;
+            }
+        }
+    }
+}