Introduction: AgriAid : IOT Smart Irrigation System

About: I am a thirteen year old enthusiast who likes to learn more and express his creativity in STEM and programming. My hobby is playing instruments like keyboard, tabla, drums and kahoon. My passion is programming…

What better way to use water than to use a single water supply to accomplish drip and sprinkler irrigation simultaneously? Aside from automating systems, real-time surveillance and IOT integration with operational controls contribute significantly to optimal food production and irrigation management.

Smart agriculture is a solution to many global agricultural issues, such as increasing productivity, monitoring results, and effective use of water. The industry is rapidly looking into adopting IoT solutions into their workflows.

Optimizing the schedule and amount of water allows us to save resources and provide the best care for crops. Sensor-based IoT technologies collect data about soil and update crop status and transmit this information from sensors to farm irrigation systems. 


Features:


-         Utilize a single water source for both drip and sprinkler irrigation systems

-         A mobile app that has real-time soil moisture data and auto / manual irrigation controls

-         A custom web dashboard that can be accessed from any device over any local network

-         A monitor connected to this setup displays the real-time surveillance of the system.

-         Two modes of operation : Auto / Manual control



This project has two controllable modes of operation: manual and automatic.

In Auto mode, the soil moisture sensors will keep monitoring the soil moisture level and sends the feedback to Raspberry pi-4 controller, once the soil moisture value is greater than threshold value, the Raspberry pi will sends to signal to Arduino UNO controller, which then turn the pump ON and the water will flow to the plants.

The watering will continue until the soil moisture value becomes less than the threshold value, and the Arduino UNO will turn the pump OFF. based on the computer vision, the moisture threshold value is set

In Manual control, the input from the soil moisture is by passed. The control shall be performed manually through mobile from a remote location.


The entire operation shall be monitored via a camera, connected to the Raspberry pi.

Supplies

1 ) Micro Controllers :

  1. Raspberry pi 4 [ 4GB ram ]
  2. Arduino UNO
  3. Arduino Wi-Fi r3 module ( ESP 8266 Atmega )


2 ) Sensors:

  1. Soil moisture sensor - 2
  2. Raspberry pi camera module


3 ) Pump and relay:

  1. 5v mini submersible pump
  2. 5v one channel relay module


4 ) Power supply:

  1. Power bank
  2. 9v battery


5 ) Peripherals [ Raspberry pi ]:

  1. Keyboard
  2. Mouse
  3. Monitor


6 ) Cables:

  1. USB 2.0 cable
  2. USB cable
  3. Micro HDMI to HDMI Converter
  4. Type c charger cable


7 ) Memory cards:

  1. Sd card [ Raspberry pi ]


8 ) DIY:

  1. Cardboard
  2. Plastic box [ water container ]
  3. Paper
  4. Hot glue gun
  5. Cello tape
  6. Scissors
  7. Thermocol sheet
  8. Soil
  9. Plants / leaves
  10. Aluminum sheet / foil

Step 1: Circuit

Arduino Circuit:

1 ) Soil-moisture sensor -

  • Connect GND with Arduino's GND
  • Connect VCC to Arduino's 3.3V pin
  • Connect IN / A0 pin with Arduino A0 PIN

2 ) Relay -

  • Connect IN pin with GPIO PIN 13
  • Connect VCC with 5V
  • Connect GND with GND

3 ) Pump -

  • Connect negative / GND with 9v battery negative / GND
  • Connect positive / VCC with NO [ normally open ] in relay

4 ) Battery -

  • Connect negative / GND with pump negative / GND
  • Connect positive / VCC to CO [ Common ] in relay


Arduino Wi-Fi circuit:

1 ) Soil-moisture sensor -

  • Connect GND with Arduino's GND
  • Connect VCC to Arduino's 3.3V pin
  • Connect IN / A0 pin with Arduino A0 PIN


Raspberry pi 4 [ Model B ] 4 GB Ram:

1 ) Raspberry pi camera - Connect the serial bus of camera to raspberry pi camera port

2 ) Arduino to Raspberry pi - Connect Arduino UNO to raspberry pi via USB port

3 ) Peripherals, Power supply and display -

  • Connect keyboard and mouse to USB ports 1 and 3 respectively
  • Connect the power bank to type-c power port
  • Connect Micro-HDMI to HDMI converter to Micro-HDMI port
  • Insert SD Card below the raspberry pi in kiosk


