Precision Irrigation Optimization and Efficient Water Utilization

by svvetal14 in Circuits > Electronics

1195 Views, 12 Favorites, 0 Comments

Precision Irrigation Optimization and Efficient Water Utilization

cover.jpeg
1.jpeg

Traditional agriculture practices lack real-time monitoring and efficient operation on fields. This leads to inefficiencies and low production levels. The purpose of this project is to design a solution that addresses this issue by utilizing internet of Things (IoT) technology and low-cost communication protocols. The proposed solution should integrate real-time monitoring of agriculture field data and weather conditions using Wi-Fi, cellular access, and long-distance communication protocols. The goal is to improve operational efficiency and production levels through various tasks such as irrigation practices, automated pest management, livestock monitoring, vehicle tracking, field, animal and bird tracks, and inventory monitoring.

The solution will be based on loT technology and allow all these necessary tasks to be performed remotely via cellular communication protocols. Moreover, ML (Machine Learning) based techniques will be incorporated with image processing to maintain the proportion of three plant nutrients in order: nitrogen (N). phosphorus (P) and potassium (K) in the field. The objective is to design a sustainable loT solution that makes traditional agriculture practices more accessible and more efficient.

  • To optimize the Irrigation facilities in the Agriculture sector which is predominantly used in the farming sites for effective management of irrigation from time to time. Making use of IoT technology in the pre-existing irrigation setup and to collect data and send it as a feedback to the user mobile application.
  • Using sensory feedback systems such as Soil moisture sensor, Humidity and Temperature sensor etc. in order to receive data and to perform necessary actuations accordingly.
  • Also to develop an OpenCV based ML model to detect the Nitrogen, Phosphorus and Potassium contents from soil and to predict the required irrigation need based on the data gained.
  • The overall proposed system includes two ESP32 as the core microcontrollers, of which one is placed on the farming site and the other at the farmer’s house. The major reason to follow this approach is due to the lack of Internet connectivity at the farming sites. As a result of which both the devices would be communicating with each other using a low power and long distance IoT communication protocol which is the LoRaWAN. Thus the farm site ESP32 performing actions of data collection and sends the data over the LoRaWAN pipeline for a distance of about 15 kms. This data is then received by the ESP32 at home connected to the internet, which then pushes the data over to the cloud and to the user application. 
  • Thus User can take actions related to irrigation based on the data received and thus the overall concept of irrigation could be optimized to a greater extent.

Supplies

HARDWARE REQUIREMENTS:

  1. ESP32 Micro-controller (WROOM) x 2
  2. SX1278 LoRa Module (Ra- 02 433 MHz) x 2
  3. DHT-11 Temparature Sensor x 1
  4. Soil Mositure Sensor x 1
  5. Relay Module (5V) x 1
  6. 12V DC Water Pump x 1
  7. LED (for demo purpose instead of water pump) x 1
  8. BreadBoard x 1
  9. Jumper Wires x 15


SOFTWARE REQUIREMENTS:

  1. Arduino IDE (ESP32 Programing)
  2. Fritzing (Circuit Designing)
  3. HTML
  4. CSS
  5. Javascript
  6. Bootstrap
  7. Flask

Idea and Approach

idea.png
  • Train ML model using OpenCV for checking  NPK-soil content, Soil moisture Data using sensors, Local Weather Data by using some Google API’s available in order to push this data over to a Flask Website.
  • The models address the sensitivity of the characteristics of a farm environment and are to be implemented by robust hardware withstanding harsh weather conditions.
  • The sensors controlled by the ESP32 establish communication to the ESP32 at the service location eventually reaching the farmer on a locally hosted website.
  • This would make operation possible within a range of 15 km of the farm.
  • This data then at the user end would be worked upon by another ESP32,eventually stored in a database for future use. 
  • The website developed on Flask and Flask as a web server will be easily navigable, with multiple language support. 
  • Additional Applications such as Inventory management, pest control, irrigation could also be directly dealt with by the data received as per the results over the website. Soil content though need to be righted by the farmer upon a notification received on the Website.


Proposed Methodology

  • To optimize the Irrigation facilities in the Agriculture sector which is predominantly used in the farming sites for effective management of irrigation from time to time. Making use of IoT technology in the pre-existing irrigation setup and to collect data and send it as a feedback to the user web application.
  • Using sensory feedback systems such as Soil moisture sensor, Humidity and Temperature sensor etc. in order to receive data and to perform necessary actuations accordingly.
  • Also to develop an OpenCV based ML model to detect the Nitrogen, Phosphorus and Potassium contents from soil and to predict the required irrigation need based on the data gained.
  • The overall proposed system includes two ESP32's as the core microcontrollers, of which one is placed on the farming site and the other at the farmer’s house. The major reason to follow this approach is due to the lack of Internet connectivity at the farming sites. As a result of which both the devices would be communicating with each other using a low power and long distance IoT communication protocol which is the LoRaWAN. Thus the farm site raspi performing actions of data collection and sends the data over the LoRaWAN pipeline for a distance of about 15 kms. This data is then received by the ESP32 at home connected to the internet, which then pushes the data over to the website. 
  • Thus User can take actions related to irrigation based on the data received and thus the overall concept of irrigation could be optimized to a greater extent.


Use Case and Description

  • User : If User is Logged In, redirects to the Application HomePage or Dashboard. 
  • User Navigation : User will be able to monitor all the aspects, choosing and then evaluating the report and real time data.
  • Sensors: The sensors during installation would have to be set appropriately. 
  • Database: Store data and retrieve it for evaluation on future actions. 
  • Website Application: Completely handle the entire system providing easy control of the devices involved.


Unique Selling Points

  • Low Power LoRaWAN IOT Protocol for long distance communication.
  • Database storage.
  • Supports Un-interrupted Data Transfer upto a range of 15 kms.
  • No Internet or Wifi required at the system deployment site.
  • Realtime data upload with minimum delay.
  • On site ML data processing on ESP32 in order to avoid data congestion and lower system performance.
  • User Website Application for actuation the essential components on the farm based on the received data.
  • Compatible with most of the Smartphones.


System Architecture

Blank diagram.png

The System Architecture is majorly divided into two main categories :

  1. The Farm Site Deployment
  2. The Farmer Home Setup

FARM SITE :

  1. The Farm consists of a network of required sensors required to monitor the necessary parameters related to soil moisture and temperature.
  2. The sensors are connected to a micro-controller on the farmer site with a ESP32. All of the data collected and sensed by the sensors is first observed on this ESP32.
  3. The ESP32 then sends the data to the Lo-Ra sender module (RA-02) via using the SPI communication protocol.
  4. The Lo-Ra then transmits this data further to the receiver module tuned at the same frequency (433 MHz) and ready for data reception.
  5. The ESP32 is also connected to a sprinkler type of mechanism on the feild so as to regulate and control the Motor pump based on the actuation commands it receives by analysing the received data.

FARMER HOME :

  1. The data transmitted from the farm site is received by the Lo-Ra receiver setup at the farmer house, without any significant data loss upto a distance of about 15km.
  2. The receiver Lo-Ra module then transfers this data to the ESP32 connected at the farmer's home which has a internet connected using the Wi-Fi network available.
  3. The home ESP32 acts a Web server to collect and push the incoming data to a locally hosted flask website and also uses AJAX for realtime data upload and realtime display of change in data.
  4. It also stores and helps in data monitoring.

