LoRaWAN class A/B/C application example for the SKiM980A.
 
#include <stdio.h>
#include "utilities.h"
#include "board.h"
#include "gpio.h"
#include "uart.h"
 
#include "cli.h"
#include "Commissioning.h"
#include "LmHandler.h"
#include "LmhpCompliance.h"
#include "CayenneLpp.h"
#include "LmHandlerMsgDisplay.h"
 
#ifndef ACTIVE_REGION
 
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default."
 
#define ACTIVE_REGION LORAMAC_REGION_EU868
 
#endif
 
#define LORAWAN_DEFAULT_CLASS                       CLASS_A
 
#define APP_TX_DUTYCYCLE                            5000
 
#define APP_TX_DUTYCYCLE_RND                        1000
 
#define LORAWAN_ADR_STATE                           LORAMAC_HANDLER_ADR_ON
 
#define LORAWAN_DEFAULT_DATARATE                    DR_0
 
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE         LORAMAC_HANDLER_UNCONFIRMED_MSG
 
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE            242
 
#define LORAWAN_DUTYCYCLE_ON                        true
 
#define LORAWAN_APP_PORT                            3
 
typedef enum
{
    LORAMAC_HANDLER_TX_ON_TIMER,
    LORAMAC_HANDLER_TX_ON_EVENT,
}LmHandlerTxEvents_t;
 
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE];
 
static LmHandlerAppData_t AppData =
{
    .Buffer = AppDataBuffer,
    .BufferSize = 0,
    .Port = 0
};
 
static bool AppLedStateOn = false;
 
static TimerEvent_t TxTimer;
 
static TimerEvent_t Led4Timer;
 
static TimerEvent_t Led2Timer;
 
static TimerEvent_t LedBeaconTimer;
 
static void OnMacProcessNotify( void );
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size );
static void OnNetworkParametersChange( CommissioningParams_t* params );
static void OnJoinRequest( LmHandlerJoinParams_t* params );
static void OnTxData( LmHandlerTxParams_t* params );
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params );
static void OnBeaconStatusChange( LoRaMAcHandlerBeaconParams_t* params );
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 )
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection );
#else
static void OnSysTimeUpdate( void );
#endif
static void PrepareTxFrame( void );
static void StartTxProcess( LmHandlerTxEvents_t txEvent );
static void UplinkProcess( void );
 
static void OnTxTimerEvent( void* context );
 
static void OnLed4TimerEvent( void* context );
 
static void OnLed2TimerEvent( void* context );
 
static void OnLedBeaconTimerEvent( void* context );
 
static LmHandlerCallbacks_t LmHandlerCallbacks =
{
    .GetBatteryLevel = BoardGetBatteryLevel,
    .GetTemperature = NULL,
    .GetRandomSeed = BoardGetRandomSeed,
    .OnMacProcess = OnMacProcessNotify,
    .OnNvmDataChange = OnNvmDataChange,
    .OnNetworkParametersChange = OnNetworkParametersChange,
    .OnMacMcpsRequest = OnMacMcpsRequest,
    .OnMacMlmeRequest = OnMacMlmeRequest,
    .OnJoinRequest = OnJoinRequest,
    .OnTxData = OnTxData,
    .OnRxData = OnRxData,
    .OnClassChange= OnClassChange,
    .OnBeaconStatusChange = OnBeaconStatusChange,
    .OnSysTimeUpdate = OnSysTimeUpdate,
};
 
static LmHandlerParams_t LmHandlerParams =
{
    .Region = ACTIVE_REGION,
    .AdrEnable = LORAWAN_ADR_STATE,
    .TxDatarate = LORAWAN_DEFAULT_DATARATE,
    .PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK,
    .DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON,
    .DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE,
    .DataBuffer = AppDataBuffer
};
 
static LmhpComplianceParams_t LmhpComplianceParams =
{
    .AdrEnabled = LORAWAN_ADR_STATE,
    .DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON,
    .StopPeripherals = NULL,
    .StartPeripherals = NULL,
};
 
static volatile uint8_t IsMacProcessPending = 0;
 
static volatile uint8_t IsTxFramePending = 0;
 
extern Gpio_t Led4; 
extern Gpio_t Led2; 
extern Gpio_t Led3; 
 
extern Uart_t Uart1;
 
int main( void )
{
    BoardInitMcu( );
    BoardInitPeriph( );
 
    TimerInit( &Led4Timer, OnLed4TimerEvent );
    TimerSetValue( &Led4Timer, 25 );
 
    TimerInit( &Led2Timer, OnLed2TimerEvent );
    TimerSetValue( &Led2Timer, 25 );
 
    TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent );
    TimerSetValue( &LedBeaconTimer, 5000 );
 
    const Version_t appVersion = { .Fields.Major = 1, .Fields.Minor = 0, .Fields.Patch = 0 };
    const Version_t gitHubVersion = { .Fields.Major = 4, .Fields.Minor = 4, .Fields.Patch = 7 };
    DisplayAppInfo( "periodic-uplink-lpp", 
                    &appVersion,
                    &gitHubVersion );
 
    if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )
    {
        printf( "LoRaMac wasn't properly initialized\n" );
        
        while ( 1 )
        {
        }
    }
 
    
    LmHandlerSetSystemMaxRxError( 20 );
 
    
    
    LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams );
 
    LmHandlerJoin( );
 
    StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER );
 
    while( 1 )
    {
        
        CliProcess( &Uart1 );
 
        
        LmHandlerProcess( );
 
        
        UplinkProcess( );
 
        CRITICAL_SECTION_BEGIN( );
        if( IsMacProcessPending == 1 )
        {
            
            IsMacProcessPending = 0;
        }
        else
        {
            
            BoardLowPowerHandler( );
        }
        CRITICAL_SECTION_END( );
    }
}
 
