make2explore
Published © CC BY-SA

Application of LoRa WSN in Landslide Monitoring Systems

DIY project using Wio-E5 LoRa Dev Boards for testing use of LoRa WSN in Landslide Monitoring/Detection Systems

AdvancedFull instructions providedOver 3 days2,148
Application of LoRa WSN in Landslide Monitoring Systems

Things used in this project

Story

Read more

Schematics

Wio-E5-DIY-Project-SensorNode

Wio-E5-DIY-Project-GatewayNode

Wio-E5-DIY-Project-EndNode

TFT-display-Mega-Interfacing-m2e

Wio-E5-DevKit-Mega-Interfacing-m2e

ADXL345-Mega-Interfacing-m2e

240P-IPSdisplay-ESP-Interfacing-m2e

Wio-E5-Mini-ESP-Interfacing-m2e

SEEED-Wio-E5-LoRa-Dev-Boards-Overview

Code

Wio-E5-DIY-Project-SensorNode

C/C++
// This code is of **Wireless (LoRa) Sensor Node**. Which will be collecting data from Landslide prone site physical data (Rain + Soil Moisture + Humidity + Temperature + Vibrations + Descending Velocity) and send it to Gateway Node in order relay it over End node
// ---------------------------------- make2explore.com -------------------------------------------------------//
// Project           - Application of LoRa WSN in Landslide Monitoring/Detection/Prevention Systems
// Created By        - info@make2explore.com
// Last Modified     - 14/02/2023 12:30:00 @admin
// Software          - C/C++, PlatformIO IDE, Visual Studio Code Editor, Libraries
// Hardware          - Arduino Mega 2560, NodeMCU ESP8266, Wio Terminal, Wio E5 Dev Boards, Displays, Sensors         
// Sensors Used      - DHT22, Soil Moisture - Resistive, Capacitive, Vibration, Rain, ADXL345 Accelerometer
// Source Repo       - github.com/make2explore
// -----------------------------------------------------------------------------------------------------------//
// This code is of **Wireless (LoRa) Sensor Node**. Which will be collecting data from Landslide prone site
// physical data (Rain + Soil Moisture + Humidity + Temperature + Vibrations + Descending Velocity)
// and sending it to Gateway Node in order relay it over End node

// The main Objective of this project is to test the use of LoRa WSN in such "Landslide Monitoring" systems
// in order to achieve good data reception and reliablity in hilly Areas, "Shadow Regions" or 
// Remote Areas with very low coverage of other Wired/Wireless Networks (e.g GSM/WiFi/Ethernet etc.)
// -----------------------------------------------------------------------------------------------------------//

// Include Libraries
#include <Arduino.h>
#include <Adafruit_GFX.h>       // Include core graphics library
#include <Adafruit_ST7735.h>    // Include Adafruit_ST7735 library to drive the display
#include "DHT.h"                // Include DHT Sensors library
#include <Adafruit_Sensor.h>    // Include Generic Sensor Library
#include <Adafruit_ADXL345_U.h> // Include MEMS ADXL345 Sensor Library

// Declare pins for the display:
#define TFT_CS     53
#define TFT_RST    49  // You can also connect this to the Arduino reset in which case, set this #define pin to -1!
#define TFT_DC     48
// The rest of the pins are pre-selected as the default hardware SPI for Arduino Mega (SCK = 52 and SDA = 51)

// Vibration sensor connected to pin 8
#define vibSensor_pin 8

// Invoke Display and Create display Instance 
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

// LoRa Data receive buffer
static char recv_buf[512];
static bool is_exist = false;

// DHT Sensor Definitions
#define DHTPIN 9     // Digital pin connected to the DHT sensor 
#define DHTTYPE    DHT22     // DHT 22 (AM2302)
DHT dht(DHTPIN, DHTTYPE);

// Sensor variable declarations
// m1  = Capacitive Soil moisture
// m2  = Resistive Soil moisture
uint8_t m1, m2, humi, temp, rain_per;
float disp;
bool vib, stat;
String status = "";

// Soil Moisture Sensors Calibration values
const int AirValueM1 = 877;   //replace the value with value when placed in air using calibration code 
const int WaterValueM1 = 480; //replace the value with value when placed in water using calibration code 

const int AirValueM2 = 1023;   //replace the value with value when placed in air using calibration code 
const int WaterValueM2 = 336; //replace the value with value when placed in water using calibration code 

// Rain Sensor (10K Pot as Tipping bucket Rain Gauge) attached to Analog Pin A2
const int rainSensor = A2;  // Mega2560 Analog Pin A2 = A2 for tipping bucket Rain Sensor

// Soil Moisture Sensors Connections
const int CapSoil = A0;
const int ResSoil = A1;

// Rain Sensors Calibration values
const uint16_t noRain = 21;
const uint16_t FullRain = 1024;

// Readings Update Interval Settings
const unsigned long sendInterval = 20000;
const unsigned long updateInterval = 5000;

unsigned long previousTime = 0;
unsigned long previousUpdateTime = 0;

// ADXL345 Accelerometer 
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified();


