LoRaWAN smart agriculture application using FRDM-K64F ARM mbed board along with SX1272MB2xAS LoRa shield as the LoRa Node.
Dependencies: DHT11 LMiC SX1272Lib mbed
Fork of LoRaWAN-lmic-app by
Diff: main.cpp
- Revision:
- 6:3758685f4b75
- Parent:
- 5:1b2fcc2582e8
- Child:
- 7:8ffc2f64b0f8
--- a/main.cpp Thu Nov 26 17:20:53 2015 +0000 +++ b/main.cpp Mon Apr 02 12:16:01 2018 +0000 @@ -1,290 +1,434 @@ -/* - / _____) _ | | -( (____ _____ ____ _| |_ _____ ____| |__ - \____ \| ___ | (_ _) ___ |/ ___) _ \ - _____) ) ____| | | || |_| ____( (___| | | | -(______/|_____)_|_|_| \__)_____)\____)_| |_| - (C)2015 Semtech - -Description: MBED LoRaWAN example application - -License: Revised BSD License, see LICENSE.TXT file include in the project - -Maintainer: Miguel Luis and Gregory Cristian -*/ -#include "mbed.h" +/******************************************************************************* + * Internet of Things (IoT) smart monitoring + * device for agriculture using LoRaWAN technology. + * + * LoRa Gateway: Single-channel Dragino LG01-P LoRa Gateway + * + * Measurement parameters: Temperature (Celcius) + * Humidity (Relative Humidity %) + * Light Intenisty (Volts) + * Soil Moisture (Volts) + * + * Evaluation board: FRDM-K64F ARM mbed board + * + * LoRa shield: Semtech SX1272MB2xAS + * + * IoT Cloud Server: The Things Network (Europe EU-868.1 frequency band) + * + * API Platform: All Things Talk Maker + * + * - Time-triggered program periodically sends playload data (including + * temperature, humidity, light intensity and soil moisture sensor parameters) + * by using FRDM-K64F ARM mbed board and Semtech SX1272MB2xAS as the LoRa Node. + * + * - DHT library and Digital Input pin used for the successful measurement + * of temperature and humidity sensor parameters. + * + * - Analog Input pins used for the successful employement of soil moisture and + * light intensity sensor parameters. + * + * - Semtech's SX1272Lib used for the successful configuration and set up of + * of SX1272MB2xAS LoRa shield. + * + * - IBM's LMiC library used for the successful implementation of LoRa modulation. + * + * - LoRa Node transmitting playload data directly to the single-channel Dragino + * LG01-P LoRa Gateway which is connected to The Things Network Cloud Server. + * + * - ABP (Activation By Personalization) selected as the activation method over + * The Things Network Cloud Server. + * + * - The Things Network Cloud Server makes playload data available online. + * + * - Through required integration, The Things Network Cloud Server passes + * playload data to All Things Talk Maker API which visualizes the data in + * a meaningful way for end-user's reference. + * + * @Author: Giorgos Tsapparellas + * @Revisions: + * v0.1 -- sensors gathering on FRDM-K64F + * v0.2 -- attach sx1272 and take as a transmitter + * v0.3 -- + * + * Porgram available at: 1) GITHUB LINK + * 2) MBED LINK + * + * SEE readME file for instructions of how to compile and run the program. + * + *******************************************************************************/ + // ADD revisions and links above + // ADD IFDEF, AND events, comments + // ADD LED FLASHING + // BUTTON PRESSED-RESET SEND A LORA PACKET + // ADD to git hub as well + // TEST THE CODE IF OK -#include "lmic.h" -#include "debug.h" +#include <mbed.h> +#include <lmic.h> +#include <hal.h> +#include <SPI.h> +#include <DHT.h> +#include <debug.h> -/*! - * When set to 1 the application uses the Over-the-Air activation procedure - * When set to 0 the application uses the Personalization activation procedure - */ -#define OVER_THE_AIR_ACTIVATION 0 +/////////////////////////////////////////////////// +// DEFINITION DECLARATIONS // +///////////////////////////////////////////////// + +#define SINGLE_CHANNEL_GATEWAY // Force it to use 868.1 MHz frequency band only due to Dragino LG01-P LoRa Gateway hardware limitation. +#define TRANSMIT_INTERVAL 1800 // Transmit interval in seconds, too often may get traffic ignored. +#define DEBUG_LEVEL 0 // Set debug level to 1 for outputting messages to the UART Terminal (e.g. Tera Term). +#define ACTIVATION_METHOD 0 // Set activation method to 0 for ABP (Activation By Personalization) + // Set activation method to 1 for OTAA (Over The Air Activation) -#if( OVER_THE_AIR_ACTIVATION == 0 ) +/////////////////////////////////////////////////// +// GLOBAL VARIABLES DECLARATIONS // +///////////////////////////////////////////////// + +// Transmit interval in seconds. +uint16_t transmit_interval = TRANSMIT_INTERVAL; -/*! - * Defines the network ID when using personalization activation procedure - */ -#define LORAWAN_NET_ID ( uint32_t )0x00000000 +// Playload frame length of size 8. +uint8_t LMIC_FRAME_LENGTH = 8; + +#if ACTIVATION_METHOD == 1 // if OTAA (Over The Air Activation) is applied. -/*! - * Defines the device address when using personalization activation procedure - */ -#define LORAWAN_DEV_ADDR ( uint32_t )0x12345678 +// LoRaWAN Application identifier (AppEUI) associated with The Things Network Cloud Server. +static const u1_t APPEUI[8] = { 0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x00, 0xA4, 0x54 }; + +// LoRaWAN unique device ID (DevEUI) associated with The Things Network Cloud Server. +static const u1_t DEVEUI[8] = { 0x00, 0x1D, 0x45, 0x32, 0xEC, 0xA8, 0x01, 0x59 }; #endif -/*! - * Defines the application data transmission duty cycle - */ -#define APP_TX_DUTYCYCLE 5000 // 5 [s] value in ms -#define APP_TX_DUTYCYCLE_RND 1000 // 1 [s] value in ms +// Acquired activation method +#if ACTIVATION_METHOD == 0 // if ABP (Activation By Personalization) is applied. -/*! - * LoRaWAN Adaptative Data Rate - */ -#define LORAWAN_ADR_ON 1 +// LoRaWAN network session key (NwkSKey) associated with The Things Network Cloud Server. +static const u1_t NWKSKEY[16] = { 0xDF, 0x9B, 0xB1, 0x30, 0xE8, 0x33, 0x42, 0x76, 0x33, 0x0C, 0x88, 0xBB, 0x30, 0xE2, 0xC2, 0xE9 }; -/*! - * LoRaWAN confirmed messages - */ -#define LORAWAN_CONFIRMED_MSG_ON 1 +// LoRaWAN application session key (AppSKey) associated with The Things Network Cloud Server. +static const u1_t APPSKEY[16] = { 0xE0, 0x52, 0x18, 0x15, 0x0B, 0xE1, 0xEF, 0x1F, 0xAF, 0x8C, 0x8A, 0x31, 0x09, 0xB9, 0xAB, 0x9C }; -/*! - * LoRaWAN application port - */ -#define LORAWAN_APP_PORT 15 - -/*! - * User application data buffer size - */ -#if ( LORAWAN_CONFIRMED_MSG_ON == 1 ) -#define LORAWAN_APP_DATA_SIZE 6 - -#else -#define LORAWAN_APP_DATA_SIZE 1 +// LoRaWAN end-device address (DevAddr) associated with The Things Network Cloud Server. +static const u4_t DEVADDR = 0x26011B39 ; #endif -////////////////////////////////////////////////// -// CONFIGURATION (FOR APPLICATION CALLBACKS BELOW) -////////////////////////////////////////////////// +/*On board's LEDs Declaration +DigitalOut RED_LED(PTB22); // PTB22 = Red pin -- Indicates an error occur +DigitalOut GREEN_LED(PTE26); // PTE26 = Green pin -- Indicates transmission is in progress +DigitalOut BLUE_LED(PTB21); // PTB21 = Blue pin -- Indicates MCU is sleeping +*/ + +// Digital Input pin of temperature and humidity sensor set to D6. +DHT sensorTempHum(D6, DHT11); + +// Analog Input pin of light intensity sensor set to A1. +AnalogIn sensorLight(A1); + +// Analog Input pin of soil moisture sensor set to A3. +AnalogIn sensorSoilMoisture(A3); -// application router ID (LSBF) -static const uint8_t AppEui[8] = -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; +/////////////////////////////////////////////////// +// LOCAL FUNCTIONS DECLARATIONS // +///////////////////////////////////////////////// + +/* + * getTemperatureHumidity function of type void. + * + * Gets temperature (celcius) and humidity (relative humidity %) + * measurements using DHT library. Otherwise, print an error. + * + * Input parameters: float temperature + * float humidity + * + */ +void getTemperatureHumidity(float& temperature, float& humidity) { -// unique device ID (LSBF) -static const u1_t DevEui[8] = -{ - 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF -}; - -// device-specific AES key (derived from device EUI) -static const uint8_t DevKey[16] = -{ - 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, - 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C -}; - -#if( OVER_THE_AIR_ACTIVATION == 0 ) -// network session key -static uint8_t NwkSKey[] = -{ - 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, - 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C -}; + // Set err variable to 0 (none). + uint8_t err = ERROR_NONE; + // Set humidity variable to 0. + humidity = 0.0f; + // Set temperature variable to 0. + temperature = 0.0f; + + // Store sensor data (40 bits(16-bit temperature, 16-bit humidity and 8-bit + // CRC checksum)) into err variable. + err = sensorTempHum.readData(); + + if (err == ERROR_NONE) // if err equals to 0. + { + // Store float temperature value in celcius. + temperature = sensorTempHum.ReadTemperature(CELCIUS); + // Store float humidity value. + humidity = sensorTempHum.ReadHumidity(); + + // Output temperature and humidity values on UART Terminal + #if DEBUG_LEVEL == 1 + printf("Temperature: %4.2f Celsius \r\n", temperature); + printf("Humidity: %4.2f % Relative Humidity \r\n", humidity); + #endif + } + else // if err occurs. + { + // Output error message on UART Terminal and flash the RED LED. + #if DEBUG_LEVEL == 1 + printf("Error: %d\r\n", err); + //RED_LED = !RED_LED; + #endif + } +}// end of getTemperatureHumidity function. -// application session key -static uint8_t ArtSKey[] = -{ - 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, - 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C -}; - -#endif - -// LEDs and Frame jobs -osjob_t rxLedJob; -osjob_t txLedJob; -osjob_t sendFrameJob; - -// LED state -static bool AppLedStateOn = false; +/* + * getLightIntensity function of type void. + * + * Gets the light's intensity analogue value at first instance + * and then converts it using 16-bit ADC converter into voltage + * counting from 0.0 to 5.0. + * + * Input parameters: float lightIntensityVoltage + * + */ +void getLightIntensity(float& lightIntensityVoltage) { + + // Set light intensity voltage variable to 0. + lightIntensityVoltage = 0.0f; + // Set light intensity analogue value to 0. + uint16_t lightIntensityAnalogue = 0; + + // Read light intensity 16-bit analogue value. + lightIntensityAnalogue = sensorLight.read_u16(); + //Convert the light intensity analog reading (which goes from 0 - 65536) to a voltage (0 - 5V). + lightIntensityVoltage = (float) lightIntensityAnalogue*(5.0/65536.0); + + // Output light intensity voltage as well as resistance value on UART Terminal. + #if DEBUG_LEVEL == 1 + float resistance = 0.0f; + // Groove's calculation for resistance value. + resistance = (float)(65536-lightIntensityAnalogue)*10/lightIntensityAnalogue; + printf("Light Intensity: %2.2f Volts -- ", lightIntensityVoltage); + printf("Resistance: %2.2f Kiloohm \r\n", resistance); + #endif +}// end of getLightIntensity function. -////////////////////////////////////////////////// -// Utility functions -////////////////////////////////////////////////// -/*! - * \brief Computes a random number between min and max +/* + * getSoilMoisture function of type void. + * + * Gets the soil's moisture analogue value at first instance + * and then converts it using 16-bit ADC converter into voltage + * counting from 0.0 to 5.0. + * + * Input parameters: float lightOutputVoltage * - * \param [IN] min range minimum value - * \param [IN] max range maximum value - * \retval random random value in range min..max - */ -int32_t randr( int32_t min, int32_t max ) -{ - return ( int32_t )rand( ) % ( max - min + 1 ) + min; -} + */ +void getSoilMoisture(float& soilMoistureVoltage) { + + // Set soil moisture voltage variable to 0. + soilMoistureVoltage = 0.0f; + // Set soil moisture analogue value to 0. + uint16_t soilMoistureAnalogue = 0; + + // Read soil moisture 16-bit analogue value. + soilMoistureAnalogue = sensorSoilMoisture.read_u16(); + //Convert the soil moisture analog reading (which goes from 0 - 65536) to a voltage (0 - 5V). + soilMoistureVoltage = (float) soilMoistureAnalogue*(5.0/65536.0); + + // Output soil moisture voltage as well as soil moisture analogue value on UART Terminal. + #if DEBUG_LEVEL == 1 + printf("Soil Moisture: %2.2f Volts -- ", soilMoistureVoltage); + printf("Analogue Value: %d \r\n", soilMoistureAnalogue); + #endif +}// end of getSoilMoisture function. -////////////////////////////////////////////////// -// APPLICATION CALLBACKS -////////////////////////////////////////////////// +/////////////////////////////////////////////////// +// LMiC APPLICATION CALLBACKS // +///////////////////////////////////////////////// +static osjob_t sendjob; +unsigned int xmit_count = 1; // provide application router ID (8 bytes, LSBF) -void os_getArtEui( uint8_t *buf ) -{ - memcpy( buf, AppEui, 8 ); +void os_getArtEui (u1_t* buf) { + #if ACTIVATION_METHOD == 1 // if OTAA (Over The Air Activation) is applied. + memcpy(buf, APPEUI, 8); + #endif } // provide device ID (8 bytes, LSBF) -void os_getDevEui( uint8_t *buf ) -{ - memcpy( buf, DevEui, 8 ); +void os_getDevEui (u1_t* buf) { + #if ACTIVATION_METHOD == 1 // if OTAA (Over The Air Activation) is applied. + memcpy(buf, DEVEUI, 8); + #endif } // provide device key (16 bytes) -void os_getDevKey( uint8_t *buf ) -{ - memcpy( buf, DevKey, 16 ); -} - -////////////////////////////////////////////////// -// MAIN - INITIALIZATION AND STARTUP -////////////////////////////////////////////////// - -static void onRxLed( osjob_t* j ) -{ - debug_val("LED2 = ", 0 ); -} - -static void onTxLed( osjob_t* j ) -{ - debug_val("LED1 = ", 0 ); +void os_getDevKey (u1_t* buf) { + memcpy(buf, NWKSKEY, 16); } -static void prepareTxFrame( void ) -{ - LMIC.frame[0] = AppLedStateOn; -#if ( LORAWAN_CONFIRMED_MSG_ON == 1 ) - LMIC.frame[1] = LMIC.seqnoDn >> 8; - LMIC.frame[2] = LMIC.seqnoDn; - LMIC.frame[3] = LMIC.rssi >> 8; - LMIC.frame[4] = LMIC.rssi; - LMIC.frame[5] = LMIC.snr; -#endif -} + +void onEvent (ev_t ev) { + debug_event(ev); -void processRxFrame( void ) -{ - switch( LMIC.frame[LMIC.dataBeg - 1] ) // Check Rx port number - { - case 1: // The application LED can be controlled on port 1 or 2 - case 2: - if( LMIC.dataLen == 1 ) + switch(ev) { + case EV_SCAN_TIMEOUT: + printf("EV_SCAN_TIMEOUT\n"); + break; + case EV_BEACON_FOUND: + printf("EV_BEACON_FOUND\n"); + break; + case EV_BEACON_MISSED: + printf("EV_BEACON_MISSED\n"); + break; + case EV_BEACON_TRACKED: + printf("EV_BEACON_TRACKED\n"); + break; + case EV_JOINING: + printf("EV_JOINING\n"); + break; + case EV_JOINED: + printf("EV_JOINED\n"); + break; + case EV_RFU1: + printf("EV_RFU1\n"); + break; + case EV_JOIN_FAILED: + printf("EV_JOIN_FAILED\n"); + break; + case EV_REJOIN_FAILED: + printf("EV_REJOIN_FAILED\n"); + break; + case EV_TXCOMPLETE: + printf("EV_TXCOMPLETE (waiting for RX windows)\n"); + if (LMIC.txrxFlags & TXRX_ACK) { - AppLedStateOn = LMIC.frame[LMIC.dataBeg] & 0x01; - debug_val( "LED3 = ", AppLedStateOn ); + printf("Received ack\n"); + } + if(LMIC.dataLen) + { // data received in rx slot after tx + printf("Received "); + printf("%u", LMIC.dataLen); + printf(" bytes of payload\n"); } break; - default: + case EV_LOST_TSYNC: + printf("EV_LOST_TSYNC\n"); + break; + case EV_RESET: + printf("EV_RESET\n"); + break; + case EV_RXCOMPLETE: + // data received in ping slot + printf("EV_RXCOMPLETE\n"); break; + case EV_LINK_DEAD: + printf("EV_LINK_DEAD\n"); + break; + case EV_LINK_ALIVE: + printf("EV_LINK_ALIVE\n"); + break; + default: + printf("Unknown event\n"); + break; } } -static void onSendFrame( osjob_t* j ) -{ - prepareTxFrame( ); - LMIC_setTxData2( LORAWAN_APP_PORT, LMIC.frame, LORAWAN_APP_DATA_SIZE, LORAWAN_CONFIRMED_MSG_ON ); - - // Blink Tx LED - debug_val( "LED1 = ", 1 ); - os_setTimedCallback( &txLedJob, os_getTime( ) + ms2osticks( 25 ), onTxLed ); -} -// Initialization job -static void onInit( osjob_t* j ) -{ - // reset MAC state - LMIC_reset( ); - LMIC_setAdrMode( LORAWAN_ADR_ON ); -#if defined(CFG_eu868) - LMIC_setDrTxpow( DR_SF12, 14 ); -#elif defined(CFG_us915) - LMIC_setDrTxpow( DR_SF10, 14 ); -#endif +void transmit(osjob_t* j){ + //printf("txCnhl: %u , Channel Ready? ", LMIC.txChnl); + if (LMIC.opmode & (1 << 7)) + { + printf("NO, waiting...\n\n"); + } + else + { + //printf("YES, sensor readings...\n\n"); + // Prepare upstream data transmission at the next possible time. + // LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); + + float temperature, humidity, lightIntensity, soilMoisture; + getTemperatureHumidity(temperature, humidity); + getLightIntensity(lightIntensity); + getSoilMoisture(soilMoisture); - // start joining -#if( OVER_THE_AIR_ACTIVATION != 0 ) - LMIC_startJoining( ); -#else - LMIC_setSession( LORAWAN_NET_ID, LORAWAN_DEV_ADDR, NwkSKey, ArtSKey ); - onSendFrame( NULL ); -#endif - // init done - onEvent( ) callback will be invoked... -} + int16_t temp = temperature*100; + int16_t hum = humidity*100; + int16_t light = lightIntensity*100; + int16_t soil = soilMoisture*100; + + //printf(" ----->Preparing packet...\n"); + + LMIC.frame[0] = temp >> 8; + LMIC.frame[1] = temp & 0xFF; + LMIC.frame[2] = hum >> 8; + LMIC.frame[3] = hum & 0xFF; + LMIC.frame[4] = light >> 8; + LMIC.frame[5] = light & 0xFF; + LMIC.frame[6] = soil >> 8; + LMIC.frame[7] = soil & 0xFF; + + //printf(" ----->Packet READY\n\n"); + LMIC_setTxData2(1, LMIC.frame, 8, 0); + //printf(" ----->Sending packet %u of byte size %u\n\n", xmit_count++, LMIC_FRAME_LENGTH); + } + // Schedule a timed job to run at the given timestamp (absolute system time) + os_setTimedCallback(j, os_getTime()+sec2osticks(TRANSMIT_INTERVAL), transmit); -int main( void ) -{ - osjob_t initjob; - - // initialize runtime env - os_init( ); - // setup initial job - os_setCallback( &initjob, onInit ); - // execute scheduled jobs and events - os_runloop( ); - // (not reached) } -////////////////////////////////////////////////// -// LMIC EVENT CALLBACK -////////////////////////////////////////////////// -void onEvent( ev_t ev ) -{ - bool txOn = false; - debug_event( ev ); +void setUp() { + + //printf("IoT smart monitoring device for agriculture using LoRaWAN technology\n\n"); + //printf("setting up\n"); - switch( ev ) - { - // network joined, session established - case EV_JOINED: - debug_val( "Net ID = ", LMIC.netid ); - txOn = true; - break; - // scheduled data sent (optionally data received) - case EV_TXCOMPLETE: - debug_val( "Datarate = ", LMIC.datarate ); - // Check if we have a downlink on either Rx1 or Rx2 windows - if( ( LMIC.txrxFlags & ( TXRX_DNW1 | TXRX_DNW2 ) ) != 0 ) - { - debug_val( "LED2 = ", 1 ); - os_setTimedCallback( &rxLedJob, os_getTime( ) + ms2osticks( 25 ), onRxLed ); + os_init(); + //printf("MBED_OS_INIT\n\n"); + //printf("os_init\n"); + + // Reset the MAC state. Session and pending data transfers will be discarded. + LMIC_reset(); + + // Set static session parameters. Instead of dynamically establishing a session + // by joining the network, precomputed session parameters are be provided. + LMIC_setSession (0x1, DEVADDR, (uint8_t*)NWKSKEY, (uint8_t*)APPSKEY); + + // Disable data rate adaptation + LMIC_setAdrMode(0); + + // Disable link check validation + LMIC_setLinkCheckMode(0); + + // Disable beacon tracking + LMIC_disableTracking (); + + // Stop listening for downstream data (periodical reception) + LMIC_stopPingable(); + + // Set data rate and transmit power (note: txpow seems to be ignored by the library) + LMIC_setDrTxpow(DR_SF7,14); + + //printf("LMiC_LoRa_MAC_INIT\n\n"); - if( LMIC.dataLen != 0 ) - { // data received in rx slot after tx - debug_buf( LMIC.frame + LMIC.dataBeg, LMIC.dataLen ); - processRxFrame( ); - } - } - txOn = true; - break; - default: - break; - } - if( txOn == true ) - { - //Sends frame every APP_TX_DUTYCYCLE +/- APP_TX_DUTYCYCLE_RND random time (if not duty cycle limited) - os_setTimedCallback( &sendFrameJob, - os_getTime( ) + ms2osticks( APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ) ), - onSendFrame ); - - ////Sends frame as soon as possible (duty cylce limitations) - //onSendFrame( NULL ); - } + fflush(stdout); + #ifdef SINGLE_CHANNEL_GATEWAY + //printf(" ----->Disabling all channels but 0 (868.1 MHz) for single-channel gateway compatibility\n\n\n"); + for (int i=1; i<16; i++) + LMIC_disableChannel(i); + #endif + //printf("//////////Entering into TIME-TRIGGERED packet sending through LoRaWAN//////////\n"); + //printf("---------------------Packets to be sent every %u seconds----------------------\n\n", transmit_interval); } + +void loop() { + +transmit(&sendjob); + +while(1) { + os_runloop_once(); + wait_ms(20); + } + } + +int main(int argc, char **argv) { + setUp(); + while(1) loop(); + } \ No newline at end of file