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.
sensorDemoVT100.cpp
- Committer:
- dudmuck
- Date:
- 2018-03-01
- Revision:
- 1:3c1d13a0489e
- Parent:
- 0:62e456e60083
- Child:
- 2:da3c8d5b3f49
File content as of revision 1:3c1d13a0489e:
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2018 Semtech
Description: LoRaMac classB device implementation
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#include "LoRaMac.h"
#include "SerialDisplay.h"
#include "LoRaMacString.h"
#ifdef ENABLE_VT100
/*!
* Defines the application data transmission duty cycle. 5s, value in [ms].
*/
#define APP_TX_DUTYCYCLE_us 8000000
/*!
* Defines a random delay for application data transmission duty cycle. 1s,
* value in [ms].
*/
#define APP_TX_DUTYCYCLE_RND_us 2000000
/*!
* Default datarate
*/
#if defined( USE_BAND_ARIB_8CH )
#define LORAWAN_DEFAULT_DATARATE DR_3
#else
#define LORAWAN_DEFAULT_DATARATE DR_0
#endif
/*!
* 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 )
/*!
* 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 2
/*!
* User application data buffer size
*/
#define LORAWAN_APP_DATA_SIZE 3
#ifdef LORAWAN_JOIN_EUI
static uint8_t DevEui[] = LORAWAN_DEVICE_EUI;
static const uint8_t JoinEui[] = LORAWAN_JOIN_EUI;
static const uint8_t NwkKey[] = LORAWAN_ROOT_NWKKEY;
#ifdef LORAWAN_ROOT_APPKEY
static const uint8_t AppKey[] = LORAWAN_ROOT_APPKEY;
#endif
#else
static const uint8_t FNwkSIntKey[] = LORAWAN_FNwkSIntKey;
static const uint8_t AppSKey[] = LORAWAN_APPSKEY;
static const uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS;
#if defined(LORAWAN_SNwkSIntKey) && defined(LORAWAN_NwkSEncKey)
static const uint8_t SNwkSIntKey[] = LORAWAN_SNwkSIntKey;
static const uint8_t NwkSEncKey[] = LORAWAN_NwkSEncKey;
#endif
#endif
/*!
* Application port
*/
static uint8_t AppPort = LORAWAN_APP_PORT;
/*!
* User application data size
*/
static uint8_t gAppDataSize = 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 gIsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
/*!
* Timer to handle the application data transmission duty cycle
*
*/
LowPowerTimeout tx_timeout;
/*!
* Indicates if a new packet can be sent
*/
static volatile struct {
uint8_t gmi : 1;
uint8_t gmc : 1;
} flags;
/*!
* Device states
*/
volatile enum eDevicState
{
/* 0 */ DEVICE_STATE_INIT = 0,
/* 1 */ DEVICE_STATE_SEND,
/* 2 */ DEVICE_STATE_TRIGGER,
/* 3 */ DEVICE_STATE_SLEEP,
#ifdef LORAWAN_JOIN_EUI
/* 4 */ DEVICE_STATE_JOIN,
/* 5 */ DEVICE_STATE_JOIN_OK
#endif /* LORAWAN_JOIN_EUI */
} DeviceState, WakeUpState;
#if defined(TARGET_MOTE_L152RC) && !defined(TARGET_FF_ARDUINO)
#define TARGET_FF_ARDUINO
#endif
#if defined(TARGET_FF_ARDUINO)
DigitalIn d8(D8);
DigitalOut extLed(D15);
#endif /* TARGET_FF_ARDUINO */
#if defined(TARGET_FF_MORPHO) && !defined(TARGET_DISCO_L072CZ_LRWAN1)
#define JUMPER_ENABLE
#endif /* */
#ifdef JUMPER_ENABLE
#define TX_INTERVAL_US 15000000
DigitalOut jumper_out(PC_10);
InterruptIn jumper_in(PC_12);
#endif /* JUMPER_ENABLE */
uint8_t c_ch;
us_timestamp_t buttonStartAt;
#ifdef TARGET_DISCO_L072CZ_LRWAN1
PwmOut pwm(PA_0);
#elif defined(TARGET_FF_ARDUINO)
PwmOut pwm(PB_11);
#endif /* TARGET_DISCO_L072CZ_LRWAN1 */
volatile int cayenne_ack_ch;
/*!
* 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;
McpsIndication_t gmi;
McpsConfirm_t gmc;
#ifdef JUMPER_ENABLE
void autoUplink()
{
if (jumper_in.read() == 1) {
tx_timeout.attach_us(autoUplink, TX_INTERVAL_US);
}
c_ch = 0xff;
DeviceState = DEVICE_STATE_SEND;
}
void jumper_callback()
{
tx_timeout.attach_us(autoUplink, TX_INTERVAL_US);
}
#endif /* JUMPER_ENABLE */
static void
clearIndications()
{
vt.SetCursorPos(ROW_MCPS_CONF, 1);
vt.printf("\e[K");
vt.SetCursorPos(ROW_MCPS_IND, 1);
vt.printf("\e[K");
vt.SetCursorPos(ROW_MLME_IND, 1);
vt.printf("\e[K");
vt.SetCursorPos(ROW_MLME_CONF, 1);
vt.printf("\e[K");
vt.SetCursorPos(ROW_MIC+3, 1);
vt.printf("\e[K");
}
#define LPP_DIGITAL_INPUT 0 // 1 byte
#define LPP_DIGITAL_OUTPUT 1 // 1 byte
#define LPP_ANALOG_INPUT 2 // 2 bytes, 0.01 signed
#define LPP_ANALOG_OUTPUT 3 // 2 bytes, 0.01 signed
#define LPP_LUMINOSITY 101 // 2 bytes, 1 lux unsigned
#define LPP_PRESENCE 102 // 1 byte, 1
#define LPP_TEMPERATURE 103 // 2 bytes, 0.1°C signed
#define LPP_RELATIVE_HUMIDITY 104 // 1 byte, 0.5% unsigned
#define LPP_ACCELEROMETER 113 // 2 bytes per axis, 0.001G
#define LPP_BAROMETRIC_PRESSURE 115 // 2 bytes 0.1 hPa Unsigned
#define LPP_GYROMETER 134 // 2 bytes per axis, 0.01 °/s
#define LPP_GPS 136 // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01m
// Data ID + Data Type + Data Size
#define LPP_DIGITAL_INPUT_SIZE 3
#define LPP_DIGITAL_OUTPUT_SIZE 3
#define LPP_ANALOG_INPUT_SIZE 4
#define LPP_ANALOG_OUTPUT_SIZE 4
#define LPP_LUMINOSITY_SIZE 4
#define LPP_PRESENCE_SIZE 3
#define LPP_TEMPERATURE_SIZE 4
#define LPP_RELATIVE_HUMIDITY_SIZE 3
#define LPP_ACCELEROMETER_SIZE 8
#define LPP_BAROMETRIC_PRESSURE_SIZE 4
#define LPP_GYROMETER_SIZE 8
#define LPP_GPS_SIZE 11
#define CAYENNE_CH_DOUT 2
#define CAYENNE_CH_AOUT 3
#define CAYENNE_CH_TEMP 0
#define CAYENNE_CH_POT 1
AnalogIn a1(A1);
AnalogIn a3(A3);
const unsigned R0 = 100000;
const unsigned B = 4275;
/*!
* \brief Prepares the payload of the frame
*/
static void PrepareTxFrame( uint8_t port )
{
uint16_t u16, rot;
float t, f, R;
if (c_ch != 0xff) {
gAppDataSize = 0;
AppData[gAppDataSize++] = c_ch;
switch (c_ch) {
case CAYENNE_CH_TEMP:
AppData[gAppDataSize++] = LPP_TEMPERATURE;
u16 = a3.read_u16() >> 4;
R = 4096.0 / u16 - 1.0;
R = R0 * R;
t = 1.0/(log(R/R0)/B+1/298.15)-273.15;
u16 = t * 10; // 0.1C per bit
AppData[gAppDataSize++] = u16 >> 8;
AppData[gAppDataSize++] = u16;
break;
case CAYENNE_CH_POT:
AppData[gAppDataSize++] = LPP_ANALOG_INPUT;
u16 = a1.read_u16(); // pot (rotary angle)
f = u16 / 198.6; // scale 65535/3.3 to 0.01v per bit
rot = (uint16_t) f;
AppData[gAppDataSize++] = rot >> 8;
AppData[gAppDataSize++] = rot;
break;
case CAYENNE_CH_DOUT:
AppData[gAppDataSize++] = LPP_DIGITAL_OUTPUT;
AppData[gAppDataSize++] = extLed.read();
break;
case CAYENNE_CH_AOUT:
AppData[gAppDataSize++] = LPP_ANALOG_OUTPUT;
u16 = pwm.read() * 100;
AppData[gAppDataSize++] = u16 >> 8;
AppData[gAppDataSize++] = u16;
break;
}
return;
} else if (cayenne_ack_ch != -1) {
switch (cayenne_ack_ch) {
case CAYENNE_CH_DOUT:
AppData[gAppDataSize++] = LPP_DIGITAL_OUTPUT;
AppData[gAppDataSize++] = extLed.read();
break;
case CAYENNE_CH_AOUT:
AppData[gAppDataSize++] = LPP_ANALOG_OUTPUT;
u16 = pwm.read() * 100;
AppData[gAppDataSize++] = u16 >> 8;
AppData[gAppDataSize++] = u16;
break;
}
cayenne_ack_ch = -1;
}
while (d8.read() == 1) {
us_timestamp_t duration = LoRaMacReadTimer() - buttonStartAt;
if (duration > 1000000) {
gAppDataSize = 0;
AppData[gAppDataSize++] = CAYENNE_CH_DOUT;
AppData[gAppDataSize++] = LPP_DIGITAL_OUTPUT;
AppData[gAppDataSize++] = extLed.read();
return;
}
}
switch( port ) {
case LORAWAN_APP_PORT:
gAppDataSize = 0;
AppData[gAppDataSize++] = CAYENNE_CH_TEMP;
AppData[gAppDataSize++] = LPP_TEMPERATURE;
u16 = a3.read_u16() >> 4;
R = 4096.0 / u16 - 1.0;
R = R0 * R;
t = 1.0/(log(R/R0)/B+1/298.15)-273.15;
u16 = t * 10; // 0.1C per bit
AppData[gAppDataSize++] = u16 >> 8;
AppData[gAppDataSize++] = u16;
AppData[gAppDataSize++] = CAYENNE_CH_POT;
AppData[gAppDataSize++] = LPP_ANALOG_INPUT;
u16 = a1.read_u16(); // pot (rotary angle)
f = u16 / 198.6; // scale 65535/3.3 to 0.01v per bit
rot = (uint16_t) f;
AppData[gAppDataSize++] = rot >> 8;
AppData[gAppDataSize++] = rot;
AppData[gAppDataSize++] = CAYENNE_CH_DOUT;
AppData[gAppDataSize++] = LPP_DIGITAL_OUTPUT;
AppData[gAppDataSize++] = extLed.read();
break;
case 224:
if( ComplianceTest.LinkCheck == true ) {
ComplianceTest.LinkCheck = false;
gAppDataSize = 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:
gAppDataSize = 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 LoRaMacStatus_t SendFrame(bool IsTxConfirmed, uint8_t AppDataSize)
{
LoRaMacStatus_t status;
char str[64];
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.fBuffer = NULL;
mcpsReq.Req.fBufferSize = 0;
mcpsReq.Req.Datarate = LORAWAN_DEFAULT_DATARATE;
}
else
{
SerialDisplayUpdateFrameType(IsTxConfirmed);
if( IsTxConfirmed == false )
{
mcpsReq.Type = MCPS_UNCONFIRMED;
mcpsReq.Req.fPort = AppPort;
mcpsReq.Req.fBuffer = AppData;
mcpsReq.Req.fBufferSize = AppDataSize;
mcpsReq.Req.Datarate = LORAWAN_DEFAULT_DATARATE;
}
else
{
mcpsReq.Type = MCPS_CONFIRMED;
mcpsReq.Req.fPort = AppPort;
mcpsReq.Req.fBuffer = AppData;
mcpsReq.Req.fBufferSize = AppDataSize;
mcpsReq.Req.NbTrials = 8;
mcpsReq.Req.Datarate = LORAWAN_DEFAULT_DATARATE;
}
}
clearIndications();
status = LoRaMacMcpsRequest( &mcpsReq );
if (status == LORAMAC_STATUS_OK) {
SerialDisplayUplink(mcpsReq.Req.fPort, AppData, mcpsReq.Req.fBufferSize);
vt.SetCursorPos( ROW_END, 1 );
vt.printf("sendFrame() OK %u\e[K", AppDataSize);
} else {
LoRaMacStatus_to_string(status, str);
vt.SetCursorPos( ROW_END, 1 );
vt.printf("sendFrame() %s rx%d\e[K", str, LoRaMacGetRxSlot());
}
return status;
} // ..SendFrame()
/*!
* \brief MCPS-Confirm event function
*
* \param [IN] mcpsConfirm - Pointer to the confirm structure,
* containing confirm attributes.
*/
static void McpsConfirm( const McpsConfirm_t *mcpsConfirm )
{
char str[64];
vt.SetCursorPos( ROW_MCPS_CONF, 1);
vt.printf("McpsConfirm up:%uhz ", mcpsConfirm->UpLinkFreqHz);
LoRaMacEventInfoStatus_to_string(mcpsConfirm->Status, str);
if (mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
vt.printf("%s \e[K", str);
else
vt.printf("\e[31m%s\e[0m \e[K", str);
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;
}
} else {
/* fail */
}
memcpy(&gmc, mcpsConfirm, sizeof(McpsConfirm_t));
flags.gmc = true;
DeviceState = DEVICE_STATE_TRIGGER;
} // ..McpsConfirm()
/*!
* \brief MCPS-Indication event function
*
* \param [IN] mcpsIndication - Pointer to the indication structure,
* containing indication attributes.
*/
static void McpsIndication( const McpsIndication_t *mcpsIndication )
{
char str[64];
memcpy(&gmi, mcpsIndication, sizeof(McpsIndication_t));
flags.gmi = true;
vt.SetCursorPos(ROW_MCPS_CONF, 1);
vt.printf("\e[K"); // clear stale mcpsconf if retrying
vt.SetCursorPos( ROW_MCPS_IND, 0);
vt.printf("McpsIndication rx%d ", mcpsIndication->RxSlot);
if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK)
{
LoRaMacEventInfoStatus_to_string(mcpsIndication->Status, str);
vt.printf("\e[31m%s attempt%u\e[0m\e[K", str, mcpsIndication->attempt);
return;
}
vt.printf("OK \e[K");
switch( mcpsIndication->McpsIndication )
{
case MCPS_UNCONFIRMED:
{
break;
}
case MCPS_CONFIRMED:
{
/* ack sent by mac layer */
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 )
{
unsigned n;
for (n = 0; n < mcpsIndication->BufferSize; n += 4) {
uint16_t val = mcpsIndication->Buffer[n+1] << 8;
val += mcpsIndication->Buffer[n+2];
cayenne_ack_ch = mcpsIndication->Buffer[n];
switch (mcpsIndication->Buffer[n]) {
case CAYENNE_CH_DOUT:
extLed.write(val);
break;
case CAYENNE_CH_AOUT:
pwm.write(val / 100.0);
break;
default:
break;
}
}
switch( mcpsIndication->Port )
{
case 1: // The application LED can be controlled on port 1 or 2
case 2:
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 ) )
{
gIsTxConfirmed = false;
AppPort = 224;
gAppDataSize = 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 )
DutyCycleOn = false;
#endif
}
}
else
{
ComplianceTest.State = mcpsIndication->Buffer[0];
switch( ComplianceTest.State )
{
case 0: // Check compliance test disable command (ii)
gIsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
AppPort = LORAWAN_APP_PORT;
gAppDataSize = 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 )
DutyCycleOn = LORAWAN_DUTYCYCLE_ON;
#endif
break;
case 1: // (iii, iv)
gAppDataSize = 2;
break;
case 2: // Enable confirmed messages (v)
gIsTxConfirmed = true;
ComplianceTest.State = 1;
break;
case 3: // Disable confirmed messages (vi)
gIsTxConfirmed = false;
ComplianceTest.State = 1;
break;
case 4: // (vii)
gAppDataSize = mcpsIndication->BufferSize;
AppData[0] = 4;
for( uint8_t i = 1; i < gAppDataSize; 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)
{
#ifdef LORAWAN_JOIN_EUI
MlmeReq_t mlmeReq = {};
// Disable TestMode and revert back to normal operation
gIsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
AppPort = LORAWAN_APP_PORT;
gAppDataSize = 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 )
DutyCycleOn = LORAWAN_DUTYCYCLE_ON;
#endif
mlmeReq.Type = MLME_JOIN;
mlmeReq.Req.Join.DevEui = DevEui;
mlmeReq.Req.Join.JoinEui = JoinEui;
mlmeReq.Req.Join.NwkKey = NwkKey;
#ifdef LORAWAN_ROOT_APPKEY
mlmeReq.Req.Join.AppKey = AppKey;
#endif /* LORAWAN_ROOT_APPKEY */
LoRaMacMlmeRequest( &mlmeReq );
#endif /* LORAWAN_JOIN_EUI */
DeviceState = DEVICE_STATE_SLEEP;
}
break;
case 7: // Switch end device Class
{
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_SWITCH_CLASS;
// CLASS_A = 0, CLASS_B = 1, CLASS_C = 2
mlmeReq.Req.SwitchClass.Class = ( DeviceClass_t )mcpsIndication->Buffer[1];
LoRaMacMlmeRequest( &mlmeReq );
PrepareTxFrame( AppPort );
/*status =*/ SendFrame(gIsTxConfirmed, gAppDataSize);
}
break;
case 8: // Send PingSlotInfoReq
{
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_PING_SLOT_INFO;
mlmeReq.Req.PingSlotInfo.Value = mcpsIndication->Buffer[1];
LoRaMacMlmeRequest( &mlmeReq );
PrepareTxFrame( AppPort );
/*status =*/ SendFrame(gIsTxConfirmed, gAppDataSize);
}
break;
case 9: // Send BeaconTimingReq
{
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_BEACON_TIMING;
LoRaMacMlmeRequest( &mlmeReq );
PrepareTxFrame( AppPort );
/*status =*/ SendFrame(gIsTxConfirmed, gAppDataSize);
}
break;
default:
break;
}
}
break;
default:
break;
}
}
} // ..McpsIndication()
#ifdef LORAWAN_JOIN_EUI
void
join(uint8_t tries)
{
char str[64];
LoRaMacStatus_t status;
MlmeReq_t mlmeReq = { };
mlmeReq.Type = MLME_JOIN;
clearIndications();
#ifdef LORAWAN_ROOT_APPKEY
mlmeReq.Req.Join.AppKey = AppKey;
#endif
mlmeReq.Req.Join.DevEui = DevEui;
mlmeReq.Req.Join.JoinEui = JoinEui;
mlmeReq.Req.Join.NwkKey = NwkKey;
mlmeReq.Req.Join.NbTrials = tries;
status = LoRaMacMlmeRequest( &mlmeReq );
if (status != LORAMAC_STATUS_OK) {
LoRaMacStatus_to_string(status, str);
} else
extLed = 1;
}
#endif /* LORAWAN_JOIN_EUI */
/*!
* \brief MLME-Confirm event function
*
* \param [IN] mlmeConfirm - Pointer to the confirm structure,
* containing confirm attributes.
*/
static void MlmeConfirm( const MlmeConfirm_t *mlmeConfirm )
{
char str[64];
static uint8_t failCnt = 0;
vt.SetCursorPos(ROW_MLME_CONF, 1);
Mlme_to_string(mlmeConfirm->MlmeRequest, str);
vt.printf("MlmeConfirm %s ", str);
LoRaMacEventInfoStatus_to_string(mlmeConfirm->Status, str);
if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK)
vt.printf("\e[31m%s \e[0m \e[K", str);
else
vt.printf("%s \e[K", str);
#if defined(LORAWAN_ROOT_APPKEY) && defined(LORAWAN_JOIN_EUI)
/* 1v1 joinNonce is incrementing non-volatile value */
if (mlmeConfirm->MlmeRequest == MLME_JOIN) {
vt.printf(" rxJoinNonce:%u vs %u",
mlmeConfirm->fields.join.rxJoinNonce,
mlmeConfirm->fields.join.myJoinNonce
);
}
#endif /* LORAWAN_ROOT_APPKEY */
if (mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK)
{
failCnt = 0;
switch (mlmeConfirm->MlmeRequest)
{
#ifdef LORAWAN_JOIN_EUI
case MLME_JOIN:
{
// Status is OK, node has joined the network
/* collect any mac cmds from server until expected channel mask */
extLed = 0;
DeviceState = DEVICE_STATE_JOIN_OK;
break;
}
#endif /* LORAWAN_JOIN_EUI*/
case MLME_LINK_CHECK:
{
// Check DemodMargin
// Check NbGateways
if( ComplianceTest.Running == true )
{
ComplianceTest.LinkCheck = true;
ComplianceTest.DemodMargin = mlmeConfirm->fields.link.DemodMargin;
ComplianceTest.NbGateways = mlmeConfirm->fields.link.NbGateways;
}
break;
}
case MLME_TIME_REQ:
break;
default:
/* TODO: handle unknown MLME request */
DeviceState = DEVICE_STATE_SLEEP;
break;
}
}
else // not ok...
{
failCnt++;
#ifdef LORAWAN_JOIN_EUI
if (failCnt > 5) {
join(1);
return;
}
#endif
switch( mlmeConfirm->MlmeRequest )
{
#ifdef LORAWAN_JOIN_EUI
case MLME_JOIN:
{
// Join failed, restart join procedure
break;
}
#endif /* LORAWAN_JOIN_EUI */
case MLME_LINK_CHECK:
DeviceState = DEVICE_STATE_SLEEP;
break;
#ifdef LORAWAN_JOIN_EUI
case MLME_REJOIN_0:
break;
case MLME_REJOIN_2:
break;
case MLME_TIME_REQ:
break;
#endif /* LORAWAN_JOIN_EUI */
default:
DeviceState = DEVICE_STATE_SLEEP;
break;
}
}
} // ..MlmeConfirm
static void MlmeIndication( const MlmeIndication_t *MlmeIndication )
{
char str[48];
MibRequestConfirm_t mibReq;
vt.SetCursorPos(ROW_MLME_IND, 1);
Mlme_to_string(MlmeIndication->MlmeIndication, str);
vt.printf("MlmeIndication %s %08x ", str, RCC->CSR);
LoRaMacEventInfoStatus_to_string(MlmeIndication->Status, str);
if (MlmeIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK)
vt.printf("\e[31m%s \e[0m \e[K", str);
else
vt.printf("%s \e[K", str);
switch( MlmeIndication->MlmeIndication )
{
case MLME_SWITCH_CLASS:
{
/* mac gave up on beacon */
mibReq.Type = MIB_DEVICE_CLASS;
mibReq.Param.Class = CLASS_A;
LoRaMacMibSetRequestConfirm( &mibReq );
// Switch to class A again
DeviceState = DEVICE_STATE_SLEEP; // class-B manual switch
break;
}
case MLME_BEACON:
{
LoRaMacEventInfoStatus_to_string(MlmeIndication->Status, str);
break;
}
#ifdef LORAWAN_JOIN_EUI
case MLME_JOIN:
vt.printf("%uhz try%u", MlmeIndication->freqHz, MlmeIndication->JoinRequestTrials);
break;
#endif /* !LORAWAN_JOIN_EUI */
default:
break;
}
} // ..MlmeIndication()
uint8_t periodicity;
void SerialDisplayRefresh( void )
{
#ifdef LORAWAN_JOIN_EUI
MibRequestConfirm_t mibReq;
#endif
SerialDisplayInit( );
#ifdef LORAWAN_JOIN_EUI
SerialDisplayUpdateActivationMode(true);
SerialDisplayUpdateEui( ROW_DEVEUI, DevEui);
SerialDisplayUpdateEui( ROW_JOINEUI, JoinEui);
SerialDisplayUpdateKey( ROW_NWKKEY, NwkKey);
#ifdef LORAWAN_ROOT_APPKEY
SerialDisplayUpdateKey(ROW_APPKEY, AppKey);
#endif
mibReq.Type = MIB_NETWORK_JOINED;
LoRaMacMibGetRequestConfirm( &mibReq );
SerialDisplayUpdateNetworkIsJoined( mibReq.Param.IsNetworkJoined );
#else
//SerialDisplayUpdateNwkId( LORAWAN_NETWORK_ID );
SerialDisplayUpdateDevAddr( DevAddr );
SerialDisplayUpdateKey( ROW_FNwkSIntKey, FNwkSIntKey);
SerialDisplayUpdateKey( ROW_AppSKey, AppSKey );
#if defined(LORAWAN_SNwkSIntKey) && defined(LORAWAN_NwkSEncKey)
SerialDisplayUpdateKey(ROW_NwkSEncKey, NwkSEncKey);
SerialDisplayUpdateKey(ROW_SNwkSIntKey, SNwkSIntKey);
#endif /* 1v1 ABP */
vt.SetCursorPos( ROW_END, 1 );
vt.printf("FCntUp:%08x", eeprom_read(EEPROM_FCNTUP));
vt.printf(" AFCntDown:%08x", get_fcntdwn(true));
vt.printf(" NFCntDown:%08x", get_fcntdwn(false));
#endif
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 )
{
LoRaMacStatus_t status;
MlmeReq_t mlmeReq;
#ifndef LORAWAN_JOIN_EUI
static uint8_t icnt = 0;
#endif
if( SerialDisplayReadable( ) == true ) {
char ch = SerialDisplayGetChar();
#ifndef LORAWAN_JOIN_EUI
if (ch == 'I') {
if (++icnt == 3) {
vt.SetCursorPos( ROW_END, 1 );
vt.printf("reset-fcnts\e[K");
eeprom_clear(EEPROM_AFCNTDWN);
eeprom_clear(EEPROM_NFCNTDWN);
eeprom_clear(EEPROM_FCNTUP);
}
} else
icnt = 0;
#endif /* !LORAWAN_JOIN_EUI */
if ( ch >= '0' && ch <= '9') {
c_ch = ch - '0';
DeviceState = DEVICE_STATE_SEND;
return;
}
switch( ch ) {
case 'R':
case 'r':
// Refresh Serial screen
SerialDisplayRefresh( );
break;
case 'L':
clearIndications();
mlmeReq.Type = MLME_LINK_CHECK;
status = LoRaMacMlmeRequest( &mlmeReq );
if (status == LORAMAC_STATUS_OK)
SendFrame(0, false);
break;
#ifdef LORAWAN_JOIN_EUI
case 'j':
DeviceState = DEVICE_STATE_JOIN;
break;
#endif
default:
break;
}
}
}
static const LoRaMacPrimitives_t LoRaMacPrimitives = {
McpsConfirm,
McpsIndication,
MlmeConfirm,
MlmeIndication
};
static const LoRaMacCallback_t LoRaMacCallbacks = {
BoardGetBatteryLevel,
NULL
};
/**
* Main application entry point.
*/
int main()
{
MibRequestConfirm_t mibReq;
DeviceState = DEVICE_STATE_INIT;
if (sleep_manager_can_deep_sleep())
sleep_manager_lock_deep_sleep(); // prevent deep sleep
#ifdef JUMPER_ENABLE
jumper_out = 1;
jumper_in.mode(PullDown);
jumper_in.rise(jumper_callback);
#endif /* JUMPER_ENABLE */
while( 1 )
{
SerialRxProcess( );
if (flags.gmi) {
flags.gmi = false;
SerialDisplayMcpsIndication(&gmi);
}
if (flags.gmc) {
flags.gmc = false;
SerialDisplayMcpsConfirm(&gmc);
}
switch( DeviceState )
{
case DEVICE_STATE_INIT:
{
LoRaMacStatus_t status = LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks );
if (LORAMAC_STATUS_OK != status) {
char str[48];
LoRaMacStatus_to_string(status, str);
vt.SetCursorPos(1, 1);
vt.printf("MacInit: %s\e[K", str);
for (;;) asm("nop");
}
pwm.period(1.0 / 60);
cayenne_ack_ch = -1;
c_ch = 0xff;
d8.mode(PullDown);
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 )
DutyCycleOn = 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_CHANNEL;
mibReq.Param.Rx2Channel = ( Rx2ChannelParams_t ){ 869525000, DR_3 };
LoRaMacMibSetRequestConfirm( &mibReq );
#endif
#endif
SerialDisplayRefresh();
#ifdef LORAWAN_JOIN_EUI
#ifndef SENETCO /* for senet, use network provided DevEUI */
// Initialize LoRaMac device unique ID
HardwareIDtoDevEUI(DevEui);
#ifdef LORAWAN_ROOT_APPKEY
// inverted DevEui provisioned as v1.1 on server (non-inv = lorawan1v0)
for (int i = 0; i < 8; i++)
DevEui[i] ^= 0xff;
#endif /* LORAWAN_ROOT_APPKEY */
#endif /* !SENETCO */
SerialDisplayUpdateEui( 5, DevEui );
DeviceState = DEVICE_STATE_JOIN;
#else /* ABP... */
mibReq.Type = MIB_DEV_ADDR;
mibReq.Param.DevAddr = DevAddr;
LoRaMacMibSetRequestConfirm( &mibReq );
SerialDisplayUpdateDevAddr(DevAddr);
mibReq.Type = MIB_APP_SKEY;
mibReq.Param.key = AppSKey;
LoRaMacMibSetRequestConfirm( &mibReq );
SerialDisplayUpdateKey(ROW_AppSKey, AppSKey);
#if defined(LORAWAN_SNwkSIntKey) && defined(LORAWAN_NwkSEncKey)
/* lorawan 1v1 ABP */
mibReq.Type = MIB_NwkSEncKey;
mibReq.Param.NwkSEncKey = NwkSEncKey;
LoRaMacMibSetRequestConfirm( &mibReq );
SerialDisplayUpdateKey(ROW_NwkSEncKey, NwkSEncKey);
mibReq.Type = MIB_SNwkSIntKey;
mibReq.Param.SNwkSIntKey = SNwkSIntKey;
LoRaMacMibSetRequestConfirm( &mibReq );
SerialDisplayUpdateKey(ROW_SNwkSIntKey, SNwkSIntKey);
mibReq.Type = MIB_FNwkSIntKey;
mibReq.Param.key = FNwkSIntKey;
LoRaMacMibSetRequestConfirm( &mibReq );
SerialDisplayUpdateKey(ROW_FNwkSIntKey, mibReq.Param.key);
#else
/* lorawan 1v0 ABP */
mibReq.Type = MIB_NwkSKey;
mibReq.Param.key = FNwkSIntKey;
LoRaMacMibSetRequestConfirm( &mibReq );
SerialDisplayUpdateKey(ROW_FNwkSIntKey, mibReq.Param.key);
#endif
DeviceState = DEVICE_STATE_TRIGGER;
#endif /* !LORAWAN_JOIN_EUI */
break;
}
#ifdef LORAWAN_JOIN_EUI
case DEVICE_STATE_JOIN:
{
join(8);
DeviceState = DEVICE_STATE_SLEEP;
break;
}
case DEVICE_STATE_JOIN_OK:
MibRequestConfirm_t mibReq;
mibReq.Type = MIB_NETWORK_JOINED;
LoRaMacMibGetRequestConfirm( &mibReq );
SerialDisplayUpdateNetworkIsJoined( mibReq.Param.IsNetworkJoined );
mibReq.Type = MIB_DEV_ADDR;
LoRaMacMibGetRequestConfirm( &mibReq );
SerialDisplayUpdateDevAddr(mibReq.Param.DevAddr);
mibReq.Type = MIB_FNwkSIntKey;
LoRaMacMibGetRequestConfirm( &mibReq );
SerialDisplayUpdateKey( ROW_FNwkSIntKey, mibReq.Param.key );
mibReq.Type = MIB_APP_SKEY;
LoRaMacMibGetRequestConfirm( &mibReq );
SerialDisplayUpdateKey( ROW_AppSKey, mibReq.Param.key );
#ifdef LORAWAN_ROOT_APPKEY
mibReq.Type = MIB_SNwkSIntKey;
LoRaMacMibGetRequestConfirm( &mibReq );
SerialDisplayUpdateKey(ROW_SNwkSIntKey, mibReq.Param.key);
mibReq.Type = MIB_NwkSEncKey;
LoRaMacMibGetRequestConfirm( &mibReq );
SerialDisplayUpdateKey(ROW_NwkSEncKey, mibReq.Param.key);
#endif /* LORAWAN_ROOT_APPKEY */
DeviceState = DEVICE_STATE_TRIGGER;
break;
#endif /* LORAWAN_JOIN_EUI */
case DEVICE_STATE_SEND:
SerialDisplayUpdateUplinkAcked( false );
SerialDisplayUpdateDonwlinkRxData( false );
PrepareTxFrame( AppPort );
/*status =*/ SendFrame(gIsTxConfirmed, gAppDataSize);
/* McpsConfirm or McpsIndication callback will continue */
DeviceState = DEVICE_STATE_SLEEP;
break;
case DEVICE_STATE_SLEEP:
{
// Wake up through events
sleep_manager_sleep_auto();
break;
}
case DEVICE_STATE_TRIGGER:
sleep_manager_sleep_auto();
if (d8.read() == 1) {
c_ch = 0xff;
DeviceState = DEVICE_STATE_SEND;
buttonStartAt = LoRaMacReadTimer();
}
break;
default:
DeviceState = DEVICE_STATE_INIT;
break;
} // ..switch( DeviceState )
} // ..while( 1 )
}
#endif /* ENABLE_VT100 */