An IoT plant watering robot combines soil moisture sensing, an automated water pump, and real-time Telegram alerts to keep your plants watered even when you travel—a genuinely useful project that solves a real problem. This guide builds a complete system using an ESP32 (or ESP8266), a capacitive soil moisture sensor, a 5V relay-controlled submersible pump, and the Telegram Bot API for remote monitoring and manual override.
Table of Contents
- Hardware Components
- Wiring Diagram
- Telegram Bot Setup
- Complete ESP32 Code
- Sensor Calibration
- Watering Schedule Logic
- Recommended Products
- FAQ
Hardware Components
- ESP32 DevKit V1 (or NodeMCU ESP8266)
- Capacitive soil moisture sensor v1.2 (NOT resistive — resistive corrodes in soil)
- 5V single-channel relay module
- 3–5V submersible mini pump (RS385 type) OR 5V peristaltic pump for cleaner flow
- Silicone tubing (4–6 mm ID)
- Water reservoir (1–2 litre bottle or container)
- 5V 2A USB adapter or LiPo battery + TP4056 charger for portable use
- Jumper wires, breadboard or PCB
- Waterproof enclosure box (optional, for outdoor use)
Wiring Diagram
ESP32 Pin Assignments:
- 3.3V → Capacitive moisture sensor VCC
- GND → Common ground
- GPIO34 (ADC1_6) → Moisture sensor AOUT
- GPIO26 → Relay IN (active LOW)
- GPIO2 → Onboard LED (watering status indicator)
Relay Module:
- VCC → 5V from USB
- GND → GND
- IN → GPIO26
- COM → 5V pump+
- NO → Pump VCC (normally open — pump OFF when relay inactive)
Pump:
- Pump+ → Relay NO terminal
- Pump- → GND (5V rail ground)
IMPORTANT: Pump and ESP32 must share a common GND.
Use a 100µF capacitor across pump terminals to suppress voltage spikes.
Telegram Bot Setup
- Open Telegram and search for @BotFather
- Send
/newbot, choose a name and username - Copy the Bot Token (looks like:
5839847291:AAFxxx...) - Send a message to your new bot
- Get your Chat ID:
https://api.telegram.org/bot{TOKEN}/getUpdates— look for"chat":{"id":123456789} - Test:
https://api.telegram.org/bot{TOKEN}/sendMessage?chat_id={CHAT_ID}&text=Hello
The bot can also accept commands from you to manually trigger watering or check soil status.
Complete ESP32 Code
#include <WiFi.h>
#include <HTTPClient.h>
#include <time.h>
// WiFi credentials
const char* SSID = "YourWiFiName";
const char* PASS = "YourWiFiPassword";
// Telegram
const String BOT_TOKEN = "YOUR_BOT_TOKEN";
const String CHAT_ID = "YOUR_CHAT_ID";
// Hardware pins
const int MOISTURE_PIN = 34;
const int RELAY_PIN = 26;
const int LED_PIN = 2;
// Thresholds (calibrate for your soil)
const int DRY_THRESHOLD = 2800; // ADC above this = soil is dry
const int WET_THRESHOLD = 1200; // ADC below this = soil is wet
// Watering settings
const int WATER_DURATION_MS = 5000; // Pump on for 5 seconds
const int CHECK_INTERVAL_MS = 30 * 60 * 1000; // Check every 30 min
void sendTelegram(String message) {
if (WiFi.status() != WL_CONNECTED) return;
HTTPClient http;
String url = "https://api.telegram.org/bot" + BOT_TOKEN +
"/sendMessage?chat_id=" + CHAT_ID +
"&text=" + message;
http.begin(url);
int httpCode = http.GET();
if (httpCode != 200) {
Serial.println("Telegram send failed: " + String(httpCode));
}
http.end();
}
int readMoisture() {
// Average 10 readings for stability
long sum = 0;
for (int i = 0; i DRY_THRESHOLD) {
// Soil is dry — water it
waterPlant(moisture);
} else if (moisture 0) {
lastUpdateId++;
waterPlant(readMoisture());
return true;
}
if (payload.indexOf("/status") > 0) {
lastUpdateId++;
int m = readMoisture();
int pct = constrain(map(m, 2800, 1200, 0, 100), 0, 100);
String msg = "📊 Plant Status%0AMoisture: " + String(pct) + "%25%0AADC: " + String(m);
sendTelegram(msg);
return true;
}
}
http.end();
return false;
}
void setup() {
Serial.begin(115200);
pinMode(RELAY_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
digitalWrite(RELAY_PIN, HIGH); // Relay OFF (active LOW)
WiFi.begin(SSID, PASS);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500); Serial.print(".");
}
Serial.println("nConnected! IP: " + WiFi.localIP().toString());
sendTelegram("🤖 Plant Watering Bot online!%0AIP: " + WiFi.localIP().toString());
checkAndWater(); // Initial check on boot
}
void loop() {
static unsigned long lastCheck = 0;
// Check Telegram commands every 10 seconds
checkTelegramCommands();
// Check soil moisture on schedule
if (millis() - lastCheck >= CHECK_INTERVAL_MS) {
lastCheck = millis();
checkAndWater();
}
delay(10000); // 10s polling interval
}
Sensor Calibration
// Calibration procedure:
// 1. Run this code with sensor in DRY soil (or in air)
// → note the ADC value (e.g. 3200) — this is DRY_THRESHOLD
// 2. Submerge sensor tip in water
// → note the ADC value (e.g. 1100) — this is WET_THRESHOLD
// 3. Set midpoint for "water now" trigger:
// e.g. DRY_THRESHOLD = 2700 (water when above this)
void calibrate() {
Serial.println("Place sensor in DRY soil. Reading in 3s...");
delay(3000);
int dry = readMoisture();
Serial.println("Dry value: " + String(dry));
Serial.println("Submerge in water. Reading in 3s...");
delay(3000);
int wet = readMoisture();
Serial.println("Wet value: " + String(wet));
Serial.println("Set DRY_THRESHOLD to " + String(dry - 200));
Serial.println("Set WET_THRESHOLD to " + String(wet + 200));
}
Watering Schedule Logic
The simplest approach is moisture-based triggering (pump only when dry). For plants that prefer consistent schedules regardless of current moisture, add time-based overrides:
// Time-based watering (requires NTP sync)
configTime(19800, 0, "pool.ntp.org"); // IST = UTC+5:30 = 19800s
bool isWateringTime() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) return false;
// Water at 7:00 AM and 6:00 PM IST
if (timeinfo.tm_hour == 7 && timeinfo.tm_min == 0) return true;
if (timeinfo.tm_hour == 18 && timeinfo.tm_min == 0) return true;
return false;
}
ESP32-based all-in-one driver board with GPIO, ADC, and UART. Ideal for IoT plant watering robots with motor/pump control and Wi-Fi built in.
View ESP32 Driver Board on Zbotic →
Power your IoT plant watering bot from a 9V battery for portable, wireless operation in your garden or balcony.
View Battery on Zbotic →
FAQ
Why use a capacitive sensor instead of a resistive one?
Resistive soil sensors pass current through the soil to measure conductivity. This causes electrolysis that corrodes the sensor within 2–4 weeks. Capacitive sensors measure dielectric constant without current flow through the soil — they last years in permanent installations.
Can I water multiple plants with one ESP32?
Yes. Add one relay and one capacitive sensor per plant. Wire all moisture sensors to separate ADC pins (ESP32 has 18 ADC channels). Control each relay independently based on its corresponding sensor reading.
The pump runs but no water flows. What is wrong?
Common issues: (1) pump inlet is not submerged — ensure the intake is at the bottom of the reservoir, (2) tubing has an air lock — prime the tube by sucking briefly with mouth or by tilting the reservoir, (3) pump is underpowered — check that the 5V supply provides at least 500 mA for the pump.
My ESP32 resets when the relay clicks. How do I fix this?
The relay coil and pump create voltage spikes. Fixes: (1) use a flyback diode (1N4007) across the relay coil, (2) add a 100µF capacitor from 5V to GND near the relay, (3) use a separate 5V rail for the relay and pump, sharing only GND with the ESP32.
Add comment