Introduction: LoRa-based Visual Monitoring System for Agriculture IoT

About: Electrical & Electronic Engineering Student| President Electronic, Robotics and Innovation Club

LORA is a low-power wide-range wireless networking technology suitable for low-rate long-range applications in the Internet of Things (IoT). For example, in the agricultural industry, LORA based environmental sensing systems enable farmers to remotely monitor the status of large farms in real time. From measuring environmental conditions that influence crop production to tracking livestock health indicators, Internet of Things (IoT) technology for agriculture enables efficiencies which reduce environmental impact, maximize yield and minimize expenses.

Step 1: Understanding LoRa Technology

LoRa (Long Range) is a wireless technology that offers long-range, low power, and secure data transmission for M2M (Machine to Machine) and IoT applications. LoRa is a spread spectrum modulation technology that is derived from chirp spread spectrum (CSS) technology. LoRa can be used to connect sensors, gateways, machines, devices, etc. wirelessly.
LoRa was introduced by a company called Semtech. LoRa Technologies works in different frequency bands in different countries: In the USA it operates in the 915 MHz band, in Europe region, it operates in the 868 MHz band and in Asia region it operates in the 865 to 867 MHz, 920 to 923 MHz band. Working of LoRa is more like a cellular communication. LoRa communication block diagram is shown below. The signal from one LoRa Node travels to another Node through a LoRa Gateway. Network server gets signal from LoRa Gateway and sends it to the end-user through Application server. According to the official information, LoRa can achieve a distance of 715km when there is no obstacle between the Node and Gateway.

Step 2: Components Required

For this project you will need :

  • Arduino Uno
  • 2×LoRa SX1278 Module
  • DHT11 Sensor
  • LCD 16×2 Module with i2c
  • LCD 16×4 Module with i2c
  • PCB Board
  • 2 × Project Box
  • Node mcu Board
  • MQ135 Gas Sensor
  • Soil Moisture Sensor Module
  • DHT11–Temperature and Humidity Sensor

Step 3: Circuit Diagram:

Circuit diagrams for LoRa transmitting and receiving side are given above . In this project, we are going to send temperature and humidity values , Gas Sensor Value (Co2) , Soil Moisture Value from one Arduino to another using LoRa SX1278 module. The DHT11 sensor, Gas Sensor,Soil Moisture Sensor is connected to transmitting side, Arduino. So this Arduino will get Sensor Values from DHT11 and then send it to Receiver end via LoRa SX1278 module. These Sensor values will be printed on LCD connected to Transmitter and Receiver.

Receiver end nodeMcu Send the real time Sensor data values to Google Firebase Using Wifi , Iot Web Application designed by using angular

Transmitting Side- Interfacing LoRa with Arduino UNO

On the transmitting side, we will use an Arduino UNO with LoRa module and DHT11 sensor,Gas Sensor,.Soil Moisture Sensor.The interfacing of the Arduino UNO with LoRa and Sensors is shown above,The LoRa module consists of 16 pins, out of these six pins are GPIO pins, and four are Ground pins. This LoRa module operates at 3.3V, and so the 3.3V pin on LoRa is connected to the 3.3v pin on the Arduino UNO board. Complete connections are given in below,

LoRa SX1278 Module Arduino Uno

  • 3.3V ----------------> 3.3v
  • Gnd ----------------> Gnd
  • NSS ----------------> D10
  • DIO0 ----------------> D2
  • SCK ----------------> D13
  • MISO ----------------> D12
  • MOSI ----------------> D11
  • RST ----------------> D9

DHT 11 Sensor Arduino Uno

  • VCC ----------------> 3.3V
  • Gnd ----------------> Gnd
  • Data ---------------->D7

MQ135 Gas Sensor Arduino Uno

  • VCC ----------------> 5.0V
  • Gnd ----------------> Gnd
  • Data ---------------->A2

Soil Moisture Sensor

  • VCC ----------------> 5.0V
  • Gnd ----------------> Gnd
  • Data ---------------->A1

LCD 16×2 Module(I2c) Arduino Uno

  • SDA ----------------> A4
  • SCL ----------------> A5
  • VCC ----------------> 5.0V
  • GND ----------------> Gnd


Receiving Side- Interfacing LoRa SX1278 with Arduino UNO

For the Receiving side, we will use an Node Mdu with LoRa module and 16×2 LCD Display module. The circuit diagram to connect the Node Mcu with LoRa and LCD module is shown as above.

LoRa SX1278 Module -----> Nodemcu

  • 3.3V -------------------->3.3V
  • GND -------------------->GND
  • NSS -------------------->D10
  • DIO0 -------------------->D2
  • SCK --------------------->D13
  • MISO -------------------->D12
  • MOSI -------------------->D11
  • RST ---------------------->D9

Lcd Display i2c ------------>Nodemcu

  • VCC--------------------->5.0V
  • Gnd---------------------->Gnd
  • SDA--------------------->D4
  • SCL--------------------->D3

Step 4: Code : Explain

The complete program for the Arduino (transmitter and receiver) is given at the end of this tutorial, here we explain some important parts of the both codes Transmitter and receiver.

