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: mbed LoRaWAN-lib SX1276Lib
app/main.cpp
- Committer:
- uss1994
- Date:
- 2018-12-07
- Revision:
- 10:9a4efdd07a77
- Parent:
- 9:ee9dcbb9708d
- Child:
- 11:9e35ddff7ed8
File content as of revision 10:9a4efdd07a77:
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(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 "Commissioning.h"
#include "SerialDisplay.h"
/*!
* Defines the application data transmission duty cycle. 5s, value in [ms].
*/
#define APP_TX_DUTYCYCLE 5000
/*!
* Defines a random delay for application data transmission duty cycle. 1s,
* value in [ms].
*/
#define APP_TX_DUTYCYCLE_RND 1000
/*!
* Default datarate
*/
#define LORAWAN_DEFAULT_DATARATE DR_0
/*!
* LoRaWAN confirmed messages
*/
#define LORAWAN_CONFIRMED_MSG_ON true
/*!
* 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 false
#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 }
#define LC10 { 868300000, { ( ( DR_6 << 4 ) | DR_6 ) }, 1 }
#endif
#endif
/*!
* LoRaWAN application port
*/
#define LORAWAN_APP_PORT 15
/*!
* User application data buffer size
*/
#define LORAWAN_APP_DATA_SIZE 41
static uint8_t DevEui[] = LORAWAN_DEVICE_EUI;
static uint8_t AppEui[] = LORAWAN_APPLICATION_EUI;
static uint8_t AppKey[] = LORAWAN_APPLICATION_KEY;
#if( OVER_THE_AIR_ACTIVATION == 0 )
static uint8_t NwkSKey[] = LORAWAN_NWKSKEY;
static uint8_t AppSKey[] = LORAWAN_APPSKEY;
/*!
* Device address
*/
static uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS;
#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 TimerEvent_t TxNextPacketTimer;
/*!
* SOME APPLICATION PARAMETERS
*/
DigitalOut myled(D7);
DigitalOut relayPin(D6);
AnalogIn BAT_PIN(A1);
AnalogIn LIGHT_1_PIN(A2);
AnalogIn LIGHT_2_PIN(A5);
AnalogIn VCE_PIN(PB_1);
unsigned int time_window = 5; // Default 3600 seconds in an hour
unsigned long long_interval = 604800; // Default 31557600 seconds in a year
unsigned int hb_interval = 3600; // Default 86400 seconds in a day
unsigned long test_interval = 86400; // Default 2592000 seconds in a month
time_t next_stest;
time_t next_ltest;
uint8_t hb_data[5];
uint8_t short_tdata[14];
uint8_t long_tdata[41];
uint8_t received_data[28];
uint8_t *data;
uint8_t data_len;
bool running_test;
bool joining;
bool received_downlink;
time_t current_time;
unsigned long last_measurement;
unsigned long last_hb;
unsigned long test_start;
int measureCount;
uint8_t s_measurements[4];
uint8_t l_measurements[22];
uint8_t *measurements;
int available_slots = 9;
unsigned long measurement_interval;
unsigned long test_duration;
/*!
* Specifies the state of the application LED
*/
static bool AppLedStateOn = false;
volatile bool Led3StateChanged = false;
/*!
* Timer to handle the state of LED1
*/
static TimerEvent_t Led1Timer;
volatile bool Led1State = false;
volatile bool Led1StateChanged = false;
/*!
* Timer to handle the state of LED2
*/
static TimerEvent_t Led2Timer;
volatile bool Led2State = false;
volatile bool Led2StateChanged = false;
/*!
* Indicates if a new packet can be sent
*/
static bool NextTx = true;
/*!
* Device states
*/
static enum eDeviceState
{
DEVICE_STATE_INIT,
DEVICE_STATE_JOIN,
DEVICE_STATE_SEND,
DEVICE_STATE_CYCLE,
DEVICE_STATE_SLEEP
}DeviceState;
static enum eMessageType
{
MESSAGE_TYPE_HB,
MESSAGE_TYPE_SHORT_TEST,
MESSAGE_TYPE_LONG_TEST
}MessageType;
/*!
* 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;
/*
* SerialDisplay managment variables
*/
/*!
* Indicates if the MAC layer network join status has changed.
*/
static bool IsNetworkJoinedStatusUpdate = false;
/*!
* Strucure containing the Uplink status
*/
struct sLoRaMacUplinkStatus
{
uint8_t Acked;
int8_t Datarate;
uint16_t UplinkCounter;
uint8_t Port;
uint8_t *Buffer;
uint8_t BufferSize;
}LoRaMacUplinkStatus;
volatile bool UplinkStatusUpdated = false;
/*!
* Strucure containing the Downlink status
*/
struct sLoRaMacDownlinkStatus
{
int16_t Rssi;
int8_t Snr;
uint16_t DownlinkCounter;
bool RxData;
uint8_t Port;
uint8_t *Buffer;
uint8_t BufferSize;
}LoRaMacDownlinkStatus;
volatile bool DownlinkStatusUpdated = false;
void SerialDisplayRefresh( void )
{
MibRequestConfirm_t mibReq;
SerialDisplayInit( );
SerialDisplayUpdateActivationMode( OVER_THE_AIR_ACTIVATION );
#if( OVER_THE_AIR_ACTIVATION == 0 )
SerialDisplayUpdateNwkId( LORAWAN_NETWORK_ID );
SerialDisplayUpdateDevAddr( DevAddr );
SerialDisplayUpdateKey( 12, NwkSKey );
SerialDisplayUpdateKey( 13, AppSKey );
#endif
SerialDisplayUpdateEui( 5, DevEui );
SerialDisplayUpdateEui( 6, AppEui );
SerialDisplayUpdateKey( 7, AppKey );
mibReq.Type = MIB_NETWORK_JOINED;
LoRaMacMibGetRequestConfirm( &mibReq );
SerialDisplayUpdateNetworkIsJoined( mibReq.Param.IsNetworkJoined );
SerialDisplayUpdateAdr( LORAWAN_ADR_ON );
#if defined( USE_BAND_868 )
SerialDisplayUpdateDutyCycle( LORAWAN_DUTYCYCLE_ON );
#else
SerialDisplayUpdateDutyCycle( false );
#endif
SerialDisplayUpdatePublicNetwork( LORAWAN_PUBLIC_NETWORK );
SerialDisplayUpdateLedState( 3, AppLedStateOn );
}
void SerialRxProcess( void )
{
if( SerialDisplayReadable( ) == true )
{
switch( SerialDisplayGetChar( ) )
{
case 'R':
case 'r':
// Refresh Serial screen
SerialDisplayRefresh( );
break;
default:
break;
}
}
}
void flash_builtin() {
myled = 1; // turn the LED on (HIGH is the voltage level)
wait(2); // wait for a second
myled = 0; // turn the LED off by making the voltage LOW
wait(1); // wait for a second
}
// **************************** COMMUNICATION PACKET DEFINITION METHODS ******************************** //
void heartbeat_message(uint8_t *hb_data, uint8_t *current_date) {
hb_data[0] = 0xD2;
memcpy(hb_data+1, current_date, 4);
}
void short_test_result(uint8_t *short_tdata, uint8_t *test_start_date, uint8_t *next_test_date, uint8_t *measurements) {
short_tdata[0] = 0xD7;
memcpy(short_tdata+1, test_start_date, 4);
memcpy(short_tdata+5, next_test_date, 4);
short_tdata[9] = (uint8_t) 2;
memcpy(short_tdata+10, measurements, 4);
}
void long_test_result(uint8_t *long_tdata, uint8_t *test_start_date, uint8_t *next_test_date, uint8_t *measurements) {
long_tdata[0] = 0xD8;
memcpy(long_tdata+1, test_start_date, 4);
memcpy(long_tdata+5, next_test_date, 4);
long_tdata[9] = (uint8_t) 2;
memcpy(long_tdata+10, measurements, 31);
}
/*!
* \brief Prepares the payload of the frame
*/
static void PrepareTxFrame( uint8_t port )
{
switch( port )
{
case 15:
{
switch ( MessageType )
{
case MESSAGE_TYPE_HB:
{
AppDataSize = 5;
heartbeat_message(AppData, (uint8_t *)¤t_time);
break;
}
case MESSAGE_TYPE_SHORT_TEST:
{
AppDataSize = 14;
short_test_result(AppData, (uint8_t *)¤t_time, (uint8_t *)&next_stest, s_measurements);
break;
}
case MESSAGE_TYPE_LONG_TEST:
{
AppDataSize = 32;
long_test_result(AppData, (uint8_t *)¤t_time, (uint8_t *)&next_ltest, l_measurements);
break;
}
default:
{
AppDataSize = 5;
heartbeat_message(AppData, (uint8_t *)¤t_time);
break;
}
}
}
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 = LORAWAN_DEFAULT_DATARATE;
LoRaMacUplinkStatus.Acked = false;
LoRaMacUplinkStatus.Port = 0;
LoRaMacUplinkStatus.Buffer = NULL;
LoRaMacUplinkStatus.BufferSize = 0;
SerialDisplayUpdateFrameType( false );
}
else
{
LoRaMacUplinkStatus.Acked = false;
LoRaMacUplinkStatus.Port = AppPort;
LoRaMacUplinkStatus.Buffer = AppData;
LoRaMacUplinkStatus.BufferSize = AppDataSize;
SerialDisplayUpdateFrameType( IsTxConfirmed );
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 = 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;
}
}
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;
TimerStop( &TxNextPacketTimer );
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 )
{
TimerStop( &Led1Timer );
// Switch LED 1 OFF
Led1State = false;
Led1StateChanged = true;
}
/*!
* \brief Function executed on Led 2 Timeout event
*/
static void OnLed2TimerEvent( void )
{
TimerStop( &Led2Timer );
// Switch LED 2 OFF
Led2State = false;
Led2StateChanged = true;
}
/*!
* \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
LoRaMacUplinkStatus.Acked = mcpsConfirm->AckReceived;
break;
}
case MCPS_PROPRIETARY:
{
break;
}
default:
break;
}
LoRaMacUplinkStatus.Datarate = mcpsConfirm->Datarate;
LoRaMacUplinkStatus.UplinkCounter = mcpsConfirm->UpLinkCounter;
// Switch LED 1 ON
Led1State = true;
Led1StateChanged = true;
TimerStart( &Led1Timer );
UplinkStatusUpdated = true;
}
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
LoRaMacDownlinkStatus.Rssi = mcpsIndication->Rssi;
if( mcpsIndication->Snr & 0x80 ) // The SNR sign bit is 1
{
// Invert and divide by 4
LoRaMacDownlinkStatus.Snr = ( ( ~mcpsIndication->Snr + 1 ) & 0xFF ) >> 2;
LoRaMacDownlinkStatus.Snr = -LoRaMacDownlinkStatus.Snr;
}
else
{
// Divide by 4
LoRaMacDownlinkStatus.Snr = ( mcpsIndication->Snr & 0xFF ) >> 2;
}
LoRaMacDownlinkStatus.DownlinkCounter++;
LoRaMacDownlinkStatus.RxData = mcpsIndication->RxData;
LoRaMacDownlinkStatus.Port = mcpsIndication->Port;
LoRaMacDownlinkStatus.Buffer = mcpsIndication->Buffer;
LoRaMacDownlinkStatus.BufferSize = mcpsIndication->BufferSize;
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;
Led3StateChanged = true;
}
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;
case 6: // (ix)
{
MlmeReq_t mlmeReq;
// Disable TestMode and revert back to normal operation
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
mlmeReq.Type = MLME_JOIN;
mlmeReq.Req.Join.DevEui = DevEui;
mlmeReq.Req.Join.AppEui = AppEui;
mlmeReq.Req.Join.AppKey = AppKey;
mlmeReq.Req.Join.NbTrials = 3;
LoRaMacMlmeRequest( &mlmeReq );
DeviceState = DEVICE_STATE_SLEEP;
}
break;
case 7: // (x)
{
if( mcpsIndication->BufferSize == 3 )
{
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_TXCW;
mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] );
LoRaMacMlmeRequest( &mlmeReq );
}
else if( mcpsIndication->BufferSize == 7 )
{
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_TXCW_1;
mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] );
mlmeReq.Req.TxCw.Frequency = ( uint32_t )( ( mcpsIndication->Buffer[3] << 16 ) | ( mcpsIndication->Buffer[4] << 8 ) | mcpsIndication->Buffer[5] ) * 100;
mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[6];
LoRaMacMlmeRequest( &mlmeReq );
}
ComplianceTest.State = 1;
}
break;
default:
break;
}
}
break;
default:
break;
}
}
// Switch LED 2 ON for each received downlink
Led2State = true;
Led2StateChanged = true;
TimerStart( &Led2Timer );
DownlinkStatusUpdated = true;
}
/*!
* \brief MLME-Confirm event function
*
* \param [IN] mlmeConfirm - Pointer to the confirm structure,
* containing confirm attributes.
*/
static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm )
{
switch( mlmeConfirm->MlmeRequest )
{
case MLME_JOIN:
{
if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
{
// Status is OK, node has joined the network
IsNetworkJoinedStatusUpdate = true;
DeviceState = DEVICE_STATE_SEND;
MessageType = MESSAGE_TYPE_HB;
joining = false;
next_stest = current_time + 5;
next_ltest = current_time + long_interval;
}
else
{
// Join was not successful. Try to join again
DeviceState = DEVICE_STATE_JOIN;
}
break;
}
case MLME_LINK_CHECK:
{
if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
{
// Check DemodMargin
// Check NbGateways
if( ComplianceTest.Running == true )
{
ComplianceTest.LinkCheck = true;
ComplianceTest.DemodMargin = mlmeConfirm->DemodMargin;
ComplianceTest.NbGateways = mlmeConfirm->NbGateways;
}
}
break;
}
default:
break;
}
NextTx = true;
UplinkStatusUpdated = true;
}
// **************************** TEST METHODS ******************************** //
int takeMeasurement(uint8_t *measurements, int count) {
// This method should only run for long tests
int battery_reading = BAT_PIN.read()*1000;
int vce_reading = VCE_PIN.read()*1000;
int light_1_reading = LIGHT_1_PIN.read()*1000 - vce_reading;
int light_2_reading = LIGHT_2_PIN.read()*1000 - vce_reading;
int index = count;
measurements[(uint8_t)index] = (uint8_t) (battery_reading/10);
measurements[(uint8_t)index+10] = (uint8_t) light_1_reading;
measurements[(uint8_t)index+20] = (uint8_t) light_2_reading;
return index+1;
}
void startTest(uint8_t *measurements) {
// Signal that test is running
flash_builtin();
flash_builtin();
// Determine test length & measurement interval
switch (MessageType)
{
case MESSAGE_TYPE_SHORT_TEST:
test_duration = 30;
// set interval to twice the test length so that it never happens
measurement_interval = 2*test_duration;
break;
case MESSAGE_TYPE_LONG_TEST:
test_duration = 5400;
// interval is divided by two longer than available measurement
// slots so that it completes before the end of the test
measurement_interval = test_duration/(available_slots + 2);
break;
default:
test_duration = 0;
break;
}
// Measure voltage preval
measurements[0] = (uint8_t) (BAT_PIN.read()*1000)/10;
// Set measurement count to 1
measureCount = 1;
// Start test
relayPin = 1;
}
void endTest(uint8_t *measurements) {
// Measure endvals
int battery_reading = BAT_PIN.read()*1000;
int vce_reading = VCE_PIN.read()*1000;
int light_1_reading = LIGHT_1_PIN.read()*1000 - vce_reading;
int light_2_reading = LIGHT_2_PIN.read()*1000 - vce_reading;
switch (MessageType)
{
case MESSAGE_TYPE_SHORT_TEST:
measurements[1] = (uint8_t) (battery_reading/10);
measurements[2] = (uint8_t) light_1_reading;
measurements[3] = (uint8_t) light_2_reading;
break;
default:
measurements[10] = (uint8_t) (battery_reading/10);
measurements[20] = (uint8_t) light_1_reading;
measurements[30] = (uint8_t) light_2_reading;
break;
}
// End test
relayPin = 0;
}
/**
* Main application entry point.
*/
int main( void )
{
LoRaMacPrimitives_t LoRaMacPrimitives;
LoRaMacCallback_t LoRaMacCallbacks;
MibRequestConfirm_t mibReq;
BoardInit( );
SerialDisplayInit( );
SerialDisplayUpdateEui( 5, DevEui );
SerialDisplayUpdateEui( 6, AppEui );
SerialDisplayUpdateKey( 7, AppKey );
#if( OVER_THE_AIR_ACTIVATION == 0 )
SerialDisplayUpdateNwkId( LORAWAN_NETWORK_ID );
SerialDisplayUpdateDevAddr( DevAddr );
SerialDisplayUpdateKey( 12, NwkSKey );
SerialDisplayUpdateKey( 13, AppSKey );
#endif
DeviceState = DEVICE_STATE_INIT;
set_time(1514764800);
relayPin = 0;
flash_builtin();
flash_builtin();
flash_builtin();
flash_builtin();
running_test = false;
joining = true;
received_downlink = false;
time_t start_time = time(NULL);
last_hb = start_time;
last_measurement = start_time;
while( 1 )
{
current_time = time(NULL);
SerialRxProcess( );
if( IsNetworkJoinedStatusUpdate == true )
{
IsNetworkJoinedStatusUpdate = false;
mibReq.Type = MIB_NETWORK_JOINED;
LoRaMacMibGetRequestConfirm( &mibReq );
SerialDisplayUpdateNetworkIsJoined( mibReq.Param.IsNetworkJoined );
}
if( Led1StateChanged == true )
{
Led1StateChanged = false;
SerialDisplayUpdateLedState( 1, Led1State );
}
if( Led2StateChanged == true )
{
Led2StateChanged = false;
SerialDisplayUpdateLedState( 2, Led2State );
}
if( Led3StateChanged == true )
{
Led3StateChanged = false;
SerialDisplayUpdateLedState( 3, AppLedStateOn );
}
if( UplinkStatusUpdated == true )
{
UplinkStatusUpdated = false;
SerialDisplayUpdateUplink( LoRaMacUplinkStatus.Acked, LoRaMacUplinkStatus.Datarate, LoRaMacUplinkStatus.UplinkCounter, LoRaMacUplinkStatus.Port, LoRaMacUplinkStatus.Buffer, LoRaMacUplinkStatus.BufferSize );
}
if( DownlinkStatusUpdated == true )
{
DownlinkStatusUpdated = false;
SerialDisplayUpdateLedState( 2, Led2State );
SerialDisplayUpdateDownlink( LoRaMacDownlinkStatus.RxData, LoRaMacDownlinkStatus.Rssi, LoRaMacDownlinkStatus.Snr, LoRaMacDownlinkStatus.DownlinkCounter, LoRaMacDownlinkStatus.Port, LoRaMacDownlinkStatus.Buffer, LoRaMacDownlinkStatus.BufferSize );
}
switch( DeviceState )
{
case DEVICE_STATE_INIT:
{
LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm;
LoRaMacPrimitives.MacMcpsIndication = McpsIndication;
LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm;
LoRaMacCallbacks.GetBatteryLevel = BoardGetBatteryLevel;
LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks );
TimerInit( &TxNextPacketTimer, OnTxNextPacketTimerEvent );
TimerInit( &Led1Timer, OnLed1TimerEvent );
TimerSetValue( &Led1Timer, 25 );
TimerInit( &Led2Timer, OnLed2TimerEvent );
TimerSetValue( &Led2Timer, 25 );
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 );
SerialDisplayUpdateDutyCycle( 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 );
LoRaMacChannelAdd( 9, ( ChannelParams_t )LC10 );
mibReq.Type = MIB_RX2_DEFAULT_CHANNEL;
mibReq.Param.Rx2DefaultChannel = ( Rx2ChannelParams_t ){ 869525000, DR_3 };
LoRaMacMibSetRequestConfirm( &mibReq );
mibReq.Type = MIB_RX2_CHANNEL;
mibReq.Param.Rx2Channel = ( Rx2ChannelParams_t ){ 869525000, DR_3 };
LoRaMacMibSetRequestConfirm( &mibReq );
#endif
#endif
SerialDisplayUpdateActivationMode( OVER_THE_AIR_ACTIVATION );
SerialDisplayUpdateAdr( LORAWAN_ADR_ON );
SerialDisplayUpdatePublicNetwork( LORAWAN_PUBLIC_NETWORK );
LoRaMacDownlinkStatus.DownlinkCounter = 0;
DeviceState = DEVICE_STATE_JOIN;
break;
}
case DEVICE_STATE_JOIN:
{
#if( OVER_THE_AIR_ACTIVATION != 0 )
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_JOIN;
mlmeReq.Req.Join.DevEui = DevEui;
mlmeReq.Req.Join.AppEui = AppEui;
mlmeReq.Req.Join.AppKey = AppKey;
if( NextTx == true )
{
LoRaMacMlmeRequest( &mlmeReq );
}
DeviceState = DEVICE_STATE_SLEEP;
#else
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
IsNetworkJoinedStatusUpdate = true;
break;
}
case DEVICE_STATE_SEND:
{
if( NextTx == true )
{
SerialDisplayUpdateUplinkAcked( false );
SerialDisplayUpdateDonwlinkRxData( false );
PrepareTxFrame( AppPort );
NextTx = SendFrame( );
}
if( ComplianceTest.Running == true )
{
// Schedule next packet transmission
TxDutyCycleTime = 5000; // 5000 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
// TimerSetValue( &TxNextPacketTimer, TxDutyCycleTime );
// TimerStart( &TxNextPacketTimer );
// Is a test is running
if (running_test) {
// If it is
//check if it's time to take the next interval measurement
if (current_time - last_measurement >= measurement_interval) {
if (measureCount <= available_slots) {
measureCount = takeMeasurement(measurements, measureCount);
}
last_measurement = current_time;
}
//check if it's time to end the test
if (current_time - test_start >= test_duration) {
endTest(measurements);
// sendResult(measurements, ttype);
DeviceState = DEVICE_STATE_SEND;
last_hb = current_time;
// Set the times for the next tests
switch ( MessageType )
{
case MESSAGE_TYPE_SHORT_TEST:
{
next_stest = test_start + test_interval;
break;
}
default:
{
// If long test, reset both test types,
// to prevent two tests at the same time
next_stest = test_start + test_interval;
next_ltest = test_start + long_interval;
break;
}
}
running_test = false;
}
} else {
// If it isn't
// check if downlink action is required
//if (received_downlink == true) {
// dl_handler(received_data);
//}
//check if it's time to run a test
if (current_time >= next_stest || current_time >= next_ltest) {
// Check what kind of test to run
if (current_time >= next_ltest) {
MessageType = MESSAGE_TYPE_LONG_TEST;
measurements = l_measurements;
} else {
MessageType = MESSAGE_TYPE_SHORT_TEST;
measurements = s_measurements;
}
startTest(measurements);
running_test = true;
test_start = current_time;
last_measurement = current_time;
//check if it's time for a heartbeat
} else if (current_time - last_hb >= hb_interval) {
// sendHb();
DeviceState = DEVICE_STATE_SEND;
MessageType = MESSAGE_TYPE_HB;
last_hb = current_time;
}
}
break;
}
case DEVICE_STATE_SLEEP:
{
// Wake up through events
break;
}
default:
{
DeviceState = DEVICE_STATE_INIT;
break;
}
}
}
}