LoRaMAC  4.5.2
Documentation of the API
periodic-uplink-lpp/SKiM880B/main.c

LoRaWAN class A/B/C application example for the SKiM880B.

#include <stdio.h>
#include "../firmwareVersion.h"
#include "../../common/githubVersion.h"
#include "utilities.h"
#include "board.h"
#include "gpio.h"
#include "uart.h"
#include "RegionCommon.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
#ifndef LORAWAN_DEFAULT_CLASS
#define LORAWAN_DEFAULT_CLASS CLASS_A
#endif
#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 OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn );
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn );
static void OnJoinRequest( LmHandlerJoinParams_t* params );
static void OnTxData( LmHandlerTxParams_t* params );
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params );
static void OnClassChange( DeviceClass_t deviceClass );
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 OnTxPeriodicityChanged( uint32_t periodicity );
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed );
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity );
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,
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_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 =
{
.FwVersion.Value = FIRMWARE_VERSION,
.OnTxPeriodicityChanged = OnTxPeriodicityChanged,
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged,
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged,
};
static volatile uint8_t IsMacProcessPending = 0;
static volatile uint8_t IsTxFramePending = 0;
static volatile uint32_t TxPeriodicity = 0;
extern Gpio_t Led4; // Tx
extern Gpio_t Led2; // Rx and blinks every 5 seconds when beacon is acquired
extern Gpio_t Led3; // App
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 );
// Initialize transmission periodicity variable
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
const Version_t appVersion = { .Value = FIRMWARE_VERSION };
const Version_t gitHubVersion = { .Value = GITHUB_VERSION };
DisplayAppInfo( "periodic-uplink-lpp",
&appVersion,
&gitHubVersion );
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )
{
printf( "LoRaMac wasn't properly initialized\n" );
// Fatal error, endless loop.
while ( 1 )
{
}
}
// Set system maximum tolerated rx error in milliseconds
LmHandlerSetSystemMaxRxError( 20 );
// The LoRa-Alliance Compliance protocol package should always be
// initialized and activated.
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams );
LmHandlerJoin( );
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER );
while( 1 )
{
// Process characters sent over the command line interface
CliProcess( &Uart1 );
// Processes the LoRaMac events
LmHandlerProcess( );
// Process application uplinks management
UplinkProcess( );
CRITICAL_SECTION_BEGIN( );
if( IsMacProcessPending == 1 )
{
// Clear flag and prevent MCU to go into low power modes.
IsMacProcessPending = 0;
}
else
{
// The MCU wakes up through events
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 );
}
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn )
{
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn );
}
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t 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: // The application LED can be controlled on port 1 or 2
case LORAWAN_APP_PORT:
{
AppLedStateOn = appData->Buffer[0] & 0x01;
GpioWrite( &Led3, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 1 : 0 );
}
break;
default:
break;
}
// Switch LED 2 ON for each received downlink
GpioWrite( &Led2, 1 );
TimerStart( &Led2Timer );
}
static void OnClassChange( DeviceClass_t deviceClass )
{
DisplayClassUpdate( deviceClass );
// Inform the server as soon as possible that the end-device has switched to ClassB
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;
// Read the current potentiometer setting in percent
potiPercentage = BoardGetPotiLevel( );
// Read the current voltage level
BoardGetBatteryLevel( ); // Updates the value returned by BoardGetBatteryVoltage( ) function.
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, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS )
{
// Switch LED 4 ON
GpioWrite( &Led4, 1 );
TimerStart( &Led4Timer );
}
}
static void StartTxProcess( LmHandlerTxEvents_t txEvent )
{
switch( txEvent )
{
default:
// Intentional fall through
case LORAMAC_HANDLER_TX_ON_TIMER:
{
// Schedule 1st packet transmission
TimerInit( &TxTimer, OnTxTimerEvent );
TimerSetValue( &TxTimer, TxPeriodicity );
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 OnTxPeriodicityChanged( uint32_t periodicity )
{
TxPeriodicity = periodicity;
if( TxPeriodicity == 0 )
{ // Revert to application default periodicity
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
}
// Update timer periodicity
TimerStop( &TxTimer );
TimerSetValue( &TxTimer, TxPeriodicity );
TimerStart( &TxTimer );
}
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed )
{
LmHandlerParams.IsTxConfirmed = isTxConfirmed;
}
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity )
{
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity;
}
static void OnTxTimerEvent( void* context )
{
TimerStop( &TxTimer );
IsTxFramePending = 1;
// Schedule next transmission
TimerSetValue( &TxTimer, TxPeriodicity );
TimerStart( &TxTimer );
}
static void OnLed4TimerEvent( void* context )
{
TimerStop( &Led4Timer );
// Switch LED 4 OFF
GpioWrite( &Led4, 0 );
}
static void OnLed2TimerEvent( void* context )
{
TimerStop( &Led2Timer );
// Switch LED 2 OFF
GpioWrite( &Led2, 0 );
}
static void OnLedBeaconTimerEvent( void* context )
{
GpioWrite( &Led2, 1 );
TimerStart( &Led2Timer );
TimerStart( &LedBeaconTimer );
}
LoRaMacStatus_t
LoRaMacStatus_t
Definition: LoRaMac.h:2269
DeviceClass_t
DeviceClass_t
Definition: LoRaMacTypes.h:571
MlmeReq_t
Definition: LoRaMac.h:1202
REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY
#define REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY
Definition: RegionCommon.h:108
McpsReq_t
Definition: LoRaMac.h:858
RegionCommon.h
Region independent implementations which are common to all regions.