Electronics Setup

elec_full.png

This shows the overall electronics setup required to make the project running.

The left side system consists of the components to be placed at the farmer's site a Lo-Ra module which establishes low cost and long distance communication over a channel of 433 MHz.

The right side system is deployed at the farmer's home which is responsible to receive the data and to push it to the ESP32 hosted web server locally and to formulate the realtime data display on the website.

Now let's break the overall circuit into its individual components so that we get to know the connection diagram step by step...!!!

ESP32 Pinout

Screenshot from 2024-08-04 13-45-16.png

Note : Refer to this diagram while making the connections for EPS32

Lo-Ra Module Pinout

LORA.jpeg

Note : Refer to this diagram while making the connections for Lo-Ra Module

ESP32 - LoRa Module Interfacing (SENDER)

lora_receiver.png
  1. This shows the electronics setup for the interfacing between the Lo-Ra sender with the ESP32.
  2. The connections are to be made as shown below to ensure smooth functioning of the connected circuit.

Connections :

ESP32 --------------------------- Lo-Ra

GND ------------------------- GND

3.3V -------------------------- VCC

GPIO5 -------------------------- NSS

GPIO23 -------------------------- MOSI

GPIO19 -------------------------- MISO

GPIO18 -------------------------- SCK

GPIO14 -------------------------- RST

GPIO2 -------------------------- DIO0

ESP32 - LoRa Module Interfacing (RECEIVER)

lora_receiver.png
  1. This shows the electronics setup for the interfacing between the Lo-Ra receiver with the ESP32.
  2. The connections are to be made as shown below to ensure smooth functioning of the connected circuit.

Connections :

ESP32 --------------------------- Lo-Ra

GND ------------------------- GND

3.3V -------------------------- VCC

GPIO5 -------------------------- NSS

GPIO23 -------------------------- MOSI

GPIO19 -------------------------- MISO

GPIO18 -------------------------- SCK

GPIO14 -------------------------- RST

GPIO2 -------------------------- DIO0

DHT11 - Temparature Sensor Interfacing

humidity.png
  1. The above shows the connection diagram for interfacing DHT11 temparature sensor to interface with the ESP32 sender circuitry.
  2. The connections are to be made as shown below to ensure smooth functioning of the connected circuit.

Connections :

ESP32 ----------------------- DHT11

5V ------------------------- 5V

GND ------------------------- GND

GPIO22 ------------------------- Signal

Soil Moisture Sensor Interfacing

moisture.png
  1. The above shows the connection diagram for interfacing soil moisture sensor to interface with the ESP32 sender circuitry.
  2. The connections are to be made as shown below to ensure smooth functioning of the connected circuit.

Connections :

ESP32 ----------------------- Soil Moisture Sensor

5V ------------------------- 5V

GND ------------------------- GND

GPIO36 ------------------------- Signal

Relay and Water Pump Interfacing

realy.png
  1. The above shows the connection diagram for interfacing the water pump or DC motor to ensure actuation action in order to activate the sprinkler mechanism placed at the feild site to interface with the ESP32 sender circuitry.
  2. A relay is added to perform the switching action.
  3. The connections are to be made as shown below to ensure smooth functioning of the connected circuit.

Connections :

ESP32 ----------------------- Water Pump (DC Motor)

5V ------------------------- 5V

GND ------------------------- GND

GPIO26 ------------------------- Signal

LED Interfacing

led.png
  1. The above shows the connection diagram for interfacing LED to interface with the ESP32 sender circuitry and to act as a status indication LED.
  2. The connections are to be made as shown below to ensure smooth functioning of the connected circuit.

Connections :

ESP32 ----------------------- LED

GPIO22 ------------------------- LED (+)

GND ------------------------- GND

Powering Up the Circuit

electronics.jpeg
  1. After the correct setup of the overall electronics circuitry we are ready to finally power up the whole system.
  2. You can use a 5V battery to power each of the two category components i.e, at the farm site and the home site.
  3. I have used a 5V adapter of B-Type Cable to power both the ESP's through their USB ports itself.

Our Electronics setup is ready now.....

Let's now get to the Programming side to make it fully functional!!!

Let's Start to Code

code.jpg

The coding section is too divided into 4 major aspects:

  1. The sender.
  2. The receiver.
  3. Realtime website display.
  4. ML based model used for water Pump actuation.

So, let's get our hands on to write some robust code...!!!

ESP32 - LoRa Sender Code

This code is designed to read data from a DHT11 temperature and humidity sensor and a soil moisture sensor, then transmit the readings wirelessly using a LoRa transceiver. Here’s a summary of what the code does:

  1. Include Libraries: It includes the necessary libraries for SPI, LoRa communication, and DHT sensor.
  2. Define Pins: It defines the pins used for the DHT11 sensor, soil moisture sensor, and LoRa transceiver module.
  3. Setup Function:
  • Initializes the serial monitor for debugging with a baud rate of 115200.
  • Initializes the DHT11 sensor.
  • Sets up the LoRa transceiver with specified pins for SS, RST, and DIO0.
  • Attempts to begin LoRa communication at a frequency of 433 MHz (for Asia).
  • Sets a sync word for LoRa communication to ensure messages are not confused with those from other LoRa devices.
  • Prints a message to the serial monitor indicating successful initialization.
  1. Loop Function:
  • Reads an analog value from the soil moisture sensor, maps it to a specific range, and adjusts the value.
  • Reads temperature and humidity values from the DHT11 sensor.
  • Prints the soil moisture value, temperature, and humidity to the serial monitor.
  • Constructs a LoRa packet containing the soil moisture value, temperature, and humidity, then sends it.
  • Waits for 1 second before repeating the loop.

In summary, the code continuously reads data from sensors and transmits the data over LoRa at regular intervals.

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

#define DHT11PIN 22
#define AOUT_PIN 36

DHT dht(DHT11PIN, DHT11);

//define the pins used by the transceiver module
#define ss 5
#define rst 15
#define dio0 2

void setup() {
  //initialize Serial Monitor
  Serial.begin(115200);
  dht.begin();
  while (!Serial);
  Serial.println("LoRa Sender");

  //setup LoRa transceiver module
  LoRa.setPins(ss, rst, dio0);
  
  //replace the LoRa.begin(---E-) argument with your location's frequency 
  //433E6 for Asia
  //866E6 for Europe
  //915E6 for North America
  while (!LoRa.begin(433E6)) {
    Serial.println(".");
    delay(500);
  }
   // Change sync word (0xF3) to match the receiver
  // The sync word assures you don't get LoRa messages from other LoRa transceivers
  // ranges from 0-0xFF
  LoRa.setSyncWord(0xF3);
  Serial.println("LoRa Initializing OK!");
}

void loop() {
  int value = analogRead(AOUT_PIN); // read the analog value from sensor
  value = map(value, 0, 4095, 0, 999) ;
  value = (1300- value) ;

  Serial.print("Moisture value: ");
  Serial.println(value);

  float humi = dht.readHumidity();
  float temp = dht.readTemperature();
  Serial.print("Temperature: ");
  Serial.print(temp);
  Serial.print("ºC ");
  Serial.print("Humidity: ");
  Serial.println(humi);

  //Send LoRa packet to receiver
  LoRa.beginPacket();
  LoRa.print(value);
  LoRa.print(",");
  LoRa.print(temp);
  LoRa.print(",");
  LoRa.print(humi);
  LoRa.endPacket();

  delay(1000);
}

