classC example
main.cpp
- Committer:
- Wayne Roberts
- Date:
- 2018-05-29
- Revision:
- 1:708d73c0e43c
- Parent:
- 0:4b6e76c6608f
File content as of revision 1:708d73c0e43c:
/* / _____) _ | | ( (____ _____ ____ _| |_ _____ ____| |__ \____ \| ___ | (_ _) ___ |/ ___) _ \ _____) ) ____| | | || |_| ____( (___| | | | (______/|_____)_|_|_| \__)_____)\____)_| |_| (C)2018 Semtech Description: LoRaMac classB device implementation License: Revised BSD License, see LICENSE.TXT file include in the project */ #include "LoRaMac1v1.h" #include "LoRaMacString.h" /*! * 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 #ifdef TARGET_DISCO_L072CZ_LRWAN1 #define GREEN LED1 #define RED2 LED2 #define BLUE LED3 #define RED4 LED4 DigitalOut blue(BLUE); #define LED_ON 1 #define LED_OFF 0 #endif /* TARGET_DISCO_L072CZ_LRWAN1 */ /*! * 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 startup : 1; uint8_t ackDownlink : 1; } flags; /*! * Device states */ volatile enum eDevicState { /* 0 */ DEVICE_STATE_INIT = 0, /* 1 */ DEVICE_STATE_SEND, /* 2 */ DEVICE_STATE_TRIGGER, /* 3 */ DEVICE_STATE_SLEEP, /* 4 */ DEVICE_STATE_SWITCH_CLASS_C, #ifdef LORAWAN_JOIN_EUI /* 5 */ DEVICE_STATE_JOIN, /* 6 */ 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); #elif defined(TARGET_FF_ARDUINO) PwmOut pwm(PB_11); #endif /* TARGET_DISCO_L072CZ_LRWAN1 */ /*! * 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; #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 */ /*! * \brief Prepares the payload of the frame */ static void PrepareTxFrame( uint8_t port ) { while (button_pin.read() == BUTTON_PRESSED) { us_timestamp_t duration = LoRaMacReadTimer() - buttonStartAt; if (duration > 1000000) { gAppDataSize = 0; AppData[gAppDataSize++] = 0; return; } } gAppDataSize = 0; AppData[gAppDataSize++] = 0; } /*! * \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 { 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; } } status = LoRaMacMcpsRequest( &mcpsReq ); if (status == LORAMAC_STATUS_OK) { APP_PRINTF("sendFrame() OK "); if (mcpsReq.Req.fBufferSize > 0) { APP_PRINTF("FPort%u ", mcpsReq.Req.fPort); print_buf(AppData, mcpsReq.Req.fBufferSize, "uplink"); } } else { LoRaMacStatus_to_string(status, str); APP_PRINTF("sendFrame() \e[31m%s rx%d\r\n", str, LoRaMacGetRxSlot()); } return status; } // ..SendFrame() #define ACK_DEFER_US 800000 void ack_isr() { SendFrame(false, 0); } /*! * \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]; APP_PRINTF("McpsConfirm up:%" PRIu32 "hz ", mcpsConfirm->UpLinkFreqHz); LoRaMacEventInfoStatus_to_string(mcpsConfirm->Status, str); if (mcpsConfirm->AckReceived) APP_PRINTF("Ack "); APP_PRINTF("dr%u FCntUp:%" PRIu32 " ", mcpsConfirm->Datarate, mcpsConfirm->UpLinkCounter); if (mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) { APP_PRINTF("%s\r\n", str); } else { APP_PRINTF("\e[31m%s\e[0m\r\n", 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; } DeviceState = DEVICE_STATE_TRIGGER; } else { /* fail */ DeviceState = DEVICE_STATE_TRIGGER; if (flags.startup && mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT) { MibRequestConfirm_t mibReq; /* server has nothing more to send, startup complete */ flags.startup = 0; mibReq.Type = MIB_RX2_CHANNEL; LoRaMacMibGetRequestConfirm( &mibReq ); APP_PRINTF("startup-end rx2 %uhz dr%u\r\n", mibReq.Param.Rx2Channel.FrequencyHz, mibReq.Param.Rx2Channel.Datarate); DeviceState = DEVICE_STATE_SWITCH_CLASS_C; } } } // ..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]; int8_t Snr; APP_PRINTF("McpsIndication rx%d ADR_ACK_CNT:%u ", mcpsIndication->RxSlot, mcpsIndication->ADR_ACK_CNT); if (mcpsIndication->Snr & 0x80) { // The SNR sign bit is 1 // Invert and divide by 4 Snr = ( ( ~mcpsIndication->Snr + 1 ) & 0xFF ) >> 2; Snr = -Snr; } else { // Divide by 4 Snr = ( mcpsIndication->Snr & 0xFF ) >> 2; } if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK) { LoRaMacEventInfoStatus_to_string(mcpsIndication->Status, str); APP_PRINTF("\e[31m%s\e[0m\r\n", str); return; } APP_PRINTF("OK Snr:%d FCntDown:%u\r\n", Snr, mcpsIndication->receivedFCntDown); switch( mcpsIndication->McpsIndication ) { case MCPS_UNCONFIRMED: { break; } case MCPS_CONFIRMED: { if (flags.ackDownlink) { tx_timeout.attach_us(ack_isr, ACK_DEFER_US); APP_PRINTF("confAck "); } 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) { APP_PRINTF("FPort%u ", mcpsIndication->Port); print_buf(mcpsIndication->Buffer, mcpsIndication->BufferSize, ""); #ifdef TARGET_DISCO_L072CZ_LRWAN1 if (mcpsIndication->Buffer[0]) blue = LED_ON; else blue = LED_OFF; #else /* your target LED */ #endif /* ..TARGET_DISCO_L072CZ_LRWAN1 */ 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; #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; flags.startup = 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; Mlme_to_string(mlmeConfirm->MlmeRequest, str); APP_PRINTF("MlmeConfirm %s ", str); LoRaMacEventInfoStatus_to_string(mlmeConfirm->Status, str); if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) { APP_PRINTF("\e[31m%s \e[0m\r\n", str); } else { APP_PRINTF("%s\r\n", str); } #if defined(LORAWAN_ROOT_APPKEY) && defined(LORAWAN_JOIN_EUI) /* 1v1 joinNonce is incrementing non-volatile value */ if (mlmeConfirm->MlmeRequest == MLME_JOIN) { vt.APP_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; Mlme_to_string(MlmeIndication->MlmeIndication, str); APP_PRINTF("MlmeIndication %s ", str); LoRaMacEventInfoStatus_to_string(MlmeIndication->Status, str); if (MlmeIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK) { APP_PRINTF("\e[31m%s \e[0m\r\n", str); } else { APP_PRINTF("%s\r\n", 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: APP_PRINTF("%" PRIu32 "hz try%u ", MlmeIndication->freqHz, MlmeIndication->JoinRequestTrials); break; #endif /* !LORAWAN_JOIN_EUI */ default: break; } } // ..MlmeIndication() uint8_t periodicity; void SerialRxProcess( void ) { char str[48]; MibRequestConfirm_t mibReq; LoRaMacStatus_t status; MlmeReq_t mlmeReq; #ifndef LORAWAN_JOIN_EUI static uint8_t icnt = 0; #endif if( pc.readable( ) == true ) { char ch = pc.getc(); #ifndef LORAWAN_JOIN_EUI if (ch == 'I') { if (++icnt == 3) { APP_PRINTF("reset-fcnts\r\n"); 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 'C': mibReq.Type = MIB_DEVICE_CLASS; mibReq.Param.Class = CLASS_C; status = LoRaMacMibSetRequestConfirm( &mibReq ); LoRaMacStatus_to_string(status, str); APP_PRINTF("to classC:%s\r\n", str); break; case 'L': mlmeReq.Type = MLME_LINK_CHECK; status = LoRaMacMlmeRequest( &mlmeReq ); if (status == LORAMAC_STATUS_OK) SendFrame(false, 0); else { LoRaMacStatus_to_string(status, str); APP_PRINTF("%s\r\n", str); } break; #ifdef LORAWAN_JOIN_EUI case 'j': DeviceState = DEVICE_STATE_JOIN; break; #endif case 'a': flags.ackDownlink ^= 1; APP_PRINTF("ackDownlink:%u\r\n", flags.ackDownlink); break; case '.': print_buf(DevEui, 8, "DevEui"); mibReq.Type = MIB_DEVICE_CLASS; LoRaMacMibGetRequestConfirm( &mibReq ); APP_PRINTF("class-"); switch (mibReq.Param.Class) { case CLASS_A: APP_PRINTF("A "); break; case CLASS_B: APP_PRINTF("B "); break; case CLASS_C: APP_PRINTF("C "); break; } mibReq.Type = MIB_RX2_CHANNEL; LoRaMacMibGetRequestConfirm( &mibReq ); APP_PRINTF(" rx2 %uhz dr%u, ackDownlink:%u\r\n", mibReq.Param.Rx2Channel.FrequencyHz, mibReq.Param.Rx2Channel.Datarate, flags.ackDownlink); LoRaMacPrintStatus(); break; case '?': APP_PRINTF("C start classC\r\n"); APP_PRINTF("L send link check\r\n"); APP_PRINTF("j send joinReq\r\n"); APP_PRINTF("a toggle ackDownlink\r\n"); APP_PRINTF(". print status\r\n"); break; 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( ); switch( DeviceState ) { case DEVICE_STATE_INIT: { status = LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks ); if (LORAMAC_STATUS_OK != status) { char str[48]; LoRaMacStatus_to_string(status, str); APP_PRINTF("MacInit: %s\r\n", str); for (;;) asm("nop"); } pwm.period(1.0 / 60); 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 #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 */ print_buf(DevEui, 8, "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 */ #if defined( USE_BAND_ARIB_8CH ) mibReq.Type = MIB_MAX_LISTEN_TIME; mibReq.Param.MaxListenTime = 7000000; LoRaMacMibSetRequestConfirm( &mibReq ); #endif /* USE_BAND_ARIB_8CH */ flags.ackDownlink = 1; flags.startup = 1; 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 ); if (mibReq.Param.IsNetworkJoined) APP_PRINTF("joined "); mibReq.Type = MIB_DEV_ADDR; LoRaMacMibGetRequestConfirm( &mibReq ); APP_PRINTF("DevAddr:%08" PRIx32 " ", mibReq.Param.DevAddr); mibReq.Type = MIB_FNwkSIntKey; LoRaMacMibGetRequestConfirm( &mibReq ); print_buf(mibReq.Param.key, 16, "FNwkSIntKey"); mibReq.Type = MIB_APP_SKEY; LoRaMacMibGetRequestConfirm( &mibReq ); print_buf(mibReq.Param.key, 16, "AppSKey"); #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_SWITCH_CLASS_C: APP_PRINTF("toclassC "); mibReq.Type = MIB_DEVICE_CLASS; mibReq.Param.Class = CLASS_C; status = LoRaMacMibSetRequestConfirm( &mibReq ); if (status != LORAMAC_STATUS_OK) { char str[48]; LoRaMacStatus_to_string(status, str); APP_PRINTF("%s\r\n", str); DeviceState = DEVICE_STATE_SLEEP; } else { /* send uplink with payload, so app server gets new session */ APP_PRINTF("OK\r\n"); DeviceState = DEVICE_STATE_SEND; } break; case DEVICE_STATE_SEND: 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 ) }