Hackster is hosting Hackster Holidays, Finale: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Tuesday!Stream Hackster Holidays, Finale on Tuesday!
Muhammad Afzal
Published © Apache-2.0

LoraWan Based Smart Agriculture Monitoring

In this project I will demonstrate how we can use Smart Agriculture Devices using Long Range Communication(LoraWan) in Rural areas.

AdvancedFull instructions providedOver 1 day1,545
LoraWan Based Smart Agriculture Monitoring

Things used in this project

Hardware components

Wio Terminal
Seeed Studio Wio Terminal
×2
Seeed Studio Grove - Wio-E5 (STM32WLE5JC), for Long Range Application
×1
Seeed Studio Wio Terminal Chassis - Wio-E5 and GNSS, for Long Range Application
×1
DHT22 Temperature Sensor
DHT22 Temperature Sensor
×1
Adafruit Waterproof DS18B20 Digital temperature sensor
Adafruit Waterproof DS18B20 Digital temperature sensor
×1
oil Humidity Sensor Tester Module Potentiometer Hygrometer Moisture Detection Sensor Probe for Arduino
×1

Software apps and online services

Arduino IDE
Arduino IDE
ThingSpeak API
ThingSpeak API

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Code

Sender

Arduino
Wio Terminal Sender
/*
 * 
 *      LoraWan Based SmartA AgriCulture using Wio Terminal
 *      Node Type:Sender
 *      Author:Muhammad Afzal
 *      Hackster Profile:https://www.hackster.io/mafzal/
 *      Linkedin Profile:https://www.linkedin.com/in/mafzal9/
 *      
 */
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>
#include <Arduino.h>
#include"TFT_eSPI.h"
#include<SoftwareSerial.h>

//LoraWan E5 Module 
SoftwareSerial e5(0, 1);

static char recv_buf[512];
static bool is_exist = false;

//TFT Display Setup
TFT_eSPI tft;
TFT_eSprite spr = TFT_eSprite(&tft);  //sprite


//DHT22 Setup
#define DHTPIN D3     // Digital pin connected to the DHT sensor Wio Termianl Pin#18
#define DHTTYPE    DHT22     // DHT11/22 Type of DHT
DHT_Unified dht(DHTPIN, DHTTYPE);
uint32_t delayMS;

//DS18B20 Setup
// Data wire is plugged into port#22 on Wio Terminal and Pin#4 in Arduino
#define ONE_WIRE_BUS D4

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// arrays to hold device address
DeviceAddress insideThermometer;



float SoilTemperature;
float SoilMoisture;
float DHTTemp=0;
float DHTHum=0;
int AirValue=1023;
int WaterValue=358;


static int at_send_check_response(char *p_ack, int timeout_ms, char *p_cmd, ...)
{
    int ch = 0;
    int index = 0;
    int startMillis = 0;
    va_list args;
    memset(recv_buf, 0, sizeof(recv_buf));
    va_start(args, p_cmd);
    e5.printf(p_cmd, args);
    Serial.printf(p_cmd, args);
    va_end(args);
    delay(200);
    startMillis = millis();
 
    if (p_ack == NULL)
    {
        return 0;
    }
 
    do
    {
        while (e5.available() > 0)
        {
            ch = e5.read();
            recv_buf[index++] = ch;
            Serial.print((char)ch);
            delay(2);
        }
 
        if (strstr(recv_buf, p_ack) != NULL)
        {
            return 1;
        }
 
    } while (millis() - startMillis < timeout_ms);
    return 0;
}

static int node_send(uint32_t timeout)
{
    static uint16_t count = 0;
    int ret = 0;
    char data[32];
    char cmd[128];
    uint16_t error;
    char errorMessage[256];
    
    memset(data, 0, sizeof(data));
    sprintf(data, "%04X,%04X,%04X,%04X", int(SoilMoisture), int(SoilTemperature), int(DHTTemp),int(DHTHum));
    sprintf(cmd, "AT+TEST=TXLRPKT,\"5345454544%s\"\r\n", data);
    ret = at_send_check_response("TX DONE", 2000, cmd);
    if (ret == 1)
    {
 
        //send Airquality
        Serial.print("Sent successfully!\r\n");
    }
    else
    {
        Serial.print("Send failed!\r\n");
    }
    return ret;
}

 

