Building a drip irrigation automation system with ESP32 and soil moisture control is one of the highest-impact projects for Indian farmers and home gardeners. India’s drip irrigation area has grown significantly under the Pradhan Mantri Krishi Sinchayee Yojana (PMKSY) scheme, but even manually operated drip systems waste water through human error in timing. An ESP32-based automation system that reads soil moisture and triggers irrigation only when soil is dry can reduce water usage by 30-50% while improving crop health. This comprehensive tutorial covers the complete build.
Table of Contents
- System Design Overview
- Components List and Costs
- Wiring Diagram
- Controlling the Pump with a Relay
- Complete ESP32 Code
- Local Web Interface for Monitoring
- Adding Time-Based Scheduling
- Frequently Asked Questions
System Design Overview
The automation system works on a simple principle: continuously monitor soil moisture, and when it drops below a threshold (soil is dry), activate the drip irrigation pump until moisture reaches the upper threshold (soil is adequately moist). This hysteresis-based control prevents rapid cycling and ensures gradual, deep watering.
System components and their roles:
- ESP32: Central controller — reads sensors, controls relay, hosts web interface, handles Wi-Fi connectivity
- Capacitive soil moisture sensor: Measures soil moisture level (0-100% VWC approximately)
- 5V relay module: Switches mains-powered (230V AC) or 12V DC pump on/off based on ESP32 signal
- 12V DC submersible pump or 24V solenoid valve: Controls water flow into drip irrigation system
- Real-time clock (DS3231): Optional — for time-based scheduling override
- OLED display: Optional — local status display
Components List and Costs
- ESP32 development board (₹350-500)
- Capacitive soil moisture sensor x2 (₹160-400) — use 2 sensors for averaging
- 5V 1-channel relay module (₹50-100)
- 12V DC submersible pump or 24V AC solenoid valve (₹300-600)
- 12V 2A adapter for pump power supply (₹200-300)
- DS3231 RTC module (₹100-200)
- 0.96-inch OLED display (optional, ₹100-200)
- IP65 enclosure (₹200-400)
- Weatherproof cable glands (₹50-100)
- Power supply for ESP32 (5V adapter or LiPo + TP4056)
Total cost: approximately ₹1,500-2,800. This is a fraction of commercial irrigation timers that sell for ₹5,000-15,000 and lack soil moisture sensing capability.
Wiring Diagram
| Component | Pin | ESP32 GPIO |
|---|---|---|
| Soil Sensor 1 | AOUT | GPIO 34 (ADC1_6) |
| Soil Sensor 2 | AOUT | GPIO 35 (ADC1_7) |
| Relay Module | IN | GPIO 26 |
| Relay Module | VCC | 5V (Vin) |
| Pump | +/– | Via relay NO/COM |
Safety note: If controlling a 230V AC pump, use a properly rated relay (minimum 10A 250VAC rating) and ensure all mains wiring is done by or checked by a qualified electrician. In India, mains voltage is 230V AC 50Hz — never work on live AC circuits. For safety, strongly prefer using a 12V or 24V DC submersible pump or solenoid valve that can be switched by a DC relay without involving mains power.
Controlling the Pump with a Relay
The 5V relay module has three terminals for the switched load:
- COM (Common): Connect to power supply positive (+12V or +24V)
- NO (Normally Open): Connect to pump positive terminal. Circuit is open (pump off) when relay is not energised.
- NC (Normally Closed): Leave unconnected for this application
Most relay modules are active-LOW — the relay energises when the control pin is LOW. Set the relay pin LOW to turn the pump ON and HIGH to turn it OFF. Some relay modules are active-HIGH — check the module documentation. The code below handles both cases with a configurable RELAY_ACTIVE_LOW constant.
Complete ESP32 Code
#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#define MOISTURE_PIN1 34
#define MOISTURE_PIN2 35
#define RELAY_PIN 26
#define RELAY_ACTIVE_LOW true // Most relay modules are active-LOW
// Calibrated values - adjust for your sensors
const int DRY_VALUE = 3200; // ADC when soil is dry
const int WET_VALUE = 1500; // ADC when soil is fully moist
// Irrigation thresholds
const int MOISTURE_LOW = 35; // % - turn pump ON when below this
const int MOISTURE_HIGH = 65; // % - turn pump OFF when above this
const int MAX_RUN_TIME = 300; // seconds - safety limit on pump run time
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
WebServer server(80);
bool pumpRunning = false;
unsigned long pumpStartTime = 0;
void setPump(bool state) {
bool relayState = RELAY_ACTIVE_LOW ? !state : state;
digitalWrite(RELAY_PIN, relayState ? HIGH : LOW);
pumpRunning = state;
if (state) pumpStartTime = millis();
Serial.println(state ? "Pump ON" : "Pump OFF");
}
int readMoisture() {
int raw1 = analogRead(MOISTURE_PIN1);
int raw2 = analogRead(MOISTURE_PIN2);
int avgRaw = (raw1 + raw2) / 2;
int moisture = map(avgRaw, DRY_VALUE, WET_VALUE, 0, 100);
return constrain(moisture, 0, 100);
}
void handleRoot() {
int moisture = readMoisture();
String html = "<html><body><h1>Drip Irrigation Controller</h1>";
html += "<p>Soil Moisture: <b>" + String(moisture) + "%</b></p>";
html += "<p>Pump: <b>" + String(pumpRunning ? "ON" : "OFF") + "</b></p>";
html += "<form action='/pump' method='post'>";
html += "<button name='state' value='on'>Force ON</button> ";
html += "<button name='state' value='off'>Force OFF</button></form>";
html += "</body></html>";
server.send(200, "text/html", html);
}
void setup() {
Serial.begin(115200);
pinMode(RELAY_PIN, OUTPUT);
setPump(false); // Ensure pump is off at start
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("IP: " + WiFi.localIP().toString());
server.on("/", handleRoot);
server.begin();
}
void loop() {
server.handleClient();
// Safety: auto-off after max run time
if (pumpRunning && (millis() - pumpStartTime > MAX_RUN_TIME * 1000UL)) {
Serial.println("Safety timeout - pump off");
setPump(false);
}
int moisture = readMoisture();
if (!pumpRunning && moisture MOISTURE_HIGH) {
setPump(false);
}
Serial.printf("Moisture: %d%% Pump: %s
", moisture, pumpRunning ? "ON" : "OFF");
delay(10000); // Check every 10 seconds
}
Local Web Interface for Monitoring
The code above includes a basic web server that serves a control dashboard at the ESP32’s IP address on your local network. Access it from any browser on the same Wi-Fi: type the IP address (shown in serial monitor on startup) into a browser. The dashboard shows real-time moisture percentage, pump status, and manual override buttons.
For remote access without a static IP, add mDNS: include ESP32’s ESPmDNS library and call MDNS.begin(“irrigation”) in setup. Then access the dashboard at http://irrigation.local from any device on the same network without knowing the IP address.
Adding Time-Based Scheduling
In Indian agricultural contexts, irrigation scheduling must account for time of day — watering during peak afternoon heat (12-3 PM) in summer causes excessive evaporation. Early morning (5-7 AM) or evening (6-8 PM) watering is far more efficient. Add a DS3231 RTC and scheduling logic:
#include <RTClib.h>
RTC_DS3231 rtc;
bool isIrrigationTime() {
DateTime now = rtc.now();
int hour = now.hour();
// Allow irrigation only 5-7 AM and 6-8 PM
return (hour >= 5 && hour = 18 && hour < 20);
}
// In loop(), modify the condition:
if (!pumpRunning && moisture < MOISTURE_LOW && isIrrigationTime()) {
setPump(true);
}
Frequently Asked Questions
How many zones can a single ESP32 control for drip irrigation?
Each irrigation zone requires one relay channel. With a 4-channel relay module and 4 soil moisture sensors, one ESP32 can control 4 independent zones — enough for a small farm or large kitchen garden. For larger systems with 8+ zones, use a 16-channel relay board and analog multiplexer (CD4051 or MUX36S08) to read more sensors. Industrial-scale multi-zone irrigation requires dedicated irrigation controllers, not ESP32 expansion.
What submersible pump is suitable for drip irrigation automation in India?
For garden and small farm drip systems, a 12V DC submersible pump with 500-800 litre/hour flow rate is ideal. Ensure the pump head pressure is compatible with your drip emitter requirements (typically 0.5-1.5 bar for standard drip emitters). For larger farm systems (1 hectare and above), use a 0.5 HP or 1 HP agricultural motor with a 24V AC solenoid valve per zone — the ESP32 controls solenoids, not the motor directly.
How do I prevent false triggers from reading errors during rain?
During rain, soil moisture rises rapidly and may trigger the system to think irrigation is needed less than it is. Add a rain sensor (a simple raindrops detection module) as an override: if rain is detected, suspend all irrigation regardless of soil moisture readings. Also implement a minimum post-irrigation waiting period (e.g., do not re-irrigate within 4 hours of the previous cycle) to allow moisture to distribute through the soil before re-reading.
My capacitive soil moisture sensor reads differently after 6 months. Why?
Even capacitive sensors drift over time due to: oxidation of the exposed copper PCB traces (despite polymer coating), biological fouling from soil microorganisms, and gradual changes in soil structure around the probe (compaction from roots, swelling/shrinking of clay soils). Recalibrate annually — remove the sensor, clean with a dry cloth, take fresh dry and wet reference readings, and update your DRY_VALUE and WET_VALUE constants.
Can I integrate this system with Alexa or Google Home?
Yes, using the SinricPro library for ESP32 which provides Alexa and Google Home integration without paid accounts. Alternatively, use Home Assistant (self-hosted) with the ESP32 MQTT integration. Commands like “Alexa, turn on garden irrigation” trigger the manual override through the relay. Note that voice commands bypass moisture sensing — add a safety timer to prevent indefinite pump operation from voice commands.
Add comment