Downloads

ESP32 - LoRa Receiver Code

This code is designed to receive sensor data from a LoRa transmitter, control an LED based on the pump status, and serve a web interface to display the sensor data and pump status. Here’s a summary of what the code does:

  1. Include Libraries: It includes the necessary libraries for DHT sensor, WiFi, web server, SPI, LoRa communication, and ArduinoJson.
  2. Define Pins and Variables:
  • Defines the pins used for the LoRa transceiver module, LED, and variables to store sensor data and pump status.
  • Sets the WiFi credentials.
  1. Setup Function:
  • Initializes the serial monitor with a baud rate of 115200.
  • Sets the LED pin as an output.
  • Initializes the LoRa transceiver module and sets the sync word.
  • Connects to the specified WiFi network and prints the local IP address to the serial monitor.
  • Sets up the web server routes:
  • Root route ("/"): Serves an HTML page with embedded JavaScript to periodically request and update sensor data.
  • Data route ("/data"): Responds with a JSON string containing the current sensor data and pump status.
  • Pump status route ("/pump_status"): Accepts a POST request with a JSON payload to update the pump status.
  1. Loop Function:
  • Handles incoming client requests for the web server.
  • Controls the LED based on the pump status (turns it on if pumpStatus is 1, otherwise turns it off).
  • Attempts to parse incoming LoRa packets. If a packet is received, it reads the data string and splits it into moisture, temperature, and humidity values. These values are then converted to float and stored in their respective variables.

In summary, the code receives sensor data via LoRa, updates the pump status and LED based on web server interactions, and serves a dynamic web page to display the current sensor data and pump status.

#include "DHT.h"
#include <WiFi.h>
#include <WebServer.h>
#include <SPI.h>
#include <LoRa.h>
#include <ArduinoJson.h>

//define the pins used by the transceiver module
#define ss 5
#define rst 15
#define dio0 2
#define led 22

const char* ssid = "{YOUR_WIFI_SSID}";
const char* password = "YOUR_WIFI_PASSWORD";

float moisture = 0 ;
float humidity = 0 ;
float temperature = 0 ;
int pumpStatus = 0; // Variable to store pump status

WebServer server(80);

void setup() {
  Serial.begin(115200);
  pinMode(led, OUTPUT) ;

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

  //setup LoRa transceiver module
  LoRa.setPins(ss, rst, dio0);
  
  //replace the LoRa.begin(---E-) argument with your location's frequency 
  //433E6 for Asia
  //866E6 for Europe
  //915E6 for North America
  while (!LoRa.begin(433E6)) {
    Serial.println(".");
    delay(500);
  }
   // Change sync word (0xF3) to match the receiver
  // The sync word assures you don't get LoRa messages from other LoRa transceivers
  // ranges from 0-0xFF
  LoRa.setSyncWord(0xF3);
  Serial.println("LoRa Initializing OK!");

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");
  Serial.println(WiFi.localIP());

  // Set up the server routes
  server.on("/", HTTP_GET, [](){
    String htmlResponse = "<!DOCTYPE html><html><head><script>";
    htmlResponse += "function updateData() {";
    htmlResponse += "var xhttp = new XMLHttpRequest();";
    htmlResponse += "xhttp.onreadystatechange = function() {";
    htmlResponse += "if (this.readyState == 4 && this.status == 200) {";
    htmlResponse += "var data = JSON.parse(this.responseText);";
    htmlResponse += "document.getElementById('humidity').innerHTML = data.humidity;";
    htmlResponse += "document.getElementById('moisture').innerHTML = data.moisture;";
    htmlResponse += "document.getElementById('temperature').innerHTML = data.temperature;";
    htmlResponse += "document.getElementById('pumpStatus').innerHTML = data.pump_status;";
    htmlResponse += "}";
    htmlResponse += "};";
    htmlResponse += "xhttp.open('GET', '/data', true);";
    htmlResponse += "xhttp.send();";
    htmlResponse += "}";
    htmlResponse += "setInterval(updateData, 200);";
    htmlResponse += "</script></head><body>";
    htmlResponse += "<h1>Current Humidity: <span id='humidity'></span>%</h1>";
    htmlResponse += "<h1>Current Moisture: <span id='moisture'></span>%</h1>";
    htmlResponse += "<h1>Current Temperature: <span id='temperature'></span>°C</h1>";
    htmlResponse += "<h1>Pump Status: <span id='pumpStatus'></span></h1>";
    htmlResponse += "</body></html>";
    server.send(200, "text/html", htmlResponse);
  });

  server.on("/data", HTTP_GET, [](){
    String jsonData = "{\"humidity\":" + String(humidity) + ",";
    jsonData += "\"moisture\":" + String(moisture) + ",";
    jsonData += "\"temperature\":" + String(temperature) + ",";
    jsonData += "\"pump_status\":" + String(pumpStatus) + "}";
    server.send(200, "application/json", jsonData);
  });
  
  server.on("/pump_status", HTTP_POST, [](){
    String postData = server.arg("plain"); // Get the POST data
    StaticJsonDocument<200> doc; // Create a JSON document
    deserializeJson(doc, postData); // Deserialize the JSON
    pumpStatus = doc["pump_status"]; // Extract pump status from JSON
    server.send(200); // Respond to the POST request
  });

  server.begin();
  Serial.println("HTTP server started");
}

void loop() {
  server.handleClient();

  if(pumpStatus == 1){
    digitalWrite(led, HIGH) ;
  }
  else{
    digitalWrite(led, LOW) ;
  }

  // try to parse packet
  int packetSize = LoRa.parsePacket();
  if (packetSize) {
    // read packet
    while (LoRa.available()) {
      String data = LoRa.readString();
  
      // Parse the string using comma as delimiter
      int commaIndex1 = data.indexOf(',');
      int commaIndex2 = data.indexOf(',', commaIndex1 + 1);
  
      // Extract substrings and convert them to respective data types
      String substring1 = data.substring(0, commaIndex1);
      String substring2 = data.substring(commaIndex1 + 1, commaIndex2);
      String substring3 = data.substring(commaIndex2 + 1);
  
      moisture = substring1.toFloat(); // Convert substring1 to float
      temperature = substring2.toFloat(); // Convert substring2 to float
      humidity = substring3.toFloat(); // Convert substring3 to float
      
      Serial.println(moisture) ;
      Serial.println(temperature) ;
      Serial.println(humidity) ;
    }
  }
  delay(10);
}

Downloads

ML Pipeline for Prediction

The main aim of this is to use a ML based approach to turn on and turn off the water pump based upon the realtime sensor data received from the farm site. It provides us with a ML model which could be then converted to onnx format and be used to perform the necessary actuation.

This Python code uses the pandas library to handle a dataset, splits the data into training and testing sets, and employs the Gaussian Naive Bayes classifier from scikit-learn to predict pump operation based on soil moisture, air temperature, and humidity. 

