A very easy to understand LoRaWAN-node code.

Dependencies:   LoRaWAN-lib SX1272Lib X_NUCLEO_IKS01A1 mbed

Important parameters:

• In comissioning.h: DevEUI, AppEUI, AppKEY, DevADR, NwksKEY, AppsKEY, OTAA and public network. Frequency and channel block to use, confirmed or unconfirmed messages, app port, app data size and OTAA and Tx duty cycles.

• In LoRaMac.h: Maximum payload and MAC commands length, receive delays, max FCNT, adr ack limit, timeout and delay, max ack retries, rssi threshold and sync words.

• In LoRaMac.cpp: Maximum payload, MAC commands and FRMpayload length.

• In LoRaMac-board.h: Tx power, data rates and band settings.

NOTE: Please refer to LoRaWAN regional parameters (page 12 for US band) to know which parameters you can modify.

Revision:
0:60ff878b27b8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Code/main.cpp	Tue Apr 03 17:09:34 2018 +0000
@@ -0,0 +1,411 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (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 "LoRaDeviceFunctions.h"
+#include "LoRaMacLayerService.h"
+
+
+#if( OVER_THE_AIR_ACTIVATION != 0 )         
+
+uint8_t DevEui[] = LORAWAN_DEVICE_EUI;
+uint8_t AppEui[] = LORAWAN_APPLICATION_EUI;
+uint8_t AppKey[] = LORAWAN_APPLICATION_KEY;
+
+#else
+
+uint8_t NwkSKey[] = LORAWAN_NWKSKEY;
+uint8_t AppSKey[] = LORAWAN_APPSKEY;
+uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS;
+
+#endif
+
+
+uint8_t AppPort = LORAWAN_APP_PORT;     //application port
+
+uint8_t AppDataSize = LORAWAN_APP_DATA_SIZE;    //data size
+
+uint8_t AppData[LORAWAN_APP_DATA_MAX_SIZE];     //payload array
+
+uint8_t IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;    //Indicates if the node is sending confirmed or unconfirmed messages
+
+TimerEvent_t TxNextPacketTimer;     //Timer to handle the application data transmission duty cycle
+
+bool IsTxIntUpdate = false;     //Indicates if a new transmit interrupt can be set
+
+bool NextTx = true;     //Indicates if a new packet can be sent
+
+bool IsNetworkJoinedStatusUpdate = false;   //Indicates if the MAC layer network join status has changed.
+
+bool IsTxUpdate = false;    //Indicates if the message sent.
+
+bool IsRxUpdate = false;    //Indicates if the message received in the RX window.
+
+float sensor_data;          //Used to store the sensor data
+
+uint32_t TxDutyCycleTime = APP_TX_DUTYCYCLE;
+
+eDevicState DeviceState;
+
+sLoRaMacUplinkStatus LoRaMacUplinkStatus;
+
+sLoRaMacDownlinkStatus LoRaMacDownlinkStatus;
+
+LoRaMacPrimitives_t LoRaPrimitives;
+
+LoRaMacCallback_t LoRaCallbacks;
+
+MibRequestConfirm_t LoRaMibReq;
+
+MlmeReq_t mlmeReq;
+
+uint16_t ChannelMaskTemp[6] = {0};
+
+uint8_t BoardGetBatteryLevel( void ) 
+{
+    return 0xFE;
+}
+DigitalIn I2cInterrupt( D5 );
+I2C I2c(I2C_SDA, I2C_SCL);
+
+DigitalOut Pc7( D9 );
+DigitalIn Pc1( A4 );
+
+//DigitalIn UsrButton( PC_13 );
+DigitalIn UsrButton( D10 );
+
+DigitalOut Led( D6 );
+
+#ifdef USE_IKS01A1_SENSOR
+X_NUCLEO_IKS01A1 *mems_expansion_board = X_NUCLEO_IKS01A1::Instance();
+MotionSensor *accelerometer = mems_expansion_board->GetAccelerometer();
+HumiditySensor *humidity_sensor = mems_expansion_board->ht_sensor;;
+PressureSensor *pressure_sensor = mems_expansion_board->pt_sensor;
+TempSensor *temp_sensor1 = mems_expansion_board->ht_sensor;
+TempSensor *temp_sensor2 = mems_expansion_board->pt_sensor;
+#endif
+
+SX1272MB2xAS Radio( NULL );
+
+
+uint32_t BoardGetRandomSeed( void )
+{
+    return ( ( *( uint32_t* )ID1 ) ^ ( *( uint32_t* )ID2 ) ^ ( *( uint32_t* )ID3 ) );
+}
+
+void BoardGetDevEUI( uint8_t *id )
+{
+    uint32_t *pDevEuiHWord = ( uint32_t* )&id[4];
+
+    if( *pDevEuiHWord == 0 )
+    {        
+        *pDevEuiHWord = BoardGetRandomSeed( );
+    }
+    
+}
+
+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;            
+        }
+        else
+        {
+            DeviceState = DEVICE_STATE_JOIN;
+        }
+        NextTx = true;
+    }
+}
+
+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;        
+    }
+    else
+    {
+        LoRaMacUplinkStatus.Acked = false;
+        LoRaMacUplinkStatus.Port = AppPort;
+        LoRaMacUplinkStatus.Buffer = AppData;
+        LoRaMacUplinkStatus.BufferSize = AppDataSize;
+
+        if( ( IsTxConfirmed == false ) || ( LoRaMacUplinkStatus.UplinkCounter == 0 ) )
+        {
+            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;
+        }
+    }
+
+    LoRaMacUplinkStatus.Type = mcpsReq.Type;
+
+    if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK )
+    {
+        return false;
+    }
+    return true;
+}
+
+int main( void )
+{
+   
+    TimerTimeCounterInit( );  //initialize timer
+
+    DeviceState = DEVICE_STATE_INIT; // Indicate Device state
+
+    while( 1 )
+    {               
+        if( IsNetworkJoinedStatusUpdate == true )
+        {
+            //Checks if the device has joined the server and updates the joined status
+            IsNetworkJoinedStatusUpdate = false;
+            LoRaMibReq.Type = MIB_NETWORK_JOINED;
+            LoRaMacMibGetRequestConfirm( &LoRaMibReq ); 
+        }
+
+        if( IsTxIntUpdate == true )
+        {
+            // Initialize next Tx Interrupt
+            IsTxIntUpdate = false;
+            TimerSetValue( &TxNextPacketTimer, TxDutyCycleTime );
+            TimerStart( &TxNextPacketTimer );
+          
+        }
+
+        if( IsRxUpdate == true )
+        {
+            // If downlink received then update Serial Terminal Rx parameters
+            IsRxUpdate = false;
+            SerialDisplayRxUpdate( );            
+        }
+        
+        switch( DeviceState )           //realizes different tasks depending on device state
+        {
+            case DEVICE_STATE_INIT:
+            {
+                
+                LoRaPrimitives.MacMcpsConfirm = McpsConfirm;    //Update uplink and event info status
+                LoRaPrimitives.MacMcpsIndication = McpsIndication;      //
+                LoRaPrimitives.MacMlmeConfirm = MlmeConfirm;
+                LoRaCallbacks.GetBatteryLevel = BoardGetBatteryLevel;      //0: external power source, 1-254: battery level, 255: couldnt measure battery level
+                LoRaMacInitialization( &LoRaPrimitives, &LoRaCallbacks );  //Sets radio parameters (channels, frequency, timers, windows)
+            
+                TimerInit( &TxNextPacketTimer, OnTxNextPacketTimerEvent );
+            
+                LoRaMibReq.Type = MIB_ADR;
+                LoRaMibReq.Param.AdrEnable = LORAWAN_ADR_ON;
+                LoRaMacMibSetRequestConfirm( &LoRaMibReq );
+            
+                LoRaMibReq.Type = MIB_PUBLIC_NETWORK;
+                LoRaMibReq.Param.EnablePublicNetwork = LORAWAN_PUBLIC_NETWORK;
+                LoRaMacMibSetRequestConfirm( &LoRaMibReq );
+            
+                LoRaMibReq.Type = MIB_CHANNELS_TX_POWER;
+                LoRaMibReq.Param.ChannelsTxPower = LORAWAN_TX_POWER;
+                LoRaMacMibSetRequestConfirm( &LoRaMibReq );
+                
+                LoRaMibReq.Type = MIB_CHANNELS_MASK;
+                LoRaMibReq.Param.ChannelsMask = ChannelMaskTemp;       
+                //channel mask for USE_BAND_915_HYBRID
+                ChannelMaskTemp[0] = 0x00FF;
+                ChannelMaskTemp[1] = 0x0000;
+                ChannelMaskTemp[2] = 0x0000;
+                ChannelMaskTemp[3] = 0x0000;
+                ChannelMaskTemp[4] = 0x0001;
+                LoRaMacMibSetRequestConfirm( &LoRaMibReq );
+                
+                LoRaMacDownlinkStatus.DownlinkCounter = 0;
+          
+                DeviceState = DEVICE_STATE_JOIN;        // Change Device state
+                break;
+            }
+            case DEVICE_STATE_JOIN:
+            {
+#if( OVER_THE_AIR_ACTIVATION != 0 )                // OTAA 
+                           
+                BoardGetDevEUI( DevEui );        // Generate DevEUI if not defined by User 
+                
+                mlmeReq.Type = MLME_JOIN;       //Changes Mlmereq content to send in join request
+                mlmeReq.Req.Join.DevEui = DevEui;
+                mlmeReq.Req.Join.AppEui = AppEui;
+                mlmeReq.Req.Join.AppKey = AppKey;
+            
+                if( NextTx == true )
+                {
+                    LoRaMacMlmeRequest( &mlmeReq );
+                }          
+          
+                SerialDisplayJoinUpdate( );     // Show on serial terminal                           
+
+                DeviceState = DEVICE_STATE_SLEEP;
+
+#else                                           // ABP
+    
+                 // Choose a random device address if not already defined in Config.h
+                if( DevAddr == 0 )
+                {
+                    // Random seed initialization
+                    srand1( BoardGetRandomSeed( ) );
+                    DevAddr = randr( 0, 0x01FFFFFF );
+                }
+            
+                LoRaMibReq.Type = MIB_NET_ID;
+                LoRaMibReq.Param.NetID = LORAWAN_NETWORK_ID;
+                LoRaMacMibSetRequestConfirm( &LoRaMibReq );
+            
+                LoRaMibReq.Type = MIB_DEV_ADDR;
+                LoRaMibReq.Param.DevAddr = DevAddr;
+                LoRaMacMibSetRequestConfirm( &LoRaMibReq );
+            
+                LoRaMibReq.Type = MIB_NWK_SKEY;
+                LoRaMibReq.Param.NwkSKey = NwkSKey;
+                LoRaMacMibSetRequestConfirm( &LoRaMibReq );
+            
+                LoRaMibReq.Type = MIB_APP_SKEY;
+                LoRaMibReq.Param.AppSKey = AppSKey;
+                LoRaMacMibSetRequestConfirm( &LoRaMibReq );
+            
+                LoRaMibReq.Type = MIB_NETWORK_JOINED;
+                LoRaMibReq.Param.IsNetworkJoined = true;
+                LoRaMacMibSetRequestConfirm( &LoRaMibReq );
+
+                DeviceState = DEVICE_STATE_SEND;
+#endif
+                IsNetworkJoinedStatusUpdate = true;
+                break;
+            }
+            case DEVICE_STATE_SEND:
+            {   
+                            
+                if( NextTx == true )
+                {   
+                    //===============Functions to get X_NUCLEO_IKS01A1 sensor data===============              
+
+                    temp_sensor2->GetTemperature(&sensor_data);
+                    int16_t tmp_data = (int16_t) (sensor_data*10 + 0.5);
+                    AppData[2] = (int8_t) ( ( tmp_data >> 8 ) & 0xFF );
+                    AppData[3] = (int8_t) tmp_data & 0xFF; 
+                                    
+                    pressure_sensor->GetPressure(&sensor_data);
+                    int16_t tmp =  (int16_t) ( sensor_data * 10 );
+                    AppData[6] = ( tmp >> 8 ) & 0xFF;
+                    AppData[7] = ( tmp ) & 0xFF;
+                    
+                    humidity_sensor->GetHumidity(&sensor_data);
+                    AppData[10] = (uint8_t) ( sensor_data * 2 );
+                    
+                    //accelerometer->Get_X_Axes(Accl_Value);
+                    //AppData[BuffPtr++] = ( (int16_t) Accl_Value[0] >> 8 ) & 0xFF;
+                    //AppData[BuffPtr++] = ( (int16_t) Accl_Value[0] ) & 0xFF; 
+                    //AppData[BuffPtr++] = ( (int16_t) Accl_Value[1] >> 8 ) & 0xFF;
+                    //AppData[BuffPtr++] = ( (int16_t) Accl_Value[1] ) & 0xFF; 
+                    //AppData[BuffPtr++] = ( (int16_t) Accl_Value[2] >> 8 ) & 0xFF;
+                    //AppData[BuffPtr++] = ( (int16_t) Accl_Value[2] ) & 0xFF;  
+                    //===========================Custom payload============================
+                    
+                    //In this case the custom payload is the cayene LPP structure for the sensors data
+                    AppData[0]=0x01;
+                    AppData[1]=0x67;
+ 
+                    AppData[4]=0x02;
+                    AppData[5]=0x73;
+
+                    AppData[8]=0x03;
+                    AppData[9]=0x68;
+   
+      
+                    //=========================Cayenne LPP format=======================
+                    /*
+                    STRUCTURE:
+                    
+                    1 Byte      1 Byte      N Bytes     1 Byte       1 Byte  M Bytes ...
+                    
+                    Data1 Ch.   Data1 Type   Data1     Data2 Ch.   Data2 Type  Data2 ...
+                    
+                    COMMON TYP IDENTIFIERS AND RESOLUTIONS:
+                    
+                    TYPE                LPP    HEX   DATA SIZE     RESOLUTION PER BIT
+                    
+                    Temperature         103     67      2           0.1 °C Signed MSB
+                    Humidity            104     68      1           0.5 % Unsigned
+                    Accelerometer       113     71      6           0.001 G Signed MSB per axis
+                    Barometer           115     73      2           0.1 hPa Unsigned MSB
+                      
+                    */                           
+                    // Send payload over the air
+                    NextTx = SendFrame( );             
+                }
+
+                if( NextTx == false )
+                {
+
+                    SerialDisplayTxUpdate( );
+                }
+                
+                DeviceState = DEVICE_STATE_SLEEP;
+                break;
+            }            
+            case DEVICE_STATE_SLEEP:
+            {
+                break;
+            }
+            default:
+            {
+                DeviceState = DEVICE_STATE_INIT;
+                break;
+            }
+        }
+    }
+}