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.

Code/main.cpp

Committer:
dgabino
Date:
2018-04-03
Revision:
0:60ff878b27b8

File content as of revision 0:60ff878b27b8:

/*
 / _____)             _              | |
( (____  _____ ____ _| |_ _____  ____| |__
 \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 _____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
    (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;
            }
        }
    }
}