This Python code uses the pandas library to handle a dataset, splits the data into training and testing sets, and employs the Gaussian Naive Bayes classifier from scikit-learn to predict pump operation based on soil moisture, air temperature, and humidity.

  1. Import Libraries:
  • Imports the necessary libraries: pandas for data manipulation, train_test_split from sklearn.model_selection to split the dataset, GaussianNB from sklearn.naive_bayes for the classifier, and accuracy_score from sklearn.metrics to evaluate the model's accuracy.
  1. Load Dataset:
  • Loads the dataset from a CSV file into a pandas DataFrame.
  1. Select Features and Target:
  • Defines the feature columns to be used for prediction and selects them from the DataFrame.
  • Defines the target vector containing the pump data, indicating whether the pump is on or off.
  1. Split Data:
  • Splits the dataset into training and testing sets.
  • Specifies that 30% of the data is used for testing and ensures reproducibility of the split.
  1. Initialize and Train Model:
  • Instantiates a Gaussian Naive Bayes model.
  • Trains the model using the training data.
  1. Make Predictions:
  • Uses the trained model to make predictions on the testing data.
  1. Evaluate Model:
  • Computes the accuracy of the model by comparing the predicted labels with the actual labels.
  • Prints the accuracy to the console, giving an indication of how well the model performs in predicting pump operation based on the given features.

Code to train the Model :

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import accuracy_scor

df = pd.read_csv("Soil Moisture, Air Temperature and humidity, and Water Motor onoff Monitor data.AmritpalKaur.csv")
columns = ["Soil Moisture","Temperature","Air Humidity"]
X = df[columns]
y = df['Pump Data']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
model = GaussianNB()

model.fit(X_train,y_train)

y_pred = model.predict(X_test)

accuracy = accuracy_score(y_test, y_pred)
print("Accuracy:", accuracy)

Find below the attached code file and the training data .csv file

Link to download the .csv dataset : https://data.mendeley.com/datasets/fpdwmm7nrb/1

RealTime Website Data Display

WhatsApp Image 2024-07-29 at 7.02.14 AM.jpeg
WhatsApp Image 2024-07-29 at 7.02.14 AM (1).jpeg

This Python code creates a web application using Flask to predict the pump status and soil moisture based on sensor data, utilizing ONNX models for inference.

Import Libraries:

  • Imports the necessary libraries: requests for making HTTP requests, onnxruntime for running ONNX models, numpy for numerical operations, and Flask for creating the web application.

Initialize Flask App:

  • Initializes a Flask web application.

Define Prediction Functions:

  • predict_pump_status(soil_moisture, temperature, humidity): Loads the ONNX model for predicting pump status, prepares the input features, runs the model, and returns the prediction.
  • predict_soil_moisture(temperature, humidity): Loads the ONNX model for predicting soil moisture, prepares the input features, runs the model, and returns the prediction.

Define ESP32 Server IP:

  • Sets the IP address of the ESP32 server.

Define Timer Calculation Function:

  • get_timer(data): Calculates the timer for the pump based on sensor data. If the pump status is predicted to be on (1), it predicts the required soil moisture, calculates the deficit, and determines the timer based on the rate of restoration. Otherwise, it returns '0' as the timer and the pump status.

Define Flask Routes:

  • /: Renders the main index page (new_index.html).
  • /sensor_data: Fetches sensor data from the ESP32 server, calculates the timer and pump status using the prediction functions, sends the pump status back to the ESP32 server, and returns the sensor data as a JSON response. If there is an error fetching the data, it returns default values with 'N/A'.

Run Flask App:

  • Runs the Flask application on host 0.0.0.0 with debugging enabled.

This code compiles all the systems together including the data fetching from the ESP32 local server and to display it on a locally hosted flask based website and integrates the ML model to make accurate predictions. In order to run this code you should have the following directory structure :

.
├── app.py
├── models
│   ├── model1.onnx
│   └── model2.onnx
├── static
│   └── style.css
└── templates
    └── new_index.html

After this setup just run the app.py file and your good to go...!!!

Code for index.html :

<!DOCTYPE html>
<html>


<head>
    <!-- jQuery library -->
    <script src="//code.jquery.com/jquery-3.1.1.slim.min.js"
        integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n"
        crossorigin="anonymous"></script>


    <!-- Tether -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"
        integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb"
        crossorigin="anonymous"></script>


    <!-- Bootstrap 4 Alpha JS -->
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"
        integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn"
        crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <link href="https://fonts.googleapis.com/css?family=Merriweather" rel="stylesheet">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="main.css">


    <link href="https://fonts.googleapis.com/css?family=Open+Sans|Roboto" rel="stylesheet">


    <style>
        /* Style for image containers */
        .imageContainer {
            padding: 1%;
            display: inline-block;
        }


        .imageContainer img {
            align: center;
            width: 280px;
            height: auto;


            margin-bottom: 3px;
            /* Add some space between images */


        }


        body {
            padding-top: 70px;
            font-family: 'Open Sans', sans-serif;
        }


        #about {
            padding-top: 25px;
        }


        #about p {
            padding-left: 2%;
        }


        #services {
            padding-top: 10px;
        }


        #plantinfo {
            padding-top: 10px;
        }


        #contact {
            padding-top: 10px;
            background-color: #333;
            color: #fff;
        }


        .navbar-nav li {
            text-align: left;
            list-style-type: none;
        }


        nav {
            border-bottom: #475257 4px solid;
        }


        h1,
        h3,
        h2,
        h4 {
            padding-top: 3%;
            font-family: 'Merriweather', serif;


        }


        h6 {
            padding-left: 3%;
            padding-top: 1%;
        }


        #small-picture {
            width: 100%;
        }


        .graph-container {
            display: flex;
            justify-content: space-around;
            margin-top: 20px;
        }


        .graph {
            flex: 0 0 calc(50% - 10px);
            /* Adjust width here */
            margin-bottom: 20px;
        }
    </style>
    <title>Agrosync</title>


    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
        $(document).ready(function () {
            $('#getTimerButton').click(function () {
                var soilMoisture = $('#soilMoistureInput').val();
                var temperature = $('#temperatureInput').val();
                var humidity = $('#humidityInput').val();


                $.ajax({
                    url: '/timer',
                    type: 'POST',
                    contentType: 'application/json',
                    data: JSON.stringify({
                        soil_moisture: soilMoisture,
                        temperature: temperature,
                        humidity: humidity
                    }),
                    success: function (response) {
                        $('#timerDisplay').text('Timer: ' + response.timer);
                    },
                    error: function (xhr, status, error) {
                        console.error('Error:', error);
                    }
                });
            });
        });
    </script>
</head>