Programming Arduino LoRa as Transmitter

Run the program by adding the required libraries and launching the pins. This code uses SPI and LoRa library for LoRa module. You can download the Lora library from here.

#include <SPI.h>  
#include <LoRa.h>  
#include <Wire.h>  
#include <DHT.h>
#define DHTPIN 7  // data pin 7

DHT dht(DHTPIN, DHT11);
<br>

Inside the void setup() function we begin the serial monitor. In my case, 433E6 (433 MHz) is my LoRa operating frequency (Sri lanka) you might have to change it based on the type of module you are using.

 Serial.begin(115200);
  dht.begin();   // to start dht sensor
  pinMode(A1, INPUT);  // soil moisture sensor data pin
  pinMode(A2, INPUT);  // co2 sensor data pin<br>  

  pinMode(13, OUTPUT);

  while (!Serial);
  Serial.println("LoRa Sender");

  if (!LoRa.begin(433E6)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }

  LoRa.setSyncWord(0xF3);<br>

Inside the void loop function, we calculate the temperature and humidity values using dht.readTemperature() and dht.readHumidity() functions and to read the values of the co2 sensor and the soil moisture sensor values analogRead() functions are used in order read the values of the sensors.

please note that you need to calibrate the sensors .soil moisture sensor and the co2 sensor. by using linear interpolation methods you can calibrate the sensor values .

digitalWrite(13, HIGH);
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  float m = analogRead(A1); // to read the soil moisture values 
  float c = analogRead(A2);// to read the  co2 values 
 

  if (isnan(h) || isnan(t))
  {
    Serial.println(F("Failed to read from DHT sensor!"));
    return;
  }


We have used the keyword “ t ”, " h" ," m", "c" to separate the sequence of the sending data so that at the receiving end we have use some kind of trick to separate the sensor data.

Before using this code add the following library to Arduino IDE:

  1. Dht 11 Library: Download
  2. Lora Library: Download

Transmitter Code

#include <SPI.h>
#include <LoRa.h>
#include <Wire.h>
#include <DHT.h>

#define DHTPIN 7
DHT dht(DHTPIN, DHT11);

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  Serial.begin(115200);
  dht.begin();

  pinMode(A1, INPUT);
  pinMode(A2,INPUT);
  pinMode(13, OUTPUT);

  while (!Serial);
  Serial.println("LoRa Sender");

  if (!LoRa.begin(433E6)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }

  LoRa.setSyncWord(0xF3);
}

void loop() {

  digitalWrite(13, HIGH);
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  float m = analogRead(A1);
  float c = analogRead(A2);
  

  if (isnan(h) || isnan(t))
  {
    Serial.println(F("Failed to read from DHT sensor!"));
    return;
  }

  Serial.print("Sending packet: ");
  Serial.print(t);
  Serial.print(" ");
  
  Serial.println(h);

  LoRa.beginPacket();
  
  LoRa.print(t);
  LoRa.print("t");
  LoRa.print(h);
  LoRa.print("h");
  LoRa.print(m);
  LoRa.print("m");
  LoRa.print(c);
  LoRa.print("c");
  LoRa.endPacket();

  delay(5000);
}   

Programming Nodemcu LoRa as Receiver

Nodemcu is use as the receiving device of the LoRa , Receive data will be send to the Firebase in order to get data to web Application . this tutorial we will show only how to upload the data to the real-time database of firebase. using the real time data base you can make any iot application such as web application or mobile application.and using the lcd display it will display the sensor data values in the LCD display.Click here to go to how to make a iot web application using real-time database of Firebase and angular

#define ss 15 // nodemcu pin D8
#define rst 16 // nodemcu pin D0
#define Dio 2 //nodemcu pin D4

Before using this code add the following library to Arduino IDE:

  1. ArduinoJson :Download
  2. NTPClient: Download
  3. ESP8266WiFi: Download
  4. LiquidCrystal_I2C :Download
  5. FirebaseArduino :Download

use your Firebase secret key and auth key:

#define FIREBASE_HOST "*************************"
#define FIREBASE_AUTH "************************"

use your wifi ssid and password :

Receiver Code

#include <ArduinoJson.h>
#include <NTPClient.h>
#include "NTPClient.h"
#include "WiFiUdp.h"
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <FirebaseArduino.h>
#include <LiquidCrystal_I2C.h>
#include <LoRa.h>
#include <SPI.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

#define FIREBASE_HOST "********************"
#define FIREBASE_AUTH "********************"
#define WIFI_SSID "**************"
#define WIFI_PASSWORD "********"

const long utcOffsetInSeconds = 19800;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
String months[12] = {"Jan", "Feb", "March", "April", "May", "Ju", "July", "August", "September", "October", "November", "December"};

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);

#define ss 15
#define rst 16
#define dio0 2

