AgriAid V2: AI Self Managing Irrigation System

by ElectraFlame in Circuits > Electronics

287 Views, 4 Favorites, 0 Comments

AgriAid V2: AI Self Managing Irrigation System

agriaid-v2-ai-irrigation-management

A HUGE SHOUTOUT TO MY FRIEND @DarkFLAME who helped me make the 3d model of this project and made it possible to implement AI in this project in the first place.

this is the version 2 of my previous project AgriAID : IOT based smart irrigation system.


The main idea of this project is the same as the one above, refer to it for more info. This project focuses on enhancing irrigation by providing the farmer cost effective measure to produce a huge amount of yield in fields. this method of irrigation should be highly encouraged as it does not only provide irrigation to drip irrigation crops but also to sprinkler irrigating crops simultaneously all while utilizing a single water source. the project utilizes PREDICTION SYSTEM in an ANDROID APP that helps the farmer get a huge idea on the soil moisture conditions.


This project operates on two modes: AUTO mode and MANUAL mode, in auto mode, the farmer does not need to worry about watering the crops in time as this irrigation is automated, based on the moisture threshold set, the water is supplied to crops when the soil is dry. In manual mode the farmer has control over when to water his field, he can toggle on and off buttons that only function in manual mode. The modes of operation can be switched by the control on android app.


The android app function on API from openweathermap, which provide forecast for a particular city when given input. the android app also presents the farmer with forecast of weather in 3 consecutive days.


THIS PROJECT DEMONSTRATED HERE FOCUSES PURELY ON MODEL MAKING PERSPECTIVE. LARGE SCALE IMPLEMENTATION IDEA IS PROVIDED AT THE END.

Supplies

Major supplies:

  1. Raspberry pi accesories and main requirents. [ eg: keyboard, mouse, monitor etc. ]
  2. arduino UNO
  3. hardware requirements - Wires, pump, relay, SM sensor, pipes and batteries
  4. cardboard
  5. thick foam
  6. hot glue gun
  7. pastel sheets [ to cover the cardboard ]
  8. sprinkler setup - a container with holes in it to perform sprinkler irrigation
  9. water container
  10. mobile [ to operate the irrigation setup ]
  11. tulsi leaves - to create the look of a crop
  12. soil
  13. grass weeds for creating a field like feel.

Planning the Functionality

Untitled.png

The functionality of the project is described in the above image, refer to it for getting the base idea on how to implement it.

Making the Circuits

Arduino, Arduino wifi and raspberry pi circuit.png

developing the circuit

  1. Connect the arduino UNO to the raspberry to create a serial communication using a usb.
  2. connect the SM sensor and relay + pump to arduino as given above
  3. use batteries to power the relay module
  4. connect the SM sensor to arduin Wifi module.
  5. finally connect the camera module to raspberry pi.


REFER TO THE CIRCUIT IMAGE PROVIDED ABOVE.

Coding the Arduino UNO

WE need to program the arduino UNO board in order for it to perform operations on controlling the pump. the code is provided below.


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
}


The above code is controlling relay based on moisture levels ONLY IN AUTO mode. the dependency of it from switching into auto and manual mode is from the serial command given by the raspberry pi in either 0 or 1. which i will later provide in this instructable.


it is also sending data to raspberry pi in format [ soil moisture level, pump state -on / off ].

Programming the Raspberry Pi

Now we need to program the raspberry pi to create a server to send data to android app. Creating the android app is given in the next step. in order to do so, we need to provide a port for this communication.


In order for the raspberry pi to receive the serial messages sent [ soil moisture, pump status - on/off ] we need to enable it to create a communication panel for the arduino connected to amc/usb xyz port of the raspberry pi.