Step 2: Coding

The whole code is divided into 4 files:




Arduino code [ Monitoring and sending of moisture inputs along with control of pump ]


int MOISTURE_SENSOR_PIN = A0;
int RELAY_PIN = 13;
int MOISTURE_THRESHOLD = 500;
bool automode = true;
bool manualmode = false;
bool on = false;
bool off= true;

bool isPumpOn = false;

void setup() {
 pinMode(RELAY_PIN, OUTPUT);
 digitalWrite(RELAY_PIN, LOW);
 Serial.begin(9600);
}

void loop() {
 int moistureValue = analogRead(MOISTURE_SENSOR_PIN);
 char serialData = Serial.read();
 if (serialData == '1'){
  automode = true;
  manualmode = false;
 }
 else if (serialData == '2'){
  automode = false;
  manualmode = true;
 }
 else if (serialData == '3'){
  automode = false;
  manualmode = false;
  //Serial.println("pump on");
  digitalWrite(RELAY_PIN, LOW);
  isPumpOn = false;
 }
 else if (serialData == '4'){
  automode = false;
  manualmode = false;
  //Serial.println("pump off");
  digitalWrite(RELAY_PIN, HIGH);
  isPumpOn = true;
 }
 // Check moisture level and control pump
 if (automode && moistureValue < MOISTURE_THRESHOLD) {
  digitalWrite(RELAY_PIN, HIGH);
  isPumpOn = true;
 }
 else if (automode && moistureValue > MOISTURE_THRESHOLD){
  digitalWrite(RELAY_PIN, LOW);
  isPumpOn = false;
 }
 else if (manualmode){
  digitalWrite(RELAY_PIN, HIGH);
  isPumpOn = true;
 }


 // Print pump status and moisture value in the format "moistureValue,pumpStatus"
 String pumpStatus = isPumpOn ? "off" : "on";
 Serial.print(moistureValue);
 Serial.print(",");
 Serial.println(pumpStatus);

 delay(1000); // Wait for 1 second
}


Arduino Wi-Fi code [ Creating the web dashboard ]


#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

const char* ssid = "Newton";
const char* password = "einstein";

ESP8266WebServer server(80);

// Function to get soil moisture level from sensor
int readMoistureLevel() {
 // Code to read moisture level from the sensor
 int moistureLevel = analogRead(A0); // Assuming sensor is connected to A0 pin
 return moistureLevel;
}