// Function to check response for AT commands
static int at_send_check_response(char *p_ack, int timeout_ms, char *p_cmd, ...)
{
    int ch;
    int index = 0;
    int startMillis = 0;
    va_list args;
    memset(recv_buf, 0, sizeof(recv_buf));
    va_start(args, p_cmd);
    Serial1.print(p_cmd);
    Serial.print(p_cmd);
    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);
    Serial.println();
    return 0;
}

// Function to configure LoRa E5 Dev Board in Test Mode - Check AT commands Specification Guide
// for more details about these command sequences  
void configLoRaModule(){
  //Configure LoRa E5 Dev Kit in Test Mode
  Serial.println("Configuring Wio E5 LoRa Dev Board ...");
  delay(250);
  if (at_send_check_response("+AT: OK", 100, "AT\r\n"))
  {
    is_exist = true;
    at_send_check_response("+MODE: TEST", 1500, "AT+MODE=TEST\r\n");
    at_send_check_response("+TEST: RFCFG", 1500, "AT+TEST=RFCFG,866,SF12,125,12,15,14,ON,OFF,OFF\r\n");
    delay(500);
  }
  else
  {
    is_exist = false;
    Serial.print("No E5 module found.\r\n");
  }
  delay(500);
}

// Function to Setup Display for Initial Screen
void setupDisplay(){
  // Display setup:
  Serial.println("");
  Serial.println("Setting up Display ...");
  delay(250);
  // Use this initializer if you're using a 1.8" TFT
  tft.initR(INITR_BLACKTAB);  // Initialize a ST7735S chip, black tab

  tft.fillScreen(ST7735_BLACK);  // Fill screen with black

  tft.setRotation(2);  // Set orientation of the display. Values are from 0 to 3. If not declared, orientation would be 0,
                         // which is portrait mode.

  tft.setTextWrap(false);  // By default, long lines of text are set to automatically “wrap” back to the leftmost column.
                           // To override this behavior (so text will run off the right side of the display - useful for
                           // scrolling marquee effects), use setTextWrap(false). The normal wrapping behavior is restored
                           // with setTextWrap(true).

  tft.fillRect(0, 0, 127, 20, ST7735_MAGENTA); // Draw filled rectangle (x,y,width,height,color)
  tft.setCursor(15, 7);  // Set position (x,y)
  tft.setTextColor(ST7735_WHITE);  // Set color of text. First is the color of text and after is color of background
  tft.setTextSize(1);  // Set text size. Goes from 0 (the smallest) to 20 (very big)
  tft.println("LoRa Sensor Node");  // Print a text or value

  // Stop using a custom font:
  tft.setFont();  // Reset to standard font, to stop using any custom font previously set
  tft.setTextSize(1);  // Set text size. Goes from 0 (the smallest) to 20 (very big)
  tft.setCursor(5, 28);  // Set position (x,y)
  //tft.setTextColor(ST7735_WHITE);  // Set color of text. First is the color of text and after is color of background
  tft.println("Soil M1 : ");  // Print a text or value
  tft.setCursor(110, 28);  // Set position (x,y)
  tft.print('%');

  tft.setCursor(5, 45);  // Set position (x,y)
  tft.println("Soil M2 : ");  // Print a text or value
  tft.setCursor(110, 45);  // Set position (x,y)
  tft.print('%');

  tft.setCursor(5, 60);  // Set position (x,y)
  tft.println("Rain Pr : ");  // Print a text or value
  tft.setCursor(110, 60);  // Set position (x,y)
  tft.print('%');

  tft.setCursor(5, 75);  // Set position (x,y)
  tft.println("Humi Pr : ");  // Print a text or value
  tft.setCursor(110, 75);  // Set position (x,y)
  tft.print('%');

  tft.setCursor(5, 90);  // Set position (x,y)
  tft.println("Temp Rd : ");  // Print a text or value
  tft.setCursor(110, 90);  // Set position (x,y)
  tft.print('C');

  tft.setCursor(5, 105);  // Set position (x,y)
  tft.println("Vibr Dt : ");  // Print a text or value
  tft.setCursor(110, 105);  // Set position (x,y)
  tft.println("Ct");

  tft.setCursor(5, 120);  // Set position (x,y)
  tft.println("Disp Ac : ");  // Print a text or value
  tft.setCursor(110, 120);  // Set position (x,y)
  tft.println("m/s");

  tft.fillRect(1,142,127,18, ST7735_BLUE);
  tft.setCursor(15, 148);  // Set position (x,y)
  tft.setTextColor(ST7735_WHITE);
  tft.println("Status : ");  // Print a text or value
}

// Function to Initialise DHT Sensor and Check Readings
void checkDHT(){
  // Wait a few seconds between measurements.
  delay(2000);

  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  float f = dht.readTemperature(true);

  temp = t;
  humi = h;

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t) || isnan(f)) {
    Serial.println(F("Failed to read from DHT sensor!"));
    return;
  }

  //Serial.print(F("Humidity: "));
  //Serial.print(h);
  //Serial.print(F("%  Temperature: "));
  //Serial.print(t);
  //Serial.print(F("°C "));

}