static void OnMacProcessNotify( void )
{
    IsMacProcessPending = 1;
}
 
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size )
{
    DisplayNvmDataChange( state, size );
}
 
static void OnNetworkParametersChange( CommissioningParams_t* params )
{
    DisplayNetworkParametersUpdate( params );
}
 
{
    DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn );
}
 
{
    DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn );
}
 
static void OnJoinRequest( LmHandlerJoinParams_t* params )
{
    DisplayJoinRequestUpdate( params );
    if( params->Status == LORAMAC_HANDLER_ERROR )
    {
        LmHandlerJoin( );
    }
    else
    {
        LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS );
    }
}
 
static void OnTxData( LmHandlerTxParams_t* params )
{
    DisplayTxUpdate( params );
}
 
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params )
{
    DisplayRxUpdate( appData, params );
 
    switch( appData->Port )
    {
    case 1: 
    case LORAWAN_APP_PORT:
        {
            AppLedStateOn = appData->Buffer[0] & 0x01;
            GpioWrite( &Led3, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 1 : 0 );
        }
        break;
    default:
        break;
    }
 
    
    GpioWrite( &Led2, 1 );
    TimerStart( &Led2Timer );
}
 
{
    DisplayClassUpdate( deviceClass );
 
    
    LmHandlerAppData_t appData =
    {
        .Buffer = NULL,
        .BufferSize = 0,
        .Port = 0
    };
    LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG );
}
 
static void OnBeaconStatusChange( LoRaMAcHandlerBeaconParams_t* params )
{
    switch( params->State )
    {
        case LORAMAC_HANDLER_BEACON_RX:
        {
            TimerStart( &LedBeaconTimer );
            break;
        }
        case LORAMAC_HANDLER_BEACON_LOST:
        case LORAMAC_HANDLER_BEACON_NRX:
        {
            TimerStop( &LedBeaconTimer );
            break;
        }
        default:
        {
            break;
        }
    }
 
    DisplayBeaconUpdate( params );
}
 
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 )
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection )
{
 
}
#else
static void OnSysTimeUpdate( void )
{
 
}
#endif
 
static void PrepareTxFrame( void )
{
    if( LmHandlerIsBusy( ) == true )
    {
        return;
    }
 
    uint8_t channel = 0;
 
    AppData.Port = LORAWAN_APP_PORT;
 
    CayenneLppReset( );
 
    uint8_t potiPercentage = 0;
    uint16_t vdd = 0;
 
    
    potiPercentage = BoardGetPotiLevel( );
 
    
    BoardGetBatteryLevel( ); 
    vdd = BoardGetBatteryVoltage( );
 
    CayenneLppAddDigitalInput( channel++, AppLedStateOn );
    CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 );
    CayenneLppAddAnalogInput( channel++, potiPercentage );
    CayenneLppAddAnalogInput( channel++, vdd );
 
    CayenneLppCopy( AppData.Buffer );
    AppData.BufferSize = CayenneLppGetSize( );
 
    if( LmHandlerSend( &AppData, LORAWAN_DEFAULT_CONFIRMED_MSG_STATE ) == LORAMAC_HANDLER_SUCCESS )
    {
        
        GpioWrite( &Led4, 1 );
        TimerStart( &Led4Timer );
    }
}
 
static void StartTxProcess( LmHandlerTxEvents_t txEvent )
{
    switch( txEvent )
    {
    default:
        
    case LORAMAC_HANDLER_TX_ON_TIMER:
        {
            
            TimerInit( &TxTimer, OnTxTimerEvent );
            TimerSetValue( &TxTimer, APP_TX_DUTYCYCLE  + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ) );
            OnTxTimerEvent( NULL );
        }
        break;
    case LORAMAC_HANDLER_TX_ON_EVENT:
        {
        }
        break;
    }
}
 
static void UplinkProcess( void )
{
    uint8_t isPending = 0;
    CRITICAL_SECTION_BEGIN( );
    isPending = IsTxFramePending;
    IsTxFramePending = 0;
    CRITICAL_SECTION_END( );
    if( isPending == 1 )
    {
        PrepareTxFrame( );
    }
}
 
static void OnTxTimerEvent( void* context )
{
    TimerStop( &TxTimer );
 
    IsTxFramePending = 1;
 
    
    TimerSetValue( &TxTimer, APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ) );
    TimerStart( &TxTimer );
}
 
static void OnLed4TimerEvent( void* context )
{
    TimerStop( &Led4Timer );
    
    GpioWrite( &Led4, 0 );
}
 
static void OnLed2TimerEvent( void* context )
{
    TimerStop( &Led2Timer );
    
    GpioWrite( &Led2, 0 );
}
 
static void OnLedBeaconTimerEvent( void* context )
{
    GpioWrite( &Led2, 1 );
    TimerStart( &Led2Timer );
 
    TimerStart( &LedBeaconTimer );
}