application layer with: button, LED, pot, tempSense
Fork of LoRaWAN-demo-72-bootcamp by
Use with sx1272 shield with grove peripherals connected:
D8 D9: Button | RX TX | A3 A4: TempSense |
D6 D7: | SCL SDA : LED | A1 A2: Pot |
Button
Sends to different payloads: short press (under 1 sec)
long press: held down > 1 sec.
serial console keys
115200bps, 8N1
Enter key not used
Keys '0' to '3': cayenne channel number
'0': pot (rotary sensor)
'1': temperature
'
2': digital out
'3': analog out
app/main.cpp
- Committer:
- dudmuck
- Date:
- 2018-02-03
- Revision:
- 11:018e7e28161d
- Parent:
- 10:52810ecbd83b
- Child:
- 12:662ff73ce484
File content as of revision 11:018e7e28161d:
/* / _____) _ | | ( (____ _____ ____ _| |_ _____ ____| |__ \____ \| ___ | (_ _) ___ |/ ___) _ \ _____) ) ____| | | || |_| ____( (___| | | | (______/|_____)_|_|_| \__)_____)\____)_| |_| (C)2015 Semtech Description: LoRaMac classA device implementation License: Revised BSD License, see LICENSE.TXT file include in the project Maintainer: Miguel Luis and Gregory Cristian */ #include "mbed.h" #include "board.h" #include "radio.h" #include "LoRaMac.h" #include "Commissioning.h" #include "SerialDisplay.h" /*! * Defines the application data transmission duty cycle. 5s, value in [ms]. */ #define APP_TX_DUTYCYCLE 5000 /*! * Defines a random delay for application data transmission duty cycle. 1s, * value in [ms]. */ #define APP_TX_DUTYCYCLE_RND 1000 /*! * Default datarate */ #define LORAWAN_DEFAULT_DATARATE DR_5 /*! * 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 ) #include "LoRaMacTest.h" /*! * 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 10 /*! * User application data buffer size */ #if ( LORAWAN_CONFIRMED_MSG_ON == 1 ) #define LORAWAN_APP_DATA_SIZE 6 #else #define LORAWAN_APP_DATA_SIZE 5 #endif static uint8_t DevEui[] = LORAWAN_DEVICE_EUI; static uint8_t AppEui[] = LORAWAN_APPLICATION_EUI; static uint8_t AppKey[] = LORAWAN_APPLICATION_KEY; #if( OVER_THE_AIR_ACTIVATION == 0 ) static uint8_t NwkSKey[] = LORAWAN_NWKSKEY; static uint8_t AppSKey[] = LORAWAN_APPSKEY; /*! * Device address */ static uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS; #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; /*! * Device states */ static enum eDeviceState { DEVICE_STATE_INIT = 0, DEVICE_STATE_JOIN, DEVICE_STATE_JOIN_OK, DEVICE_STATE_SEND, DEVICE_STATE_TRIGGER, DEVICE_STATE_SLEEP } DeviceState; /*! * 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; /* * SerialDisplay managment variables */ /*! * Strucure containing the Uplink status */ struct sLoRaMacUplinkStatus { uint8_t Acked; int8_t Datarate; uint16_t UplinkCounter; uint8_t Port; uint8_t *Buffer; uint8_t BufferSize; } LoRaMacUplinkStatus; /*! * Strucure containing the Downlink status */ struct sLoRaMacDownlinkStatus { int16_t Rssi; int8_t Snr; uint16_t DownlinkCounter; bool RxData; uint8_t Port; uint8_t *Buffer; uint8_t BufferSize; } LoRaMacDownlinkStatus; volatile bool DownlinkStatusUpdated = false; void SerialDisplayRefresh( void ) { MibRequestConfirm_t mibReq; SerialDisplayInit( ); SerialDisplayUpdateActivationMode( OVER_THE_AIR_ACTIVATION ); #if( OVER_THE_AIR_ACTIVATION == 0 ) SerialDisplayUpdateNwkId( LORAWAN_NETWORK_ID ); SerialDisplayUpdateDevAddr( DevAddr ); SerialDisplayUpdateKey( 12, NwkSKey ); SerialDisplayUpdateKey( 13, AppSKey ); #endif SerialDisplayUpdateEui( 5, DevEui ); SerialDisplayUpdateEui( 6, AppEui ); SerialDisplayUpdateKey( 7, AppKey ); mibReq.Type = MIB_NETWORK_JOINED; LoRaMacMibGetRequestConfirm( &mibReq ); SerialDisplayUpdateNetworkIsJoined( mibReq.Param.IsNetworkJoined ); 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 LoRaMacStatus_toString(LoRaMacStatus_t s, char* out) { switch (s) { case LORAMAC_STATUS_PARAMETER_INVALID: strcpy(out, "PARAMTER_INVALID"); break; case LORAMAC_STATUS_BUSY: strcpy(out, "BUSY"); break; default: sprintf(out, "<%u>", s); break; } } volatile unsigned sendCnt = 0; /*! * \brief Prepares the payload of the frame * * \retval [0: frame could be send, 1: error] */ static bool SendFrame( uint8_t len, bool conf ) { char str[48]; LoRaMacStatus_t status; McpsReq_t mcpsReq; LoRaMacTxInfo_t txInfo; if( LoRaMacQueryTxPossible( len, &txInfo ) != LORAMAC_STATUS_OK ) { // Send empty frame in order to flush MAC commands mcpsReq.Type = MCPS_UNCONFIRMED; mcpsReq.Req.Unconfirmed.fBuffer = NULL; mcpsReq.Req.Unconfirmed.fBufferSize = 0; mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE; LoRaMacUplinkStatus.Acked = false; LoRaMacUplinkStatus.Port = 0; LoRaMacUplinkStatus.Buffer = NULL; LoRaMacUplinkStatus.BufferSize = 0; SerialDisplayUpdateFrameType( false ); } else { LoRaMacUplinkStatus.Acked = false; LoRaMacUplinkStatus.Port = AppPort; LoRaMacUplinkStatus.Buffer = AppData; LoRaMacUplinkStatus.BufferSize = len;//AppDataSize; SerialDisplayUpdateFrameType( conf/*IsTxConfirmed*/ ); if( conf == false ) { mcpsReq.Type = MCPS_UNCONFIRMED; mcpsReq.Req.Unconfirmed.fPort = AppPort; mcpsReq.Req.Unconfirmed.fBuffer = AppData; mcpsReq.Req.Unconfirmed.fBufferSize = len; mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE; } else { mcpsReq.Type = MCPS_CONFIRMED; mcpsReq.Req.Confirmed.fPort = AppPort; mcpsReq.Req.Confirmed.fBuffer = AppData; mcpsReq.Req.Confirmed.fBufferSize = len; mcpsReq.Req.Confirmed.NbTrials = 8; mcpsReq.Req.Confirmed.Datarate = LORAWAN_DEFAULT_DATARATE; } } sendCnt++; status = LoRaMacMcpsRequest( &mcpsReq ); if (status == LORAMAC_STATUS_OK) { vt.SetCursorPos( 44, 1 ); vt.printf("%u sendFrame() OK\e[K", sendCnt); SerialDisplayUpdateUplink( LoRaMacUplinkStatus.Acked, LoRaMacUplinkStatus.Datarate, LoRaMacUplinkStatus.UplinkCounter, LoRaMacUplinkStatus.Port, LoRaMacUplinkStatus.Buffer, LoRaMacUplinkStatus.BufferSize ); return false; } LoRaMacStatus_toString(status, str); vt.SetCursorPos( 44, 1 ); vt.printf("%u sendFrame() %s\e[K", sendCnt, str); return true; } uint8_t c_ch; void SerialRxProcess( void ) { LoRaMacStatus_t status; MlmeReq_t mlmeReq; if( SerialDisplayReadable( ) == true ) { char ch = SerialDisplayGetChar(); 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': mlmeReq.Type = MLME_LINK_CHECK; status = LoRaMacMlmeRequest( &mlmeReq ); if (status == LORAMAC_STATUS_OK) SendFrame(0, false); break; default: break; } } } #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 a2(A2); AnalogIn a3(A3); AnalogIn a4(A4); const unsigned R0 = 100000; const unsigned B = 4275; volatile TimerTime_t buttonStartAt; DigitalIn d8(D8); DigitalOut extLed(D15); PwmOut pwm(PB_11); volatile int cayenne_ack_ch; /*! * \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) { TimerTime_t duration = TimerGetCurrentTime() - buttonStartAt; vt.SetCursorPos( 41, 1 ); if (duration > 1000) { gAppDataSize = 0; AppData[gAppDataSize++] = CAYENNE_CH_DOUT; AppData[gAppDataSize++] = LPP_DIGITAL_OUTPUT; AppData[gAppDataSize++] = extLed.read(); vt.printf("send outputs ", duration); return; } else vt.printf("dur %u ", duration); } 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(); vt.SetCursorPos( 41, 1 ); vt.printf("u16:%u, f:%f, rot:%u t:%.1f\e[K", u16, f, rot, t); 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; } } void LoRaMacEventInfoStatus_toString(LoRaMacEventInfoStatus_t s, char* out) { switch (s) { case LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL: strcpy(out, "JOIN_FAIL"); break; case LORAMAC_EVENT_INFO_STATUS_ERROR: strcpy(out, "ERROR"); break; case LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT: strcpy(out, "RX2_TIMEOUT"); break; case LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL: strcpy(out, "ADDRESS_FAIL"); break; case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS: strcpy(out, "FRAMES_LOSS"); break; case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED: strcpy(out, "DOWNLINK_REPEATED"); break; // confirmed downlink retry? case LORAMAC_EVENT_INFO_STATUS_MIC_FAIL: strcpy(out, "MIC_FAIL"); break; case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT: strcpy(out, "TX_TIMEOUT"); break; case LORAMAC_EVENT_INFO_STATUS_RX1_ERROR: strcpy(out, "RX1_ERROR"); break; case LORAMAC_EVENT_INFO_STATUS_RX2_ERROR: strcpy(out, "RX2_ERROR"); break; case LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR: strcpy(out, "SIZE"); break; default: sprintf(out, "<%d>", s); break; } } /*! * \brief MCPS-Confirm event function * * \param [IN] mcpsConfirm - Pointer to the confirm structure, * containing confirm attributes. */ static void McpsConfirm( McpsConfirm_t *mcpsConfirm ) { 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; } LoRaMacUplinkStatus.Datarate = mcpsConfirm->Datarate; LoRaMacUplinkStatus.UplinkCounter = mcpsConfirm->UpLinkCounter; } else { char str[48]; LoRaMacEventInfoStatus_toString(mcpsConfirm->Status, str); vt.SetCursorPos( 44, 1 ); vt.printf("mcpsConf %s", str); } DeviceState = DEVICE_STATE_TRIGGER; } /*! * \brief MCPS-Indication event function * * \param [IN] mcpsIndication - Pointer to the indication structure, * containing indication attributes. */ static void McpsIndication( McpsIndication_t *mcpsIndication ) { if( mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK ) { char str[48]; LoRaMacEventInfoStatus_toString(mcpsIndication->Status, str); vt.SetCursorPos( 44, 1 ); vt.printf("mcpsInd %s\e[K", str); return; } switch( mcpsIndication->McpsIndication ) { case MCPS_UNCONFIRMED: { break; } case MCPS_CONFIRMED: { 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 LoRaMacDownlinkStatus.Rssi = mcpsIndication->Rssi; if( mcpsIndication->Snr & 0x80 ) { // The SNR sign bit is 1 // Invert and divide by 4 LoRaMacDownlinkStatus.Snr = ( ( ~mcpsIndication->Snr + 1 ) & 0xFF ) >> 2; LoRaMacDownlinkStatus.Snr = -LoRaMacDownlinkStatus.Snr; } else { // Divide by 4 LoRaMacDownlinkStatus.Snr = ( mcpsIndication->Snr & 0xFF ) >> 2; } LoRaMacDownlinkStatus.DownlinkCounter++; LoRaMacDownlinkStatus.RxData = mcpsIndication->RxData; LoRaMacDownlinkStatus.Port = mcpsIndication->Port; LoRaMacDownlinkStatus.Buffer = mcpsIndication->Buffer; LoRaMacDownlinkStatus.BufferSize = mcpsIndication->BufferSize; 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: if( mcpsIndication->BufferSize == 1 ) { //AppLedStateOn = mcpsIndication->Buffer[0] & 0x01; //Led3StateChanged = true; } 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 ) LoRaMacTestSetDutyCycleOn( 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 ) LoRaMacTestSetDutyCycleOn( 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) 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 ) LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON ); #endif mlmeReq.Type = MLME_JOIN; mlmeReq.Req.Join.DevEui = DevEui; mlmeReq.Req.Join.AppEui = AppEui; mlmeReq.Req.Join.AppKey = AppKey; mlmeReq.Req.Join.NbTrials = 3; LoRaMacMlmeRequest( &mlmeReq ); DeviceState = DEVICE_STATE_SLEEP; } break; case 7: { // (x) if( mcpsIndication->BufferSize == 3 ) { MlmeReq_t mlmeReq; mlmeReq.Type = MLME_TXCW; mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] ); LoRaMacMlmeRequest( &mlmeReq ); } else if( mcpsIndication->BufferSize == 7 ) { MlmeReq_t mlmeReq; mlmeReq.Type = MLME_TXCW_1; mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] ); mlmeReq.Req.TxCw.Frequency = ( uint32_t )( ( mcpsIndication->Buffer[3] << 16 ) | ( mcpsIndication->Buffer[4] << 8 ) | mcpsIndication->Buffer[5] ) * 100; mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[6]; LoRaMacMlmeRequest( &mlmeReq ); } ComplianceTest.State = 1; } break; default: break; } } break; default: break; } } DownlinkStatusUpdated = true; } /*! * \brief MLME-Confirm event function * * \param [IN] mlmeConfirm - Pointer to the confirm structure, * containing confirm attributes. */ static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm ) { vt.SetCursorPos( 45, 1 ); vt.printf("MlmeConfirm() "); switch( mlmeConfirm->MlmeRequest ) { case MLME_JOIN: { vt.printf("MLME_JOIN "); if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) { vt.printf("OK"); DeviceState = DEVICE_STATE_JOIN_OK; extLed = 0; } else { char str[48]; LoRaMacEventInfoStatus_toString(mlmeConfirm->Status, str); vt.printf(str); // Join was not successful. Try to join again DeviceState = DEVICE_STATE_JOIN; } break; } case MLME_LINK_CHECK: { vt.printf("MLME_LINK_CHECK"); if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) { vt.printf("OK"); // Check DemodMargin // Check NbGateways if( ComplianceTest.Running == true ) { ComplianceTest.LinkCheck = true; ComplianceTest.DemodMargin = mlmeConfirm->DemodMargin; ComplianceTest.NbGateways = mlmeConfirm->NbGateways; } } break; } default: vt.printf("<%d>", mlmeConfirm->MlmeRequest); break; } vt.printf("\e[K"); } void foo() { float R, temp; uint16_t u16; vt.SetCursorPos( 43, 1 ); u16 = a3.read_u16() >> 4; R = 4096.0 / u16 - 1.0; R = R0 * R; temp = 1.0/(log(R/R0)/B+1/298.15)-273.15; vt.printf("%u d9:%u (%03x %03x %03x %03x) %.2fC\e[K\r\n", DeviceState, d8.read() >> 4, a1.read_u16() >> 4, a2.read_u16() >> 4, a3.read_u16() >> 4, a4.read_u16(), temp); } const LoRaMacPrimitives_t LoRaMacPrimitives = { McpsConfirm, McpsIndication, MlmeConfirm }; const LoRaMacCallback_t LoRaMacCallbacks = { BoardGetBatteryLevel }; /** * Main application entry point. */ int main( void ) { MibRequestConfirm_t mibReq; BoardInit( ); SerialDisplayInit( ); SerialDisplayUpdateEui( 5, DevEui ); SerialDisplayUpdateEui( 6, AppEui ); SerialDisplayUpdateKey( 7, AppKey ); #if( OVER_THE_AIR_ACTIVATION == 0 ) SerialDisplayUpdateNwkId( LORAWAN_NETWORK_ID ); SerialDisplayUpdateDevAddr( DevAddr ); SerialDisplayUpdateKey( 12, NwkSKey ); SerialDisplayUpdateKey( 13, AppSKey ); #endif DeviceState = DEVICE_STATE_INIT; while( 1 ) { foo(); SerialRxProcess( ); if( DownlinkStatusUpdated == true ) { DownlinkStatusUpdated = false; SerialDisplayUpdateDownlink( LoRaMacDownlinkStatus.RxData, LoRaMacDownlinkStatus.Rssi, LoRaMacDownlinkStatus.Snr, LoRaMacDownlinkStatus.DownlinkCounter, LoRaMacDownlinkStatus.Port, LoRaMacDownlinkStatus.Buffer, LoRaMacDownlinkStatus.BufferSize ); } switch( DeviceState ) { case DEVICE_STATE_INIT: { pwm.period(1.0 / 60); cayenne_ack_ch = -1; c_ch = 0xff; d8.mode(PullDown); LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks ); 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 ) LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON ); SerialDisplayUpdateDutyCycle( 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_DEFAULT_CHANNEL; mibReq.Param.Rx2DefaultChannel = ( Rx2ChannelParams_t ) { 869525000, DR_3 }; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_RX2_CHANNEL; mibReq.Param.Rx2Channel = ( Rx2ChannelParams_t ) { 869525000, DR_3 }; LoRaMacMibSetRequestConfirm( &mibReq ); #endif #endif SerialDisplayUpdateActivationMode( OVER_THE_AIR_ACTIVATION ); SerialDisplayUpdateAdr( LORAWAN_ADR_ON ); SerialDisplayUpdatePublicNetwork( LORAWAN_PUBLIC_NETWORK ); LoRaMacDownlinkStatus.DownlinkCounter = 0; DeviceState = DEVICE_STATE_JOIN; break; } case DEVICE_STATE_JOIN: { #if( OVER_THE_AIR_ACTIVATION != 0 ) LoRaMacStatus_t s; MlmeReq_t mlmeReq; mlmeReq.Type = MLME_JOIN; mlmeReq.Req.Join.DevEui = DevEui; mlmeReq.Req.Join.AppEui = AppEui; mlmeReq.Req.Join.AppKey = AppKey; s = LoRaMacMlmeRequest( &mlmeReq ); if (s != LORAMAC_STATUS_OK) { char str[48]; LoRaMacStatus_toString(s, str); vt.SetCursorPos( 44, 1 ); vt.printf("mlmeReq join %s\e[K", str); return -1; } else DeviceState = DEVICE_STATE_SLEEP; extLed = 1; #else mibReq.Type = MIB_NET_ID; mibReq.Param.NetID = LORAWAN_NETWORK_ID; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_DEV_ADDR; mibReq.Param.DevAddr = DevAddr; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_NWK_SKEY; mibReq.Param.NwkSKey = NwkSKey; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_APP_SKEY; mibReq.Param.AppSKey = AppSKey; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_NETWORK_JOINED; mibReq.Param.IsNetworkJoined = true; LoRaMacMibSetRequestConfirm( &mibReq ); DeviceState = DEVICE_STATE_SEND; #endif 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_NWK_SKEY; LoRaMacMibGetRequestConfirm( &mibReq ); SerialDisplayUpdateKey( 12, mibReq.Param.NwkSKey ); mibReq.Type = MIB_APP_SKEY; LoRaMacMibGetRequestConfirm( &mibReq ); SerialDisplayUpdateKey( 13, mibReq.Param.AppSKey ); //SerialDisplayUpdateNwkId( uint8_t id ); DeviceState = DEVICE_STATE_TRIGGER; break; case DEVICE_STATE_SEND: { SerialDisplayUpdateUplinkAcked( false ); SerialDisplayUpdateDonwlinkRxData( false ); PrepareTxFrame( AppPort ); SendFrame(gAppDataSize, gIsTxConfirmed); DeviceState = DEVICE_STATE_SLEEP; break; } case DEVICE_STATE_SLEEP: { // Wake up through events break; } case DEVICE_STATE_TRIGGER: if (d8.read() == 1) { c_ch = 0xff; DeviceState = DEVICE_STATE_SEND; buttonStartAt = TimerGetCurrentTime(); } break; default: { DeviceState = DEVICE_STATE_INIT; break; } } } }