void setup() {


    tft.begin();
    tft.setRotation(3);

    //Display the Headings
    tft.fillScreen(TFT_BLACK);
    tft.setFreeFont(&FreeSansBold18pt7b);
    tft.setTextColor(TFT_WHITE);
    tft.drawString("Smart Agri Sender", 5, 10 , 1);
    
    tft.setFreeFont(&FreeSans12pt7b);
    tft.setTextColor(TFT_RED);
    tft.drawString("Soil Moist:", 7 , 65 , 1);
    tft.drawString("Soil Temp:", 180 , 65 , 1);
    tft.drawString("Temperature:", 7 , 150 , 1);
    tft.drawString("Humidity:", 180 , 150 , 1);

    
    uint32_t start = millis();
    Serial.begin(9600);
    while ( !Serial && (millis() - start) < 1500 );  // Open the Serial Monitor to get started or wait for 1.5"


    // locate devices on the bus DS18B20
    Serial.print("Locating devices...");
    sensors.begin();
    Serial.print("Found ");
    Serial.print(sensors.getDeviceCount(), DEC);
    Serial.println(" devices.");
    
    if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); 
    
    // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
    sensors.setResolution(insideThermometer, 9);
  
    // Initialize DHT device.
    dht.begin();

    //LorWan Module Starting & Verify Connectivity with Wio Terminal
    e5.begin(9600);
    
    uint16_t error;
    char errorMessage[256];
    
    Serial.print("ping pong communication!\r\n");
 
 
    if (at_send_check_response("+AT: OK", 100, "AT\r\n"))
    {
        is_exist = true;
        at_send_check_response("+MODE: TEST", 1000, "AT+MODE=TEST\r\n");
        at_send_check_response("+TEST: RFCFG", 1000, "AT+TEST=RFCFG,868,SF12,125,12,15,14,ON,OFF,OFF\r\n");
        delay(200);

    }
    else
    {
        is_exist = false;
        Serial.print("No E5 module found.\r\n");
        tft.setFreeFont(&FreeSansBoldOblique12pt7b);
        tft.setTextColor(TFT_RED);
        tft.drawString("No E5 Module", 7 , 150 , 1);

    }
    
  
}

void loop() {

  //Fetch Soil Mositure
  Serial.print("Requesting Soil Mositure");
  //soilmoisturepercent[i]=map(getSoilMositure(i), WaterValue[i] ,AirValue[i], 100, 0);
  SoilMoisture=analogRead(A2);
  SoilMoisture=map(SoilMoisture, WaterValue ,AirValue, 100, 0);
  Serial.print("Raw Soil Mositure=");
  Serial.print(SoilMoisture);
  Serial.print("%");
  Serial.println("");
  delay(2000);


  Serial.print("Requesting DHT11/DHT22");
  // Get temperature event and print its value.
  sensors_event_t event;
  dht.temperature().getEvent(&event);
  

  //Print DHT Readings
  if (isnan(event.temperature)) {
    DHTTemp=0;
  }
  else {
    DHTTemp=event.temperature;
  }
  
  // Get humidity event and print its value.
  dht.humidity().getEvent(&event);
  if (isnan(event.relative_humidity)) {
    DHTHum=0;
  }
  else {
    DHTHum=event.relative_humidity;
  }
  Serial.println("DONE"); 
  
  //Print DHT Temp & Hum Readings
  Serial.print(F("Temperature: "));
  Serial.print(DHTTemp);
  Serial.print(F("°C"));
  Serial.print(" \t ");
  Serial.print(F("Humidity: "));
  Serial.print(DHTHum);
  Serial.println(F("%"));  

  delay(2000);



  
  // call sensors.requestTemperatures() to issue a global temperature 
  // request to all devices on the bus
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  Serial.println("DONE");
  
  SoilTemperature = sensors.getTempC(insideThermometer);
  if(SoilTemperature == DEVICE_DISCONNECTED_C) 
  {
    Serial.println("Error: Could not read temperature data");
    return;
  }
  Serial.print("Soil Temperature DS18B20 C: ");
  Serial.println(SoilTemperature);


  //Display the Values on Screen
  spr.createSprite(100, 30);
  spr.setFreeFont(&FreeSansBold12pt7b);
  spr.setTextColor(TFT_WHITE);
  spr.drawNumber(SoilMoisture, 0, 0, 1);
  spr.pushSprite(15, 100);
  spr.deleteSprite();
  spr.createSprite(150, 30);
  spr.drawNumber(SoilTemperature, 0, 0, 1);
  spr.pushSprite(180, 100);
  spr.deleteSprite();
  spr.createSprite(150, 30);
  spr.drawNumber(DHTHum, 0 , 0 , 1);
  spr.pushSprite(180, 185);
  spr.deleteSprite();
  spr.createSprite(100, 30);
  spr.drawNumber(DHTTemp, 0, 0, 1);
  spr.pushSprite(15, 185);
  spr.deleteSprite();
  if (is_exist)
      {
  
          //node_send_then_recv(2000);
          node_send(2000);
          delay(3000);
  
      }
  delay(60000);

}