void handleRoot() {
 // Get moisture level from sensor
 int moistureLevel = analogRead(A0);
 
 String html = "<!DOCTYPE html><html><head><title>Soil Moisture</title>";
 html += "<meta http-equiv='refresh' content='5'>"; // Auto refresh every 5 seconds
 html += "<style>body { background-color: #152238; color: white; }</style>"; // Updated background color
 html += "<script src='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js'></script>"; // Include Chart.js library
 html += "<style>#charts-container { display: flex; justify-content: center; }</style>"; // Center the charts
 html += "</head><body>";
 
 // First div for soil moisture level
 html += "<div style='text-align:center; background-color: rgba(0,0,0,0.5); padding: 20px; color: white;'>";
 html += "<h1>Soil Moisture Level</h1>";
 html += "<div id='moistureValue' style='color: white;'>Moisture Level: ";
 html += moistureLevel;
 html += "</div>";
 html += "</div>"; // Close the first div
 
 // Add a gap between the two charts
 html += "<div style='height: 20px;'></div>";
 
 // Second div for charts
 html += "<div id='charts-container' style='width: 100%; background-color: rgba(0,0,0,0.5); padding: 20px; color: white;'>"; // Updated background color
 // Chart for line graph
 html += "<div style='width: 50%;'>";
 html += "<canvas id='moistureChart' width='200' height='100'></canvas>";
 html += "</div>";
 // Chart for pie chart
 html += "<div style='width: 25%;'>";
 html += "<canvas id='moisturePieChart' width='100' height='100'></canvas>"; // Decreased size by 30%
 html += "</div>";
 html += "</div>"; // Close the second div

 // JavaScript for updating charts
 html += "<script>";
 // JavaScript for line graph
 html += "var ctx = document.getElementById('moistureChart').getContext('2d');";
 html += "var chart = new Chart(ctx, {";
 html += " type: 'line',";
 html += " data: {";
 html += "  labels: ['Now', 'In 1 Hour', 'In 2 Hours'],";
 html += "  datasets: [{";
 html += "   label: 'Moisture Level',";
 html += "   data: [" + String(moistureLevel) + ", " + String(moistureLevel) + ", " + String(moistureLevel) + "],"; // Initial data, adjust according to your need
 html += "   backgroundColor: 'rgba(75, 192, 192, 0.2)',";
 html += "   borderColor: 'rgba(75, 192, 192, 1)',";
 html += "   borderWidth: 1";
 html += "  }]";
 html += " },";
 html += " options: {";
 html += "  scales: {";
 html += "   y: {";
 html += "    beginAtZero: true";
 html += "   }";
 html += "  }";
 html += " }";
 html += "});";
 // JavaScript for pie chart
 html += "var ctx2 = document.getElementById('moisturePieChart').getContext('2d');";
 html += "var pieChart = new Chart(ctx2, {";
 html += " type: 'pie',";
 html += " data: {";
 html += "  labels: ['Dry', 'Moist', 'Wet'],";
 html += "  datasets: [{";
 html += "   label: 'Soil Moisture',";
 html += "   data: [" + String(moistureLevel) + ", " + String(1023 - moistureLevel) + ", 0],"; // Assuming max value of 1023 for moisture level
 html += "   backgroundColor: ['rgba(255, 99, 132, 0.2)', 'rgba(54, 162, 235, 0.2)', 'rgba(75, 192, 192, 0.2)'],";
 html += "   borderColor: ['rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)', 'rgba(75, 192, 192, 1)'],";
 html += "   borderWidth: 1";
 html += "  }]";
 html += " }";
 html += "});";
 html += "</script>";

 html += "</body></html>";

 server.send(200, "text/html", html);
}

void setup() {
 Serial.begin(115200);
 
 // 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");

 // Print ESP IP address
 Serial.println(WiFi.localIP());

 // Set up the server
 server.on("/", handleRoot);
 server.begin();
 Serial.println("HTTP server started");
 
}

void loop() {
 server.handleClient();
 int moistureLevel1 = analogRead(A0);
 delay(2000);
 Serial.println(moistureLevel1);
}


Raspberry pi 4 code [ Controlling sending and recieving of data ]


import serial
import socket
import threading
import time
from picamera2.encoders import H264Encoder
from picamera2 import Picamera2, Preview

# Define the serial port and baud rate
serial_port = '/dev/ttyUSB0' # Update this with the correct serial port
baud_rate = 9600

# Define the IP address and port for socket communication
server_ip = '192.168.1.6' # Update this with the IP address of your computer
server_port = 12344 # Choose a port that is not in use

def read_soil_moisture():
  try:
    # Initialize serial connection
    ser = serial.Serial(serial_port, baud_rate)

    # Initialize socket connection
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind((server_ip, server_port))
    server_socket.listen(1)
    print("Socket listening on {}:{}".format(server_ip, server_port))

    # Function to continuously send data to the KivyMD app
    def send_data_to_client(client_socket):
      while True:
        try:
          # Read data from serial port
          data = ser.readline().decode().strip()

          # Check if received data contains both soil moisture and pump state
          if "," in data:
            soil_moisture, pump_state = data.split(",")
          else:
            soil_moisture = data
            pump_state = "Off"

          # Print soil moisture data
          print("Soil Moisture : ", soil_moisture)
          print("Pump Control : ", pump_state)

          # Send soil moisture to KivyMD app
          client_socket.sendall(f"{soil_moisture}\n".encode())
          print("Soil moisture sent")

          # Send pump state to KivyMD app
          client_socket.sendall(f"{pump_state}\n".encode())
          print("Pump state sent")
        except Exception as e:
          print("Error sending data:", e)
          break

    while True:
      # Accept connection from KivyMD app
      client_socket, client_address = server_socket.accept()
      print("Connection accepted from:", client_address)

      # Start a new thread to continuously send data to the KivyMD app
      data_thread = threading.Thread(target=send_data_to_client, args=(client_socket,))
      data_thread.start()

      # Listen for data from the KivyMD app
      while True:
        try:
          # Receive data from the KivyMD app
          received_data = client_socket.recv(1024).decode().strip()
          if received_data == 'auto':
            print("Setting mode to:", received_data)
            ser.write(b'1')
          if received_data == 'manual':
            print("Setting mode to:", received_data)
            ser.write(b'2')
          if received_data == 'start':
            print("pump control:", received_data)
            ser.write(b'3')
          if received_data == 'stop':
            print("pump control:", received_data)
            ser.write(b'4')
        except Exception as e:
          print("Error receiving data:", e)
          break

  except KeyboardInterrupt:
    print("Program terminated by user.")
  except Exception as e:
    print("Error:", e)
  finally:
    # Close serial connection
    ser.close()
    # Close socket connection
    server_socket.close()

