A very easy to understand LoRaWAN-node code.
Dependencies: LoRaWAN-lib SX1272Lib X_NUCLEO_IKS01A1 mbed
Important parameters:
• In comissioning.h: DevEUI, AppEUI, AppKEY, DevADR, NwksKEY, AppsKEY, OTAA and public network. Frequency and channel block to use, confirmed or unconfirmed messages, app port, app data size and OTAA and Tx duty cycles.
• In LoRaMac.h: Maximum payload and MAC commands length, receive delays, max FCNT, adr ack limit, timeout and delay, max ack retries, rssi threshold and sync words.
• In LoRaMac.cpp: Maximum payload, MAC commands and FRMpayload length.
• In LoRaMac-board.h: Tx power, data rates and band settings.
NOTE: Please refer to LoRaWAN regional parameters (page 12 for US band) to know which parameters you can modify.
Diff: Code/main.cpp
- Revision:
- 0:60ff878b27b8
diff -r 000000000000 -r 60ff878b27b8 Code/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Code/main.cpp Tue Apr 03 17:09:34 2018 +0000 @@ -0,0 +1,411 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (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 "Comissioning.h" +#include "SerialDisplay.h" +#include "LoRaDeviceFunctions.h" +#include "LoRaMacLayerService.h" + + +#if( OVER_THE_AIR_ACTIVATION != 0 ) + +uint8_t DevEui[] = LORAWAN_DEVICE_EUI; +uint8_t AppEui[] = LORAWAN_APPLICATION_EUI; +uint8_t AppKey[] = LORAWAN_APPLICATION_KEY; + +#else + +uint8_t NwkSKey[] = LORAWAN_NWKSKEY; +uint8_t AppSKey[] = LORAWAN_APPSKEY; +uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS; + +#endif + + +uint8_t AppPort = LORAWAN_APP_PORT; //application port + +uint8_t AppDataSize = LORAWAN_APP_DATA_SIZE; //data size + +uint8_t AppData[LORAWAN_APP_DATA_MAX_SIZE]; //payload array + +uint8_t IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON; //Indicates if the node is sending confirmed or unconfirmed messages + +TimerEvent_t TxNextPacketTimer; //Timer to handle the application data transmission duty cycle + +bool IsTxIntUpdate = false; //Indicates if a new transmit interrupt can be set + +bool NextTx = true; //Indicates if a new packet can be sent + +bool IsNetworkJoinedStatusUpdate = false; //Indicates if the MAC layer network join status has changed. + +bool IsTxUpdate = false; //Indicates if the message sent. + +bool IsRxUpdate = false; //Indicates if the message received in the RX window. + +float sensor_data; //Used to store the sensor data + +uint32_t TxDutyCycleTime = APP_TX_DUTYCYCLE; + +eDevicState DeviceState; + +sLoRaMacUplinkStatus LoRaMacUplinkStatus; + +sLoRaMacDownlinkStatus LoRaMacDownlinkStatus; + +LoRaMacPrimitives_t LoRaPrimitives; + +LoRaMacCallback_t LoRaCallbacks; + +MibRequestConfirm_t LoRaMibReq; + +MlmeReq_t mlmeReq; + +uint16_t ChannelMaskTemp[6] = {0}; + +uint8_t BoardGetBatteryLevel( void ) +{ + return 0xFE; +} +DigitalIn I2cInterrupt( D5 ); +I2C I2c(I2C_SDA, I2C_SCL); + +DigitalOut Pc7( D9 ); +DigitalIn Pc1( A4 ); + +//DigitalIn UsrButton( PC_13 ); +DigitalIn UsrButton( D10 ); + +DigitalOut Led( D6 ); + +#ifdef USE_IKS01A1_SENSOR +X_NUCLEO_IKS01A1 *mems_expansion_board = X_NUCLEO_IKS01A1::Instance(); +MotionSensor *accelerometer = mems_expansion_board->GetAccelerometer(); +HumiditySensor *humidity_sensor = mems_expansion_board->ht_sensor;; +PressureSensor *pressure_sensor = mems_expansion_board->pt_sensor; +TempSensor *temp_sensor1 = mems_expansion_board->ht_sensor; +TempSensor *temp_sensor2 = mems_expansion_board->pt_sensor; +#endif + +SX1272MB2xAS Radio( NULL ); + + +uint32_t BoardGetRandomSeed( void ) +{ + return ( ( *( uint32_t* )ID1 ) ^ ( *( uint32_t* )ID2 ) ^ ( *( uint32_t* )ID3 ) ); +} + +void BoardGetDevEUI( uint8_t *id ) +{ + uint32_t *pDevEuiHWord = ( uint32_t* )&id[4]; + + if( *pDevEuiHWord == 0 ) + { + *pDevEuiHWord = BoardGetRandomSeed( ); + } + +} + +static void OnTxNextPacketTimerEvent( void ) +{ + MibRequestConfirm_t mibReq; + LoRaMacStatus_t status; + + TimerStop( &TxNextPacketTimer ); + + mibReq.Type = MIB_NETWORK_JOINED; + status = LoRaMacMibGetRequestConfirm( &mibReq ); + + if( status == LORAMAC_STATUS_OK ) + { + if( mibReq.Param.IsNetworkJoined == true ) + { + DeviceState = DEVICE_STATE_SEND; + } + else + { + DeviceState = DEVICE_STATE_JOIN; + } + NextTx = true; + } +} + +bool SendFrame( void ) +{ + 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.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; + } + else + { + LoRaMacUplinkStatus.Acked = false; + LoRaMacUplinkStatus.Port = AppPort; + LoRaMacUplinkStatus.Buffer = AppData; + LoRaMacUplinkStatus.BufferSize = AppDataSize; + + if( ( IsTxConfirmed == false ) || ( LoRaMacUplinkStatus.UplinkCounter == 0 ) ) + { + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fPort = AppPort; + mcpsReq.Req.Unconfirmed.fBuffer = AppData; + mcpsReq.Req.Unconfirmed.fBufferSize = AppDataSize; + 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 = AppDataSize; + mcpsReq.Req.Confirmed.NbTrials = 8; + mcpsReq.Req.Confirmed.Datarate = LORAWAN_DEFAULT_DATARATE; + } + } + + LoRaMacUplinkStatus.Type = mcpsReq.Type; + + if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK ) + { + return false; + } + return true; +} + +int main( void ) +{ + + TimerTimeCounterInit( ); //initialize timer + + DeviceState = DEVICE_STATE_INIT; // Indicate Device state + + while( 1 ) + { + if( IsNetworkJoinedStatusUpdate == true ) + { + //Checks if the device has joined the server and updates the joined status + IsNetworkJoinedStatusUpdate = false; + LoRaMibReq.Type = MIB_NETWORK_JOINED; + LoRaMacMibGetRequestConfirm( &LoRaMibReq ); + } + + if( IsTxIntUpdate == true ) + { + // Initialize next Tx Interrupt + IsTxIntUpdate = false; + TimerSetValue( &TxNextPacketTimer, TxDutyCycleTime ); + TimerStart( &TxNextPacketTimer ); + + } + + if( IsRxUpdate == true ) + { + // If downlink received then update Serial Terminal Rx parameters + IsRxUpdate = false; + SerialDisplayRxUpdate( ); + } + + switch( DeviceState ) //realizes different tasks depending on device state + { + case DEVICE_STATE_INIT: + { + + LoRaPrimitives.MacMcpsConfirm = McpsConfirm; //Update uplink and event info status + LoRaPrimitives.MacMcpsIndication = McpsIndication; // + LoRaPrimitives.MacMlmeConfirm = MlmeConfirm; + LoRaCallbacks.GetBatteryLevel = BoardGetBatteryLevel; //0: external power source, 1-254: battery level, 255: couldnt measure battery level + LoRaMacInitialization( &LoRaPrimitives, &LoRaCallbacks ); //Sets radio parameters (channels, frequency, timers, windows) + + TimerInit( &TxNextPacketTimer, OnTxNextPacketTimerEvent ); + + LoRaMibReq.Type = MIB_ADR; + LoRaMibReq.Param.AdrEnable = LORAWAN_ADR_ON; + LoRaMacMibSetRequestConfirm( &LoRaMibReq ); + + LoRaMibReq.Type = MIB_PUBLIC_NETWORK; + LoRaMibReq.Param.EnablePublicNetwork = LORAWAN_PUBLIC_NETWORK; + LoRaMacMibSetRequestConfirm( &LoRaMibReq ); + + LoRaMibReq.Type = MIB_CHANNELS_TX_POWER; + LoRaMibReq.Param.ChannelsTxPower = LORAWAN_TX_POWER; + LoRaMacMibSetRequestConfirm( &LoRaMibReq ); + + LoRaMibReq.Type = MIB_CHANNELS_MASK; + LoRaMibReq.Param.ChannelsMask = ChannelMaskTemp; + //channel mask for USE_BAND_915_HYBRID + ChannelMaskTemp[0] = 0x00FF; + ChannelMaskTemp[1] = 0x0000; + ChannelMaskTemp[2] = 0x0000; + ChannelMaskTemp[3] = 0x0000; + ChannelMaskTemp[4] = 0x0001; + LoRaMacMibSetRequestConfirm( &LoRaMibReq ); + + LoRaMacDownlinkStatus.DownlinkCounter = 0; + + DeviceState = DEVICE_STATE_JOIN; // Change Device state + break; + } + case DEVICE_STATE_JOIN: + { +#if( OVER_THE_AIR_ACTIVATION != 0 ) // OTAA + + BoardGetDevEUI( DevEui ); // Generate DevEUI if not defined by User + + mlmeReq.Type = MLME_JOIN; //Changes Mlmereq content to send in join request + mlmeReq.Req.Join.DevEui = DevEui; + mlmeReq.Req.Join.AppEui = AppEui; + mlmeReq.Req.Join.AppKey = AppKey; + + if( NextTx == true ) + { + LoRaMacMlmeRequest( &mlmeReq ); + } + + SerialDisplayJoinUpdate( ); // Show on serial terminal + + DeviceState = DEVICE_STATE_SLEEP; + +#else // ABP + + // Choose a random device address if not already defined in Config.h + if( DevAddr == 0 ) + { + // Random seed initialization + srand1( BoardGetRandomSeed( ) ); + DevAddr = randr( 0, 0x01FFFFFF ); + } + + LoRaMibReq.Type = MIB_NET_ID; + LoRaMibReq.Param.NetID = LORAWAN_NETWORK_ID; + LoRaMacMibSetRequestConfirm( &LoRaMibReq ); + + LoRaMibReq.Type = MIB_DEV_ADDR; + LoRaMibReq.Param.DevAddr = DevAddr; + LoRaMacMibSetRequestConfirm( &LoRaMibReq ); + + LoRaMibReq.Type = MIB_NWK_SKEY; + LoRaMibReq.Param.NwkSKey = NwkSKey; + LoRaMacMibSetRequestConfirm( &LoRaMibReq ); + + LoRaMibReq.Type = MIB_APP_SKEY; + LoRaMibReq.Param.AppSKey = AppSKey; + LoRaMacMibSetRequestConfirm( &LoRaMibReq ); + + LoRaMibReq.Type = MIB_NETWORK_JOINED; + LoRaMibReq.Param.IsNetworkJoined = true; + LoRaMacMibSetRequestConfirm( &LoRaMibReq ); + + DeviceState = DEVICE_STATE_SEND; +#endif + IsNetworkJoinedStatusUpdate = true; + break; + } + case DEVICE_STATE_SEND: + { + + if( NextTx == true ) + { + //===============Functions to get X_NUCLEO_IKS01A1 sensor data=============== + + temp_sensor2->GetTemperature(&sensor_data); + int16_t tmp_data = (int16_t) (sensor_data*10 + 0.5); + AppData[2] = (int8_t) ( ( tmp_data >> 8 ) & 0xFF ); + AppData[3] = (int8_t) tmp_data & 0xFF; + + pressure_sensor->GetPressure(&sensor_data); + int16_t tmp = (int16_t) ( sensor_data * 10 ); + AppData[6] = ( tmp >> 8 ) & 0xFF; + AppData[7] = ( tmp ) & 0xFF; + + humidity_sensor->GetHumidity(&sensor_data); + AppData[10] = (uint8_t) ( sensor_data * 2 ); + + //accelerometer->Get_X_Axes(Accl_Value); + //AppData[BuffPtr++] = ( (int16_t) Accl_Value[0] >> 8 ) & 0xFF; + //AppData[BuffPtr++] = ( (int16_t) Accl_Value[0] ) & 0xFF; + //AppData[BuffPtr++] = ( (int16_t) Accl_Value[1] >> 8 ) & 0xFF; + //AppData[BuffPtr++] = ( (int16_t) Accl_Value[1] ) & 0xFF; + //AppData[BuffPtr++] = ( (int16_t) Accl_Value[2] >> 8 ) & 0xFF; + //AppData[BuffPtr++] = ( (int16_t) Accl_Value[2] ) & 0xFF; + //===========================Custom payload============================ + + //In this case the custom payload is the cayene LPP structure for the sensors data + AppData[0]=0x01; + AppData[1]=0x67; + + AppData[4]=0x02; + AppData[5]=0x73; + + AppData[8]=0x03; + AppData[9]=0x68; + + + //=========================Cayenne LPP format======================= + /* + STRUCTURE: + + 1 Byte 1 Byte N Bytes 1 Byte 1 Byte M Bytes ... + + Data1 Ch. Data1 Type Data1 Data2 Ch. Data2 Type Data2 ... + + COMMON TYP IDENTIFIERS AND RESOLUTIONS: + + TYPE LPP HEX DATA SIZE RESOLUTION PER BIT + + Temperature 103 67 2 0.1 °C Signed MSB + Humidity 104 68 1 0.5 % Unsigned + Accelerometer 113 71 6 0.001 G Signed MSB per axis + Barometer 115 73 2 0.1 hPa Unsigned MSB + + */ + // Send payload over the air + NextTx = SendFrame( ); + } + + if( NextTx == false ) + { + + SerialDisplayTxUpdate( ); + } + + DeviceState = DEVICE_STATE_SLEEP; + break; + } + case DEVICE_STATE_SLEEP: + { + break; + } + default: + { + DeviceState = DEVICE_STATE_INIT; + break; + } + } + } +}