// Function to Test ADXL345 Accelerometer
void init_accel(){
  if(!accel.begin())
  {
    Serial.println("No ADXL345 sensor detected.");
    while(1);
  }
}

// Function to Get Readings from ADXL345 Accelerometer
void getAccel(){
  sensors_event_t event; 
  accel.getEvent(&event);
  // Wait a few seconds between measurements.
  disp = event.acceleration.x;
  //Serial.print("Accelearation : ");
  //Serial.println(disp);
  //elay(2000);  
}

// Function to Get Readings from Soil Moisture Sensors
void getSoilM(){
  //Serial.print("Capacitive Soil Moisture : ");
  //Serial.println(analogRead(CapSoil));
  //Serial.print("Resistive Soil Moisture : ");
  //Serial.println(analogRead(ResSoil));  
  m1 = map(analogRead(CapSoil), AirValueM1, WaterValueM1, 0, 100);
  m2 = map(analogRead(ResSoil), AirValueM2, WaterValueM2, 0, 100);
}

// Check Rain Sensor (10K Pot as Tipping bucket Rain Gauge) attached to Analog Pin A2
void checkRain(){
  rain_per = map(analogRead(rainSensor), noRain, FullRain, 0, 100);
}

// Function to Get All Sensor Readings at a time
void getReadings(){
  checkDHT();
  init_accel();
  getSoilM();
  checkRain();
  getAccel();
  vib = digitalRead(vibSensor_pin);
}

// Function for checking critical landslide conditions
void checkStatus(){
  if((m1 < 60) && (m2 < 60) && (rain_per < 50) && (humi < 60) && (temp > 25) && (disp < 1) && (vib == 0)){
    status = "Normal";
  }
  else if((m1 > 60) && (m2 > 60) && (rain_per > 50) && (humi > 60) && (temp < 25)){
    if ((disp > 1) && (vib == 1)){
      status = "Alert";
      stat = 1;
    }
  else
    status = "Normal";
    stat = 0;
  }
}

// Function to Display Readings from Sensors Locally
void displayReadings(){

  tft.setTextSize(1);  // Set text size. Goes from 0 (the smallest) to 20 (very big)
  tft.setCursor(70, 28);  // Set position (x,y)
  tft.setTextColor(ST7735_CYAN, ST7735_BLACK);  // Set color of text. First is the color of text and after is color of background
  tft.println(m1);  // Print a text or value

  tft.setCursor(70, 45);  // Set position (x,y)
  tft.println(m2);  // Print a text or value

  tft.setCursor(70, 60);  // Set position (x,y)
  tft.println(rain_per);  // Print a text or value

  tft.setCursor(70, 75);  // Set position (x,y)
  tft.println(humi);  // Print a text or value


  tft.setCursor(70, 90);  // Set position (x,y)
  tft.println(temp);  // Print a text or value


  tft.setCursor(70, 105);  // Set position (x,y)
  tft.println(vib);  // Print a text or value


  tft.setCursor(70, 120);  // Set position (x,y)
  tft.println(disp);  // Print a text or value

  if(stat == 1){
  tft.fillRect(1,142,127,18, ST7735_RED);
  tft.setCursor(15, 148);  // Set position (x,y)
  tft.setTextColor(ST7735_WHITE);
  tft.println("Status : ");  // Print a text or value    
  tft.setTextColor(ST7735_WHITE, ST7735_RED); 
  }else {
    tft.setTextColor(ST7735_WHITE, ST7735_BLUE); 
  }
  
  tft.setCursor(70, 148);  // Set position (x,y)
  tft.println(status);  // Print a text or value
}

// Function for LoRa packet preparation and sending
static int LoRa_send()
{
  String sensorData = "";
  char cmd[256] = "";
  char data[128] = "";
  int ret = 0;
  sensorData = sensorData + String(m1) + "," + String(m2) + "," + String(rain_per) + "," + String(humi) 
                + "," + String(temp) + "," + String(disp) + "," + String(vib) + "," + String(stat);
  //Serial.print("Printing Sensor Data String : ");
  //Serial.println(sensorData);

  strncpy(data,sensorData.c_str(),sizeof(data));
  data[sizeof(data) -1] = 0;

  sprintf(cmd, "AT+TEST=TXLRSTR,\"GW,%s\"\r\n", data);
  //Serial.print("Printing cmd String : ");
  //Serial.print(cmd);

  ret = at_send_check_response("TX DONE", 6000, cmd);
    if (ret == 1)
    {
      Serial.println("");
      Serial.print("Sent successfully!\r\n");
    }
    else
    {
      Serial.println("");
      Serial.print("Send failed!\r\n");
    }
  return ret;
}

// Function to Setup the Initializations and Configurations
void setup() {
  // put your setup code here, to run once:
  //initialize the library
  Serial.begin(9600);
  Serial1.begin(9600);
  Serial.println("LandSlide Monitoring - Starting!!");
  delay(250);
  pinMode(vibSensor_pin, INPUT);
  configLoRaModule();
  setupDisplay();
  dht.begin();
  checkDHT();
  delay(1000);
  Serial.println("Setup Completed !!");
  delay(250);
}

