Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: SX1272lib-PABOOST-HW-Modification mbed
Fork of LoRaWAN_ELMO_TxRx_Template by
main.cpp
- Committer:
- mleksio
- Date:
- 2016-03-15
- Revision:
- 1:2be292bd43f9
- Parent:
- 0:c58229885f95
- Child:
- 3:73c2fa59588c
File content as of revision 1:2be292bd43f9:
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech
Description: LoRaMac classA device implementation
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Miguel Luis and Gregory Cristian
*/
/*! \file classA/LoRaMote/main.c */
#include <string.h>
#include <math.h>
#include "mbed.h"
#include "utilities.h"
#include "LoRaMac.h"
#include "Comissioning.h"
DigitalOut Led1(LED1);
DigitalOut Led2(LED2);
/*!
* Join requests trials duty cycle.
*/
#define OVER_THE_AIR_ACTIVATION_DUTYCYCLE 10000000 // 10 [s] value in us
/*!
* Defines the application data transmission duty cycle. 5s, value in [us].
*/
#define APP_TX_DUTYCYCLE 5000000
/*!
* Defines a random delay for application data transmission duty cycle. 1s,
* value in [us].
*/
#define APP_TX_DUTYCYCLE_RND 1000000
/*!
* LoRaWAN confirmed messages
*/
#define LORAWAN_CONFIRMED_MSG_ON false
/*!
* LoRaWAN Adaptive Data Rate
*
* \remark Please note that when ADR is enabled the end-device should be static
*/
#define LORAWAN_ADR_ON 1
#if defined( USE_BAND_868 )
#include "LoRaMacTest.h"
/*!
* LoRaWAN ETSI duty cycle control enable/disable
*
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes
*/
#define LORAWAN_DUTYCYCLE_ON true
#define USE_SEMTECH_DEFAULT_CHANNEL_LINEUP 1
#if( USE_SEMTECH_DEFAULT_CHANNEL_LINEUP == 1 )
#define LC4 { 867100000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
#define LC5 { 867300000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
#define LC6 { 867500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
#define LC7 { 867700000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
#define LC8 { 867900000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
#define LC9 { 868800000, { ( ( DR_7 << 4 ) | DR_7 ) }, 2 }
#endif
#endif
/*!
* LoRaWAN application port
*/
#define LORAWAN_APP_PORT 2
/*!
* User application data buffer size
*/
#if defined( USE_BAND_868 )
#define LORAWAN_APP_DATA_SIZE 16
#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
#define LORAWAN_APP_DATA_SIZE 11
#endif
#if( OVER_THE_AIR_ACTIVATION != 0 )
static uint8_t DevEui[] = LORAWAN_DEVICE_EUI;
static uint8_t AppEui[] = LORAWAN_APPLICATION_EUI;
static uint8_t AppKey[] = LORAWAN_APPLICATION_KEY;
#else
static uint8_t NwkSKey[] = LORAWAN_NWKSKEY;
static uint8_t AppSKey[] = LORAWAN_APPSKEY;
/*!
* Device address
*/
static uint32_t DevAddr;
#endif
/*!
* Application port
*/
static uint8_t AppPort = LORAWAN_APP_PORT;
/*!
* User application data size
*/
static uint8_t AppDataSize = LORAWAN_APP_DATA_SIZE;
/*!
* User application data buffer size
*/
#define LORAWAN_APP_DATA_MAX_SIZE 64
/*!
* User application data
*/
static uint8_t AppData[LORAWAN_APP_DATA_MAX_SIZE];
/*!
* Indicates if the node is sending confirmed or unconfirmed messages
*/
static uint8_t IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
/*!
* Defines the application data transmission duty cycle
*/
static uint32_t TxDutyCycleTime;
/*!
* Timer to handle the application data transmission duty cycle
*/
static Timeout TxNextPacketTimer;
/*!
* Specifies the state of the application LED
*/
static bool AppLedStateOn = false;
/*!
* Timer to handle the state of LED1
*/
static Timeout Led1Timer;
/*!
* Timer to handle the state of LED2
*/
static Timeout Led2Timer;
/*!
* Indicates if a new packet can be sent
*/
static bool NextTx = true;
/*!
* Device states
*/
static enum eDevicState
{
DEVICE_STATE_INIT,
DEVICE_STATE_JOIN,
DEVICE_STATE_SEND,
DEVICE_STATE_CYCLE,
DEVICE_STATE_SLEEP
}DeviceState;
/*!
* LoRaWAN compliance tests support data
*/
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;
/*!
* \brief Prepares the payload of the frame
*/
static void PrepareTxFrame( uint8_t port )
{
switch( port )
{
case 2:
{
#if defined( USE_BAND_868 )
uint16_t pressure = 0;
int16_t altitudeBar = 0;
int16_t temperature = 0;
int32_t latitude, longitude = 0;
uint16_t altitudeGps = 0xFFFF;
uint8_t batteryLevel = 0;
//pressure = ( uint16_t )( MPL3115ReadPressure( ) / 10 ); // in hPa / 10
//temperature = ( int16_t )( MPL3115ReadTemperature( ) * 100 ); // in °C * 100
//altitudeBar = ( int16_t )( MPL3115ReadAltitude( ) * 10 ); // in m * 10
//batteryLevel = BoardGetBatteryLevel( ); // 1 (very low) to 254 (fully charged)
//GpsGetLatestGpsPositionBinary( &latitude, &longitude );
//altitudeGps = GpsGetLatestGpsAltitude( ); // in m
AppData[0] = AppLedStateOn;
AppData[1] = ( pressure >> 8 ) & 0xFF;
AppData[2] = pressure & 0xFF;
AppData[3] = ( temperature >> 8 ) & 0xFF;
AppData[4] = temperature & 0xFF;
AppData[5] = ( altitudeBar >> 8 ) & 0xFF;
AppData[6] = altitudeBar & 0xFF;
AppData[7] = batteryLevel;
AppData[8] = ( latitude >> 16 ) & 0xFF;
AppData[9] = ( latitude >> 8 ) & 0xFF;
AppData[10] = latitude & 0xFF;
AppData[11] = ( longitude >> 16 ) & 0xFF;
AppData[12] = ( longitude >> 8 ) & 0xFF;
AppData[13] = longitude & 0xFF;
AppData[14] = ( altitudeGps >> 8 ) & 0xFF;
AppData[15] = altitudeGps & 0xFF;
#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
int16_t temperature = 0;
int32_t latitude, longitude = 0;
uint16_t altitudeGps = 0xFFFF;
uint8_t batteryLevel = 0;
temperature = ( int16_t )( MPL3115ReadTemperature( ) * 100 ); // in °C * 100
batteryLevel = BoardGetBatteryLevel( ); // 1 (very low) to 254 (fully charged)
GpsGetLatestGpsPositionBinary( &latitude, &longitude );
altitudeGps = GpsGetLatestGpsAltitude( ); // in m
AppData[0] = AppLedStateOn;
AppData[1] = temperature;
AppData[2] = batteryLevel;
AppData[3] = ( latitude >> 16 ) & 0xFF;
AppData[4] = ( latitude >> 8 ) & 0xFF;
AppData[5] = latitude & 0xFF;
AppData[6] = ( longitude >> 16 ) & 0xFF;
AppData[7] = ( longitude >> 8 ) & 0xFF;
AppData[8] = longitude & 0xFF;
AppData[9] = ( altitudeGps >> 8 ) & 0xFF;
AppData[10] = altitudeGps & 0xFF;
#endif
}
break;
case 224:
if( ComplianceTest.LinkCheck == true )
{
ComplianceTest.LinkCheck = false;
AppDataSize = 3;
AppData[0] = 5;
AppData[1] = ComplianceTest.DemodMargin;
AppData[2] = ComplianceTest.NbGateways;
ComplianceTest.State = 1;
}
else
{
switch( ComplianceTest.State )
{
case 4:
ComplianceTest.State = 1;
break;
case 1:
AppDataSize = 2;
AppData[0] = ComplianceTest.DownLinkCounter >> 8;
AppData[1] = ComplianceTest.DownLinkCounter;
break;
}
}
break;
default:
break;
}
}
/*!
* \brief Prepares the payload of the frame
*
* \retval [0: frame could be send, 1: error]
*/
static 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 = DR_0;
}
else
{
if( IsTxConfirmed == false )
{
mcpsReq.Type = MCPS_UNCONFIRMED;
mcpsReq.Req.Unconfirmed.fPort = AppPort;
mcpsReq.Req.Unconfirmed.fBuffer = AppData;
mcpsReq.Req.Unconfirmed.fBufferSize = AppDataSize;
mcpsReq.Req.Unconfirmed.Datarate = DR_0;
}
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 = DR_0;
}
}
if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK )
{
return false;
}
return true;
}
/*!
* \brief Function executed on TxNextPacket Timeout event
*/
static void OnTxNextPacketTimerEvent( void )
{
MibRequestConfirm_t mibReq;
LoRaMacStatus_t status;
TxNextPacketTimer.detach();
mibReq.Type = MIB_NETWORK_JOINED;
status = LoRaMacMibGetRequestConfirm( &mibReq );
if( status == LORAMAC_STATUS_OK )
{
if( mibReq.Param.IsNetworkJoined == true )
{
DeviceState = DEVICE_STATE_SEND;
NextTx = true;
}
else
{
DeviceState = DEVICE_STATE_JOIN;
}
}
}
/*!
* \brief Function executed on Led 1 Timeout event
*/
static void OnLed1TimerEvent( void )
{
Led1Timer.detach();
// Switch LED 1 OFF
Led1 = 1;
}
/*!
* \brief Function executed on Led 2 Timeout event
*/
static void OnLed2TimerEvent( void )
{
Led2Timer.detach();
// Switch LED 2 OFF
Led2 = 1;
}
/*!
* \brief MCPS-Confirm event function
*
* \param [IN] McpsConfirm - Pointer to the confirm structure,
* containing confirm attributes.
*/
static void McpsConfirm( McpsConfirm_t *McpsConfirm )
{
if( McpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
{
switch( McpsConfirm->McpsRequest )
{
case MCPS_UNCONFIRMED:
{
// Check Datarate
// Check TxPower
break;
}
case MCPS_CONFIRMED:
{
// Check Datarate
// Check TxPower
// Check AckReceived
// Check NbTrials
break;
}
case MCPS_PROPRIETARY:
{
break;
}
default:
break;
}
// Switch LED 1 ON
Led1 = 0;
Led1Timer.attach_us(OnLed1TimerEvent, 25000);
}
NextTx = true;
}
/*!
* \brief MCPS-Indication event function
*
* \param [IN] McpsIndication - Pointer to the indication structure,
* containing indication attributes.
*/
static void McpsIndication( McpsIndication_t *McpsIndication )
{
if( McpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK )
{
return;
}
switch( McpsIndication->McpsIndication )
{
case MCPS_UNCONFIRMED:
{
break;
}
case MCPS_CONFIRMED:
{
break;
}
case MCPS_PROPRIETARY:
{
break;
}
case MCPS_MULTICAST:
{
break;
}
default:
break;
}
// Check Multicast
// Check Port
// Check Datarate
// Check FramePending
// Check Buffer
// Check BufferSize
// Check Rssi
// Check Snr
// Check RxSlot
if( ComplianceTest.Running == true )
{
ComplianceTest.DownLinkCounter++;
}
if( McpsIndication->RxData == true )
{
switch( McpsIndication->Port )
{
case 1: // The application LED can be controlled on port 1 or 2
case 2:
if( McpsIndication->BufferSize == 1 )
{
AppLedStateOn = McpsIndication->Buffer[0] & 0x01;
//GpioWrite( &Led3, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 0 : 1 );
}
break;
case 224:
if( ComplianceTest.Running == false )
{
// Check compliance test enable command (i)
if( ( McpsIndication->BufferSize == 4 ) &&
( McpsIndication->Buffer[0] == 0x01 ) &&
( McpsIndication->Buffer[1] == 0x01 ) &&
( McpsIndication->Buffer[2] == 0x01 ) &&
( McpsIndication->Buffer[3] == 0x01 ) )
{
IsTxConfirmed = false;
AppPort = 224;
AppDataSize = 2;
ComplianceTest.DownLinkCounter = 0;
ComplianceTest.LinkCheck = false;
ComplianceTest.DemodMargin = 0;
ComplianceTest.NbGateways = 0;
ComplianceTest.Running = true;
ComplianceTest.State = 1;
MibRequestConfirm_t mibReq;
mibReq.Type = MIB_ADR;
mibReq.Param.AdrEnable = true;
LoRaMacMibSetRequestConfirm( &mibReq );
#if defined( USE_BAND_868 )
LoRaMacTestSetDutyCycleOn( false );
#endif
}
}
else
{
ComplianceTest.State = McpsIndication->Buffer[0];
switch( ComplianceTest.State )
{
case 0: // Check compliance test disable command (ii)
IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
AppPort = LORAWAN_APP_PORT;
AppDataSize = LORAWAN_APP_DATA_SIZE;
ComplianceTest.DownLinkCounter = 0;
ComplianceTest.Running = false;
MibRequestConfirm_t mibReq;
mibReq.Type = MIB_ADR;
mibReq.Param.AdrEnable = LORAWAN_ADR_ON;
LoRaMacMibSetRequestConfirm( &mibReq );
#if defined( USE_BAND_868 )
LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON );
#endif
break;
case 1: // (iii, iv)
AppDataSize = 2;
break;
case 2: // Enable confirmed messages (v)
IsTxConfirmed = true;
ComplianceTest.State = 1;
break;
case 3: // Disable confirmed messages (vi)
IsTxConfirmed = false;
ComplianceTest.State = 1;
break;
case 4: // (vii)
AppDataSize = McpsIndication->BufferSize;
AppData[0] = 4;
for( uint8_t i = 1; i < AppDataSize; i++ )
{
AppData[i] = McpsIndication->Buffer[i] + 1;
}
break;
case 5: // (viii)
{
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_LINK_CHECK;
LoRaMacMlmeRequest( &mlmeReq );
}
break;
default:
break;
}
}
break;
default:
break;
}
}
// Switch LED 2 ON for each received downlink
Led2 = 0;
Led2Timer.attach_us(OnLed2TimerEvent, 25000);
}
/*!
* \brief MLME-Confirm event function
*
* \param [IN] MlmeConfirm - Pointer to the confirm structure,
* containing confirm attributes.
*/
static void MlmeConfirm( MlmeConfirm_t *MlmeConfirm )
{
if( MlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
{
switch( MlmeConfirm->MlmeRequest )
{
case MLME_JOIN:
{
// Status is OK, node has joined the network
break;
}
case MLME_LINK_CHECK:
{
// Check DemodMargin
// Check NbGateways
if( ComplianceTest.Running == true )
{
ComplianceTest.LinkCheck = true;
ComplianceTest.DemodMargin = MlmeConfirm->DemodMargin;
ComplianceTest.NbGateways = MlmeConfirm->NbGateways;
}
break;
}
default:
break;
}
}
NextTx = true;
}
/**
* Main application entry point.
*/
int main( void )
{
LoRaMacPrimitives_t LoRaMacPrimitives;
LoRaMacCallback_t LoRaMacCallbacks;
MibRequestConfirm_t mibReq;
//BoardInitMcu( );
//BoardInitPeriph( );
DeviceState = DEVICE_STATE_INIT;
while( 1 )
{
switch( DeviceState )
{
case DEVICE_STATE_INIT:
{
LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm;
LoRaMacPrimitives.MacMcpsIndication = McpsIndication;
LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm;
//LoRaMacCallbacks.GetBatteryLevel = BoardGetBatteryLevel;
LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks );
mibReq.Type = MIB_ADR;
mibReq.Param.AdrEnable = LORAWAN_ADR_ON;
LoRaMacMibSetRequestConfirm( &mibReq );
mibReq.Type = MIB_PUBLIC_NETWORK;
mibReq.Param.EnablePublicNetwork = LORAWAN_PUBLIC_NETWORK;
LoRaMacMibSetRequestConfirm( &mibReq );
#if defined( USE_BAND_868 )
LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON );
#if( USE_SEMTECH_DEFAULT_CHANNEL_LINEUP == 1 )
LoRaMacChannelAdd( 3, ( ChannelParams_t )LC4 );
LoRaMacChannelAdd( 4, ( ChannelParams_t )LC5 );
LoRaMacChannelAdd( 5, ( ChannelParams_t )LC6 );
LoRaMacChannelAdd( 6, ( ChannelParams_t )LC7 );
LoRaMacChannelAdd( 7, ( ChannelParams_t )LC8 );
LoRaMacChannelAdd( 8, ( ChannelParams_t )LC9 );
#endif
#endif
DeviceState = DEVICE_STATE_JOIN;
break;
}
case DEVICE_STATE_JOIN:
{
#if( OVER_THE_AIR_ACTIVATION != 0 )
MlmeReq_t mlmeReq;
#warning BoardGetUniqueId not needed in test software - commented out
// Initialize LoRaMac device unique ID
// BoardGetUniqueId( DevEui );
mlmeReq.Type = MLME_JOIN;
mlmeReq.Req.Join.DevEui = DevEui;
mlmeReq.Req.Join.AppEui = AppEui;
mlmeReq.Req.Join.AppKey = AppKey;
if( NextTx == true )
{
LoRaMacMlmeRequest( &mlmeReq );
}
// Schedule next packet transmission
TxDutyCycleTime = OVER_THE_AIR_ACTIVATION_DUTYCYCLE;
DeviceState = DEVICE_STATE_CYCLE;
#else
// Random seed initialization
srand1( BoardGetRandomSeed( ) );
// Choose a random device address
DevAddr = randr( 0, 0x01FFFFFF );
mibReq.Type = MIB_NET_ID;
mibReq.Param.NetID = LORAWAN_NETWORK_ID;
LoRaMacMibSetRequestConfirm( &mibReq );
mibReq.Type = MIB_DEV_ADDR;
mibReq.Param.DevAddr = DevAddr;
LoRaMacMibSetRequestConfirm( &mibReq );
mibReq.Type = MIB_NWK_SKEY;
mibReq.Param.NwkSKey = NwkSKey;
LoRaMacMibSetRequestConfirm( &mibReq );
mibReq.Type = MIB_APP_SKEY;
mibReq.Param.AppSKey = AppSKey;
LoRaMacMibSetRequestConfirm( &mibReq );
mibReq.Type = MIB_NETWORK_JOINED;
mibReq.Param.IsNetworkJoined = true;
LoRaMacMibSetRequestConfirm( &mibReq );
DeviceState = DEVICE_STATE_SEND;
#endif
break;
}
case DEVICE_STATE_SEND:
{
if( NextTx == true )
{
PrepareTxFrame( AppPort );
NextTx = SendFrame( );
}
if( ComplianceTest.Running == true )
{
// Schedule next packet transmission as soon as possible
TxDutyCycleTime = 300000; // 300 ms
}
else
{
// Schedule next packet transmission
TxDutyCycleTime = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
}
DeviceState = DEVICE_STATE_CYCLE;
break;
}
case DEVICE_STATE_CYCLE:
{
DeviceState = DEVICE_STATE_SLEEP;
// Schedule next packet transmission
TxNextPacketTimer.attach_us(OnTxNextPacketTimerEvent, TxDutyCycleTime);
break;
}
case DEVICE_STATE_SLEEP:
{
// Wake up through events
#warning TimerLowPowerHandler disabled
//TimerLowPowerHandler( );
break;
}
default:
{
DeviceState = DEVICE_STATE_INIT;
break;
}
}
}
}