def capture_video():
  try:
    picam2 = Picamera2()
    video_config = picam2.create_video_configuration(main={"size": (1920, 1080)}, lores={"size": (640, 480)}, display="lores")
    picam2.configure(video_config)
    encoder = H264Encoder(bitrate=10000000)
    output = "test.h264"
    picam2.start_preview(Preview.QTGL)
    picam2.start_recording(encoder, output)
    time.sleep(100000000) # Adjust the duration as needed
    picam2.stop_recording()
    picam2.stop_preview()
  except Exception as e:
    print("Error capturing video:", e)

if __name__ == "__main__":
  # Create threads for running both functions concurrently
  soil_moisture_thread = threading.Thread(target=read_soil_moisture)
  video_thread = threading.Thread(target=capture_video)

  # Start both threads
  soil_moisture_thread.start()
  video_thread.start()

  # Join both threads to the main thread
  soil_moisture_thread.join()
  video_thread.join()


Android app [ GUI and functions ]


from kivymd.app import MDApp
from kivy.lang import Builder
import matplotlib.pyplot as plt
from io import BytesIO
from PIL import Image as PILImage
from kivy.graphics.texture import Texture
from kivymd.uix.label import MDLabel
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivymd.uix.button import MDRaisedButton
import socket
from kivy.clock import Clock

# Define the IP address and port for socket communication
server_ip = '192.168.1.6' # Update this with the IP address of your computer
server_port = 1234
# Choose the same port as defined in the server script

