Saturday, 17 October 2026

AI Smart Irrigation using Soil & Weather Prediction

AI Smart Irrigation System Using Soil & Weather Prediction

Complete IoT + ESP32 + AI Agent + n8n Automation + Telegram Voice Alerts + Google Sheets + ThingSpeak

ESP32 IoT Smart Irrigation System: Remote Monitoring & Automation
 
 
Buy Weather Monitoring Station-LearnElectronics India
 
 
GitHub - simm36465/Smart-irrigation: Smart Irrigation with ESP32 and Flutter Android Application · GitHub
 
 

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

Lesson 02: Capacitive Soil Moisture Module — SunFounder Universal Maker Sensor Kit  documentation
 
 

1. Smart sensing — ESP32 + sensors

Measures soil moisture, air temperature, humidity, tank level, and optional rain conditions. Sends readings over Wi-Fi.

 
Videos DIY educativos de electrónica y robótica. | Como Lo Hice Inventor
 
 

2. Intelligent irrigation controller

Determines whether watering is required, checks safety limits, and switches a suitable pump or valve through a driver.

 
Diving deeper into n8n: node types, best practices and a workflow that tells you when to grab an umbrella
 
 

3. n8n + AI agent automation

Receives sensor readings, obtains weather information, runs decision logic or an AI agent, logs events, and routes alerts.

 
Integrating Artificial Intelligence into an Automated Irrigation System
 
 

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 switch

Layer 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

  1. ESP32 reads the sensors every few seconds.

  2. It converts the soil sensor reading into a calibrated moisture percentage.

  3. It sends telemetry to an n8n webhook.

  4. n8n retrieves weather forecasts and evaluates watering conditions.

  5. An optional AI agent generates a recommendation and a human-readable explanation.

  6. Validated commands and alerts are sent to Telegram; readings are stored in Google Sheets and ThingSpeak.

  7. The ESP32 operates the pump only when local safety rules allow it.

  8. 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 .

— Arduino ESP32 latest 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 WiFi Bluetooth Geliştirme Kartı CP2102 Driver DevKit V1 30 Pin Mikrodenetleyici Fiyatı | Reyonex
 
 

ESP32 DevKit

Quantity: 1

Wi-Fi-enabled microcontroller that reads sensors, controls the relay driver, and communicates with n8n and ThingSpeak.

 
DFRobot Capacitive Soil Moisture Sensor (SEN0193)
 – circuitrocks
 
 

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.

 
Precision Digital Temperature & Humidity Sensor Module (DHT22) – Future Electronics Egypt
 
 

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.

 
Motor bomba de água submersível 3 a 6V DC 120l/h com mangueira de PVC flexível - Robótica Educacional Brasil – Tecnologia, Criatividade e Aprendizagem | Kits STEAM e Cultura Maker
 
 

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.

 
FR120N Isolation MOSFET MOS Tube FET Module PWM Relay Switch -
 
 

Pump driver

Quantity: 1

A properly rated MOSFET driver or relay module switches the pump. Add inductive-load suppression and appropriate overcurrent protection.

 
Float Switch For Water Tank – Waked Electric
 
 

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

text

                   +-------------------------+
                   |       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

mermaid

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:

  1. Rules engine: enforces safety and determines whether irrigation is allowed.

  2. Weather-aware predictor: estimates whether watering can be deferred and estimates water demand.

  3. 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 library by Adafruit.

  • Adafruit Unified Sensor.

  • ArduinoJson by 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.

cpp

#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

  1. Record several raw ADC readings with the sensor in dry soil.

  2. Record readings in thoroughly watered soil after excess water drains.

  3. Update SOIL_DRY_RAW and SOIL_WET_RAW.

  4. Observe the calculated percentage over several watering cycles.

  5. Adjust START_WATERING_BELOW and STOP_WATERING_ABOVE for 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

mermaid

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:

json

{
  "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:

  • deviceId matches a known device.

  • soilMoisture is numeric and in the range 0–100.

  • tankLow and sensorFault are 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.

Open Meteo
+1

Example URL for a demonstration location in New Delhi:

text

https://api.open-meteo.com/v1/forecast?latitude=28.6139&longitude=77.2090&current=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:

javascript

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.

text

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:

Writing
 

🌱 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

mermaid

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

  1. Create the alert text from the validated rule result.

  2. Pass the text to a TTS provider that supports your preferred language and voice.

  3. Receive the generated audio as binary data or a file URL.

  4. Use an n8n HTTP Request node to call the Telegram Bot API's sendVoice method.

  5. Supply the bot token, destination chat ID, and audio file using the format required by Telegram.

  6. 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 .

n8n Docs

Example voice script:

Writing
 

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.

GitHub

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:

text

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