void setup() {
  Serial.begin(115200);

  while (!Serial);
  Serial.println("LoRa Receiver Callback");
  LoRa.setPins(ss, rst, dio0);

  if (!LoRa.begin(433E6)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }


  LoRa.setSyncWord(0xF3);
  Wire.begin(2, 0);  //sda , scl
  lcd.init();
  lcd.backlight();
  lcd.setCursor(3, 0);
  lcd.print("Agro Monitor");
  lcd.setCursor(5, 1);
  lcd.print("System");
  delay(3000);
  lcd.clear();
  lcd.setCursor(2, 0);
  Serial.println("Connecting to");
  lcd.print("Connecting to");
  Serial.println(WIFI_SSID);
  lcd.setCursor(3, 1);
  lcd.print("DataBase");
  delay(1000);

  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to ");
  Serial.print(WIFI_SSID);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }

  Serial.println();
  Serial.print("Connected");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
  timeClient.begin();
  Serial.println("");
  Serial.println("WiFi connected");

  lcd.setCursor(2, 0);
  lcd.print("WiFi connected");
  lcd.clear();



}
void loop() {
  timeClient.update();


  unsigned long epochTime = timeClient.getEpochTime();
  struct tm *ptm = gmtime ((time_t *)&epochTime);
  int monthDay = ptm->tm_mday;
  int currentMonth = ptm->tm_mon + 1;
  String currentMonthName = months[currentMonth - 1];
  int currentYear = ptm->tm_year + 1900;
  String currentDate = String(currentYear) + ":" + String(currentMonth) + ":" + String(monthDay);


  String phrase;
  String temp;
  String humid;
  String mouis;
  String co2;
  int packetSize = LoRa.parsePacket();


  if (packetSize) {
    // received a packet
    // read packet
    while (LoRa.available()) {
      char a;
      a = (char)LoRa.read();
      phrase = String(phrase + a);
    }
    //    Serial.println(phrase);

    int firstPosition = 0;
    int secondPOsition = 0;

    for (int i = 0; i <= phrase.length(); i++) {

      //        28.70t95.00h1023.00m

      if (phrase.charAt(i) == 't') {
        secondPOsition = i;
        temp =  phrase.substring(firstPosition, secondPOsition);
        firstPosition = secondPOsition;
        secondPOsition = 0;
      } else if (phrase.charAt(i) == 'h') {
        secondPOsition = i;
        humid =  phrase.substring(firstPosition + 1, secondPOsition);
        firstPosition = secondPOsition;
        secondPOsition = 0;
      } else if (phrase.charAt(i) == 'm') {
        secondPOsition = i;
        mouis =  phrase.substring(firstPosition + 1, secondPOsition);
        firstPosition = secondPOsition;
        secondPOsition = 0;
      } else if (phrase.charAt(i) == 'c') {
        secondPOsition = i;
        co2 =  phrase.substring(firstPosition + 1, secondPOsition);
      }
    }

    Serial.println(temp);
    Serial.println(humid);
    Serial.println(mouis);
    Serial.println(co2);

    lcd.setCursor(0, 0);
    lcd.print("T:");
    lcd.setCursor(2, 0);
    lcd.print(temp);
    lcd.setCursor(0, 1);
    lcd.print("H:");
    lcd.setCursor(2, 1);
    lcd.print(humid);

    lcd.setCursor(8, 0);
    lcd.print("M:");
    lcd.setCursor(10, 0);
    lcd.print(mouis);
    lcd.setCursor(8, 1);
    lcd.print("C:");
    lcd.setCursor(10, 1);
    lcd.print(co2);

    //delay(5000);
    StaticJsonBuffer<400> jsonBuffer;

    String datejson =
      String(currentYear) + String("-")
      + String(currentMonth) + String("-")
      + String(monthDay) + String("T")
      + String(timeClient.getHours())
      + String(":")
      + String(timeClient.getMinutes())
      + String(":")
      + String(timeClient.getSeconds());
    //
    JsonObject& objTemp = jsonBuffer.createObject();
    objTemp["date"] = datejson;
    objTemp["value"] = temp.toFloat();
    //objTemp["value_humid"]=humid;

    JsonObject& objHum = jsonBuffer.createObject();
    objHum["date"] = datejson;
    objHum["value"] =  humid.toFloat();
    //objHum["value_co2"]=co2;

    JsonObject& objsoil = jsonBuffer.createObject();
    objsoil["date"] = datejson;
    objsoil["value"] = mouis.toFloat();

    JsonObject& objph = jsonBuffer.createObject();
    objph["date"] = datejson;
    objph["value"] =  co2.toFloat();

  
    
    Firebase.push("devices/DeviceA/Temp/", objTemp);
    Firebase.setFloat("devices/DeviceA/Temp/current_temp",temp.toFloat());

    Firebase.push("devices/DeviceA/Hum/", objHum);
    Firebase.setFloat("devices/DeviceA/Hum/current_hum", humid.toFloat());

    Firebase.push("devices/DeviceA/Mous/", objsoil);
    Firebase.setFloat("devices/DeviceA/Mous/current_mous",mouis.toFloat());

    Firebase.push("devices/DeviceA/Ph/", objph);
    Firebase.setFloat("devices/DeviceA/Ph/current_ph", co2.toFloat());


    //delay(5000);


    if (Firebase.failed())
    {

      Serial.print("pushing /logs failed:");
      Serial.println(Firebase.error());
      return;
    }

  }
}





Step 5: Final Product