// Function main Loop
void loop() {
  // put your main code here, to run repeatedly:
  unsigned long currentUpdateTime = millis();
  if (currentUpdateTime - previousUpdateTime >= updateInterval) {
    getReadings();
    checkStatus();
    displayReadings();
    previousUpdateTime = currentUpdateTime;
  }

  unsigned long currentTime = millis();
  if (currentTime - previousTime >= sendInterval) {
    LoRa_send();
    previousTime = currentTime;
  }
}
// ---------------------------------- make2explore.com----------------------------------------------------//

Wio-E5-DIY-Project-GatewayNode

C/C++
// This code is of Wireless (LoRa) **Gateway Node**. Which will be *Relaying* data coming from LoRa WSN deployed at Landslide prone site, To the End node. GW Node also send its own location's parameters (Rain+Humidity+Temp) data for comparing actual situations. It'll help in calculating perimeter of Raining area. Since Sensor node and GW node are at about 5-7Km away/distant from each other.
// ---------------------------------- make2explore.com -------------------------------------------------------//
// Project           - Application of LoRa WSN in Landslide Monitoring/Detection/Prevention Systems
// Created By        - info@make2explore.com
// Last Modified     - 14/02/2023 01:26:00 @admin
// Software          - C/C++, PlatformIO IDE, Visual Studio Code Editor, Libraries
// Hardware          - Arduino Mega 2560, NodeMCU ESP8266, Wio Terminal, Wio E5 Dev Boards, Displays, Sensors         
// Sensors Used      - DHT22, Soil Moisture - Resistive, Capacitive, Vibration, Rain, ADXL345 Accelerometer
// Source Repo       - github.com/make2explore
// -----------------------------------------------------------------------------------------------------------//
// This code is of Wireless (LoRa) **Gateway Node**. Which will be *Relaying* data coming from LoRa WSN 
// deployed at Landslide prone site, To the End node, Where concerned Authorities can monitor/Analyse 
// the data to take Prevention/Rescue Measures.
// physical data (Rain + Soil Moisture + Humidity + Temperature + Vibrations + Descending Velocity)
// GW Node also send its own location's parameters (Rain+Humidity+Temp) data for comparing actual situations. 
// It helps in calculating perimeter of Raining area. Since Sensor node and GW node are at about 5-7Km 
// away/distant from each other.

// The main Objective of this project is to test the use of LoRa WSN in such "Landslide Monitoring" systems
// in order to achieve good data reception and reliablity in hilly Areas, "Shadow Regions" or 
// Remote Areas with very low coverage of other Wired/Wireless Networks (e.g GSM/WiFi/Ethernet etc.)
// -----------------------------------------------------------------------------------------------------------//

// Include Libraries
#include <Arduino.h>
#include <SPI.h>              // SPI Library needed for display
#include <TFT_eSPI.h>         // Graphics library
#include "m2e-logo.h"         // make2explore Logo Bitmap header file
#include <SoftwareSerial.h>   // Software Serial Library for communicating with Wio E5 Mini Board
#include "DHT.h"              // Include DHT Sensors library

// Invoke Display and Create display Instance 
TFT_eSPI tft = TFT_eSPI();

// Lets define Sofware serial Pins for WIo E5 Mini Dev Board
const byte rxPin = D2;
const byte txPin = D3;

// Set up a new SoftwareSerial object
SoftwareSerial e5 (rxPin, txPin);

// LoRa Data receive buffer
static char recv_buf[512];
static bool is_exist = false;

// Rain Sensor (10K Pot as Tipping bucket Rain Gauge) attached to Analog Pin A0
const int rainSensor = A0;  // ESP8266 Analog Pin ADC0 = A0 for tipping bucketRain Sensor
// Rain Sensors Calibration values
const uint16_t noRain = 21;
const uint16_t FullRain = 1024;

// DHT Sensor Definitions
#define DHTPIN D6     // Digital pin connected to the DHT sensor
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
DHT dht(DHTPIN, DHTTYPE);

// Sensor variable declarations
uint8_t GW_temperature, GW_humidity, GW_rain_per; 

// Variables for collecting Sensor data and parameters
// prfix SN is for data received from (WSN) Sensor Node
uint8_t SN_m1, SN_m2, SN_humi, SN_temp, SN_rain_per;
float SN_disp;
bool SN_vib, SN_stat;
int RSSI, SNR;

// Function for parsing the incomming data String
String getValue(String data, char separator, int index)
{
    int found = 0;
    int strIndex[] = { 0, -1 };
    int maxIndex = data.length() - 1;
 
    for (int i = 0; i <= maxIndex && found <= index; i++) {
        if (data.charAt(i) == separator || i == maxIndex) {
            found++;
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
        }
    }
    return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}

// Function for Hex to ASCII conversion
byte aNibble(char in) {
  if (in >= '0' && in <= '9') {
    return in - '0';
  } else if (in >= 'a' && in <= 'f') {
    return in - 'a' + 10;
  } else if (in >= 'A' && in <= 'F') {
    return in - 'A' + 10;
  }
  return 0;
}