Receiver

Arduino
/*
 * 
 *      LoraWan Based SmartA AgriCulture using Wio Terminal
 *      Node Type:Receiver
 *      Author:Muhammad Afzal
 *      Hackster Profile:https://www.hackster.io/mafzal/
 *      Linkedin Profile:https://www.linkedin.com/in/mafzal9/
 *      
 */
#include <Arduino.h>
#include"TFT_eSPI.h"
#include <SoftwareSerial.h>
#include "ThingSpeak.h" // always include thingspeak header file after other header files and custom macros
#include "secrets.h"
#include <rpcWiFi.h>

Serial_             * debugUart;        // link to serial used for debugginf
SoftwareSerial      * e5SwUart;         // link to the sw serial for E5 communications

static char recv_buf[512];
static bool is_exist = false;

TFT_eSPI tft;
TFT_eSprite spr = TFT_eSprite(&tft);  //sprite

WiFiClient  client;
  
static int at_send_check_response(char *p_ack, int timeout_ms, char *p_cmd, ...)
{
    int ch = 0;
    int index = 0;
    int startMillis = 0;
    va_list args;
    memset(recv_buf, 0, sizeof(recv_buf));
    va_start(args, p_cmd);
    
    Serial1.printf(p_cmd, args);//
    
    Serial.printf(p_cmd, args);
    va_end(args);
    delay(200);
    startMillis = millis();
 
    if (p_ack == NULL)
    {
        return 0;
    }
 
    do
    {
        while (Serial1.available() > 0) //
        {
            ch = Serial1.read(); //
            recv_buf[index++] = ch;
            Serial.print((char)ch);
            delay(2);
        }
 
        if (strstr(recv_buf, p_ack) != NULL)
        {
            return 1;
        }
 
    } while (millis() - startMillis < timeout_ms);
    return 0;
}
 
static int recv_prase(void)
{
    char ch;
    int index = 0;
    memset(recv_buf, 0, sizeof(recv_buf));
    while (Serial1.available() > 0)//
    {
        ch = Serial1.read(); //
        recv_buf[index++] = ch;
        Serial.print((char)ch);
        delay(2);
    }
 
    if (index)
    {
        char *p_start = NULL;
        char data[32] = {
            0,
        };
        int rssi = 0;
        int snr = 0;
 
        p_start = strstr(recv_buf, "+TEST: RX \"5345454544");
        if (p_start)
        {
            
            spr.fillSprite(TFT_BLACK);
            p_start = strstr(recv_buf, "5345454544");
            if (p_start && (1 == sscanf(p_start, "5345454544%s,", data)))
            {
                data[16] = 0;
                int SoilMoisture;
                int SoilTemperature;
                int DHTTemp;
                int DHTHum;
                char *endptr;
                char *endptr1;
                char *endptr2;
                char *endptr3;
                char datatvoc[5] = {data[0], data[1],data[2], data[3]};
                char dataco2[5] = {data[4], data[5], data[6], data[7]};
                char datatemp[5] = {data[8], data[9], data[10], data[11]};
                char datahumi[5] = {data[12], data[13],data[14], data[15]};
                
                SoilMoisture = strtol(datatvoc, &endptr, 16);
                SoilTemperature = strtol(dataco2, &endptr1, 16);
                DHTTemp = strtol(datatemp, &endptr, 16);
                DHTHum = strtol(datahumi, &endptr1, 16);
                
                spr.createSprite(100, 30);
                spr.setFreeFont(&FreeSansBold12pt7b);
                spr.setTextColor(TFT_WHITE);
                spr.drawNumber(SoilMoisture, 0, 0, 1);
                spr.pushSprite(15, 100);
                spr.deleteSprite();
                spr.createSprite(150, 30);
                spr.drawNumber(SoilTemperature, 0, 0, 1);
                spr.pushSprite(180, 100);
                spr.deleteSprite();
                spr.createSprite(100, 30);
                spr.drawNumber(DHTTemp, 0, 0, 1);
                spr.pushSprite(15, 185);
                spr.deleteSprite();
                spr.createSprite(150, 30);
                spr.drawNumber(DHTHum, 0 , 0 , 1);
                spr.pushSprite(180, 185);
                spr.deleteSprite();
                
                Serial.print("data received displaying on the wio terminal");
                Serial.print("\r\n");

                  // Connect or reconnect to WiFi
                  if(WiFi.status() != WL_CONNECTED){
                    Serial.print("Attempting to connect to SSID: ");
                    Serial.println(SECRET_SSID);
                    while(WiFi.status() != WL_CONNECTED){
                      WiFi.begin(SECRET_SSID, SECRET_PASS);  // Connect to WPA/WPA2 network. Change this line if using open or WEP network
                      Serial.print(".");
                      delay(5000);     
                    } 
                    Serial.println("\nConnected.");
                  }

                 ThingSpeak.setField(1, SoilMoisture);
                 ThingSpeak.setField(2, SoilTemperature);
                 ThingSpeak.setField(3, DHTTemp);
                 ThingSpeak.setField(4, DHTHum);
                // write to the ThingSpeak channel 
                int x = ThingSpeak.writeFields(SECRET_CH_ID, SECRET_WRITE_APIKEY);
                if(x == 200){
                  Serial.println("Channel update successful.");
                }
                else{
                  Serial.println("Problem updating channel. HTTP error code " + String(x));
                }

                
            }
 
            p_start = strstr(recv_buf, "RSSI:");
            if (p_start && (1 == sscanf(p_start, "RSSI:%d,", &rssi)))
            {
                String newrssi = String(rssi);
          
                Serial.print(rssi);
                Serial.print("\r\n");

            }
            p_start = strstr(recv_buf, "SNR:");
            if (p_start && (1 == sscanf(p_start, "SNR:%d", &snr)))
            {
                Serial.print(snr);
                Serial.print("\r\n");

                
            }
            return 1;
        }
    }
    return 0;
}
 
