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: SX127x lorawan1v1
Fork of LoRaWAN-grove-cayenne by
Diff: sensorDemoVT100.cpp
- Revision:
- 0:62e456e60083
- Child:
- 1:3c1d13a0489e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sensorDemoVT100.cpp Wed Feb 28 14:06:17 2018 -0800
@@ -0,0 +1,1238 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (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);
+ LoRaMacEventInfoStatus_to_string(MlmeIndication->Status, str);
+ Mlme_to_string(MlmeIndication->MlmeIndication, str);
+ vt.printf("MlmeIndication %s ", 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:
+ {
+ pwm.period(1.0 / 60);
+ cayenne_ack_ch = -1;
+ c_ch = 0xff;
+ d8.mode(PullDown);
+ if (LORAMAC_STATUS_OK != LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks )) {
+ return -1;
+ }
+
+ 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 */
