AgriAid : IOT Smart Irrigation System
by ElectraFlame in Circuits > Raspberry Pi
478 Views, 8 Favorites, 0 Comments
AgriAid : IOT Smart Irrigation System
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 :
- Raspberry pi 4 [ 4GB ram ]
- Arduino UNO
- Arduino Wi-Fi r3 module ( ESP 8266 Atmega )
2 ) Sensors:
- Soil moisture sensor - 2
- Raspberry pi camera module
3 ) Pump and relay:
- 5v mini submersible pump
- 5v one channel relay module
4 ) Power supply:
- Power bank
- 9v battery
5 ) Peripherals [ Raspberry pi ]:
- Keyboard
- Mouse
- Monitor
6 ) Cables:
- USB 2.0 cable
- USB cable
- Micro HDMI to HDMI Converter
- Type c charger cable
7 ) Memory cards:
- Sd card [ Raspberry pi ]
8 ) DIY:
- Cardboard
- Plastic box [ water container ]
- Paper
- Hot glue gun
- Cello tape
- Scissors
- Thermocol sheet
- Soil
- Plants / leaves
- Aluminum sheet / foil
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
Coding
The whole code is divided into 4 files:
- Arduino UNO code : https://docs.google.com/document/d/1PiZ7uuMP6b36f2fn7G43EkkPRmCT3bwd2kHAngd2Ok8/edit?usp=sharing
- Arduino WIFI code: https://docs.google.com/document/d/1BAsDJs9JhixebtFS7oXi_AvsFfy_myh6ev6VlZ1M9KY/edit?usp=sharing
- Raspberry pi code:https://docs.google.com/document/d/1ySERRNr86Z3JScW5VtITZ7JdXcc8VoTUAnAA95xJgz0/edit?usp=sharing
- Android GUI:https://docs.google.com/document/d/1ln2G4vpHRIXRaLh6YBR48w9HjOTbq1fgg_7umzXxup0/edit?usp=sharing
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()
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: Divide the 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.
Conclusion
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.