<body>
    <!--Navigation Bar-->
    <nav class="navbar navbar-toggleable-md navbar-fixed-top navbar-light bg-faded">
        <div class="container-fluid d-flex">
            <a class="navbar-brand" href="#" target="_blank">Logo</a>
            <div class="">
                <ul class="navbar-nav pull-right">
                    <!--mt-2 mt-lg-0-->


                    <li class="nav-item"><a class="nav-link" href="#">Home</a></li>
                    <li class="nav-item"><a class="nav-link" href="#plantinfo">Plant Information</a></li>
                    <li class="nav-item"><a class="nav-link" href="#services">Graphs</a></li>
                    <li class="nav-item"><a class="nav-link" href="#webcam">Camera Feed</a></li>
                    <li class="nav-item"><a class="nav-link" href="#about">About</a></li>
                    <li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li>
                </ul>
            </div>
        </div>
    </nav>


    <!--Page-->
    <div class="container-fluid">


        <!--Projects Carousel-->
        <div class="container-fluid">


            <div id="myCarousel" class="carousel slide bg-inverse" data-ride="carousel">
                <ol class="carousel-indicators">
                    <li data-target="#myCarousel" data-slide-to="0" class="active"></li>
                    <li data-target="#myCarousel" data-slide-to="1"></li>
                    <li data-target="#myCarousel" data-slide-to="2"></li>
                </ol>
                <div class="carousel-inner" role="listbox">
                    <div class="carousel-item active">


                        <img class="card-img-top img-fluid" src="https://agro.elzian.com/images/blog-03.png"
                            alt="Photo of sunset">
                        <div class="card-block">


                        </div>
                    </div>
                    <div class="carousel-item">


                        <img class="card-img-top img-fluid" id="small-picture"
                            src="https://img.freepik.com/premium-photo/_72572-3230.jpg?size=626&ext=jpg"
                            alt="Photo of sunset">
                        <div class="card-block">


                        </div>
                    </div>
                    <!-- <div class="carousel-item">


                  <img class="card-img-top img-fluid" src="https://i.ytimg.com/vi/c7oV1T2j5mc/maxresdefault.jpg" alt="Photo of sunset">
                  <div class="card-block">
                     <p class="card-text">Slide 3</p>
                  </div>
               </div> -->
                </div>
                <a class="carousel-control-prev" href="#myCarousel" role="button" data-slide="prev">
                    <span class="carousel-control-prev-icon" aria-hidden="true"></span>
                    <span class="sr-only">Previous</span>
                </a>
                <a class="carousel-control-next" href="#myCarousel" role="button" data-slide="next">
                    <span class="carousel-control-next-icon" aria-hidden="true"></span>
                    <span class="sr-only">Next</span>
                </a>
            </div>


        </div>
        <div id="plantinfo">
            <h2>Sensor Display</h2>
            <h1>Real-time Sensor Data Display</h1>
            <p id="humidity">Loading...</p>
            <p id="moisture">Loading...</p>
            <p id="temperature">Loading...</p>
            <p id="timer">Loading...</p>
        </div>


        <h2>Plant Information</h2>
        <h5>Select a Plant:</h5>
        <select id="plantDropdown">
            <option value="" disabled selected>Select a plant</option>
            <option value="onion">Onion</option>
            <option value="tomato">Tomato</option>
            <option value="wheat">Wheat</option>
            <option value="rice">Rice</option>
        </select>
        <br><br>
        <div id="plantMessage"></div>


        <!--Services-->
        <div id="services">
            <div style="display: flex;">
                <!-- Chart 1: Without Our System -->
                <div style="margin-right: 20px;">
                    <h2>Without Our System</h2>
                    <div>
                        <label for="monthSelect1">Select Month:</label>
                        <select id="monthSelect1" onchange="updateChart1(); updateTotalWater1();">
                            <option value="March">March</option>
                            <option value="April">April</option>
                            <option value="May">May</option>
                            <option value="June">June</option>
                            <option value="July">July</option>
                        </select>
                    </div>
                    <canvas id="waterUsageChart1" width="400" height="300"></canvas>
                    <p>Total Water Utilized: <span id="totalWater1">0</span></p>
                </div>


                <!-- Chart 2: With Our System -->
                <div>
                    <h2>With Our System</h2>
                    <div>
                        <label for="monthSelect2">Select Month:</label>
                        <select id="monthSelect2" onchange="updateChart2(); updateTotalWater2();">
                            <option value="March">March</option>
                            <option value="April">April</option>
                            <option value="May">May</option>
                            <option value="June">June</option>
                            <option value="July">July</option>
                        </select>
                    </div>
                    <canvas id="waterUsageChart2" width="400" height="300"></canvas>
                    <p>Total Water Utilized: <span id="totalWater2">0</span></p>
                </div>
            </div>
            <canvas id="waterUsageChart" width="0" height="0"></canvas>
        </div>
    </div>


    <h2 style="margin-left: 10px;">Yearly graph comparision</h2>
    <div class="graph-container">
        <div class="graph">
            <canvas id="myChart101" width="400" height="400"></canvas>
        </div>
        <div class="graph">
            <canvas id="myChart201" width="400" height="400"></canvas>
        </div>
    </div>


    <script>
        // Sample data for the graphs
        var data1 = {
            labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
            datasets: [{
                label: 'Before system implementation.',
                data: [38, 40, 43, 45, 52, 43, 40, 42, 41, 40, 38, 36],
                borderColor: 'red',
                backgroundColor: 'transparent',
                pointRadius: 5,
                pointHoverRadius: 10,
                lineTension: 0
            }]
        };


        var data2 = {
            labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
            datasets: [{
                label: 'After system deployment',
                data: [20, 21, 21, 21.5, 23, 22.2, 19, 17, 18, 18, 17, 17],
                borderColor: 'green',
                backgroundColor: 'transparent',
                pointRadius: 5,
                pointHoverRadius: 10,
                lineTension: 0
            }]
        };


        // Chart.js configuration
        var options = {
            responsive: false,
            scales: {
                x: {
                    display: true,
                    title: {
                        display: true,
                        text: 'Month'
                    }
                },
                y: {
                    display: true,
                    title: {
                        display: true,
                        text: 'Value'
                    },
                    suggestedMin: 0, // Set minimum value
                    suggestedMax: 60 // Set maximum value
                }
            },
            plugins: {
                legend: {
                    display: true
                },
                tooltip: {
                    enabled: true,
                    mode: 'index',
                    intersect: false
                }
            }
        };


        // Create the graphs
        var ctx1 = document.getElementById('myChart101').getContext('2d');
        var myChart101 = new Chart(ctx1, {
            type: 'line',
            data: data1,
            options: options
        });


        var ctx2 = document.getElementById('myChart201').getContext('2d');
        var myChart201 = new Chart(ctx2, {
            type: 'line',
            data: data2,
            options: options
        });
    </script>


    <!-- <div class="col-md-6">
            <img class="img-responsive" src="http://file.instiz.net/data/file/20140530/c/3/a/c3a8178c74215d01efa3c685b6c044ee.jpg">
         </div> -->
    </div>
    </div>


    <!--Webcam data-->
    <div id="webcam" class="tabcontent">
        <h2 style="margin-left: 10px;">Live camera Feed</h2>
        <video id="videoElement" width="auto" height="auto" style="margin-left: 10px;" autoplay></video>


    </div>


    <!--About Rus-->
    <div id="about">
        <h2 style="margin-left: 10px;">About</h2>
        <div>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
                dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
                ex ea commodo consequat. Duis aute
                irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
                sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
        </div>
    </div>
    <!--Contact-->


    </div>
    <div id="contact" class="container-fluid">
        <h2>Contact</h2>
        <div class="row">
            <div class="col-sm-6">
                <div>
                    <h6>Phone Number: 98901XXXXX</h6>
                    <h6>Email: agrosync@gmail.com</h6>
                </div>
            </div>
            <div class="col-sm-6">
                <h6>PICT</h6>
                <h6>PUNE</h6>
                <h6>Techfiesta hackathon</h6>
            </div>
        </div>
    </div>


    <!-- Initialize Bootstrap functionality -->
    <script>
        // Initialize tooltip component
        $(function () {
            $('[data-toggle="tooltip"]').tooltip();
        });


        // Initialize popover component
        $(function () {
            $('[data-toggle="popover"]').popover();
        });
    </script>


    <script>
        // Function to fetch sensor data from Flask server
        function fetchSensorData() {
            $.getJSON('/sensor_data', function(data) {
                $('#humidity').text('Current Humidity: ' + data.humidity + '%');
                $('#moisture').text('Current Moisture: ' + data.moisture + '%');
                $('#temperature').text('Current Temperature: ' + data.temperature + '°C');
                if (data.timer > 0) {
                    $('#timer').text('Pump Timer: ' + data.timer + ' minutes');
                } else {
                    $('#timer').text('Pump Timer: Not Active');
                }
            });
        }


        // Call fetchSensorData() initially
        fetchSensorData();


        // Call fetchSensorData() every 10 seconds
        setInterval(fetchSensorData, 10); // 10 seconds = 10000 milliseconds
    </script>


    <script>
        $(document).ready(function () {
            var plantData = {
                onion: {
                    message: 'Onion plant was selected.',
                    irrigation: 'Drip irrigation is recommended for onions.',
                    nextCrop: 'Spinach can be planted after onions.'
                },
                tomato: {
                    message: 'Tomato plant was selected.',
                    irrigation: 'Tomatoes require frequent watering. Drip irrigation is recommended.',
                    nextCrop: 'Beans can be planted after tomatoes.'
                },
                wheat: {
                    message: 'Wheat plant was selected.',
                    irrigation: 'Wheat requires regular watering, especially during the tillering stage.',
                    nextCrop: 'Mustard can be planted after wheat.'
                },
                rice: {
                    message: 'Rice plant was selected.',
                    irrigation: 'Rice requires flooded fields for cultivation.',
                    nextCrop: 'Soybean can be planted after rice.'
                }
            };


            $('#plantDropdown').change(function () {
                var selectedPlant = $(this).val();
                var plantInfo = plantData[selectedPlant];
                var message = plantInfo ? plantInfo.message : 'Invalid selection.';
                var irrigation = plantInfo ? plantInfo.irrigation : '';
                var nextCrop = plantInfo ? plantInfo.nextCrop : '';


                $('#plantMessage').html('<p>' + message + '</p><p>' + irrigation + '</p><p>' + nextCrop + '</p>');
            });
        });
    </script>


    <script>
        // Define chart data for each month
        var chartData1 = {
            March: {
                labels: ['Mar-01', 'Mar-02', 'Mar-03', 'Mar-04', 'Mar-05', 'Mar-06', 'Mar-07', 'Mar-08', 'Mar-09', 'Mar-10', 'Mar-11', 'Mar-12', 'Mar-13', 'Mar-14', 'Mar-15', 'Mar-16', 'Mar-17', 'Mar-18', 'Mar-19', 'Mar-20', 'Mar-21', 'Mar-22', 'Mar-23', 'Mar-24', 'Mar-25', 'Mar-26', 'Mar-27', 'Mar-28', 'Mar-29', 'Mar-30', 'Mar-31'],
                data: [18234, 18945, 20567, 16981, 17788, 19876, 16892, 18345, 20123, 16340, 17245, 19567, 16456, 17345, 20345, 16789, 17945, 20876, 16394, 17567, 20678, 16543, 18123, 20765, 16456, 17345, 21000, 16078, 17654, 20345, 16013]
            },
            April: {
                labels: ['Apr-01', 'Apr-02', 'Apr-03', 'Apr-04', 'Apr-05', 'Apr-06', 'Apr-07', 'Apr-08', 'Apr-09', 'Apr-10', 'Apr-11', 'Apr-12', 'Apr-13', 'Apr-14', 'Apr-15', 'Apr-16', 'Apr-17', 'Apr-18', 'Apr-19', 'Apr-20', 'Apr-21', 'Apr-22', 'Apr-23', 'Apr-24', 'Apr-25', 'Apr-26', 'Apr-27', 'Apr-28', 'Apr-29', 'Apr-30'],
                data: [27456, 26019, 29430, 25092, 28341, 26875, 24891, 29102, 27904, 26277, 25580, 29789, 24850, 28567, 29345, 26783, 24894, 27856, 26098, 28877, 25101, 29456, 27765, 24321, 28567, 26143, 29700, 27435, 25122, 28210]
            },
            May: {
                labels: ['May-01', 'May-02', 'May-03', 'May-04', 'May-05', 'May-06', 'May-07', 'May-08', 'May-09', 'May-10', 'May-11', 'May-12', 'May-13', 'May-14', 'May-15', 'May-16', 'May-17', 'May-18', 'May-19', 'May-20', 'May-21', 'May-22', 'May-23', 'May-24', 'May-25', 'May-26', 'May-27', 'May-28', 'May-29', 'May-30', 'May-31'],
                data: [22670, 21583, 19972, 21345, 22110, 22789, 20234, 23567, 20876, 21780, 23345, 19876, 22590, 24123, 22345, 20987, 24600, 21456, 23900, 20345, 22890, 22000, 19678, 23700, 24500, 20456, 21890, 22800, 19765, 23987, 19600]
            },
            June: {
                labels: ['Jun-01', 'Jun-02', 'Jun-03', 'Jun-04', 'Jun-05', 'Jun-06', 'Jun-07', 'Jun-08', 'Jun-09', 'Jun-10', 'Jun-11', 'Jun-12', 'Jun-13', 'Jun-14', 'Jun-15', 'Jun-16', 'Jun-17', 'Jun-18', 'Jun-19', 'Jun-20', 'Jun-21', 'Jun-22', 'Jun-23', 'Jun-24', 'Jun-25', 'Jun-26', 'Jun-27', 'Jun-28', 'Jun-29', 'Jun-30'],
                data: [18456, 20123, 19567, 21900, 17654, 20345, 18567, 21000, 17789, 22123, 18900, 21678, 17345, 22567, 17900, 21345, 17123, 22000, 18654, 22800, 18123, 22500, 17500, 23000, 17890, 22600, 18234, 22780, 17000, 22456]
            },
            July: {
                labels: ['Jul-01', 'Jul-02', 'Jul-03', 'Jul-04', 'Jul-05', 'Jul-06', 'Jul-07', 'Jul-08', 'Jul-09', 'Jul-10', 'Jul-11', 'Jul-12', 'Jul-13', 'Jul-14', 'Jul-15', 'Jul-16', 'Jul-17', 'Jul-18', 'Jul-19', 'Jul-20', 'Jul-21', 'Jul-22', 'Jul-23', 'Jul-24', 'Jul-25', 'Jul-26', 'Jul-27', 'Jul-28', 'Jul-29', 'Jul-30', 'Jul-31'],
                data: [15800, 16345, 14200, 15678, 16890, 14900, 17345, 14600, 17900, 14123, 18456, 14789, 18600, 14300, 19123, 14567, 19400, 14890, 19600, 15234, 19800, 15456, 20000, 15789, 14000, 16234, 14256, 16678, 14456, 17234, 15000]
            }
        };


        // Define chart data for each month
        var chartData2 = {
            March: {
                labels: ['Mar-01', 'Mar-02', 'Mar-03', 'Mar-04', 'Mar-05', 'Mar-06', 'Mar-07', 'Mar-08', 'Mar-09', 'Mar-10', 'Mar-11', 'Mar-12', 'Mar-13', 'Mar-14', 'Mar-15', 'Mar-16', 'Mar-17', 'Mar-18', 'Mar-19', 'Mar-20', 'Mar-21', 'Mar-22', 'Mar-23', 'Mar-24', 'Mar-25', 'Mar-26', 'Mar-27', 'Mar-28', 'Mar-29', 'Mar-30', 'Mar-31'],
                data: Array.from({ length: 31 }, () => randomNumber(16000, 17000))
            },
            April: {
                labels: ['Apr-01', 'Apr-02', 'Apr-03', 'Apr-04', 'Apr-05', 'Apr-06', 'Apr-07', 'Apr-08', 'Apr-09', 'Apr-10', 'Apr-11', 'Apr-12', 'Apr-13', 'Apr-14', 'Apr-15', 'Apr-16', 'Apr-17', 'Apr-18', 'Apr-19', 'Apr-20', 'Apr-21', 'Apr-22', 'Apr-23', 'Apr-24', 'Apr-25', 'Apr-26', 'Apr-27', 'Apr-28', 'Apr-29', 'Apr-30'],
                data: Array.from({ length: 30 }, () => randomNumber(24000, 25000))
            },
            May: {
                labels: ['May-01', 'May-02', 'May-03', 'May-04', 'May-05', 'May-06', 'May-07', 'May-08', 'May-09', 'May-10', 'May-11', 'May-12', 'May-13', 'May-14', 'May-15', 'May-16', 'May-17', 'May-18', 'May-19', 'May-20', 'May-21', 'May-22', 'May-23', 'May-24', 'May-25', 'May-26', 'May-27', 'May-28', 'May-29', 'May-30', 'May-31'],
                data: Array.from({ length: 31 }, () => randomNumber(19600, 21500))
            },
            June: {
                labels: ['Jun-01', 'Jun-02', 'Jun-03', 'Jun-04', 'Jun-05', 'Jun-06', 'Jun-07', 'Jun-08', 'Jun-09', 'Jun-10', 'Jun-11', 'Jun-12', 'Jun-13', 'Jun-14', 'Jun-15', 'Jun-16', 'Jun-17', 'Jun-18', 'Jun-19', 'Jun-20', 'Jun-21', 'Jun-22', 'Jun-23', 'Jun-24', 'Jun-25', 'Jun-26', 'Jun-27', 'Jun-28', 'Jun-29', 'Jun-30'],
                data: Array.from({ length: 30 }, () => randomNumber(17000, 18000))
            },
            July: {
                labels: ['Jul-01', 'Jul-02', 'Jul-03', 'Jul-04', 'Jul-05', 'Jul-06', 'Jul-07', 'Jul-08', 'Jul-09', 'Jul-10', 'Jul-11', 'Jul-12', 'Jul-13', 'Jul-14', 'Jul-15', 'Jul-16', 'Jul-17', 'Jul-18', 'Jul-19', 'Jul-20', 'Jul-21', 'Jul-22', 'Jul-23', 'Jul-24', 'Jul-25', 'Jul-26', 'Jul-27', 'Jul-28', 'Jul-29', 'Jul-30', 'Jul-31'],
                data: Array.from({ length: 31 }, () => randomNumber(14000, 15000))
            }
        };


        // Function to generate random number within a range
        function randomNumber(min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }


        // Default to March data
        var selectedMonth1 = 'March';
        var selectedMonth2 = 'March';


        // Chart 1: Without Our System
        var ctx1 = document.getElementById('waterUsageChart1').getContext('2d');
        var myChart1 = new Chart(ctx1, {
            type: 'line',
            data: {
                labels: chartData1[selectedMonth1].labels,
                datasets: [{
                    label: 'Water Usage',
                    data: chartData1[selectedMonth1].data,
                    borderColor: 'rgb(255, 0, 0)',
                    borderWidth: 1,
                    fill: false
                }]
            },
            options: {
                responsive: false,
                scales: {
                    y: {
                        beginAtZero: true
                    }
                }
            }
        });


        // Chart 2: With Our System
        var ctx2 = document.getElementById('waterUsageChart2').getContext('2d');
        var myChart2 = new Chart(ctx2, {
            type: 'line',
            data: {
                labels: chartData2[selectedMonth2].labels,
                datasets: [{
                    label: 'Water Usage',
                    data: chartData2[selectedMonth2].data,
                    borderColor: 'rgb(0, 255, 0)',
                    borderWidth: 1,
                    fill: false
                }]
            },
            options: {
                responsive: false,
                scales: {
                    y: {
                        beginAtZero: true
                    }
                }
            }
        });


        // Function to update Chart 1 based on selected month
        function updateChart1() {
            var select1 = document.getElementById('monthSelect1');
            selectedMonth1 = select1.options[select1.selectedIndex].value;


            // Update chart data
            myChart1.data.labels = chartData1[selectedMonth1].labels;
            myChart1.data.datasets[0].data = chartData1[selectedMonth1].data;
            myChart1.update();
        }


        // Function to update Chart 2 based on selected month
        function updateChart2() {
            var select2 = document.getElementById('monthSelect2');
            selectedMonth2 = select2.options[select2.selectedIndex].value;


            // Update chart data
            myChart2.data.labels = chartData2[selectedMonth2].labels;
            myChart2.data.datasets[0].data = chartData2[selectedMonth2].data;
            myChart2.update();
        }
        // Function to update total water utilized for Chart 1
        function updateTotalWater1() {
            var select1 = document.getElementById('monthSelect1');
            selectedMonth1 = select1.options[select1.selectedIndex].value;


            var totalWater1 = chartData1[selectedMonth1].data.reduce((a, b) => a + b, 0);
            document.getElementById('totalWater1').innerText = totalWater1;
        }


        // Function to update total water utilized for Chart 2
        function updateTotalWater2() {
            var select2 = document.getElementById('monthSelect2');
            selectedMonth2 = select2.options[select2.selectedIndex].value;


            var totalWater2 = chartData2[selectedMonth2].data.reduce((a, b) => a + b, 0);
            document.getElementById('totalWater2').innerText = totalWater2;
        }
    </script>


    <script>
        // Get the video element
        var video = document.getElementById('videoElement');


        // Check if getUserMedia is supported by the browser
        if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
            // Get user media (in this case, video)
            navigator.mediaDevices.getUserMedia({ video: true })
                .then(function (stream) {
                    // Set the video stream as the source for the video element
                    video.srcObject = stream;
                    // Play the video
                    video.play();
                })
                .catch(function (err) {
                    // Handle errors
                    console.error('Error accessing the webcam:', err);
                });
        } else {
            console.error('getUserMedia is not supported by this browser');
        }
    </script>


