LoRaWAN class A application example for the NucleoL152.
#include <stdio.h>
#include "utilities.h"
#include "board.h"
#include "gpio.h"
#include "Commissioning.h"
#include "NvmCtxMgmt.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 APP_TX_DUTYCYCLE 5000
#define APP_TX_DUTYCYCLE_RND 1000
#define LORAWAN_DEFAULT_DATARATE DR_0
#define LORAWAN_CONFIRMED_MSG_ON false
#define LORAWAN_ADR_ON 1
#if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 )
#define LORAWAN_DUTYCYCLE_ON true
#endif
#define LORAWAN_APP_PORT 2
#if( OVER_THE_AIR_ACTIVATION == 0 )
static uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS;
#endif
static uint8_t AppPort = LORAWAN_APP_PORT;
static uint8_t AppDataSize = 1;
static uint8_t AppDataSizeBackup = 1;
#define LORAWAN_APP_DATA_MAX_SIZE 242
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_MAX_SIZE];
static uint8_t IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
static uint32_t TxDutyCycleTime;
static TimerEvent_t TxNextPacketTimer;
static bool AppLedStateOn = false;
static TimerEvent_t Led1Timer;
static TimerEvent_t Led2Timer;
static bool NextTx = true;
static uint8_t IsMacProcessPending = 0;
static enum eDeviceState
{
DEVICE_STATE_RESTORE,
DEVICE_STATE_START,
DEVICE_STATE_JOIN,
DEVICE_STATE_SEND,
DEVICE_STATE_CYCLE,
DEVICE_STATE_SLEEP
}DeviceState;
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;
typedef enum
{
LORAMAC_HANDLER_UNCONFIRMED_MSG = 0,
LORAMAC_HANDLER_CONFIRMED_MSG = !LORAMAC_HANDLER_UNCONFIRMED_MSG
}LoRaMacHandlerMsgTypes_t;
typedef struct LoRaMacHandlerAppData_s
{
LoRaMacHandlerMsgTypes_t MsgType;
uint8_t Port;
uint8_t BufferSize;
uint8_t *Buffer;
}LoRaMacHandlerAppData_t;
LoRaMacHandlerAppData_t AppData =
{
.MsgType = LORAMAC_HANDLER_UNCONFIRMED_MSG,
.Buffer = NULL,
.BufferSize = 0,
.Port = 0
};
extern Gpio_t Led1;
extern Gpio_t Led2;
const char* MacStatusStrings[] =
{
"OK",
"Busy",
"Service unknown",
"Parameter invalid",
"Frequency invalid",
"Datarate invalid",
"Frequency or datarate invalid",
"No network joined",
"Length error",
"Region not supported",
"Skipped APP data",
"Duty-cycle restricted",
"No channel found",
"No free channel found",
"Busy beacon reserved time",
"Busy ping-slot window time",
"Busy uplink collision",
"Crypto error",
"FCnt handler error",
"MAC command error",
"ClassB error",
"Confirm queue error",
"Multicast group undefined",
"Unknown error",
};
const char* EventInfoStatusStrings[] =
{
"OK",
"Error",
"Tx timeout",
"Rx 1 timeout",
"Rx 2 timeout",
"Rx1 error",
"Rx2 error",
"Join failed",
"Downlink repeated",
"Tx DR payload size error",
"Downlink too many frames loss",
"Address fail",
"MIC fail",
"Multicast fail",
"Beacon locked",
"Beacon lost",
"Beacon not found"
};
void PrintHexBuffer( uint8_t *buffer, uint8_t size )
{
uint8_t newline = 0;
for( uint8_t i = 0; i < size; i++ )
{
if( newline != 0 )
{
printf( "\n" );
newline = 0;
}
printf( "%02X ", buffer[i] );
if( ( ( i + 1 ) % 16 ) == 0 )
{
newline = 1;
}
}
printf( "\n" );
}
static void JoinNetwork( void )
{
printf( "\n###### ===== MLME-Request - MLME_JOIN ==== ######\n" );
printf( "STATUS : %s\n", MacStatusStrings[status] );
{
printf( "###### ===== JOINING ==== ######\n" );
DeviceState = DEVICE_STATE_SLEEP;
}
else
{
{
}
DeviceState = DEVICE_STATE_CYCLE;
}
}
static void PrepareTxFrame( uint8_t port )
{
switch( port )
{
case 2:
{
AppDataSizeBackup = AppDataSize = 1;
AppDataBuffer[0] = AppLedStateOn;
}
break;
case 224:
if( ComplianceTest.LinkCheck == true )
{
ComplianceTest.LinkCheck = false;
AppDataSize = 3;
AppDataBuffer[0] = 5;
AppDataBuffer[1] = ComplianceTest.DemodMargin;
AppDataBuffer[2] = ComplianceTest.NbGateways;
ComplianceTest.State = 1;
}
else
{
switch( ComplianceTest.State )
{
case 4:
ComplianceTest.State = 1;
break;
case 1:
AppDataSize = 2;
AppDataBuffer[0] = ComplianceTest.DownLinkCounter >> 8;
AppDataBuffer[1] = ComplianceTest.DownLinkCounter;
break;
}
}
break;
default:
break;
}
}
static bool SendFrame( void )
{
{
}
else
{
if( IsTxConfirmed == false )
{
}
else
{
}
}
AppData.MsgType = ( mcpsReq.
Type ==
MCPS_CONFIRMED ) ? LORAMAC_HANDLER_CONFIRMED_MSG : LORAMAC_HANDLER_UNCONFIRMED_MSG;
printf( "\n###### ===== MCPS-Request ==== ######\n" );
printf( "STATUS : %s\n", MacStatusStrings[status] );
{
}
{
return false;
}
return true;
}
static void OnTxNextPacketTimerEvent( void* context )
{
TimerStop( &TxNextPacketTimer );
{
{
JoinNetwork( );
}
else
{
DeviceState = DEVICE_STATE_SEND;
NextTx = true;
}
}
}
static void OnLed1TimerEvent( void* context )
{
TimerStop( &Led1Timer );
GpioWrite( &Led1, 0 );
}
static void OnLed2TimerEvent( void* context )
{
TimerStop( &Led2Timer );
GpioWrite( &Led2, 0 );
}
{
printf( "\n###### ===== MCPS-Confirm ==== ######\n" );
printf(
"STATUS : %s\n", EventInfoStatusStrings[mcpsConfirm->
Status] );
{
}
else
{
{
{
break;
}
{
break;
}
{
break;
}
default:
break;
}
GpioWrite( &Led1, 1 );
TimerStart( &Led1Timer );
}
printf(
"\n###### ===== UPLINK FRAME %lu ==== ######\n", mcpsConfirm->
UpLinkCounter );
printf( "\n" );
printf(
"CLASS : %c\n",
"ABC"[mibReq.
Param.
Class] );
printf( "\n" );
printf( "TX PORT : %d\n", AppData.Port );
if( AppData.BufferSize != 0 )
{
printf( "TX DATA : " );
if( AppData.MsgType == LORAMAC_HANDLER_CONFIRMED_MSG )
{
printf(
"CONFIRMED - %s\n", ( mcpsConfirm->
AckReceived != 0 ) ?
"ACK" :
"NACK" );
}
else
{
printf( "UNCONFIRMED\n" );
}
PrintHexBuffer( AppData.Buffer, AppData.BufferSize );
}
printf( "\n" );
printf(
"DATA RATE : DR_%d\n", mcpsConfirm->
Datarate );
{
}
printf(
"TX POWER : %d\n", mcpsConfirm->
TxPower );
{
printf("CHANNEL MASK: ");
#if defined( REGION_AS923 ) || defined( REGION_CN779 ) || \
defined( REGION_EU868 ) || defined( REGION_IN865 ) || \
defined( REGION_KR920 ) || defined( REGION_EU433 ) || \
defined( REGION_RU864 )
for( uint8_t i = 0; i < 1; i++)
#elif defined( REGION_AU915 ) || defined( REGION_US915 ) || defined( REGION_CN470 )
for( uint8_t i = 0; i < 5; i++)
#else
#error "Please define a region in the compiler options."
#endif
{
}
printf("\n");
}
printf( "\n" );
}
{
printf( "\n###### ===== MCPS-Indication ==== ######\n" );
printf(
"STATUS : %s\n", EventInfoStatusStrings[mcpsIndication->
Status] );
{
return;
}
{
{
break;
}
{
break;
}
{
break;
}
{
break;
}
default:
break;
}
{
OnTxNextPacketTimerEvent( NULL );
}
if( ComplianceTest.Running == true )
{
ComplianceTest.DownLinkCounter++;
}
if( mcpsIndication->
RxData ==
true )
{
switch( mcpsIndication->
Port )
{
case 1:
case 2:
{
AppLedStateOn = mcpsIndication->
Buffer[0] & 0x01;
}
break;
case 224:
if( ComplianceTest.Running == false )
{
( mcpsIndication->
Buffer[0] == 0x01 ) &&
( mcpsIndication->
Buffer[1] == 0x01 ) &&
( mcpsIndication->
Buffer[2] == 0x01 ) &&
( mcpsIndication->
Buffer[3] == 0x01 ) )
{
IsTxConfirmed = false;
AppPort = 224;
AppDataSizeBackup = AppDataSize;
AppDataSize = 2;
ComplianceTest.DownLinkCounter = 0;
ComplianceTest.LinkCheck = false;
ComplianceTest.DemodMargin = 0;
ComplianceTest.NbGateways = 0;
ComplianceTest.Running = true;
ComplianceTest.State = 1;
#if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 )
#endif
}
}
else
{
ComplianceTest.State = mcpsIndication->
Buffer[0];
switch( ComplianceTest.State )
{
case 0:
IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
AppPort = LORAWAN_APP_PORT;
AppDataSize = AppDataSizeBackup;
ComplianceTest.DownLinkCounter = 0;
ComplianceTest.Running = false;
#if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 )
#endif
break;
case 1:
AppDataSize = 2;
break;
case 2:
IsTxConfirmed = true;
ComplianceTest.State = 1;
break;
case 3:
IsTxConfirmed = false;
ComplianceTest.State = 1;
break;
case 4:
AppDataBuffer[0] = 4;
for( uint8_t i = 1; i < MIN( AppDataSize, LORAWAN_APP_DATA_MAX_SIZE ); i++ )
{
AppDataBuffer[i] = mcpsIndication->
Buffer[i] + 1;
}
break;
case 5:
{
printf( "\n###### ===== MLME-Request - MLME_LINK_CHECK ==== ######\n" );
printf( "STATUS : %s\n", MacStatusStrings[status] );
}
break;
case 6:
{
IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
AppPort = LORAWAN_APP_PORT;
AppDataSize = AppDataSizeBackup;
ComplianceTest.DownLinkCounter = 0;
ComplianceTest.Running = false;
#if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 )
#endif
JoinNetwork( );
}
break;
case 7:
{
{
printf( "\n###### ===== MLME-Request - MLME_TXCW ==== ######\n" );
printf( "STATUS : %s\n", MacStatusStrings[status] );
}
{
printf( "\n###### ===== MLME-Request - MLME_TXCW1 ==== ######\n" );
printf( "STATUS : %s\n", MacStatusStrings[status] );
}
ComplianceTest.State = 1;
}
break;
case 8:
{
printf( "\n###### ===== MLME-Request - MLME_DEVICE_TIME ==== ######\n" );
printf( "STATUS : %s\n", MacStatusStrings[status] );
}
break;
default:
break;
}
}
break;
default:
break;
}
}
GpioWrite( &Led2, 1 );
TimerStart( &Led2Timer );
const char *slotStrings[] = { "1", "2", "C", "C Multicast", "B Ping-Slot", "B Multicast Ping-Slot" };
printf(
"\n###### ===== DOWNLINK FRAME %lu ==== ######\n", mcpsIndication->
DownLinkCounter );
printf(
"RX WINDOW : %s\n", slotStrings[mcpsIndication->
RxSlot] );
printf(
"RX PORT : %d\n", mcpsIndication->
Port );
{
printf( "RX DATA : \n" );
}
printf( "\n" );
printf(
"DATA RATE : DR_%d\n", mcpsIndication->
RxDatarate );
printf(
"RX RSSI : %d\n", mcpsIndication->
Rssi );
printf(
"RX SNR : %d\n", mcpsIndication->
Snr );
printf( "\n" );
}
{
printf( "\n###### ===== MLME-Confirm ==== ######\n" );
printf(
"STATUS : %s\n", EventInfoStatusStrings[mlmeConfirm->
Status] );
{
}
{
{
{
printf( "###### ===== JOINED ==== ######\n" );
printf( "\nOTAA\n\n" );
printf( "\n\n" );
printf( "\n" );
DeviceState = DEVICE_STATE_SEND;
}
else
{
JoinNetwork( );
}
break;
}
{
{
if( ComplianceTest.Running == true )
{
ComplianceTest.LinkCheck = true;
ComplianceTest.NbGateways = mlmeConfirm->
NbGateways;
}
}
break;
}
default:
break;
}
}
{
{
printf( "\n###### ===== MLME-Indication ==== ######\n" );
printf(
"STATUS : %s\n", EventInfoStatusStrings[mlmeIndication->
Status] );
}
{
}
{
{
OnTxNextPacketTimerEvent( NULL );
break;
}
default:
break;
}
}
void OnMacProcessNotify( void )
{
IsMacProcessPending = 1;
}
int main( void )
{
uint8_t devEui[8] = { 0 };
uint8_t joinEui[8] = { 0 };
uint8_t sePin[4] = { 0 };
BoardInitMcu( );
BoardInitPeriph( );
{
printf( "LoRaMac wasn't properly initialized, error: %s", MacStatusStrings[status] );
while ( 1 )
{
}
}
DeviceState = DEVICE_STATE_RESTORE;
printf( "###### ===== ClassA demo application v1.0.0 ==== ######\n\n" );
while( 1 )
{
if( Radio.IrqProcess != NULL )
{
Radio.IrqProcess( );
}
switch( DeviceState )
{
case DEVICE_STATE_RESTORE:
{
if( NvmCtxMgmtRestore( ) == NVMCTXMGMT_STATUS_SUCCESS )
{
printf( "\n###### ===== CTXS RESTORED ==== ######\n\n" );
}
else
{
#if( OVER_THE_AIR_ACTIVATION == 0 )
mibReq.
Param.
AbpLrWanVersion.Value = ABP_ACTIVATION_LRWAN_VERSION;
#if( STATIC_DEVICE_ADDRESS != 1 )
srand1( BoardGetRandomSeed( ) );
DevAddr = randr( 0, 0x01FFFFFF );
#endif
#endif // #if( OVER_THE_AIR_ACTIVATION == 0 )
}
DeviceState = DEVICE_STATE_START;
break;
}
case DEVICE_STATE_START:
{
TimerInit( &TxNextPacketTimer, OnTxNextPacketTimerEvent );
TimerInit( &Led1Timer, OnLed1TimerEvent );
TimerSetValue( &Led1Timer, 25 );
TimerInit( &Led2Timer, OnLed2TimerEvent );
TimerSetValue( &Led2Timer, 25 );
#if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 )
#endif
{
{
DeviceState = DEVICE_STATE_JOIN;
}
else
{
DeviceState = DEVICE_STATE_SEND;
NextTx = true;
}
}
break;
}
case DEVICE_STATE_JOIN:
{
for( int i = 1; i < 8; i++ )
{
}
printf( "\n" );
for( int i = 1; i < 8; i++ )
{
}
printf( "\n" );
for( int i = 1; i < 4; i++ )
{
}
printf( "\n\n" );
#if( OVER_THE_AIR_ACTIVATION == 0 )
printf( "###### ===== JOINED ==== ######\n" );
printf( "\nABP\n\n" );
printf( "DevAddr : %08lX\n", DevAddr );
printf( "\n\n" );
DeviceState = DEVICE_STATE_SEND;
#else
JoinNetwork( );
#endif
break;
}
case DEVICE_STATE_SEND:
{
if( NextTx == true )
{
PrepareTxFrame( AppPort );
NextTx = SendFrame( );
}
DeviceState = DEVICE_STATE_CYCLE;
break;
}
case DEVICE_STATE_CYCLE:
{
DeviceState = DEVICE_STATE_SLEEP;
if( ComplianceTest.Running == true )
{
TxDutyCycleTime = 5000;
}
else
{
TxDutyCycleTime = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
}
TimerSetValue( &TxNextPacketTimer, TxDutyCycleTime );
TimerStart( &TxNextPacketTimer );
break;
}
case DEVICE_STATE_SLEEP:
{
if( NvmCtxMgmtStore( ) == NVMCTXMGMT_STATUS_SUCCESS )
{
printf( "\n###### ===== CTXS STORED ==== ######\n" );
}
CRITICAL_SECTION_BEGIN( );
if( IsMacProcessPending == 1 )
{
IsMacProcessPending = 0;
}
else
{
BoardLowPowerHandler( );
}
CRITICAL_SECTION_END( );
break;
}
default:
{
DeviceState = DEVICE_STATE_START;
break;
}
}
}
}