// Function for Hex to ASCII conversion
char * unHex(const char* input, char* target, size_t len) {
  if (target != nullptr && len) {
    size_t inLen = strlen(input);
    if (inLen & 1) {
      Serial.println(F("unhex: malformed input"));
    }
    size_t chars = inLen / 2;
    if (chars >= len) {
      Serial.println(F("unhex: target buffer too small"));
      chars = len - 1;
    }
    for (size_t i = 0; i < chars; i++) {
      target[i] = aNibble(*input++);
      target[i] <<= 4;
      target[i] |= aNibble(*input++);
    }
    target[chars] = 0;
  } else {
    Serial.println(F("unhex: no target buffer"));
  }
  return target;
}

// Function to check response for AT commands
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;
}

// Function for parsing the incoming LoRa data
static int recv_parse(void)
{
    char ch;
    int index = 0;
    memset(recv_buf, 0, sizeof(recv_buf));
    while (e5.available() > 0)
    {
        ch = e5.read();
        recv_buf[index++] = ch;
        Serial.print((char)ch);
        delay(2);
    }
 
    if (index)
    {
        char *p_start = NULL;
        char data[128] = {
            0,
        };
        int rss = 0;
        int snr = 0;

        p_start = strstr(recv_buf, "+TEST: RX \"47572C");
        if (p_start)
        {
            p_start = strstr(recv_buf, "47572C");
            if (p_start && (1 == sscanf(p_start, "47572C%s", data)))
            {
              data[70] = 0;
              //Serial.println(data);
              char output[128];
              char* text = unHex(data, output, sizeof(output));
              //Serial.println(text);
              SN_m1 = (getValue(text, ',', 0)).toInt();
              SN_m2 = (getValue(text, ',', 1)).toInt();
              SN_rain_per = (getValue(text, ',', 2)).toInt();
              SN_humi = (getValue(text, ',', 3)).toInt();
              SN_temp = (getValue(text, ',', 4)).toInt();
              SN_disp = (getValue(text, ',', 5)).toFloat();
              SN_vib = (getValue(text, ',', 6)).toInt();
              SN_stat = (getValue(text, ',', 7)).toInt();
              Serial.println("\r\n");

            }
            p_start = strstr(recv_buf, "RSSI:");
            if (p_start && (1 == sscanf(p_start, "RSSI:%d,", &rss))){
              RSSI = rss;
            }

            p_start = strstr(recv_buf, "SNR:");
            if (p_start && (1 == sscanf(p_start, "SNR:%d", &snr))){
              SNR = snr;
            }
            return 1;
        }
    }
    return 0;
}

// Function for Receiving incomming LoRa Packets
static int node_recv(uint32_t timeout_ms)
{
    at_send_check_response("+TEST: RXLRPKT", 1500, "AT+TEST=RXLRPKT\r\n");
    int startMillis = millis();
    do
    {
        if (recv_parse())
        {
            return 1;
        }
    } while (millis() - startMillis < timeout_ms);
    return 0;
}

// Function for LoRa packet preparation and sending
static int LoRa_send()
{
  String sensorData = "";
  char cmd[256] = "";
  int ret = 0;
  char data[128] = "";

  sensorData = sensorData + String(SN_m1) + "," + String(SN_m2) + "," + String(SN_rain_per) + "," + String(SN_humi) 
                + "," + String(SN_temp) + "," + String(SN_disp) + "," + String(SN_vib) + "," + String(SN_stat) + "," 
                + String(GW_rain_per) + "," + String(GW_humidity) + "," + String(GW_temperature);

  strncpy(data,sensorData.c_str(),sizeof(data));
  data[sizeof(data) -1] = 0;
  
  sprintf(cmd, "AT+TEST=TXLRSTR,\"EN,%s\"\r\n", data);
  //Serial.print("Printing cmd String : ");
  //Serial.println(cmd);

  ret = at_send_check_response("TX DONE", 6000, cmd);
    if (ret == 1)
    {
      Serial.println("");
      Serial.print("Sent successfully!\r\n");
    }
    else
    {
      Serial.println("");
      Serial.print("Send failed!\r\n");
    }
  return ret;
}

// Function for First receive data from WSN then Relay(Send) to End Node via LoRa
static void node_recv_then_send(uint32_t timeout)
{
    int ret = 0;
    ret = node_recv(timeout);
    delay(100);
    if (!ret)
    {
        Serial.print("\r\n");
        return;
    }
    LoRa_send();
    Serial.print("\r\n");
}

// Function to configure Wio E5 Mini in Test Mode - Check AT commands Specification Guide
// for more details about these command sequences  
void configLoRaModule(){
  //Configure Wio E5 Mini Board in Test Mode
  Serial.println("Configuring Wio E5 Mini Board ...");
  delay(250);
  if (at_send_check_response("+AT: OK", 100, "AT\r\n"))
  {
    is_exist = true;
    at_send_check_response("+MODE: TEST", 1500, "AT+MODE=TEST\r\n");
    at_send_check_response("+TEST: RFCFG", 1500, "AT+TEST=RFCFG,866,SF12,125,12,15,14,ON,OFF,OFF\r\n");
    delay(500);
  }
  else
  {
    is_exist = false;
    Serial.print("No E5 module found.\r\n");
  }
  delay(500);
}