the code is as follows, however this is the COMPLETED code for raspberry pi which also includes the MACHINE LEARNING part and also the process of receiving data from server.'''


import pandas as pd
import socket
import threading
import serial
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_absolute_error

global sms_ts
sms_ts = 0

# Initialize serial connection
ser = serial.Serial('/dev/ttyUSB0', 9600)

# Load data
melbourne_file_path = '/home/samvit/Desktop/Harshit-The-Great/large_soil_moisture_dataset.csv'
melbourne_data = pd.read_csv(melbourne_file_path)

# Drop missing values
melbourne_data = melbourne_data.dropna(axis=0)

# Select target variable
y = melbourne_data.Sensor_Value

# Select features
melbourne_features = ['Temperature', 'Humidity']
X = melbourne_data[melbourne_features]

# One-hot encode categorical data
X = pd.get_dummies(X)

# Define model
melbourne_model = DecisionTreeRegressor(random_state=1)

# Split data into training and validation data
train_X, val_X, train_y, val_y = train_test_split(X, y, random_state=0)

# Fit model on training data
melbourne_model.fit(train_X, train_y)

# Predict on validation data
val_predictions = melbourne_model.predict(val_X)
print("Mean Absolute Error on validation data:", mean_absolute_error(val_y, val_predictions))

# Function to predict soil moisture based on temperature and humidity
def predict_sms(Temperature, Humidity):
# Create a dictionary with the input values
user_data = {'Temperature': [Temperature], 'Humidity': [Humidity]}
# Convert to DataFrame
user_df = pd.DataFrame(user_data)
# One-hot encode the user's input to match the training data format
user_df = pd.get_dummies(user_df)
# Ensure the user input has the same columns as the training data
user_df = user_df.reindex(columns=X.columns, fill_value=0)
# Predict soil moisture
user_prediction = melbourne_model.predict(user_df)
return user_prediction[0]

def read_soil_moisture():
# Initialize socket connection
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('raspberrypi', 12349)) # Update with the correct server IP and port
server_socket.listen(1)
print("Socket listening on {}:{}".format('192.168.1.6', 12344))

# Function to continuously send data to the KivyMD app
def send_data_to_client(client_socket):
global sms_ts
while True:
# 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)
print("Predicted soil moisture value: ", sms_ts)

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

while True:
try:
# 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:
received_data = client_socket.recv(1024).decode().strip()

# Check for temperature and humidity data
if ',' in received_data:
Temperature, humidity = received_data.split(',')
Temperature = float(Temperature) # Convert Temperature to float
humidity = float(humidity) # Convert humidity to float
print(f"Received Temperature: {Temperature}, Humidity: {humidity}")

# Call the prediction function
predicted_sms = predict_sms(Temperature, humidity)
global sms_ts
sms_ts = predicted_sms
print(f"The predicted soil moisture for the given conditions is: {predicted_sms:.2f}")

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 (socket.error, KeyboardInterrupt) as e:
print("Socket error or manual interruption: ", e)
break

# Close serial and socket connections
ser.close()
server_socket.close()

if __name__ == "__main__":
# Create and start a thread for reading soil moisture
soil_moisture_thread = threading.Thread(target=read_soil_moisture)
soil_moisture_thread.start()

# Join the thread to the main thread
soil_moisture_thread.join()


The above code contains installing libraries that cannot be installed by normal pip, either use a virtual environment or put the library files in your directory. The code deals in both sending and receiving data from the kivymd app in mobile. it sends the soil moisture and pump status, simultaneously waiting for a response from kivymd app. the kivymd app here sends the operation of modes in this project. it also sends the data of targeted value to predict soil moisture. the raspberry pi then uses ML to analyze a dataset from your directory and then sends back the soil moisture predicted value. Now the raspberry pi sends the value in format :


[ soil moisture, pumpstatus ]

[ predicted value - by default 0 ]


This way it establishes a two way communication between the server and client.

Making the Android App

To make the android app, first we need to develop the GUI. I am using KIVYMD framework in python to develop this app. I advice first developing this in PYDROID3 for gui convenience. developing it in a laptop and then implementing git to a mobile device might scatter the gui. however if you need to proceed with developing the app in laptop, specify your screen size according to your needs.


The app shall contain 5 tabs:

Tab1: It contains the data received from raspberry pi regarding sm data and pump state. it presents the soil moisture level in a linear graph as well as in a label.


Tab2: It contains the pump control, it first contains 2 buttons, allowing the user to switch between manual and auto mode. and again two buttons that only act if manual mode is on to start and stop the pump according to will. at the bottom it has a label that showcases the pump state if its on or off as received from raspberry pi.


Tab3:

THANKS TO SBDEVELOPER for this tab's layout and coding the functionality.

https://www.youtube.com/watch?v=CRj39iFiI1E


It contains the info from API that shows the humidity, moisture, windspeed and temperature of a city when inputted.


Tab4: This is the main prediction tab that predicts the soil moisture value. it contains inputs for the the assisting values, windspeed, and moisture or any two values of your choice according to the database you have.


Tab5: This is the last tab that has the forecast for three consecutive days when entered a city name. it uses api to get the value from google.


The coding for this app is as follows, make sure to have these libraries installed. the code given is the whole code for this app and contains the coding of sending data too.


import requests
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
from kivy.core.window import Window
from datetime import datetime, timedelta
from kivymd.uix.list import OneLineListItem # Correct import for OneLineListItem


#Window.size = (350,600)

API_KEY = '2aece83b3adbe88f6c83c36fb780bfea'
BASE_URL = 'http://api.openweathermap.org/data/2.5/forecast'

# 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 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"
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 "
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 "
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: manualControl
text: "PUMP"
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: "START"
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: "STOP"
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'


MDBottomNavigationItem:
name: 'data'
text: 'Weather'
icon: 'application-cog'
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

MDFloatLayout:
md_bg_color:1,1,1,1

MDLabel:
id:location
text: "Delhi,IN"
pos_hint: {'center_x' : 0.5, 'center_y':0.89}
halign: "center"
font_size: "20sp"
#font_name: "BPoppins"

Image:
id: weather_image
source: "assets/sun.png"
pos_hint: {"center_x":0.5,"center_y":0.77}

MDLabel:
id:temperature
text: "[b]40[/b]°"
markup: True
pos_hint: {'center_x' : 0.5, 'center_y':0.62}
halign: "center"
font_size: "60sp"
#font_name: "BPoppins"

MDLabel:
id:weather
text: "Partly cloudy"
#markup: True
pos_hint: {'center_x' : 0.5, 'center_y':0.54}
halign: "center"
font_size: "30sp"
#font_name: "BPoppins"


MDFloatLayout:
pos_hint: {"center_x":.25, "center_y":.4}
size_hint: .22,.1

Image:
source: "assets/humidity.png"
pos_hint: {"center_x":0.1,"center_y":0.5}
MDLabel:
id:humidity
text: "80%"
pos_hint: {'center_x' : 1, 'center_y':0.7}
halign: "center"
font_size: "14sp"
#font_name: "BPoppins"
MDLabel:
text: "Humidity"
pos_hint: {'center_x' : 1, 'center_y':0.3}
halign: "center"
font_size: "14sp"
#font_name: "BPoppins"
MDFloatLayout:
pos_hint: {"center_x":.7, "center_y":.4}
size_hint: .22,.1

Image:
source: "assets/wind.png"
pos_hint: {"center_x":0.1,"center_y":0.5}
MDLabel:
id:wind_speed
text: "40km/h"
pos_hint: {'center_x' : 1.1, 'center_y':0.7}
halign: "center"
font_size: "16sp"
#font_name: "BPoppins"
MDLabel:
text: "Wind speed"
pos_hint: {'center_x' : 1.1, 'center_y':0.3}
halign: "center"
font_size: "14sp"
#font_name: "BPoppins"

MDFloatLayout:
size_hint_y:.3
canvas:
Color:
rgb: rgba(148,117,225,225)
RoundedRectangle:
size: self.size
pos: self.pos
radius: [10,10,0,0]

MDFloatLayout:
pos_hint: {"center_x":.5, "center_y":.71}
size_hint:.9,.32
canvas:
Color:
rgb: rgba(131,69,225,225)
RoundedRectangle:
size: self.size
pos: self.pos
radius: [6]

TextInput:
id: city_name
hint_text: "Enter city name"
size_hint: 1, None
pos_hint: {"center_x":.5, "center_y":.5}
height: self.minimum_height
multiline: False
font_size: "20sp"
hint_text_color: 1,1,1,1
foreground_color: 1,1,1,1
background_color: 1,1,1,0
padding: 15
cursor_color: 1,1,1,1
cursor_width:"2sp"

Button:
text: "Get weather"
font_size:"20sp"
size_hint: .9, .32
pos_hint: {"center_x":.5, "center_y":.29}
background_color: 1,1,1,0
color: rgba(148,117,225,225)
on_release: app.search_weather()
canvas.before:
Color:
rgb: 1,1,1,1
RoundedRectangle:
size: self.size
pos: self.pos
radius: [6]

MDBottomNavigationItem:
name: 'prediction'
text: 'Predict'
icon: 'weather-cloudy-clock'
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
MDFloatLayout:
md_bg_color: 1, 1, 1, 1
padding: '20dp'
spacing: '10dp'
# First text input for location
MDTextField:
id: weather_input
hint_text: "Enter Temperature"
size_hint_x: 0.8
size_hint_y: None
height: '40dp'
pos_hint: {'center_x': 0.5, 'center_y': 0.7}
multiline: False
# Second text input for date
MDTextField:
id: humidity_input
hint_text: "Enter Humidity"
size_hint_x: 0.8
size_hint_y: None
height: '40dp'
pos_hint: {'center_x': 0.5, 'center_y': 0.55}
multiline: False
# Predict button
MDRaisedButton:
text: "Predict"
size_hint_x: 0.8
size_hint_y: None
height: '50dp'
pos_hint: {'center_x': 0.5, 'center_y': 0.4}
on_press: app.predict_weather() # Call the predict weather method
# Label for the predicted temperature text
MDLabel:
text: "Prediction: "
size_hint_y: None
size_hint_x: 0.8
height: '40dp'
pos_hint: {'center_x': 0.5, 'center_y': 0.25}
# Label for displaying the predicted temperature
MDLabel:
id: temp_label
size_hint_y: None
size_hint_x: 0.8
text: "40 degrees"
size_hint_y: None
height: '40dp'
pos_hint: {'center_x': 0.5, 'center_y': 0.195}

MDBottomNavigationItem:
name: 'weather'
text: 'Forecast'
icon: 'weather-cloudy'
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

MDFloatLayout:
md_bg_color: 1, 1, 1, 1
padding: '20dp'
spacing: '10dp'

MDTextField:
id: city_input
hint_text: 'Enter city name'
size_hint_x: 0.8
size_hint_y: None
height: '40dp'
pos_hint: {'center_x': 0.5, 'center_y': 0.7}
multiline: False

MDRaisedButton:
text: 'Get Forecast'
size_hint_x: 0.8
size_hint_y: None
height: '40dp'
pos_hint: {'center_x': 0.5, 'center_y': 0.55}
on_release: app.get_forecast()

Widget:
size_hint_y: None
height: '20dp'
MDScrollView:
do_scroll_x: True # Enable horizontal scrolling
size_hint_x: None
size_hint_y: None
height: '300dp' # Set a fixed height for the ScrollView
width: "2200"
MDList:
id: weather_list
size_hint_x: 0.7
#orientation : 'horizontal'
font_size:'7px'
pos_hint: {'center_x': 0.5, 'center_y': 0.7}



"""


class SoilMoistureApp(MDApp):

api_key="2aece83b3adbe88f6c83c36fb780bfea"

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
self.temp_text = self.root.ids.temp_label

# 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:
# Receive all data at once
data = self.client_socket.recv(1024).decode().strip()

# Split the data by a delimiter, e.g., a newline character
data_parts = data.split('\n')

# Ensure we have all parts (soil moisture, pump state, and temperature)
if len(data_parts) == 3:
soil_moisture = data_parts[0].strip()
pump_state = data_parts[1].strip()
temperature = data_parts[2].strip()

# Update labels with the received data
self.soil_moisture_label.text = f"Soil Moisture: {soil_moisture}"
self.pump_state_label.text = f"Pump State: {pump_state}"
self.temp_text.text = f"Predicted soil moisture: {temperature}"

# Update the graph with the soil moisture data
self.update_graph(soil_moisture)

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)

def get_weather(self, city_name):
try:
url = f"https://api.openweathermap.org/data/2.5/weather?q={city_name}&appid={self.api_key}"
response = requests.get(url)
x = response.json()

# Print the JSON response for debugging
print(x)

if x["cod"] != "404":
temperature = str(round(x["main"]["temp"] - 273.15))
humidity = str(x["main"]["humidity"])
weather = x["weather"][0]["main"]
wind_speed = str(round(x["wind"]["speed"] * 18 / 5)) # converting m/s to km/h
location = x["name"] + ", " + x["sys"]["country"]

# Updating UI elements with fetched data
self.root.ids.temperature.text = f"{temperature}°"
self.root.ids.weather.text = weather
self.root.ids.humidity.text = f"{humidity}%"
self.root.ids.wind_speed.text = f"{wind_speed} km/h"
self.root.ids.location.text = location
else:
print("City not found")
except requests.ConnectionError:
print("No internet")

def search_weather(self):
city_name = self.root.ids.city_name.text
if city_name == "":
city_name="Delhi"

self.get_weather(city_name)

def predict_weather(self):
weather_input = self.root.ids.weather_input.text
humidity_input = self.root.ids.humidity_input.text

# Format the data as "weather,humidity"
data_to_send = f"{weather_input},{humidity_input}"

# Send the formatted data to the client
self.client_socket.send(data_to_send.encode('utf-8'))

# Optionally, you can clear the input fields after sending
self.root.ids.weather_input.text = ""
self.root.ids.humidity_input.text = ""

def get_forecast(self):
city = self.root.ids.city_input.text
if not city:
return

# Construct the API URL
url = f"{BASE_URL}?q={city}&appid={API_KEY}&units=metric"

# Make the API request
response = requests.get(url)
data = response.json()

# Check if the request was successful
if response.status_code == 200:
current_date = datetime.now()
forecast_dates = [current_date + timedelta(days=i) for i in range(3)]

# Dictionary to store aggregated forecast data for each day
daily_forecast = {date.strftime('%Y-%m-%d'): {'temp_sum': 0, 'humidity_sum': 0, 'count': 0} for date in
forecast_dates}

# Parse the forecast data
for entry in data['list']:
timestamp = datetime.strptime(entry['dt_txt'], '%Y-%m-%d %H:%M:%S')
date_str = timestamp.strftime('%Y-%m-%d')

if date_str in daily_forecast:
daily_forecast[date_str]['temp_sum'] += entry['main']['temp']
daily_forecast[date_str]['humidity_sum'] += entry['main']['humidity']
daily_forecast[date_str]['count'] += 1

# Clear the existing list
self.root.ids.weather_list.clear_widgets()

# Generate and show the forecast info in the list
for date_str, info in daily_forecast.items():
if info['count'] > 0:
avg_temp = info['temp_sum'] / info['count']
avg_humidity = info['humidity_sum'] / info['count']
forecast_text = f"{date_str}: Avg Temp: {avg_temp:.1f}°C, Avg Humidity: {avg_humidity:.1f}%"
else:
forecast_text = f"{date_str}: No data available"

# Add the forecast information as list items
self.root.ids.weather_list.add_widget(
OneLineListItem(text=forecast_text)
)

else:
error_message = f"Error: {data.get('message', 'Unable to fetch weather data')}"
self.root.ids.weather_list.add_widget(
OneLineListItem(text=error_message)
)
if __name__ == '__main__':
SoilMoistureApp().run()

Hardware Setup

Screenshot 2024-11-16 155708.png

Making the hardware setup for this project is ez. divide your whole project into 3 segments on a cardboard or a thick foam. gluegun the cardboard above it to make it stable. now enclose this setup with cardboard such that they form boundaries. now make the segments as follows:


Segment1: Circuitry segment: as the name suggests this segment is dedicated to putting all the circuits in. make space for the hdmi, power, and soil moisture to stick out so that these are connected respectively.


Segment 2: Drip irrigation: this is the segment where drip irrigation takes pace, put soil here and then add a few leaves of coriander so that it makes a crop like feel. create poles for the pipe to stand on to perform drip irrigation.


segment 3: sprinkler irrigation: this is the segment where the sprinkler irrigation happens continue the pipe from drip irrigation till the middle of this section after which connect it's end to the sprinkler like setup you made. now place the setup of this pipe such that the sprinkler is standing. finally place soil and grass weeds to give a field like feel.

ALL SOURCE CODES:

The code for all three are given below kindly refer to it.

Large Scale Implementation

To implement this in a large scale envision that you are standing in your field, then take measurements for your division of fields into sprinkler and drip, making the circuits section is not necessary as adding the circuit directly anywhere near the tank can do the job. now implement the same functionality as the prototype.


Your filed since now divided into drip and sprinkler irrigation setups can function perfectly. however requirement of wifi or ethernet or sim is necessary for the board and the app to communicate.


Fell free to implement this project in a large scale.

Conclusion

In conclusion this COST EFFECTIVE project helps the farmer in many ways as it provides a good yield and conserves water utilizing both sprinkler and drip irrigation. this way farmer is not able to grow any one type of crop however also multiple. the android app adds to a layer of automation and control over the irrigation. the prediction systems satisfy the farmer when he has put the field in manual mode.