static int node_recv(uint32_t timeout_ms)
{
    at_send_check_response("+TEST: RXLRPKT", 1000, "AT+TEST=RXLRPKT\r\n");
    int startMillis = millis();
    do
    {
        if (recv_prase())
        {
            return 1;
        }
    } while (millis() - startMillis < timeout_ms);
    return 0;
}
 

 
void setup(void)
{

    
    tft.begin();
    tft.setRotation(3);
    
    uint32_t start = millis();
    Serial.begin(9600);
    while ( !Serial && (millis() - start) < 1500 );  // Open the Serial Monitor to get started or wait for 1.5"
 
    Serial1.begin(9600);
    Serial.print("Receiver\r\n");


    //Wifi
    WiFi.begin(SECRET_SSID, SECRET_PASS);
    WiFi.mode(WIFI_STA);  
    
    // Initialize ThingSpeak 
    ThingSpeak.begin(client);  
    
        //Display the Headings
    tft.fillScreen(TFT_BLACK);
    tft.setFreeFont(&FreeSansBold18pt7b);
    tft.setTextColor(TFT_WHITE);
    tft.drawString("SmartAgri Recvr", 5, 10 , 1);
    
    tft.setFreeFont(&FreeSans12pt7b);
    tft.setTextColor(TFT_RED);
    tft.drawString("Soil Moist:", 7 , 65 , 1);
    tft.drawString("Soil Temp:", 180 , 65 , 1);
    tft.drawString("Temperature:", 7 , 150 , 1);
    tft.drawString("Humidity:", 180 , 150 , 1);
    
    if (at_send_check_response("+AT: OK", 100, "AT\r\n"))
    {
        is_exist = true;
        at_send_check_response("+MODE: TEST", 1000, "AT+MODE=TEST\r\n");
        at_send_check_response("+TEST: RFCFG", 1000, "AT+TEST=RFCFG,868,SF12,125,12,15,14,ON,OFF,OFF\r\n");
        delay(200);
    }
    else
    {
        is_exist = false;
        Serial.print("No E5 module found.\r\n");
        tft.setFreeFont(&FreeSansBoldOblique12pt7b);
        tft.setTextColor(TFT_RED);
        tft.drawString("No E5 Module", 7 , 150 , 1);

    }
}
 
void loop(void)
{
    if (is_exist)
    {

        node_recv(60000);

    }
}

secrets.h

Arduino
/*
 * 
 *      LoraWan Based SmartA AgriCulture using Wio Terminal
 *      Node Type:Receiver
 *      Author:Muhammad Afzal
 *      Hackster Profile:https://www.hackster.io/mafzal/
 *      Linkedin Profile:https://www.linkedin.com/in/mafzal9/
 *      
 */

// Use this file to store all of the private credentials 
// and connection details

#define SECRET_SSID "----SSID-----"    // replace MySSID with your WiFi network name
#define SECRET_PASS "-----Password-----"  // replace MyPassword with your WiFi password

#define SECRET_CH_ID 0000000     // replace 0000000 with your channel number
#define SECRET_WRITE_APIKEY "Channel-Write-Key"   // replace XYZ with your channel write API Key

Credits

Muhammad Afzal
25 projects • 117 followers
I am Software Eng having 13+ Years of experience. Hackster.io & Cayenne Mydevices Ambassador in Pakistan.

Comments