// Function to Setup Display for Initial Screen
void HomeScreen(){
  tft.fillScreen(TFT_BLACK);                    // Black background
  tft.drawRect(0,0,239,239,TFT_WHITE);       // A 100x100 black rectangle starting from (110, 70)
  tft.fillRect(0,0,240,40,TFT_BROWN);
  tft.setTextColor(TFT_WHITE);          //sets the text colour to black
  tft.setTextSize(2);                   //sets the size of text
  tft.drawString("LoRa Gateway Node", 14,12);
  tft.setTextColor(TFT_YELLOW);          //sets the text colour to black
  tft.drawString("GW node local data", 12,55);

  tft.setTextColor(TFT_WHITE); 
  tft.drawString("Rain = ", 10, 85);
  tft.drawString("%", 215, 85);

  tft.drawString("Humi = ", 10, 110);
  tft.drawString("%", 215, 110);

  tft.drawString("Temp = ", 10, 135);
  tft.drawString("C", 215,135);

  tft.drawString("RSSI = ", 10, 160);
  tft.drawString("dBm", 195, 160);

  tft.drawString("SNR  = ", 10, 185);
  tft.drawString("dB", 205, 185);

  tft.drawString("Stat = ", 10, 210);
}

// Function to Display the sensor Readings
void displayReadings(){

  tft.setTextColor(TFT_CYAN, TFT_BLACK); 

  tft.drawFloat(GW_rain_per, 2, 100, 85);
  
  tft.drawFloat(GW_humidity, 2, 100, 110);

  tft.drawFloat(GW_temperature, 2, 100, 135);

  tft.drawNumber(RSSI, 100, 160);

  tft.drawNumber(SNR, 100, 185);

  if(SN_stat == 1){
    tft.drawString("Alert!", 100, 210);
  } else {
    tft.drawString("OK", 100, 210);
  }
  
}

// Function to Initialise DHT Sensor and Check Readings
void getDHTReadings(){
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  float f = dht.readTemperature(true);
  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t) || isnan(f)) {
    Serial.println(F("Failed to read from DHT sensor!"));
    return;
  }
  GW_temperature = t;
  GW_humidity = h;
  //Serial.print("Humidity: ");
  //Serial.print(h);
  //Serial.println("%");
  //Serial.print("Temperature: ");
  //Serial.print(t);
  //Serial.println("°C ");      
}

// Check Rain Sensor (10K Pot as Tipping bucket Rain Gauge) attached to Analog Pin A0
void getRainReading(){
  GW_rain_per = map(analogRead(rainSensor), noRain, FullRain, 0, 100);
  //Serial.print("Rain Percentage : ");
  //Serial.print(rain_per);
  //Serial.println("%");
}

// Function to Setup the Initializations and Configurations
void setup(void) {
  
  Serial.begin (9600);
  e5.begin(9600);
  tft.begin ();                                 // initialize a ST7789 chip
  tft.setSwapBytes (true);                      // swap the byte order for pushImage() - corrects endianness

  tft.setRotation(0);
  tft.fillScreen (TFT_BLACK);
  tft.pushImage (0,0,240,240,m2elogo);
  delay(3000);
  
  HomeScreen();         // Display Home Screen

  configLoRaModule();   // Configure Wio E5 Mini Dev Board

  dht.begin();          // Init DHT Sensor
}

// Function main Loop
void loop() {
  if (is_exist)
  {
    getDHTReadings();
    getRainReading();
    displayReadings();
    node_recv_then_send(5000);
  }
}

// ---------------------------------- make2explore.com----------------------------------------------------//

Wio-E5-DIY-Project-EndNode

C/C++
// This code is of Wireless (LoRa) **End Node**. Which will be *Displaying* data coming from LoRa WSN deployed at Landslide prone site. Here concerned Authorities can monitor/Analyze the data to take Prevention/Rescue Measures
// ---------------------------------- make2explore.com -------------------------------------------------------//
// Project           - Application of LoRa WSN in Landslide Monitoring/Detection/Prevention Systems
// Created By        - info@make2explore.com
// Last Modified     - 14/02/2023 01:26:00 @admin
// Software          - C/C++, PlatformIO IDE, Visual Studio Code Editor, Libraries
// Hardware          - Arduino Mega 2560, NodeMCU ESP8266, Wio Terminal, Wio E5 Dev Boards, Displays, Sensors         
// Sensors Used      - DHT22, Soil Moisture - Resistive, Capacitive, Vibration, Rain, ADXL345 Accelerometer
// Source Repo       - github.com/make2explore
// -----------------------------------------------------------------------------------------------------------//
// This code is of Wireless (LoRa) **End Node**. Which will be *Displaying* data coming from LoRa WSN 
// deployed at Landslide prone site. Here concerned Authorities can monitor/Analyse 
// the data to take Prevention/Rescue Measures.

