Ottawa Bootcamp Publish
Dependencies: SX127x lorawan1v1
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 */