AI Smart Irrigation System Using Soil & Weather Prediction
Complete IoT + ESP32 + AI Agent + n8n Automation + Telegram Voice Alerts + Google Sheets + ThingSpeak
This project is an AI-powered, IoT-based smart irrigation system that automatically waters plants according to soil moisture, weather conditions, and predicted water requirements. An ESP32 collects sensor data, an AI agent helps make irrigation decisions, and an n8n automation workflow coordinates the pump, Telegram notifications, cloud dashboard, and Google Sheets logging.
The goal is to reduce water wastage, minimize manual work, prevent overwatering, and provide remote monitoring and control through a web dashboard and Telegram.
This documentation includes the project architecture, components, circuit connections, flowcharts, AI decision logic, n8n workflows, Telegram voice notifications, ESP32 firmware, dashboard code, cloud integration, testing, and deployment instructions.
1. Project overview
1. Smart sensing — ESP32 + sensors
Measures soil moisture, air temperature, humidity, tank level, and optional rain conditions. Sends readings over Wi-Fi.
2. Intelligent irrigation controller
Determines whether watering is required, checks safety limits, and switches a suitable pump or valve through a driver.
3. n8n + AI agent automation
Receives sensor readings, obtains weather information, runs decision logic or an AI agent, logs events, and routes alerts.
4. Cloud dashboard + notifications
ThingSpeak displays trends, Google Sheets stores historical records, and Telegram sends text and optional spoken alerts.
Main features
-
Automatic irrigation using soil moisture thresholds.
-
Weather-aware watering decisions using current conditions and rainfall forecasts.
-
AI agent for recommendations, explanations, and anomaly detection.
-
ESP32-based pump control with maximum runtime and safety interlocks.
-
n8n workflow automation using webhooks and scheduled jobs.
-
Telegram notifications with text and generated voice messages.
-
Google Sheets logging for historical analysis.
-
ThingSpeak cloud charts for sensor monitoring.
-
Responsive webpage for monitoring and manual control.
-
Optional manual/automatic modes and remote commands.
Important design principle: The ESP32 must enforce the essential safety rules locally. The cloud AI agent may recommend watering, but it should never be the only mechanism preventing a pump from running dry or continuously.
2. System architecture
The project has four main layers: sensing, edge control, AI/automation, and user applications.
Layer 1 — Field sensors
Soil moisture · Temperature/humidity · Rain sensor · Tank-level switchLayer 2 — ESP32 edge controller
Sensor readings · Local irrigation rules · Relay/driver · Pump safety timer
Layer 3 — n8n automation server
Webhook · Weather API · AI Agent · Decision validation · Event routing
Telegram
Text + voice alerts and commands
Google Sheets
Logs and reports
ThingSpeak
Sensor history and charts
Web dashboard
Status and manual controls
Data flow
-
ESP32 reads the sensors every few seconds.
-
It converts the soil sensor reading into a calibrated moisture percentage.
-
It sends telemetry to an n8n webhook.
-
n8n retrieves weather forecasts and evaluates watering conditions.
-
An optional AI agent generates a recommendation and a human-readable explanation.
-
Validated commands and alerts are sent to Telegram; readings are stored in Google Sheets and ThingSpeak.
-
The ESP32 operates the pump only when local safety rules allow it.
-
The webpage displays the latest measurements and irrigation state.
The ESP32 can use Wi-Fi station mode to communicate with the automation server, as described in the Espressif Arduino-ESP32 Wi-Fi documentation .
3. Hardware components required
The following is a suitable prototype bill of materials. The exact pump, power supply, sensor calibration, and wiring depend on the size of your irrigation system.
Typical prototype: one ESP32, one soil-moisture zone, one low-voltage DC pump.
ESP32 DevKit
Quantity: 1
Wi-Fi-enabled microcontroller that reads sensors, controls the relay driver, and communicates with n8n and ThingSpeak.
Capacitive soil-moisture sensor
Quantity: 1 per zone
Provides an analog reading proportional to soil moisture. Calibrate it in dry and wet soil rather than relying on a generic percentage scale.
DHT22 / AM2302 sensor
Quantity: 1 (optional)
Measures ambient temperature and relative humidity. Weather forecasts can provide these measurements instead, but local readings help with monitoring.
DC water pump or solenoid valve
Quantity: 1 per controlled circuit
Delivers water. Choose its voltage, flow rate, and current rating to match the irrigation requirement.
Pump driver
Quantity: 1
A properly rated MOSFET driver or relay module switches the pump. Add inductive-load suppression and appropriate overcurrent protection.
Tank-level float switch
Quantity: 1 (strongly recommended)
Prevents the pump from running when the water tank is empty. A flow sensor can provide additional pump-failure detection.
Additional items:
-
5 V USB power supply for the ESP32, as appropriate for your board.
-
A separate, correctly rated pump power supply.
-
Jumper wires, breadboard for low-power testing, enclosure, tubing, and drip emitters.
-
Optional rain sensor, flow meter, OLED display, and backup power supply.
Safety: Never power a pump directly from an ESP32 GPIO pin. Do not connect a pump to the 3.3 V sensor supply. Keep wet components enclosed and use suitable isolation, fusing, and protection. If mains-powered equipment is involved, use a qualified electrician.
4. Circuit and schematic diagram
4.1 ESP32 wiring table
This example uses a generic capacitive sensor with analog output, an optional DHT22, a low-voltage pump driver, and a float switch.
|
Component |
ESP32 connection |
Purpose |
|---|---|---|
|
Soil sensor AO |
GPIO 34 |
Analog moisture reading |
|
Soil sensor VCC |
3.3 V, if supported by sensor |
Sensor supply |
|
Soil sensor GND |
GND |
Common signal reference |
|
DHT22 DATA |
GPIO 4 |
Temperature and humidity |
|
Driver input |
GPIO 26 |
Pump ON/OFF command |
|
Float switch |
GPIO 27 to GND |
Tank-low safety input |
|
Driver supply |
External rated supply |
Pump power |
Use an ESP32-compatible analog voltage. GPIO 34 is input-only, which is appropriate for analog measurement. Ensure the sensor output never exceeds the ESP32's permitted input voltage. Many capacitive sensors are sold for 3.3 V or 5 V; verify the specific model.
4.2 Electrical schematic
+-------------------------+
| ESP32 DevKit |
| |
Soil sensor AO --->| GPIO34 (ADC) |
DHT22 DATA ------->| GPIO4 |
Tank float ------->| GPIO27 (INPUT_PULLUP) |
| |
| GPIO26 ---------------------> Driver IN
| GND ------------------+ |
+-----------------------|---+
|
Soil sensor GND ---------------------------+
DHT22 GND ---------------------------------+
Driver signal GND -------------------------+
Pump power circuit (separate supply):
External DC (+) ---- Fuse ---- Pump (+)
Pump (-)
|
MOSFET / rated driver
|
External DC (-) -----------------+
Use a suitable flyback diode for a bare DC motor/inductive load
when required by the driver design. Follow the driver datasheet.
All low-voltage signal references must be connected as required
by the selected driver; use isolation where appropriate.
A relay module's input logic can be active HIGH or active LOW. The code below assumes active LOW. If your module is active HIGH, change the two relay constants and test the startup state with the pump disconnected.
5. Software and cloud accounts
|
Software / service |
Role |
|---|---|
|
Arduino IDE |
Compile and upload ESP32 firmware |
|
ESP32 Arduino core |
Wi-Fi, ADC and HTTP support |
|
n8n |
Webhooks, workflow orchestration, scheduling |
|
Open-Meteo |
Weather and precipitation forecast |
|
Telegram Bot API |
Text messages and voice-message delivery |
|
Google Sheets |
Persistent logs and event history |
|
ThingSpeak |
Time-series IoT dashboard |
|
Optional AI API |
Natural-language analysis and recommendations |
|
HTML, CSS, JavaScript |
Web dashboard |
Useful documentation:
6. Irrigation decision logic
The controller must distinguish measured soil moisture from forecast weather and from the AI's interpretation.
For the first version, use a transparent threshold algorithm. After the system has collected reliable data, add historical prediction or a trained model.
Example decision table
|
Soil moisture |
Rain forecast |
Tank |
Action |
|---|---|---|---|
|
Below 30% |
No significant rain |
Adequate |
Water, subject to local safety rules |
|
Below 30% |
Significant rain expected soon |
Adequate |
Usually defer and reassess |
|
Above 45% |
Any |
Adequate |
Keep pump OFF |
|
Any |
Any |
Empty / low |
Force pump OFF and alert |
|
Sensor invalid |
Any |
Any |
Fail safe: pump OFF |
|
Pump runtime limit reached |
Any |
Any |
Stop pump and alert |
These percentages are illustrative, not universal crop recommendations. Set thresholds according to soil type, plant species, root depth, and sensor calibration.
Flowchart
flowchart TD
A([Start / timer]) --> B[Read soil and tank sensors]
B --> C{Sensor data valid?}
C -- No --> D[Keep pump OFF]
D --> E[Log fault and send alert]
C -- Yes --> F[Send telemetry to n8n]
F --> G[Get weather forecast]
G --> H{Tank water available?}
H -- No --> I[Pump OFF + low-water alert]
H -- Yes --> J{Soil below dry threshold?}
J -- No --> K[Pump OFF]
J -- Yes --> L{Rain expected soon?}
L -- Yes --> M[Defer watering if safe]
L -- No --> N[Check cooldown and max runtime]
N --> O{All checks pass?}
O -- No --> K
O -- Yes --> P[Start pump]
P --> Q[Monitor moisture and elapsed time]
Q --> R{Target reached or timeout?}
R -- No --> Q
R -- Yes --> S[Stop pump]
S --> T[Log event and notify]
K --> U[Update cloud dashboard]
I --> U
M --> U
E --> U
T --> U
U --> A
6.1 How to add AI prediction
An AI agent is not automatically a predictive model. A practical implementation can combine three things:
-
Rules engine: enforces safety and determines whether irrigation is allowed.
-
Weather-aware predictor: estimates whether watering can be deferred and estimates water demand.
-
AI agent: explains the result, detects unusual readings, and answers Telegram questions using trusted data.
For a more advanced model, collect several weeks of sensor data and labels, then train a regression model to estimate future moisture or water demand. Candidate features include:
-
Current soil moisture and the last several readings.
-
Temperature and relative humidity.
-
Forecast rainfall amount and probability.
-
Crop type, soil type, and irrigation history.
-
Recent pump duration and flow-meter readings.
Use measured future moisture as the training target. Validate on a separate time period before allowing predictions to influence automated irrigation.
7. ESP32 firmware — complete starter code
This firmware implements the first working version of the irrigation controller. It:
-
Reads the soil sensor and optional DHT22.
-
Checks the tank float switch.
-
Starts and stops the pump using locally enforced moisture thresholds.
-
Limits pump runtime.
-
Sends sensor data to an n8n webhook.
-
Uploads readings to ThingSpeak.
-
Keeps the pump OFF if the sensor or tank safety check fails.
This baseline does not yet use an AI-generated weather decision to control the pump. That integration is added in the n8n section, and should only be enabled after command validation and fail-safe behavior are tested.
Step 1 — Install Arduino libraries
In Arduino IDE, install:
-
ESP32 board support by Espressif Systems.
-
DHT sensor libraryby Adafruit. -
Adafruit Unified Sensor. -
ArduinoJsonby Benoit Blanchon.
Select the appropriate ESP32 Dev Module and serial port.
Step 2 — Configure the firmware
Replace the Wi-Fi details and API placeholders before uploading. Keep private API keys out of public source repositories.
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <DHT.h>
// ---------- USER CONFIGURATION ----------
const char* WIFI_SSID = "YOUR_WIFI_NAME";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* N8N_WEBHOOK =
"https://YOUR_N8N_HOST/webhook/irrigation-telemetry";
const char* THINGSPEAK_WRITE_KEY =
"YOUR_THINGSPEAK_WRITE_KEY";
// ---------- PIN CONFIGURATION ----------
const int SOIL_PIN = 34;
const int DHT_PIN = 4;
const int PUMP_PIN = 26;
const int TANK_PIN = 27;
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);
// Most relay boards are active LOW.
// Change these if your driver uses active-HIGH logic.
const int PUMP_ON_LEVEL = LOW;
const int PUMP_OFF_LEVEL = HIGH;
// ---------- SOIL CALIBRATION ----------
// Replace these with actual ADC readings from your sensor.
// Many ESP32 boards provide 12-bit ADC readings (0-4095).
const int SOIL_DRY_RAW = 3000;
const int SOIL_WET_RAW = 1300;
// Illustrative moisture thresholds.
const float START_WATERING_BELOW = 30.0;
const float STOP_WATERING_ABOVE = 45.0;
// ---------- SAFETY LIMITS ----------
const unsigned long SAMPLE_INTERVAL_MS = 5000;
const unsigned long UPLOAD_INTERVAL_MS = 60000;
const unsigned long MAX_PUMP_MS = 30000;
const unsigned long MIN_OFF_MS = 120000;
bool pumpOn = false;
bool sensorFault = false;
unsigned long pumpStartedAt = 0;
unsigned long lastPumpStoppedAt = 0;
unsigned long lastSampleAt = 0;
unsigned long lastUploadAt = 0;
float soilPercent = 0;
float temperatureC = NAN;
float humidityPct = NAN;
bool tankLow = true;
void setPump(bool on) {
if (on == pumpOn) return;
digitalWrite(PUMP_PIN,
on ? PUMP_ON_LEVEL : PUMP_OFF_LEVEL);
pumpOn = on;
if (on) {
pumpStartedAt = millis();
Serial.println("PUMP ON");
} else {
lastPumpStoppedAt = millis();
Serial.println("PUMP OFF");
}
}
float readSoilPercent(int raw) {
// Dry raw value should be greater than wet raw value.
if (SOIL_DRY_RAW <= SOIL_WET_RAW) return NAN;
float value = 100.0f *
(SOIL_DRY_RAW - raw) /
(SOIL_DRY_RAW - SOIL_WET_RAW);
return constrain(value, 0.0f, 100.0f);
}
void connectWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
Serial.print("Connecting to Wi-Fi");
unsigned long start = millis();
while (WiFi.status() != WL_CONNECTED &&
millis() - start < 20000) {
delay(500);
Serial.print(".");
}
Serial.println();
if (WiFi.status() == WL_CONNECTED) {
Serial.print("Connected. IP: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("Wi-Fi unavailable; local control continues.");
}
}
void readSensors() {
int raw = analogRead(SOIL_PIN);
soilPercent = readSoilPercent(raw);
temperatureC = dht.readTemperature();
humidityPct = dht.readHumidity();
// INPUT_PULLUP: switch between GPIO27 and GND.
// This example assumes LOW means the tank is low.
tankLow = (digitalRead(TANK_PIN) == LOW);
sensorFault = !isfinite(soilPercent);
Serial.printf(
"Soil: %.1f%% | Tank low: %s | Temp: %.1f C\n",
soilPercent,
tankLow ? "YES" : "NO",
temperatureC
);
}
void controlPump() {
// Fail-safe: never irrigate with invalid soil readings.
if (sensorFault || tankLow) {
setPump(false);
return;
}
// Hard maximum runtime is checked locally.
if (pumpOn &&
millis() - pumpStartedAt >= MAX_PUMP_MS) {
setPump(false);
Serial.println("Maximum pump runtime reached.");
return;
}
// Hysteresis prevents rapid ON/OFF cycling.
if (!pumpOn &&
soilPercent < START_WATERING_BELOW &&
millis() - lastPumpStoppedAt >= MIN_OFF_MS) {
setPump(true);
} else if (pumpOn &&
soilPercent >= STOP_WATERING_ABOVE) {
setPump(false);
}
}
void sendTelemetry() {
if (WiFi.status() != WL_CONNECTED) return;
// Send telemetry to n8n.
HTTPClient http;
http.setTimeout(5000);
http.begin(N8N_WEBHOOK);
http.addHeader("Content-Type", "application/json");
JsonDocument doc;
doc["deviceId"] = "irrigation-01";
doc["soilMoisture"] = soilPercent;
doc["temperatureC"] = isfinite(temperatureC)
? temperatureC : -999;
doc["humidityPct"] = isfinite(humidityPct)
? humidityPct : -999;
doc["tankLow"] = tankLow;
doc["pumpOn"] = pumpOn;
doc["sensorFault"] = sensorFault;
doc["uptimeMs"] = millis();
String payload;
serializeJson(doc, payload);
int status = http.POST(payload);
Serial.printf("n8n HTTP status: %d\n", status);
http.end();
// Send telemetry to ThingSpeak.
HTTPClient ts;
String url =
"https://api.thingspeak.com/update?api_key=" +
String(THINGSPEAK_WRITE_KEY) +
"&field1=" + String(soilPercent, 1) +
"&field2=" +
String(isfinite(temperatureC) ? temperatureC : 0, 1) +
"&field3=" +
String(isfinite(humidityPct) ? humidityPct : 0, 1) +
"&field4=" + String(pumpOn ? 1 : 0) +
"&field5=" + String(tankLow ? 1 : 0);
ts.setTimeout(5000);
ts.begin(url);
int tsStatus = ts.GET();
Serial.printf("ThingSpeak HTTP status: %d\n", tsStatus);
ts.end();
}
void setup() {
Serial.begin(115200);
// Configure output and establish OFF state immediately.
digitalWrite(PUMP_PIN, PUMP_OFF_LEVEL);
pinMode(PUMP_PIN, OUTPUT);
digitalWrite(PUMP_PIN, PUMP_OFF_LEVEL);
pinMode(TANK_PIN, INPUT_PULLUP);
analogReadResolution(12);
dht.begin();
// Initialize cooldown so watering is not delayed at boot.
lastPumpStoppedAt = millis() - MIN_OFF_MS;
connectWiFi();
}
void loop() {
unsigned long now = millis();
// Continue local safety control even if Wi-Fi fails.
if (now - lastSampleAt >= SAMPLE_INTERVAL_MS) {
lastSampleAt = now;
readSensors();
controlPump();
}
if (now - lastUploadAt >= UPLOAD_INTERVAL_MS) {
lastUploadAt = now;
if (WiFi.status() != WL_CONNECTED) {
WiFi.reconnect();
}
sendTelemetry();
}
delay(20);
}
Step 3 — Calibrate the moisture sensor
-
Record several raw ADC readings with the sensor in dry soil.
-
Record readings in thoroughly watered soil after excess water drains.
-
Update
SOIL_DRY_RAWandSOIL_WET_RAW. -
Observe the calculated percentage over several watering cycles.
-
Adjust
START_WATERING_BELOWandSTOP_WATERING_ABOVEfor your crop.
The formula is a linear calibration, not a laboratory measurement of volumetric water content. Soil type, sensor placement, salinity, and temperature affect its accuracy.
Step 4 — Test before connecting the pump
-
Disconnect the pump and upload the firmware.
-
Open Serial Monitor at 115200 baud.
-
Confirm the moisture reading changes when the sensor is moved between dry and wet soil.
-
Verify the float-switch polarity.
-
Confirm that a sensor fault or low tank stops the output.
-
Test the relay/driver with a suitable low-voltage test load.
-
Only then connect the properly rated pump circuit.
Firmware limitation: The runtime and cooldown values are examples. The sketch uses a tank-low interlock, but does not yet include a physical emergency-stop circuit, flow verification, or persistent fault latching. Add those for a permanent installation.
8. n8n automation — workflow design
Create an n8n instance with HTTPS access if the ESP32 will send data over the public internet. A local n8n server is also possible, provided the ESP32 can reach it.
Create a workflow called AI Smart Irrigation Automation.
Workflow A — Sensor ingestion and decision support
flowchart TD
A[ESP32 HTTP POST] --> B[Webhook Trigger]
B --> C[Validate payload and device]
C --> D{Valid readings?}
D -- No --> E[Log fault + Telegram alert]
D -- Yes --> F[HTTP Request: Weather API]
F --> G[Code: Apply deterministic rules]
G --> H[Optional AI Agent: Explain decision]
H --> I[Validate AI output]
I --> J[Google Sheets: Append row]
J --> K[ThingSpeak / dashboard data]
K --> L{Alert needed?}
L -- Yes --> M[Telegram text + optional voice]
L -- No --> N[Finish]
M --> N
The critical nodes and their configurations are described below.
Node 1 — Webhook
-
Node:
Webhook -
HTTP method:
POST -
Path:
irrigation-telemetry -
Response mode: Respond to Webhook node, or use the configured immediate response.
-
Expected content type:
application/json
Example payload:
{
"deviceId": "irrigation-01",
"soilMoisture": 24.6,
"temperatureC": 32.1,
"humidityPct": 41.5,
"tankLow": false,
"pumpOn": false,
"sensorFault": false,
"uptimeMs": 360000
}
During development, use the test webhook URL shown by n8n. When the workflow is active, change the ESP32 to the production webhook URL. Do not expose an unrestricted webhook that accepts arbitrary commands.
Node 2 — Validate the sensor data
Add an IF node or Code node after the webhook.
Check:
-
deviceIdmatches a known device. -
soilMoistureis numeric and in the range 0–100. -
tankLowandsensorFaultare booleans. -
The payload contains a timestamp or uptime.
-
The device is authorized using a secret token or equivalent authentication mechanism.
Invalid data should generate a diagnostic event, not an irrigation command.
Node 3 — Retrieve the weather forecast
Use an HTTP Request node to call Open-Meteo. Its forecast endpoint supports weather and precipitation variables.
Example URL for a demonstration location in New Delhi:
https://api.open-meteo.com/v1/forecast?latitude=28.6139&longitude=77.2090¤t=temperature_2m,relative_humidity_2m,precipitation&hourly=precipitation_probability,precipitation&forecast_days=1&timezone=Asia%2FKolkata
For a real deployment, replace the coordinates with the actual farm or garden location.
Suggested configuration:
-
Method:
GET -
Response format: JSON
-
Timeout: 10 seconds
-
On API failure: retain the local controller's rules and do not assume that rain is expected or that the forecast is reliable.
The forecast's hourly precipitation probability and precipitation amount can be used to determine whether watering should be deferred. Use a configured time horizon, such as the next three hours, and tune the threshold for your local climate.
Node 4 — Apply deterministic rules
Before invoking the AI agent, add a Code node that creates a trusted irrigation recommendation.
Example:
const input = $node["Webhook"].json.body;
// Example only: connect these paths to the actual
// JSON returned by your HTTP Request weather node.
const weather = $node["Weather Forecast"].json;
const moisture = Number(input.soilMoisture);
const tankLow = input.tankLow === true;
const sensorFault = input.sensorFault === true;
const hourly = weather.hourly || {};
const probabilities = hourly.precipitation_probability || [];
const rainfall = hourly.precipitation || [];
const upcomingRainProbability = probabilities.length
? Math.max(...probabilities.slice(0, 3))
: null;
const upcomingRainMm = rainfall.length
? rainfall.slice(0, 3).reduce((sum, n) => sum + Number(n || 0), 0)
: null;
const forecastUnavailable =
upcomingRainProbability === null || upcomingRainMm === null;
let action = "KEEP_OFF";
let reason = "Soil moisture is sufficient.";
if (sensorFault || !Number.isFinite(moisture)) {
reason = "Invalid sensor reading; fail safe.";
} else if (tankLow) {
reason = "Tank water is low.";
} else if (moisture < 30) {
if (forecastUnavailable) {
reason = "Forecast unavailable; local controller decides.";
} else if (upcomingRainProbability >= 70 &&
upcomingRainMm >= 1) {
reason = "Rain is likely soon; defer and reassess.";
} else {
action = "WATER_RECOMMENDED";
reason = "Soil is dry and significant rain is not forecast.";
}
}
return [{
json: {
deviceId: input.deviceId,
soilMoisture: moisture,
tankLow,
sensorFault,
upcomingRainProbability,
upcomingRainMm,
forecastUnavailable,
action,
reason,
timestamp: new Date().toISOString()
}
}];
Important: The node name references (Webhook and Weather Forecast) must exactly match your actual n8n node names. Depending on your n8n version, the Webhook node's input may be nested differently. Inspect the execution data and adjust the JSON paths accordingly.
This node produces a recommendation, not a command to turn on the pump. The ESP32 sketch above remains the actual controller until a separately authenticated, acknowledged, safety-checked command interface is implemented.
Node 5 — Add the AI Agent
Connect an AI Agent node after the deterministic rules node. Configure a supported language model credential and a prompt like the following.
You are the advisory agent for an automated irrigation system.
Analyze only the supplied sensor readings and weather forecast.
Explain whether irrigation is recommended and why.
Rules:
1. Never override sensor faults, low tank status, or safety limits.
2. Never invent readings or claim that watering has occurred.
3. Treat missing weather data as unavailable.
4. Give concise, farmer-friendly explanations.
5. Your response is advisory, not a direct hardware command.
6. If readings are inconsistent, recommend inspection.
Return structured JSON with:
{
"recommendation": "WATER | DEFER | KEEP_OFF | INSPECT",
"reason": "short explanation",
"confidence": 0.0
}
Configure the AI node to receive the validated rule output and weather summary, not arbitrary untrusted text from a public webhook.
The next node should validate the AI result against an allowed set of recommendations. Never connect a language model directly to a GPIO, unrestricted HTTP endpoint, or pump relay.
Node 6 — Google Sheets logging
Create a spreadsheet named Smart Irrigation Logs and a tab named Readings.
Use the Google Sheets node with an append-row operation.
Suggested columns:
|
Column |
Description |
|---|---|
|
Timestamp |
Event time |
|
Device ID |
ESP32 identifier |
|
Soil Moisture |
Calibrated moisture percentage |
|
Temperature |
Ambient temperature |
|
Humidity |
Relative humidity |
|
Rain Probability |
Forecast probability |
|
Forecast Rain (mm) |
Expected precipitation |
|
Tank Low |
Boolean |
|
Pump State |
Observed pump state |
|
Recommendation |
Rule-engine recommendation |
|
AI Explanation |
Agent explanation |
|
Fault |
Error or diagnostic |
Map each column to the corresponding validated value. Use a separate sheet or event type for fault records so that failed sensor readings are not mistaken for valid moisture measurements.
Node 7 — Telegram text notification
Create a Telegram bot through the official @BotFather , store its token as an n8n credential, and send a test message to your private chat.
Example notification:
🌱 AI Smart Irrigation Report
Device: irrigation-01 Soil moisture: 24.6% Temperature: 32.1°C Tank status: Water available Weather: No significant rain expected
Recommendation: Watering recommended.
Reason: The soil is below the configured dry threshold. The controller will still enforce its local safety limits.
Status: Advisory generated by the AI irrigation system.
In the Telegram node:
-
Select the send-message operation.
-
Configure your Telegram bot credential and target chat ID.
-
Enable Markdown or HTML parsing only if the message is formatted for that mode.
-
Send alerts for pump faults, low water, repeated sensor errors, and unusually long watering cycles.
For a production deployment, allow only approved Telegram users to issue commands.
9. Telegram voice alerts
A Telegram bot can deliver both a written alert and a spoken version of the same message. To generate an actual voice message, the workflow needs a text-to-speech (TTS) service; Telegram does not automatically convert ordinary text messages to speech.
Voice alert workflow
flowchart TD
A[Validated irrigation event] --> B[Create alert text]
B --> C[Telegram Send Message]
B --> D[Text-to-Speech API]
D --> E[Receive audio file]
E --> F[Send Voice to Telegram]
F --> G[Log delivery status]
Implementation steps
-
Create the alert text from the validated rule result.
-
Pass the text to a TTS provider that supports your preferred language and voice.
-
Receive the generated audio as binary data or a file URL.
-
Use an n8n
HTTP Requestnode to call the Telegram Bot API'ssendVoicemethod. -
Supply the bot token, destination chat ID, and audio file using the format required by Telegram.
-
Record the event and delivery status in Google Sheets.
Telegram's Bot API supports voice messages, and n8n can orchestrate the notification workflow. See the Telegram node documentation .
Example voice script:
Attention. Your smart irrigation system has detected dry soil. Current soil moisture is twenty-five percent. The weather forecast indicates a low chance of significant rain. Please check that the water tank is filled. The irrigation controller will operate only when its safety conditions are satisfied.
For Hindi or another regional language, generate the spoken text in that language and configure a matching TTS voice. If the provider returns MP3 rather than an OGG/Opus voice-note format, verify Telegram's accepted audio format or convert the file before sending.
Optional: Voice commands from Telegram
You can also create a command workflow for messages such as:
-
/status— request the latest sensor and pump state. -
/auto— enable automatic mode. -
/stop— request an emergency stop. -
/water 10— request watering for a limited duration.
Do not let an unverified Telegram message control the pump. Validate the sender's numeric Telegram user ID, validate command arguments, enforce a maximum duration, and require local safety checks. An emergency stop should remain effective even if the internet or n8n is unavailable.
10. ThingSpeak cloud dashboard
ThingSpeak stores time-series sensor values and displays charts without requiring you to build a charting backend from scratch. A channel supports up to eight numeric fields, making it suitable for this prototype.
Step 1 — Create a channel
Open ThingSpeak and create a channel named AI Smart Irrigation.
Configure these fields:
|
Field |
Name |
Value |
|---|---|---|
|
Field 1 |
Soil Moisture |
Percentage |
|
Field 2 |
Temperature |
°C |
|
Field 3 |
Humidity |
% |
|
Field 4 |
Pump State |
0 = OFF, 1 = ON |
|
Field 5 |
Tank Low |
0 = normal, 1 = low |
|
Field 6 |
Rain Probability |
% (optional) |
|
Field 7 |
Rainfall Forecast |
mm (optional) |
|
Field 8 |
Irrigation Recommendation |
Encode numerically if needed |
Copy the channel's Write API Key into the ESP32 firmware. Keep the channel private unless you intentionally want public read access.
Step 2 — Upload readings
The firmware in Section 7 sends moisture, temperature, humidity, pump state, and tank status to ThingSpeak using the update endpoint.
The key pattern is:
https://api.thingspeak.com/update?api_key=YOUR_WRITE_KEY&field1=24.6&field2=32.1&field3=41.5&field4=0&field5=0
Replace the example values with live measurements. A successful update normally returns a positive entry ID; a failed