// Data from Sensor Node (Rain + Soil Moisture + Humidity + Temperature + Vibrations + Descending Velocity)
// Data from GW Node (Rain+Humidity+Temp) data for comparing actual situations. 

// All data coming from SN and GW Node will be displayed at End Node. Here we can implement more complex
// System using ML and AI for predition of Landslide

// The main Objective of this project is to test the use of LoRa WSN in such "Landslide Monitoring" systems
// in order to achieve good data reception and reliablity in hilly Areas, "Shadow Regions" or 
// Remote Areas with very low coverage of other Wired/Wireless Networks (e.g GSM/WiFi/Ethernet etc.)
// -----------------------------------------------------------------------------------------------------------//

// Include Libraries
#include <Arduino.h>
#include <SoftwareSerial.h>   // Software Serial Library for communicating with Wio E5 LoRa Module
#include "TFT_eSPI.h"         // Include TFT LCD library 
#include "Free_Fonts.h"       // Include free fonts library 
#include "Seeed_FS.h"         // Including SD card library
#include "RawImage.h"         // Including image processing library

// Invoke Display and Create display Instance 
TFT_eSPI tft; //initialize TFT LCD

// Lets define Sofware serial Pins for WIo E5 Module
const byte rxPin = D0;
const byte txPin = D1;

// Set up a new SoftwareSerial object
SoftwareSerial e5 (rxPin, txPin);

// LoRa Data receive buffer
static char recv_buf[512];
static bool is_exist = false;

// Variables for collecting Sensor data and parameters
// prfix SN is for data received from (WSN) Sensor Node
float SN_m1, SN_m2, SN_humi, SN_temp, SN_disp, SN_rain_per, GW_temperature, GW_humidity, GW_rain_per;;
bool SN_vib, SN_stat;

// Readings Update Interval Settings
const unsigned long updateInterval = 5000;
unsigned long previousTime = 0;
unsigned long previousUpdateTime = 0;

// Function for parsing the incomming data String
String getValue(String data, char separator, int index)
{
    int found = 0;
    int strIndex[] = { 0, -1 };
    int maxIndex = data.length() - 1;
 
    for (int i = 0; i <= maxIndex && found <= index; i++) {
        if (data.charAt(i) == separator || i == maxIndex) {
            found++;
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
        }
    }
    return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}

// Function for Hex to ASCII conversion
byte aNibble(char in) {
  if (in >= '0' && in <= '9') {
    return in - '0';
  } else if (in >= 'a' && in <= 'f') {
    return in - 'a' + 10;
  } else if (in >= 'A' && in <= 'F') {
    return in - 'A' + 10;
  }
  return 0;
}

// Function for Hex to ASCII conversion
char * unHex(const char* input, char* target, size_t len) {
  if (target != nullptr && len) {
    size_t inLen = strlen(input);
    if (inLen & 1) {
      Serial.println(F("unhex: malformed input"));
    }
    size_t chars = inLen / 2;
    if (chars >= len) {
      Serial.println(F("unhex: target buffer too small"));
      chars = len - 1;
    }
    for (size_t i = 0; i < chars; i++) {
      target[i] = aNibble(*input++);
      target[i] <<= 4;
      target[i] |= aNibble(*input++);
    }
    target[chars] = 0;
  } else {
    Serial.println(F("unhex: no target buffer"));
  }
  return target;
}

// Function to check response for AT commands
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;
}

// Function for parsing the incoming LoRa data
static int recv_parse(void)
{
    char ch;
    int index = 0;
    memset(recv_buf, 0, sizeof(recv_buf));
    while (e5.available() > 0)
    {
        ch = e5.read();
        recv_buf[index++] = ch;
        Serial.print((char)ch);
        delay(2);
    }
 
    if (index)
    {
        char *p_start = NULL;
        char data[128] = {
            0,
        };

        p_start = strstr(recv_buf, "+TEST: RX \"454E2C");
        if (p_start)
        {
            p_start = strstr(recv_buf, "454E2C");
            if (p_start && (1 == sscanf(p_start, "454E2C%s", data)))
            {
                data[60] = 0;
                //Serial.println(data);
                //Serial.println("Hello");
                char output[128];
                char* text = unHex(data, output, sizeof(output));
                SN_m1 = (getValue(text, ',', 0)).toFloat();
                SN_m2 = (getValue(text, ',', 1)).toFloat();
                SN_rain_per = (getValue(text, ',', 2)).toFloat();
                SN_humi = (getValue(text, ',', 3)).toFloat();
                SN_temp = (getValue(text, ',', 4)).toFloat();
                SN_disp = (getValue(text, ',', 5)).toFloat();
                SN_vib = (getValue(text, ',', 6)).toInt();
                SN_stat = (getValue(text, ',', 7)).toInt();

                GW_rain_per = (getValue(text, ',', 8)).toFloat();  
                GW_humidity = (getValue(text, ',', 9)).toFloat();  
                GW_temperature = (getValue(text, ',', 10)).toFloat();
                //Serial.println(GW_temperature);              
                Serial.print("\r\n");
            }
            return 1;
        }
    }
    return 0;
}


