Complete Smart Irrigation System
š§ Complete Smart Irrigation System - Fully Automated Farm Watering
š§ What You'll Learn:
- š¤ Build a complete automated irrigation system with ESP32
- š§ļø Integrate rain sensors and weather forecasts to skip unnecessary watering
- š Program time-based schedules (6 AM and 6 PM, Monday-Friday)
- š§ Combine soil moisture, rain detection, and scheduling for smart decisions
A complete smart irrigation system combines multiple data sources to decide exactly when and how much to water. This system uses soil moisture, rain detection, weather forecast, and time-based scheduling to ensure your crops get the right amount of water at the right time - automatically.
š§ Hardware Components
- ESP32 board ($6-8): The brain of the system
- Capacitive soil moisture sensor ($8-12): Measures water content at root depth
- 4-channel relay module ($6-8): Controls water pumps or solenoid valves
- Rain sensor ($3): Detects rainfall to skip irrigation
- Power supply: 12V/2A for pumps + 5V/2A for ESP32 (or solar + battery)
- Water pumps or solenoid valves ($10-30): Controls water flow to irrigation lines
š System Decision Logic
š Irrigation Decision Flow:
Check Rain ā Check Weather Forecast ā Check Soil Moisture ā Check Schedule ā Decide to Water
- Priority 1 - Rain: If raining OR forecast > 5mm ā SKIP irrigation
- Priority 2 - Soil Moisture: If moisture < 30% ā WATER even if off-schedule
- Priority 2 - Soil Wet: If moisture > 70% ā SKIP even if scheduled
- Priority 3 - Schedule: Water only during scheduled times (6 AM and 6 PM, Mon-Fri)
Schedule alone wastes water if it rained. Soil moisture alone might water at the wrong time (noon = evaporation). Combining all three gives you optimal efficiency - water only when crops need it, at the best time of day (morning/evening), and never waste water before rain.
š Complete Smart Irrigation Code
/*
* Complete Smart Irrigation System
* Combines soil moisture, rain detection, weather forecast, and time-based scheduling
*/
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <time.h>
// ========== WIFI CONFIGURATION ==========
const char* ssid = "YOUR_WIFI";
const char* password = "YOUR_PASSWORD";
// ========== PIN DEFINITIONS ==========
#define SOIL_PIN 32 // Soil moisture sensor (capacitive)
#define RAIN_PIN 33 // Rain sensor (LOW = rain)
#define PUMP_RELAY 4 // Main pump relay
#define ZONE1_RELAY 5 // Zone 1 control
#define ZONE2_RELAY 18 // Zone 2 control
// ========== SOIL CALIBRATION ==========
const int DRY_VALUE = 3800;
const int WET_VALUE = 1500;
// ========== IRRIGATION SCHEDULE ==========
int wateringTimes[] = {6, 18}; // 6 AM and 6 PM
int wateringDays[] = {1,2,3,4,5}; // Monday=1, Tuesday=2, ..., Friday=5
int wateringDuration = 600; // 10 minutes per zone
// ========== WEATHER API ==========
const char* apiKey = "YOUR_OPENWEATHER_API_KEY";
const char* city = "Nairobi";
struct WeatherData {
float temp;
float rain; // mm of rain forecast
bool isRaining;
};
// ========== READ SOIL MOISTURE ==========
int readSoilMoisture() {
int raw = analogRead(SOIL_PIN);
int moisture = map(raw, DRY_VALUE, WET_VALUE, 0, 100);
moisture = constrain(moisture, 0, 100);
return moisture;
}
// ========== GET WEATHER FORECAST ==========
WeatherData getWeatherForecast() {
WeatherData w = {0, 0, false};
if (WiFi.status() != WL_CONNECTED) return w;
HTTPClient http;
String url = "http://api.openweathermap.org/data/2.5/weather?q=" + String(city) + "&appid=" + apiKey + "&units=metric";
http.begin(url);
int httpCode = http.GET();
if (httpCode == 200) {
String payload = http.getString();
DynamicJsonDocument doc(2048);
deserializeJson(doc, payload);
w.temp = doc["main"]["temp"];
if (doc.containsKey("rain")) {
w.rain = doc["rain"]["1h"] | 0;
}
}
http.end();
// Check real-time rain sensor
w.isRaining = (digitalRead(RAIN_PIN) == LOW);
return w;
}
// ========== CHECK IF SHOULD WATER ==========
bool shouldWater(int moisture, WeatherData weather) {
// Priority 1: Actively raining
if (weather.isRaining) {
Serial.println("š§ļø Rain detected - skipping irrigation");
return false;
}
// Priority 2: Heavy rain forecast
if (weather.rain > 5) {
Serial.printf("š§ļø %.1fmm rain forecast - skipping irrigation\n", weather.rain);
return false;
}
// Priority 3: Emergency - soil critically dry
if (moisture < 25) {
Serial.println("šØ EMERGENCY: Soil critically dry! Watering now.");
return true;
}
// Priority 4: Soil too wet
if (moisture > 75) {
Serial.println("ā
Soil too wet - skipping irrigation");
return false;
}
// Priority 5: Check schedule
time_t now = time(nullptr);
struct tm *timeinfo = localtime(&now);
int currentHour = timeinfo->tm_hour;
int currentDay = timeinfo->tm_wday; // 0=Sunday, 1=Monday...
// Convert to Monday=1 format
int weekday = (currentDay == 0) ? 7 : currentDay;
// Check if watering day
bool isWateringDay = false;
for (int d : wateringDays) {
if (weekday == d) {
isWateringDay = true;
break;
}
}
// Check if watering time (within 2 hours of scheduled time)
bool isWateringTime = false;
for (int t : wateringTimes) {
if (abs(currentHour - t) <= 1) {
isWateringTime = true;
break;
}
}
if (isWateringDay && isWateringTime) {
Serial.printf("ā
Scheduled watering - Hour: %d, Weekday: %d\n", currentHour, weekday);
return true;
}
// Soil drying but not scheduled - wait for next window
if (moisture < 35) {
Serial.printf("ā ļø Soil drying ( %d%%) but outside watering hours - waiting\n", moisture);
}
return false;
}
// ========== CALCULATE DURATION ==========
int calculateDuration(int moisture) {
// Base duration: 10 minutes
int duration = wateringDuration;
// Adjust for dryness: more dry = more water
if (moisture < 30) {
duration = duration * 1.5; // 15 minutes for dry soil
} else if (moisture < 40) {
duration = duration * 1.2; // 12 minutes for slightly dry
} else if (moisture > 60) {
duration = duration * 0.7; // 7 minutes for wet soil
}
return constrain(duration, 300, 1200); // 5-20 minutes max
}
// ========== WATER A ZONE ==========
void waterZone(int relayPin, const char* zoneName, int seconds) {
Serial.printf("š§ Watering %s for %d seconds...\n", zoneName, seconds);
digitalWrite(relayPin, LOW); // Pump ON (active LOW)
delay(seconds * 1000);
digitalWrite(relayPin, HIGH); // Pump OFF
Serial.printf("ā
%s complete\n", zoneName);
}
// ========== SEND DECISION TO CLOUD ==========
void logDecision(int moisture, bool watered, int duration, WeatherData weather) {
if (WiFi.status() != WL_CONNECTED) return;
HTTPClient http;
http.begin("https://api.oceanremote.net/device/state");
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
String data = "token=YOUR_TOKEN";
data += "&moisture=" + String(moisture);
data += "&watered=" + String(watered ? "YES" : "NO");
data += "&duration=" + String(duration);
data += "&raining=" + String(weather.isRaining ? "YES" : "NO");
data += "&rain_forecast=" + String(weather.rain);
http.POST(data);
http.end();
}
// ========== SETUP ==========
void setup() {
Serial.begin(115200);
// Configure pins
pinMode(SOIL_PIN, INPUT);
pinMode(RAIN_PIN, INPUT_PULLUP);
pinMode(PUMP_RELAY, OUTPUT);
pinMode(ZONE1_RELAY, OUTPUT);
pinMode(ZONE2_RELAY, OUTPUT);
// Start with all pumps OFF
digitalWrite(PUMP_RELAY, HIGH);
digitalWrite(ZONE1_RELAY, HIGH);
digitalWrite(ZONE2_RELAY, HIGH);
// Connect to WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nā
WiFi connected!");
// Sync time
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
Serial.println("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
Serial.println("ā š§ SMART IRRIGATION SYSTEM v2.0 ā");
Serial.println("ā Soil + Rain + Weather + Schedule ā");
Serial.println("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
}
// ========== MAIN LOOP ==========
void loop() {
// Read sensors
int moisture = readSoilMoisture();
WeatherData weather = getWeatherForecast();
// Display status
Serial.println("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
Serial.printf("š§ Soil moisture: %d%%\n", moisture);
Serial.printf("š§ļø Raining: %s\n", weather.isRaining ? "YES" : "NO");
Serial.printf("š Rain forecast: %.1f mm\n", weather.rain);
// Make decision
bool water = shouldWater(moisture, weather);
int duration = 0;
if (water) {
duration = calculateDuration(moisture);
Serial.printf("š§ DECISION: WATER for %d seconds\n", duration);
// Water zones
waterZone(ZONE1_RELAY, "Zone 1 (Tomatoes)", duration);
delay(2000);
waterZone(ZONE2_RELAY, "Zone 2 (Peppers)", duration);
} else {
Serial.println("ā
DECISION: SKIP irrigation");
}
// Log to cloud
logDecision(moisture, water, duration, weather);
Serial.println("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
// Wait 15 minutes before next check
delay(900000);
}
A 5-hectare farm installed this complete smart irrigation system:
- š§ Before: Manual irrigation, 2 hours daily, wasted water
- š¤ After: System waters only when needed (soil < 35%)
- š§ļø Rain integration: System skipped irrigation 12 times in rainy season
- š° Savings: 40% water reduction = $150/month saved
"The system waters only when needed, and the rain detection saves us every time it rains." - Farm Manager, Tanzania
- ā Combine soil moisture, rain, weather forecast, and schedule for optimal results
- ā Rain priority: If raining OR forecast > 5mm ā skip irrigation
- ā Emergency override: Moisture < 25% waters even off-schedule
- ā Best watering times: Early morning (6 AM) and evening (6 PM) to reduce evaporation
- ā Log all decisions to OceanRemote for monitoring and improvement
- Apply these concepts directly to your farm or project.
- Take notes on important details for the quiz.
- Use the button below to track your progress.