</body>


Code for style.css :

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800;900&display=swap');
*
{
margin: 0;
padding: 0;
box-sizing: border-box;
  font-family: 'Poppins', sans-serif;
}
body 
{
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
flex-direction: column;
background: #23242a;
}
.box 
{
position: relative;
width: 380px;
height: 500px;
background: #1c1c1c;
border-radius: 8px;
overflow: hidden;
}
.box::before 
{
content: '';
z-index: 1;
position: absolute;
top: -50%;
left: -50%;
width: 380px;
height: 420px;
transform-origin: bottom right;
background: linear-gradient(0deg,transparent,#45ff86,#45f3ff);
animation: animate 6s linear infinite;
}
.box::after 
{
content: '';
z-index: 1;
position: absolute;
top: -50%;
left: -50%;
width: 380px;
height: 420px;
transform-origin: bottom right;
background: linear-gradient(0deg,transparent,#45ff86,#45ff86);
animation: animate 6s linear infinite;
animation-delay: -3s;
}
@keyframes animate 
{
0%
{
transform: rotate(0deg);
}
100%
{
transform: rotate(360deg);
}
}
form 
{
position: absolute;
inset: 2px;
background: #28292d;
padding: 50px 40px;
border-radius: 8px;
z-index: 2;
display: flex;
flex-direction: column;
}
h2 
{
color: #45ff86;
font-weight: 500;
text-align: center;
letter-spacing: 0.1em;
}
.inputBox 
{
position: relative;
width: 300px;
margin-top: 35px;
}
.inputBox input 
{
position: relative;
width: 100%;
padding: 20px 10px 10px;
background: transparent;
outline: none;
box-shadow: none;
border: none;
color: #23242a;
font-size: 1em;
letter-spacing: 0.05em;
transition: 0.5s;
z-index: 10;
}
.inputBox span 
{
position: absolute;
left: 0;
padding: 20px 0px 10px;
pointer-events: none;
font-size: 1em;
color: #8f8f8f;
letter-spacing: 0.05em;
transition: 0.5s;
}
.inputBox input:valid ~ span,
.inputBox input:focus ~ span 
{
color: #45ff7d;
transform: translateX(0px) translateY(-34px);
font-size: 0.75em;
}
.inputBox i 
{
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 2px;
background: #45ff8f;
border-radius: 4px;
overflow: hidden;
transition: 0.5s;
pointer-events: none;
z-index: 9;
}
.inputBox input:valid ~ i,
.inputBox input:focus ~ i 
{
height: 60px;
}
.links 
{
display: flex;
justify-content: space-between;
}
.links a 
{
margin: 10px 0;
font-size: 0.75em;
color: #8f8f8f;
text-decoration: beige;
}
.links a:hover, 
.links a:nth-child(2)
{
color: #58d186;
}
input[type="submit"]
{
border: none;
outline: none;
padding: 11px 25px;
background: #519d5c;
cursor: pointer;
border-radius: 4px;
font-weight: 600;
width: 100px;
margin-top: 10px;
}
input[type="submit"]:active 
{
opacity: 0.8;
}


Flask Website Code :

import requests
import onnxruntime as ort
import numpy as np
from flask import Flask, request, render_template, jsonify
from flask import Flask, render_template, request, redirect, url_for


app = Flask(__name__)
def predict_pump_status(soil_moisture, temperature, humidity):
    ort_session = ort.InferenceSession('models/model1.onnx')
    input_features = np.array([[soil_moisture, temperature, humidity]], dtype=np.float32)
    input_name = ort_session.get_inputs()[0].name
    output_name = ort_session.get_outputs()[0].name
    prediction = ort_session.run([output_name], {input_name: input_features})[0]
    return prediction[0]


def predict_soil_moisture(temperature, humidity):
    ort_session = ort.InferenceSession('models/model2.onnx')
    input_features = np.array([[temperature, humidity, 0]], dtype=np.float32)
    input_name = ort_session.get_inputs()[0].name
    output_name = ort_session.get_outputs()[0].name
    prediction = ort_session.run([output_name], {input_name: input_features})[0]
    return prediction[0]


ESP32_SERVER_IP = "YOUR_ESP32_SERVER_IP"  # Replace with the IP address of your ESP32 server


def get_timer(data):
    soil_moisture = data['moisture']
    temperature = data['temperature']
    humidity = data['humidity']
    pump_status = predict_pump_status(soil_moisture, temperature, humidity)


    if pump_status == 1:
        req_moisture = predict_soil_moisture(temperature, humidity)
        deficit = req_moisture - soil_moisture
        rate_of_restoration = 100
        timer = np.round(deficit / rate_of_restoration, 0)
        timer = str(timer[0])
        return timer, pump_status
    else:
        return '0', pump_status  # Returning '0' instead of 0 to maintain consistency with string representation


@app.route('/')
def index():
    return render_template('new_index.html')


@app.route('/sensor_data')
def get_sensor_data():
    try:
        response = requests.get(f"http://{ESP32_SERVER_IP}/data")
        sensor_data = response.json()
        timer, pump_status = get_timer(sensor_data)  # Get timer data and pump status
        sensor_data['timer'] = timer  # Add timer data to sensor data
        sensor_data['pump_status'] = int(pump_status)  # Convert pump_status to integer
        # Send pump_status back to ESP32 server
        requests.post(f"http://{ESP32_SERVER_IP}/pump_status", json={'pump_status': int(pump_status)})
    except Exception as e:
        print(f"Error fetching data from ESP32 server: {e}")
        sensor_data = {'humidity': 'N/A', 'moisture': 'N/A', 'temperature': 'N/A', 'timer': 'N/A', 'pump_status': 'N/A'}
    return jsonify(sensor_data)


if __name__ == '__main__':
    app.run(host="0.0.0.0", debug=True)


Downloads

Running the Project

WhatsApp Image 2024-07-29 at 7.02.14 AM.jpeg
1.jpeg

The project setup is now ready from both the Electronics and the Coding perspective. Its now time to run the whole project in one go...!!

Here's how you should run the overll project in the order as mentioned below :

  1. First open the arduino ide and then run the first code file LoRa_Sender.ino
  2. Once the server is up and running, run the LoRa_Receiver.ino code on the other ESP32.
  3. As soon as the transmission and reception of data is completely established, note your ESP32 Server ID.
  4. Enter this ID into the app.py file and atlast run the flask app to view and display the data on the website.


Project Video Demo

Precision Irrigation Optimization and Water Management

So now with this Demonstration video we conclude our Project!!!!!

Hope everyone would enjoy doing this.........

Thank You :)


Link to buy Components :

  1. ESP32 Micro-controller (WROOM) - Click here
  2. SX1278 LoRa Module (Ra- 02 433 MHz) - Click here
  3. DHT-11 Temparature Sensor - Click here
  4. Soil Mositure Sensor - Click here
  5. Relay Module (5V) - Click here
  6. 12V DC Water Pump - Click here
  7. LED (for demo purpose instead of water pump) - Click here
  8. BreadBoard - Click here
  9. Jumper Wires - Click here

Link for Softwares :

  1. Arduino IDE (ESP32 Programing) - Click here
  2. Fritzing (Circuit Designing) - Click here
  3. Bootstrap - Click here
  4. Flask - Click here