// Function for Receiving incomming LoRa Packets
static int node_recv(uint32_t timeout_ms)
{
    at_send_check_response("+TEST: RXLRPKT", 1500, "AT+TEST=RXLRPKT\r\n");
    int startMillis = millis();
    do
    {
        if (recv_parse())
        {
            return 1;
        }
    } while (millis() - startMillis < timeout_ms);
    return 0;
}


// Function to configure Wio E5 Module in Test Mode - Check AT commands Specification Guide
// for more details about these command sequences  
void configLoRaModule(){
  //Configure Wio E5 Module in Test Mode
  Serial.println("Configuring Wio E5 Module ...");
  delay(250);
  if (at_send_check_response("+AT: OK", 100, "AT\r\n"))
  {
    is_exist = true;
    at_send_check_response("+MODE: TEST", 1500, "AT+MODE=TEST\r\n");
    at_send_check_response("+TEST: RFCFG", 1500, "AT+TEST=RFCFG,866,SF12,125,12,15,14,ON,OFF,OFF\r\n");
    delay(500);
  }
  else
  {
    is_exist = false;
    Serial.print("No E5 module found.\r\n");
  }
  delay(500);
}


// Function to Display the Sensor Readings (WSN)
void DisplayReadings1(){

    drawImage<uint8_t>("m2e-SN.bmp", 0, 0); //Display this 8-bit image in sd card from (0, 0)
    tft.setTextColor(TFT_WHITE);
    tft.setFreeFont(&FreeSerifBold9pt7b); //set font type 

    tft.drawFloat(SN_m1,2,93,62); //draw text string

    tft.drawFloat(SN_m2,2,249,60); //draw text string

    tft.drawFloat(SN_rain_per,2,93,110); //draw text string

    tft.drawFloat(SN_disp,2,244,111); //draw text string

    tft.drawFloat(SN_temp,2,240,160); //draw text string

    tft.drawFloat(SN_humi,2,86,209); //draw text string

    if(SN_vib == 1){
        tft.setTextColor(TFT_RED);
        tft.drawString("Detected",87,162);
    } else {
        tft.setTextColor(TFT_WHITE);
        tft.drawString("Not Det",87,162);
    }
    
    if(SN_stat == 1){
        tft.setTextColor(TFT_RED);
        tft.drawString("Alert !",250,209);
    } else {
        tft.setTextColor(TFT_GREEN);
        tft.drawString("OK",250,209);
    }

}

// Function to Display the Sensor Readings (GW Node)
void DisplayReadings2(){

  drawImage<uint8_t>("m2e-GW.bmp", 0, 0); //Display this 8-bit image in sd card from (0, 0)
  tft.setTextColor(TFT_WHITE);
  tft.setFreeFont(&FreeSerifBold9pt7b); //set font type 
  tft.drawFloat(GW_rain_per,2,197,70); //draw text string

  tft.drawFloat(GW_temperature,2,82,126); //draw text string

  tft.drawFloat(GW_humidity,2,237,126); //draw text string
  
  tft.setFreeFont(&FreeSerifBold12pt7b); //set font type 
  if(SN_stat == 1){
    tft.setTextColor(TFT_RED);
    tft.drawString("Alert !",200,195);
  } else {
    tft.setTextColor(TFT_GREEN);
    tft.drawString("OK",200,195);
  }
  
}

// Function to Setup Display for Initial Screen - make2explore logo
void HomeScreen(){
  drawImage<uint16_t>("WioTerminal-screen-m2e.bmp", 0, 0); //Display this 8-bit image in sd card from (0, 0)
}

// Function to Setup Display for First (WSN Data) Screen
void FirstScreen(){
  drawImage<uint8_t>("m2e-SN.bmp", 0, 0); //Display this 8-bit image in sd card from (0, 0)
}

// Function to Setup Display for First (GW Node Data) Screen
void secondScreen(){
  drawImage<uint8_t>("m2e-GW.bmp", 0, 0); //Display this 8-bit image in sd card from (0, 0)
}

// Function to Setup the Initializations and Configurations
void setup() {
    Serial.begin (9600);
    e5.begin(9600);
    //Initialise SD card
    if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
        while (1);
    }

    pinMode(WIO_5S_PRESS, INPUT_PULLUP);

    tft.begin(); //start TFT LCD 
    tft.setRotation(1); //set screen rotation 

    configLoRaModule();

    HomeScreen();
    delay(3000);
    FirstScreen();
    
}

// Function main Loop
void loop() {
    if (is_exist)
    {
        node_recv(6000);
        DisplayReadings1();
    }

    if (digitalRead(WIO_5S_PRESS) == LOW) {
        DisplayReadings2();
        delay(2000);
    }
}
// ---------------------------------- make2explore.com ----------------------------------------------------//

Wireless (LoRa) Sensor Node

Credits

make2explore
8 projects • 15 followers
Engineering Consultant, Tech Content Creator - #Electronics #EmbeddedSystems #IoT #AI #ML

Comments