kv_string = """
BoxLayout:
  orientation: 'vertical'
  padding: '10dp'
  spacing: '10dp'
  size_hint: None, None
  size: '400dp', '800dp'

  MDBottomNavigation:
    panel_color: 0, 0, 0, 0.3 # Background color of the bottom navigation bar
    text_color_active: 1, 1, 1, 1 # Set active text color to white
    text_color_normal: 1, 1, 1, 1 # Set normal (inactive) text color to white
    size_hint: 1, 1

    MDBottomNavigationItem:
      name: 'home'
      text: 'Home'
      icon: 'home'
      text_color_active: 1, 1, 1, 1 # Set active text color to white
      text_color_normal: 1, 1, 1, 1 # Set normal (inactive) text color to white

      BoxLayout:
        orientation: 'vertical'
        padding: '10dp'
        spacing: '10dp'

        BoxLayout:
          orientation: 'vertical'
          size_hint_y: None
          height: self.minimum_height
          pos_hint: {'center_x': 0.5}

          BoxLayout:
            padding: '30dp'
            orientation: 'vertical'

            BoxLayout:
              orientation: 'vertical'
              size_hint_y: None
              height: self.minimum_height
              width: '20dp'
              pos_hint: {'center_x': 0.5}
              padding: '20dp'
              spacing: '10dp'
              canvas.before:
                Color:
                  rgba: 0, 0, 0, 0.8 # Translucent black color
                Rectangle:
                  pos: self.pos
                  size: self.size

              MDLabel:
                id: soil_moisture_label
                text: "Waiting for data..."
                halign: "center"
                size_hint_y: None
                height: '24dp'
                color: 1, 1, 1, 1

            Widget:
              size_hint_y: None
              height: '14dp'
               
             
          Widget:
            size_hint_y: None
            height: '18dp'

          Image:
            id: graph_image
            allow_stretch: True
            nocache: True
            size_hint_y: None
            height: '300dp'

          Widget:
            size_hint_y: None
            height: '124dp'

           

     
    MDBottomNavigationItem:
      name: 'Manual control'
      text: 'Pump Control'
      icon: 'water-pump'
      text_color_active: 1, 1, 1, 1 # Set active text color to white
      text_color_normal: 1, 1, 1, 1 # Set normal (inactive) text color to white
      BoxLayout:
        padding: '5dp'
        spacing: '10dp'
        width: '10dp'
        #height: '100dp'
        #size_hint_y: '100dp'
        orientation: 'vertical'
        pos_hint: {'center_x': 0.5}

         
        Widget:
          size_hint_y: None
          height: '84dp'
           
        BoxLayout:
          orientation: 'vertical'
          size_hint_y: None
          height: self.minimum_height
          width: '20dp'
          pos_hint: {'center_x': 0.5}
          padding: '20dp'
          spacing: '10dp'
          canvas.before:
            Color:
              rgba: 0, 0, 0, 0.5 # Translucent black color
            Rectangle:
              pos: self.pos
              size: self.size

          MDLabel:
            id: mode
            text: "Mode control"
            halign: "center"
            size_hint_y: None
            height: '14dp'
            color: 1, 1, 1, 1
             
        Widget:
          size_hint_y: None
          height: '154dp'

        BoxLayout:
          padding: '30dp'
          spacing: '10dp'
          width: '10dp'
          height: '30dp'
          size_hint_y: '40dp'
          orientation: 'vertical'

          MDRaisedButton:
            text: "Auto Mode"
            size_hint_y: None
            height: '100dp'
            pos_hint: {'center_x': 0.5}
            size_hint_x: '10dp'
            font_size: '20sp'
            on_press: app.auto_mode()
                 
          Widget:
            size_hint_y: None
            height: '5dp'
             
           
           


          MDRaisedButton:
            text: "Manual Control"
            size_hint_y: None
            height: '100dp'
            pos_hint: {'center_x': 0.5}
            size_hint_x: '10dp'
            font_size: '20sp'
            on_press: app.manual_control()
                 
        Widget:
          size_hint_y: None
          height: '24dp'
           
        BoxLayout:
          orientation: 'vertical'
          size_hint_y: None
          height: self.minimum_height
          width: '20dp'
          pos_hint: {'center_x': 0.5}
          padding: '20dp'
          spacing: '10dp'
          canvas.before:
            Color:
              rgba: 0, 0, 0, 0.5 # Translucent black color
            Rectangle:
              pos: self.pos
              size: self.size

          MDLabel:
            id: manualContrll
            text: "Manual control"
            halign: "center"
            size_hint_y: None
            height: '14dp'
            color: 1, 1, 1, 1

                 

        Widget:
          size_hint_y: None
          height: '44dp'


        BoxLayout:
          padding: '5dp'
          spacing: '10dp'
          width: '10dp'
          height: '50dp'
          size_hint_y: '50dp'
          orientation: 'horizontal'

          MDRaisedButton:
            text: "On"
            size_hint_y: None
            height: '100dp'
            pos_hint: {'center_x': 0.5}
            size_hint_x: '10dp'
            font_size: '20sp'
            on_press: app.send_message_to_server()
                 
             
          Widget:
            size_hint_y: None
            height: '10dp'


          MDRaisedButton:
            text: "Off"
            size_hint_y: None
            height: '100dp'
            pos_hint: {'center_x': 0.5}
            size_hint_x: '10dp'
            font_size: '20sp'
            on_press: app.send_message_to_server_stop()
             
             
        Widget:
          size_hint_y: None
          height: '24dp'
             

           


        BoxLayout:
          orientation: 'horizontal'
          size_hint_y: None
          height: self.minimum_height
          width: '20dp'
          pos_hint: {'center_x': 0.5}
          padding: '20dp'
          spacing: '10dp'
          canvas.before:
            Color:
              rgba: 0, 0, 0, 0.8 # Translucent black color
            Rectangle:
              pos: self.pos
              size: self.size

          MDLabel:
            id: pump_state_label
            text: "Pump state : Off"
            halign: "center"
            size_hint_y: None
            height: '24dp'
            color: 1, 1, 1, 1
             
        Widget:
          size_hint_y: None
          height: '84dp'
       
               


     

"""

