Ottawa Bootcamp Publish
Dependencies: SX127x lorawan1v1
sensorDemoVT100.cpp
- Committer:
- Wayne Roberts
- Date:
- 2018-07-23
- Revision:
- 8:efe6002910df
- Parent:
- 6:795461780e10
- Child:
- 13:89e36ee10179
File content as of revision 8:efe6002910df:
/* / _____) _ | | ( (____ _____ ____ _| |_ _____ ____| |__ \____ \| ___ | (_ _) ___ |/ ___) _ \ _____) ) ____| | | || |_| ____( (___| | | | (______/|_____)_|_|_| \__)_____)\____)_| |_| (C)2018 Semtech Description: LoRaMac classB device implementation License: Revised BSD License, see LICENSE.TXT file include in the project */ #include "LoRaMac1v1.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) #ifdef TARGET_DISCO_L072CZ_LRWAN1 #define LED_GREEN LED1 #define LED_RED2 LED2 // next to LD7 #define LED_BLUE LED3 #define LED_RED4 LED4 InterruptIn button_pin(USER_BUTTON); #define BUTTON_PRESSED 0 DigitalOut extLed(LED_RED4); AnalogIn ain_temp(PA_0); #else InterruptIn button_pin(D8); #define BUTTON_PRESSED 1 DigitalOut extLed(D15); AnalogIn ain_pot(A1); AnalogIn ain_temp(A3); #endif #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); #define _PWM_ #elif defined(TARGET_FF_ARDUINO) && defined(TARGET_FAMILY_STM32) PwmOut pwm(PB_11); #define _PWM_ #elif defined(TARGET_NRF52_DK) //PwmOut pwm(P0_0); #undef _PWM_ #else #error define_PWM_pin_for_this_target #endif /* */ 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 #ifndef TARGET_DISCO_L072CZ_LRWAN1 #define CAYENNE_CH_POT 1 #endif /* !TARGET_DISCO_L072CZ_LRWAN1 */ #define CAYENNE_CH_DIN 4 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 = ain_temp.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; #ifndef TARGET_DISCO_L072CZ_LRWAN1 case CAYENNE_CH_POT: AppData[gAppDataSize++] = LPP_ANALOG_INPUT; u16 = ain_pot.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; #endif /* !TARGET_DISCO_L072CZ_LRWAN1 */ case CAYENNE_CH_DOUT: AppData[gAppDataSize++] = LPP_DIGITAL_OUTPUT; AppData[gAppDataSize++] = extLed.read(); break; case CAYENNE_CH_AOUT: AppData[gAppDataSize++] = LPP_ANALOG_OUTPUT; #ifdef _PWM_ u16 = pwm.read() * 100; #endif /* _PWM_ */ 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; #ifdef _PWM_ u16 = pwm.read() * 100; #endif /* _PWM_ */ AppData[gAppDataSize++] = u16 >> 8; AppData[gAppDataSize++] = u16; break; } cayenne_ack_ch = -1; } while (button_pin.read() == BUTTON_PRESSED) { 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; //vt.SetCursorPos( ROW_END-1, 1 ); u16 = ain_temp.read_u16() >> 4; //vt.printf("0x%03x ", u16); R = 4096.0 / u16 - 1.0; R = R0 * R; t = 1.0/(log(R/R0)/B+1/298.15)-273.15; //vt.printf("%.1fC\e[K", t); u16 = t * 10; // 0.1C per bit AppData[gAppDataSize++] = u16 >> 8; AppData[gAppDataSize++] = u16; #ifndef TARGET_DISCO_L072CZ_LRWAN1 AppData[gAppDataSize++] = CAYENNE_CH_POT; AppData[gAppDataSize++] = LPP_ANALOG_INPUT; u16 = ain_pot.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; #endif /* !TARGET_DISCO_L072CZ_LRWAN1 */ /* 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: #ifdef _PWM_ pwm.write(val / 100.0); #endif /* _PWM_ */ 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 ", str); #ifdef TARGET_STM32 vt.printf(" %08x ", RCC->CSR); #endif /* TARGET_STM32 */ 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 button_isr() { 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"); } #ifdef _PWM_ pwm.period(1.0 / 60); #endif /* _PWM_ */ cayenne_ack_ch = -1; c_ch = 0xff; button_pin.mode(PullDown); #ifdef TARGET_DISCO_L072CZ_LRWAN1 button_pin.fall(button_isr); #else button_pin.rise(button_isr); #endif 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 */