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.
Fork of LoRaWAN-grove-cayenne by
sensorDemoVT100.cpp
- Committer:
- Wayne Roberts
- Date:
- 2018-03-29
- Revision:
- 4:72b8fdc9878e
- Parent:
- 3:fc23bbc14475
- Child:
- 5:53cd6c24a4ab
File content as of revision 4:72b8fdc9878e:
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(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 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 )
/*!
* 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)
InterruptIn 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
#define CAYENNE_CH_DIN 4
AnalogIn a1(A1);
AnalogIn a3(A3);
const unsigned R0 = 100000;
const unsigned B = 4275;
volatile bool dout_downlink;
/*!
* \brief Prepares the payload of the frame
*/
static void PrepareTxFrame( uint8_t port )
{
uint16_t u16, rot;
float t, f, R;
static uint8_t seq;
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;
/* limited packet size: either ack downlink, or send sequence number */
if (dout_downlink) {
AppData[gAppDataSize++] = CAYENNE_CH_DOUT;
AppData[gAppDataSize++] = LPP_DIGITAL_OUTPUT;
AppData[gAppDataSize++] = extLed.read();
dout_downlink = false;
} else {
AppData[gAppDataSize++] = CAYENNE_CH_DIN;
AppData[gAppDataSize++] = LPP_DIGITAL_INPUT;
AppData[gAppDataSize++] = seq++;
}
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.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 ADR_ACK_CNT:%u ", mcpsIndication->RxSlot, mcpsIndication->ADR_ACK_CNT);
if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK)
{
LoRaMacEventInfoStatus_to_string(mcpsIndication->Status, str);
vt.printf("\e[31m%s\e[0m\e[K", str);
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);
dout_downlink = true;
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 void d8isr()
{
c_ch = 0xff;
DeviceState = DEVICE_STATE_SEND;
buttonStartAt = LoRaMacReadTimer();
}
static const LoRaMacPrimitives_t LoRaMacPrimitives = {
McpsConfirm,
McpsIndication,
MlmeConfirm,
MlmeIndication
};
static const LoRaMacCallback_t LoRaMacCallbacks = {
BoardGetBatteryLevel,
NULL
};
/**
* Main application entry point.
*/
int main()
{
LoRaMacStatus_t status;
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);
// Q: does InterruptIn.rise() call immediately if already high?
if (jumper_in.read())
jumper_callback(); // A: probably not
#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:
{
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);
d8.rise(d8isr);
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.key = NwkSEncKey;
LoRaMacMibSetRequestConfirm( &mibReq );
SerialDisplayUpdateKey(ROW_NwkSEncKey, NwkSEncKey);
mibReq.Type = MIB_SNwkSIntKey;
mibReq.Param.key = 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);
if (status == LORAMAC_STATUS_OK) {
/* McpsConfirm or McpsIndication callback will continue */
DeviceState = DEVICE_STATE_SLEEP;
} else
DeviceState = DEVICE_STATE_TRIGGER;
break;
case DEVICE_STATE_SLEEP:
{
// Wake up through events
sleep_manager_sleep_auto();
break;
}
case DEVICE_STATE_TRIGGER:
/* wait button ISR */
sleep_manager_sleep_auto();
break;
default:
DeviceState = DEVICE_STATE_INIT;
break;
} // ..switch( DeviceState )
LoRaMacUserContext();
} // ..while( 1 )
}
#endif /* ENABLE_VT100 */