class SoilMoistureApp(MDApp):
  def build(self):
    return Builder.load_string(kv_string)

  def on_start(self):
    self.graph_image = self.root.ids.graph_image
    self.soil_moisture_label = self.root.ids.soil_moisture_label
    self.pump_state_label = self.root.ids.pump_state_label # Added this line

    # Connect to the socket server
    self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    self.client_socket.connect((server_ip, server_port))

    # Start receiving data from the server
    Clock.schedule_interval(self.receive_data, 1) # Update every 1 second

  def receive_data(self, dt):
    try:
      soil_moisture = self.client_socket.recv(1024).decode().strip()
      pump_state = self.client_socket.recv(1024).decode().strip()

      self.soil_moisture_label.text = f"Soil Moisture: {pump_state}"
      self.pump_state_label.text = f"Pump State: {soil_moisture}"
      self.update_graph(pump_state)
    except Exception as e:
      print("Error receiving data:", e)

  def update_graph(self, soil_moisture=None):
    plt.clf()
    if soil_moisture is not None:
      plt.plot([0, 1], [int(soil_moisture), int(soil_moisture)])
    plt.xlabel('Time (s)')
    plt.ylabel('Soil Moisture (%)')
    plt.title('Soil Moisture Graph')
    plt.grid(True)

    buffer = BytesIO()
    plt.savefig(buffer, format='png')
    buffer.seek(0)
    img = PILImage.open(buffer)
    img = img.transpose(PILImage.FLIP_TOP_BOTTOM)

    texture = Texture.create(size=(img.width, img.height))
    texture.blit_buffer(img.tobytes(), colorfmt='rgba', bufferfmt='ubyte')
    self.graph_image.texture = texture

  def send_message_to_server(self):
    try:
      self.client_socket.send(b"start")
    except Exception as e:
      print("Error sending message to server:", e)
       
  def send_message_to_server_stop(self):
    try:
      self.client_socket.send(b"stop")
    except Exception as e:
      print("Error sending message to server:", e)

  def auto_mode(self):
    try:
      self.client_socket.send(b"auto")
    except Exception as e:
      print("Error sending message to server:", e)

  def manual_control(self):
    try:
      self.client_socket.send(b"manual")
    except Exception as e:
      print("Error sending message to server:", e)

if __name__ == '__main__':
  SoilMoistureApp().run()

Step 3: Hardware Setup

"Working Model" perspective :

Take a Thermocol sheet and divide it into three segments.

  • First segment: The first segment is the smallest of the three and is covered on all sides with tall cardboard sheets. This segment is further divided into two equal areas. On the left side is the space where the circuit is kept and on the right side is the area for the water tank. These two areas are connected with a small hole for pump connections to Arduino.


  • Second segment: The second segment here one where drip irrigation is performed. Here coriander leaves can be placed to an impression of crop. This layer is then covered with soil making it look like a field being irrigated. add a aluminum wrapping around the boundaries to prevent water from seeping down.


  • Third segment: The third is segment is the place where sprinkler irrigation takes place. Here a sprinkler like setup is attached to the surface which is later covered by soil. Small grass weeds are placed in a horizontal alternative format making it look like a cultivated area. cover this with aluminum to prevent water leaking.


Large scale implementation perspective: Dividethe field into three segments again performing similar functions, install drip and sprinkler irrigation methods in the respective segments. use external power supply to provide power to circuit.

Step 4: Conclusion

3d Model : https://www.tinkercad.com/things/9xpC4QASdB0-agriaid?sharecode=QWn8wc6w5fjR7XetGa-kj-xI4Ae3u71gnFPeLyckTFo


In conclusion, the automated irrigation system described offers an innovative and efficient solution for modern agricultural needs. By integrating both drip and sprinkler irrigation methods from a single water source, the system ensures optimal water distribution and resource management. The inclusion of a live camera feed and a web dashboard, updating every 5 seconds, provides real-time monitoring and control, enhancing user engagement and system oversight. The complementary Android app, featuring real-time soil moisture and pump status updates, along with mode control, further enhances the user experience by offering convenience and flexibility. With two distinct modes of operation—automated and manual—the system caters to diverse irrigation requirements, ensuring both precision and adaptability. This comprehensive and user-friendly system not only optimizes water usage but also supports sustainable farming practices.