AI-Enabled IoT Greenhouse Using ESP32, n8n, Telegram, Google Sheets and ThingSpeak
1. Recommended project title
AI-Powered Agentic IoT Greenhouse Monitoring and Control System Using ESP32, n8n Automation, Telegram Voice Alerts, Google Sheets, and ThingSpeak
A shorter title for the cover page is:
Agentic IoT Greenhouse Automation Using ESP32 and n8n
2. Project abstract
This project presents an AI-powered greenhouse monitoring and control system based on an ESP32 microcontroller, IoT cloud services, n8n workflow automation, and Telegram voice notifications. The ESP32 collects real-time environmental data from temperature, humidity, soil-moisture, light, water-level, pH, and air-quality sensors. It controls a water pump, solenoid valve, ventilation fan, grow light, and warning buzzer according to configurable crop-management rules.
Sensor data is transmitted to an n8n webhook through Wi-Fi. n8n acts as the automation and orchestration layer: it validates and processes incoming data, stores records in Google Sheets, updates a ThingSpeak channel, evaluates abnormal conditions, and sends Telegram notifications. An AI agent can interpret sensor readings, summarize greenhouse conditions, recommend actions, and process authorized user commands. Text-to-speech can convert critical alerts into Telegram voice messages.
The architecture combines edge control on the ESP32 with cloud automation in n8n. Essential safety actions, such as stopping the pump when the water tank is empty, remain locally enforced on the ESP32 even if the internet or n8n service is unavailable.
n8n provides webhook triggers for receiving application events, Google Sheets integration for automated spreadsheet operations, and Telegram nodes for bot communication. ThingSpeak provides REST and MQTT interfaces for sending and visualizing live IoT data.[docs.n8n][docs.n8n][docs.n8n][se.mathworks]
3. System objectives
The project aims to:
- Monitor greenhouse temperature and humidity.
- Measure soil moisture and automate irrigation.
- Measure light intensity and control supplementary grow lighting.
- Detect low water level before dry-running the pump.
- Monitor irrigation-water pH.
- Detect air-quality changes using the MQ-135.
- Display readings locally on an OLED.
- Upload data to ThingSpeak.
- Store historical readings in Google Sheets.
- Send Telegram text alerts and voice alerts.
- Provide an AI assistant for summaries and authorized control commands.
- Maintain essential local safety control even during internet failure.
4. Overall architecture
┌───────────────────────────────────────────────────────────────┐
│ GREENHOUSE │
│ │
│ DHT22 │ Soil Sensor │ BH1750 │ Water Level │ pH │ MQ-135 │
│ │
│ ┌─────────────────────┐ │
│ │ ESP32 │ │
│ │ Local sensing │ │
│ │ Safety decisions │ │
│ │ Relay control │ │
│ │ OLED display │ │
│ └───────┬─────────────┘ │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ ▼ ▼ ▼ │
│ 4-Channel Relay OLED Buzzer │
│ │ │
│ ▼ │
│ Pump │ Valve │ Fan │ Grow Light │
└────────┬──────────────────────────────────────────────────────┘
│ Wi-Fi / HTTPS
▼
┌──────────────────────────────┐
│ n8n AUTOMATION │
│ │
│ Webhook → Validate → Route │
│ │ │ │ │
│ │ │ └─────┼──► Telegram text alert
│ │ │ └──► TTS → Telegram voice alert
│ │ └─────────────► AI Agent
│ ├──────────────────────► Google Sheets
│ └──────────────────────► ThingSpeak
└──────────────┬───────────────┘
│
┌──────────┼───────────┐
▼ ▼ ▼
Google ThingSpeak Telegram
Sheets Dashboard Bot / AI Assistant
5. Functional layers
5.1 Edge layer
The ESP32 performs the time-critical operations:
- Reads all connected sensors.
- Converts raw ADC readings into engineering values.
- Applies temperature, soil-moisture, light, and water-level thresholds.
- Controls relays.
- Displays local readings.
- Activates the buzzer.
- Sends JSON data to n8n.
- Stops irrigation locally when the water tank is empty.
5.2 Automation layer
n8n receives the ESP32 JSON payload through an HTTPS webhook. A Webhook node can start an n8n workflow when data is received.[docs.n8n]
The workflow then:
- Validates the API key and sensor payload.
- Adds a timestamp.
- Stores the reading in Google Sheets.
- Sends sensor data to ThingSpeak.
- Checks alert rules.
- Calls an AI agent for interpretation when required.
- Sends Telegram text or voice notifications.
- Optionally sends an approved control command back to the ESP32.
5.3 Application layer
The user can access:
- ThingSpeak charts for sensor history.
- Google Sheets for records and analysis.
- Telegram for alerts and commands.
- An AI assistant for natural-language explanations.
- The local OLED for operation without cloud access.
6. Hardware components
| Component | Function |
|---|---|
| ESP32 DevKit V1 | Main controller and Wi-Fi communication |
| DHT22 | Temperature and humidity measurement |
| Capacitive soil-moisture sensor | Soil moisture measurement |
| BH1750 | Digital light measurement in lux |
| Water-level sensor | Tank-level detection |
| pH sensor module | Irrigation-water pH monitoring |
| MQ-135 | Indicative air-quality measurement |
| 0.96-inch OLED | Local display |
| 4-channel relay module | Switching pump, valve, fan, and light |
| 12 V water pump | Irrigation |
| 12 V solenoid valve | Water-flow control |
| 12 V DC fan | Ventilation |
| 12 V LED grow light | Supplemental lighting |
| Active buzzer | Local warning |
| 12 V, 5 A SMPS | Main power source |
| LM2596 buck converter | 12 V to regulated low voltage |
| Fuse and holder | Power-circuit protection |
| Waterproof enclosure | Protection from moisture |
7. ESP32 pin configuration
| Device | ESP32 pin | Interface |
|---|---|---|
| DHT22 data | GPIO 4 | Digital |
| Soil-moisture analog output | GPIO 34 | ADC |
| Water-level analog output | GPIO 35 | ADC |
| pH analog output | GPIO 32 | ADC |
| MQ-135 analog output | GPIO 33 | ADC |
| BH1750 SDA | GPIO 21 | I²C |
| OLED SDA | GPIO 21 | I²C |
| BH1750 SCL | GPIO 22 | I²C |
| OLED SCL | GPIO 22 | I²C |
| Relay fan | GPIO 16 | Digital |
| Relay pump | GPIO 17 | Digital |
| Relay valve | GPIO 18 | Digital |
| Relay grow light | GPIO 19 | Digital |
| Buzzer | GPIO 23 | Digital |
The OLED and BH1750 can share the I²C bus because they normally use different addresses. Confirm the actual addresses with an I²C scanner.
8. Electrical schematic
AC MAINS
│
┌─────▼─────┐
│ 12 V SMPS │
│ 5 A DC │
└─────┬─────┘
│ +12 V
┌──▼──┐
│Fuse │
└──┬──┘
│
┌─────────────┼──────────────────────────┐
│ │ │
▼ ▼ ▼
Water pump Solenoid valve 12 V fan
│ │ │
└─────────────┼──────────────────────────┘
│
Relay COM/NO
│
4-CHANNEL RELAY MODULE
┌──────────┼──────────┐
│ │ │
CH1 Fan CH2 Pump CH3 Valve
│
CH4 Grow light
12 V SMPS ───────────────► LM2596 buck converter
│
Regulated 5 V
│
┌──────────┼──────────┐
▼ ▼ ▼
ESP32 OLED Sensors
ESP32 GND ───────────────── Common GND
Electrical precautions
- Never apply 12 V directly to an ESP32 power pin.
- Ensure every analog sensor output remains within the ESP32 ADC input range.
- Use a voltage divider or signal-conditioning circuit where necessary.
- Use a common ground between the ESP32, sensors, buck converter, and relay control side.
- Keep pump and actuator wiring separate from analog sensor wiring.
- Use flyback protection where a discrete MOSFET driver is used.
- Use an appropriately rated fuse close to the SMPS output.
- Do not place exposed electronics in a wet greenhouse environment.
- Avoid routing dangerous mains voltage onto a student-project PCB.
9. Data flow
1. Sensors measure environmental values.
2. ESP32 reads and filters the values.
3. ESP32 applies local control rules.
4. Relays switch the actuators.
5. ESP32 displays readings on the OLED.
6. ESP32 creates a JSON payload.
7. ESP32 sends the payload to n8n over HTTPS.
8. n8n validates and timestamps the payload.
9. n8n writes a row to Google Sheets.
10. n8n updates the ThingSpeak channel.
11. n8n checks alarm conditions.
12. n8n sends Telegram text or voice alerts.
13. AI agent summarizes data or handles authorized commands.
10. ESP32 control flowchart
┌──────────────┐
│ START │
└──────┬───────┘
▼
┌─────────────────────────┐
│ Initialize ESP32, pins, │
│ sensors, OLED and Wi-Fi │
└──────────┬──────────────┘
▼
┌─────────────────────────┐
│ Read all sensors │
└──────────┬──────────────┘
▼
┌─────────────────────────┐
│ Water level low? │
└───────┬─────────┬───────┘
│Yes │No
▼ ▼
┌─────────────┐ ┌──────────────────────┐
│ Pump OFF │ │ Soil moisture low? │
│ Valve OFF │ └──────┬───────────────┘
│ Alert ON │ │Yes
└──────┬──────┘ ▼
│ ┌─────────────┐
│ │ Pump ON │
│ │ Valve ON │
│ └──────┬──────┘
└───────────────┬┘
▼
┌─────────────────────┐
│ Temperature high? │
└──────┬──────────────┘
│Yes
▼
┌─────────────┐
│ Fan ON │
└──────┬──────┘
▼
┌─────────────────────┐
│ Light too low? │
└──────┬──────────────┘
│Yes
▼
┌─────────────┐
│ Light ON │
└──────┬──────┘
▼
┌─────────────────────┐
│ Check pH and MQ-135 │
└──────┬──────────────┘
▼
┌─────────────────────┐
│ Update OLED and │
│ send JSON to n8n │
└──────┬──────────────┘
▼
┌─────────────────────┐
│ Repeat continuously │
└─────────────────────┘
11. n8n workflow design
Workflow A: ESP32 telemetry and alert workflow
Webhook
│
▼
API Key Validation
│
▼
Set / Code: Normalize JSON
│
├──────────────► Google Sheets: Append Row
│
├──────────────► HTTP Request: ThingSpeak Update
│
▼
Alert Evaluation
│
├── No alert ─────► Webhook Response
│
└── Alert ────────► Build alert message
│
┌──────────┴──────────┐
▼ ▼
Telegram text Text-to-Speech
│
▼
Telegram voice message
Workflow B: Telegram AI assistant
Telegram Trigger
│
▼
Message Type Router
│
┌───┴────────┐
▼ ▼
Text Voice
│ │
│ Download audio
│ │
│ Speech-to-text
└──────┬─────┘
▼
AI Agent
│
┌──────┼─────────┐
▼ ▼ ▼
Read data Explain Request control
│ │
│ Authorization check
│ │
│ ESP32 command webhook
▼
Telegram response
n8n examples commonly use a Telegram trigger, a router that distinguishes text and voice, audio download, speech-to-text, and an AI response path.[n8n][n8n]
12. n8n node-by-node setup
Node 1: Webhook
Configure:
- Method:
POST - Path:
greenhouse/telemetry - Response mode: respond immediately or through a Webhook Response node.
- Authentication: preferably header authentication or a secret API key.
- Production URL: use the active workflow URL.
Example endpoint:
https://YOUR_N8N_DOMAIN/webhook/greenhouse/telemetry
The n8n instance must be publicly reachable over HTTPS for external webhook services such as Telegram. Telegram supports only one webhook per bot, so avoid configuring the same bot in multiple competing workflows.[docs.n8n][docs.n8n]
Node 2: Code node for validation
Use this code in an n8n Code node:
const body = $json.body ?? $json;
const expectedKey = 'CHANGE_THIS_SECRET';
const receivedKey =
body.api_key ??
body.apiKey ??
'';
if (receivedKey !== expectedKey) {
throw new Error('Unauthorized greenhouse device');
}
const required = [
'device_id',
'temperature',
'humidity',
'soil_moisture',
'light_lux',
'water_level',
'ph',
'mq135'
];
for (const field of required) {
if (body[field] === undefined || body[field] === null) {
throw new Error(`Missing field: ${field}`);
}
}
return [{
json: {
timestamp: new Date().toISOString(),
device_id: String(body.device_id),
temperature: Number(body.temperature),
humidity: Number(body.humidity),
soil_moisture: Number(body.soil_moisture),
light_lux: Number(body.light_lux),
water_level: Number(body.water_level),
ph: Number(body.ph),
mq135: Number(body.mq135),
fan: Boolean(body.fan),
pump: Boolean(body.pump),
valve: Boolean(body.valve),
grow_light: Boolean(body.grow_light),
low_water: Boolean(body.low_water),
ph_alert: Boolean(body.ph_alert),
air_quality_alert: Boolean(body.air_quality_alert)
}
}];
Node 3: Google Sheets
Create a spreadsheet with these columns:
timestamp
device_id
temperature
humidity
soil_moisture
light_lux
water_level
ph
mq135
fan
pump
valve
grow_light
low_water
ph_alert
air_quality_alert
Configure the Google Sheets node to:
- Operation: Append Row.
- Select the target spreadsheet and worksheet.
- Map each incoming field to its matching column.
The Google Sheets node is intended for integrating n8n workflows with spreadsheet data and automated spreadsheet operations.[docs.n8n]
Node 4: ThingSpeak HTTP Request
ThingSpeak accepts REST API updates using GET or POST. A write API key and one or more field values are required.[mathworks][mathworks]
Configure an n8n HTTP Request node:
- Method:
GETorPOST. - URL:
https://api.thingspeak.com/update.json
- Query or body parameters:
api_key = YOUR_THINGSPEAK_WRITE_API_KEY
field1 = {{$json.temperature}}
field2 = {{$json.humidity}}
field3 = {{$json.soil_moisture}}
field4 = {{$json.light_lux}}
field5 = {{$json.water_level}}
field6 = {{$json.ph}}
field7 = {{$json.mq135}}
field8 = {{$json.pump}}
Suggested ThingSpeak field mapping:
| ThingSpeak field | Value |
|---|---|
| Field 1 | Temperature |
| Field 2 | Humidity |
| Field 3 | Soil moisture |
| Field 4 | Light intensity |
| Field 5 | Water level |
| Field 6 | pH |
| Field 7 | MQ-135 |
| Field 8 | Pump status |
ThingSpeak is useful for live IoT visualization and historical channel data; its REST and MQTT interfaces support sending data from connected devices.[se.mathworks]
Node 5: Alert evaluation
Use an n8n IF or Code node:
const d = $json;
const alerts = [];
if (d.low_water || d.water_level < 500) {
alerts.push('Water tank level is low. Irrigation has been stopped.');
}
if (d.ph < 5.5 || d.ph > 7.5) {
alerts.push(`Water pH is outside the safe range: ${d.ph.toFixed(2)}.`);
}
if (d.temperature > 32) {
alerts.push(`High temperature detected: ${d.temperature.toFixed(1)} °C.`);
}
if (d.mq135 > 2500) {
alerts.push(`Air-quality threshold exceeded. MQ-135 value: ${d.mq135}.`);
}
return [{
json: {
...d,
alert: alerts.length > 0,
alert_text: alerts.join('\n')
}
}];
Node 6: Telegram text alert
Configure a Telegram node:
- Resource: Message.
- Operation: Send Message.
- Chat ID: your Telegram chat ID.
- Text:
🚨 Greenhouse Alert
Device: {{$json.device_id}}
Time: {{$json.timestamp}}
{{$json.alert_text}}
Temperature: {{$json.temperature}} °C
Humidity: {{$json.humidity}} %
Soil moisture: {{$json.soil_moisture}} %
pH: {{$json.ph}}
Pump: {{$json.pump ? 'ON' : 'OFF'}}
n8n’s Telegram integration supports Telegram bot operations, while Telegram bots require valid bot credentials and a correctly configured webhook when using trigger-based communication.[docs.n8n][docs.n8n]
Node 7: Telegram voice alert
A generic voice-alert path is:
Alert text
│
▼
Text-to-Speech service
│
▼
Binary audio file
│
▼
Telegram Send Audio / Send Voice
The TTS service may be:
- OpenAI text-to-speech.
- Google Cloud Text-to-Speech.
- Microsoft Azure Speech.
- ElevenLabs.
- Another compatible HTTP-based speech provider.
Use a short message for reliable voice alerts:
Greenhouse warning. The water tank level is low. Irrigation has been stopped.
The precise n8n node depends on the TTS provider. The project should not hard-code an API key into the workflow; store it in n8n credentials.
13. AI agent design
The AI agent should not directly control safety-critical hardware without restrictions. Its responsibilities should be:
- Explain current greenhouse conditions.
- Summarize daily or weekly sensor trends.
- Identify abnormal readings.
- Answer user questions.
- Recommend actions.
- Convert authorized commands into structured requests.
- Avoid bypassing local ESP32 safety rules.
Recommended AI tools
get_latest_reading: reads the latest row from Google Sheets or a database.get_history: reads recent values for trend analysis.get_thingspeak_data: retrieves ThingSpeak data.request_actuator_command: sends a command to an n8n command workflow.send_alert: sends a Telegram alert.
AI system prompt
You are the Greenhouse Operations Agent.
Your responsibilities:
1. Explain greenhouse sensor readings in simple language.
2. Identify high temperature, low soil moisture, low tank level,
abnormal pH, and air-quality warnings.
3. Use metric units.
4. Never invent sensor values.
5. Clearly distinguish measured values from recommendations.
6. Do not directly bypass ESP32 safety rules.
7. Never turn on the pump if the water tank is low.
8. Never override an emergency shutdown.
9. For actuator commands, request confirmation when appropriate.
10. Return control requests only as structured JSON.
Allowed structured command format:
{
"action": "pump|fan|valve|grow_light",
"state": "on|off",
"reason": "short explanation"
}
Example Telegram conversations
User:
What is the current greenhouse condition?
AI:
Current readings:
Temperature: 31.4 °C
Humidity: 68 %
Soil moisture: 43 %
Light: 7,200 lux
pH: 6.4
Water tank: Normal
The temperature is approaching the fan threshold, but irrigation is not
currently required.
User:
Why did I receive an alert?
AI:
The alert was generated because the water tank level fell below the
configured safety threshold. The ESP32 stopped the pump and solenoid valve
to prevent dry running.
User:
Turn on the pump.
AI:
The pump command requires a safety check. Current water level is normal.
Do you confirm turning on the pump?
14. ESP32 software
The following code is a complete edge-controller example. It:
- Reads all sensors.
- Controls the four relays.
- Uses hysteresis.
- Displays values on the OLED.
- Sends telemetry to n8n.
- Keeps pump protection local.
- Uses HTTPS for the n8n request.
Arduino libraries
Install:
- DHT sensor library.
- Adafruit Unified Sensor.
- BH1750.
- Adafruit GFX.
- Adafruit SSD1306.
ESP32 code
/*
AI-Enabled IoT Greenhouse Controller
ESP32:
- DHT22
- Capacitive soil moisture
- BH1750
- Water-level sensor
- pH sensor
- MQ-135
- OLED
- Four relays
- Buzzer
- n8n HTTPS telemetry
*/
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <DHT.h>
#include <BH1750.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
/* ---------------- Wi-Fi and n8n ---------------- */
const char* WIFI_SSID = "YOUR_WIFI_NAME";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* N8N_WEBHOOK_URL =
"https://YOUR_N8N_DOMAIN/webhook/greenhouse/telemetry";
const char* DEVICE_API_KEY = "CHANGE_THIS_SECRET";
const char* DEVICE_ID = "greenhouse-esp32-01";
/* ---------------- Sensor pins ---------------- */
#define DHT_PIN 4
#define DHT_TYPE DHT22
#define SOIL_PIN 34
#define WATER_LEVEL_PIN 35
#define PH_PIN 32
#define MQ135_PIN 33
/* ---------------- I2C ---------------- */
#define SDA_PIN 21
#define SCL_PIN 22
/* ---------------- Relay and buzzer pins ---------------- */
#define RELAY_FAN_PIN 16
#define RELAY_PUMP_PIN 17
#define RELAY_VALVE_PIN 18
#define RELAY_LIGHT_PIN 19
#define BUZZER_PIN 23
/* Most relay boards are active LOW */
#define RELAY_ON LOW
#define RELAY_OFF HIGH
/* ---------------- OLED ---------------- */
#define OLED_WIDTH 128
#define OLED_HEIGHT 64
#define OLED_RESET -1
#define OLED_ADDRESS 0x3C
/* ---------------- Thresholds ---------------- */
const float FAN_ON_TEMP = 32.0;
const float FAN_OFF_TEMP = 29.0;
const int PUMP_ON_SOIL = 40;
const int PUMP_OFF_SOIL = 60;
const float LIGHT_ON_LUX = 8000.0;
const float LIGHT_OFF_LUX = 12000.0;
const int LOW_WATER_ADC = 500;
const float PH_MINIMUM = 5.5;
const float PH_MAXIMUM = 7.5;
const int MQ135_LIMIT = 2500;
/* ---------------- Calibration ---------------- */
const int DRY_SOIL_ADC = 3200;
const int WET_SOIL_ADC = 1300;
const float PH_SLOPE = 3.5;
const float PH_OFFSET = 0.5;
/* ---------------- Timing ---------------- */
const unsigned long READ_INTERVAL = 2000;
const unsigned long SEND_INTERVAL = 10000;
unsigned long lastRead = 0;
unsigned long lastSend = 0;
/* ---------------- Objects ---------------- */
DHT dht(DHT_PIN, DHT_TYPE);
BH1750 bh1750;
Adafruit_SSD1306 display(
OLED_WIDTH,
OLED_HEIGHT,
&Wire,
OLED_RESET
);
/* ---------------- Readings ---------------- */
float temperature = 0.0;
float humidity = 0.0;
float lightLux = 0.0;
float phValue = 0.0;
int soilMoisture = 0;
int waterLevel = 0;
int mq135 = 0;
/* ---------------- States ---------------- */
bool fanOn = false;
bool pumpOn = false;
bool valveOn = false;
bool growLightOn = false;
bool lowWater = false;
bool phAlert = false;
bool airQualityAlert = false;
/* ========================================================= */
void setRelay(uint8_t pin, bool state) {
digitalWrite(pin, state ? RELAY_ON : RELAY_OFF);
}
void setFan(bool state) {
fanOn = state;
setRelay(RELAY_FAN_PIN, state);
}
void setPump(bool state) {
pumpOn = state;
setRelay(RELAY_PUMP_PIN, state);
}
void setValve(bool state) {
valveOn = state;
setRelay(RELAY_VALVE_PIN, state);
}
void setGrowLight(bool state) {
growLightOn = state;
setRelay(RELAY_LIGHT_PIN, state);
}
void setBuzzer(bool state) {
digitalWrite(BUZZER_PIN, state ? HIGH : LOW);
}
/* ========================================================= */
int soilPercentFromADC(int value) {
value = constrain(value, WET_SOIL_ADC, DRY_SOIL_ADC);
int percent = map(
value,
DRY_SOIL_ADC,
WET_SOIL_ADC,
0,
100
);
return constrain(percent, 0, 100);
}
/* ========================================================= */
float phFromADC(int value) {
float voltage = (value * 3.3) / 4095.0;
return (PH_SLOPE * voltage) + PH_OFFSET;
}
/* ========================================================= */
void readSensors() {
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t)) {
temperature = t;
}
if (!isnan(h)) {
humidity = h;
}
int soilADC = analogRead(SOIL_PIN);
soilMoisture = soilPercentFromADC(soilADC);
lightLux = bh1750.readLightLevel();
waterLevel = analogRead(WATER_LEVEL_PIN);
int phADC = analogRead(PH_PIN);
phValue = phFromADC(phADC);
mq135 = analogRead(MQ135_PIN);
}
/* ========================================================= */
void applyAutomation() {
/*
Low-water protection has priority over every irrigation request.
*/
lowWater = waterLevel < LOW_WATER_ADC;
if (lowWater) {
setPump(false);
setValve(false);
}
else {
if (soilMoisture < PUMP_ON_SOIL) {
setPump(true);
setValve(true);
}
else if (soilMoisture >= PUMP_OFF_SOIL) {
setPump(false);
setValve(false);
}
}
/*
Fan hysteresis.
*/
if (temperature >= FAN_ON_TEMP) {
setFan(true);
}
else if (temperature <= FAN_OFF_TEMP) {
setFan(false);
}
/*
Grow-light hysteresis.
*/
if (lightLux < LIGHT_ON_LUX) {
setGrowLight(true);
}
else if (lightLux >= LIGHT_OFF_LUX) {
setGrowLight(false);
}
phAlert =
phValue < PH_MINIMUM ||
phValue > PH_MAXIMUM;
airQualityAlert =
mq135 > MQ135_LIMIT;
setBuzzer(
lowWater ||
phAlert ||
airQualityAlert
);
}
/* ========================================================= */
void updateOLED() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("AI GREENHOUSE");
display.print("T:");
display.print(temperature, 1);
display.print("C H:");
display.print(humidity, 0);
display.println("%");
display.print("Soil:");
display.print(soilMoisture);
display.println("%");
display.print("Lux:");
display.println(lightLux, 0);
display.print("pH:");
display.print(phValue, 2);
display.print(" W:");
display.println(lowWater ? "LOW" : "OK");
display.print("F:");
display.print(fanOn ? "ON " : "OFF");
display.print(" P:");
display.print(pumpOn ? "ON " : "OFF");
display.print(" L:");
display.print(growLightOn ? "ON" : "OFF");
display.display();
}
/* ========================================================= */
String jsonBool(bool value) {
return value ? "true" : "false";
}
/* ========================================================= */
String buildTelemetryJson() {
String json = "{";
json += "\"api_key\":\"";
json += DEVICE_API_KEY;
json += "\",";
json += "\"device_id\":\"";
json += DEVICE_ID;
json += "\",";
json += "\"temperature\":";
json += String(temperature, 2);
json += ",";
json += "\"humidity\":";
json += String(humidity, 2);
json += ",";
json += "\"soil_moisture\":";
json += String(soilMoisture);
json += ",";
json += "\"light_lux\":";
json += String(lightLux, 2);
json += ",";
json += "\"water_level\":";
json += String(waterLevel);
json += ",";
json += "\"ph\":";
json += String(phValue, 2);
json += ",";
json += "\"mq135\":";
json += String(mq135);
json += ",";
json += "\"fan\":";
json += jsonBool(fanOn);
json += ",";
json += "\"pump\":";
json += jsonBool(pumpOn);
json += ",";
json += "\"valve\":";
json += jsonBool(valveOn);
json += ",";
json += "\"grow_light\":";
json += jsonBool(growLightOn);
json += ",";
json += "\"low_water\":";
json += jsonBool(lowWater);
json += ",";
json += "\"ph_alert\":";
json += jsonBool(phAlert);
json += ",";
json += "\"air_quality_alert\":";
json += jsonBool(airQualityAlert);
json += "}";
return json;
}
/* ========================================================= */
void sendTelemetryToN8N() {
if (WiFi.status() != WL_CONNECTED) {
return;
}
WiFiClientSecure client;
/*
For testing only. In production, use a validated
root certificate instead of disabling certificate checks.
*/
client.setInsecure();
HTTPClient http;
if (!http.begin(client, N8N_WEBHOOK_URL)) {
Serial.println("Could not connect to n8n webhook.");
return;
}
http.addHeader(
"Content-Type",
"application/json"
);
String payload = buildTelemetryJson();
int responseCode = http.POST(payload);
Serial.print("n8n response: ");
Serial.println(responseCode);
http.end();
}
/* ========================================================= */
void connectWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
unsigned long startTime = millis();
while (
WiFi.status() != WL_CONNECTED &&
millis() - startTime < 20000
) {
delay(500);
Serial.print(".");
}
Serial.println();
if (WiFi.status() == WL_CONNECTED) {
Serial.print("Wi-Fi connected. IP: ");
Serial.println(WiFi.localIP());
}
else {
Serial.println("Wi-Fi unavailable. Local control continues.");
}
}
/* ========================================================= */
void setup() {
Serial.begin(115200);
pinMode(RELAY_FAN_PIN, OUTPUT);
pinMode(RELAY_PUMP_PIN, OUTPUT);
pinMode(RELAY_VALVE_PIN, OUTPUT);
pinMode(RELAY_LIGHT_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
setFan(false);
setPump(false);
setValve(false);
setGrowLight(false);
setBuzzer(false);
analogReadResolution(12);
analogSetPinAttenuation(SOIL_PIN, ADC_11db);
analogSetPinAttenuation(WATER_LEVEL_PIN, ADC_11db);
analogSetPinAttenuation(PH_PIN, ADC_11db);
analogSetPinAttenuation(MQ135_PIN, ADC_11db);
Wire.begin(SDA_PIN, SCL_PIN);
dht.begin();
bh1750.begin();
if (!display.begin(
SSD1306_SWITCHCAPVCC,
OLED_ADDRESS
)) {
Serial.println("OLED initialization failed.");
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("AI Greenhouse");
display.println("Starting...");
display.display();
connectWiFi();
Serial.println("System ready.");
}
/* ========================================================= */
void loop() {
unsigned long now = millis();
if (
now - lastRead >= READ_INTERVAL
) {
lastRead = now;
readSensors();
applyAutomation();
updateOLED();
Serial.println(buildTelemetryJson());
}
if (
now - lastSend >= SEND_INTERVAL
) {
lastSend = now;
if (WiFi.status() != WL_CONNECTED) {
connectWiFi();
}
sendTelemetryToN8N();
}
}
15. Optional command path from n8n to ESP32
The safest method is for n8n to send a command to an ESP32 endpoint only after authorization. The ESP32 should validate the command and still enforce local protections.
Example command JSON
{
"api_key": "CHANGE_THIS_SECRET",
"command": "pump_off",
"request_id": "telegram-12345"
}
Recommended command rules
pump_on:
allowed only when water level is normal
allowed only when manual control is enabled
must be logged
pump_off:
always allowed
fan_on/fan_off:
allowed, but automatic temperature protection remains active
grow_light_on/grow_light_off:
allowed within configured operating hours
valve_on:
allowed only when pump and water level are safe
The ESP32 should not accept arbitrary relay pin numbers or unvalidated text commands. Use a small whitelist of commands.
16. Telegram voice-alert workflow
ESP32 telemetry
│
▼
n8n Webhook
│
▼
Alert condition?
│
├── No → Log data only
│
└── Yes
│
▼
Create alert text
│
▼
Text-to-Speech API
│
▼
Receive MP3/OGG audio
│
▼
Telegram Send Voice
Example voice message:
Greenhouse warning. The water tank level is low.
The irrigation pump has been stopped to protect the system.
Telegram voice-message workflows generally require a Telegram bot token, an audio file, and a correctly configured Telegram node. n8n workflow examples also demonstrate voice-message transcription in the reverse direction.[n8n][n8n]
17. Google Sheets documentation format
Create a worksheet named Telemetry.
Header row
Timestamp | Device ID | Temperature | Humidity | Soil Moisture |
Light Lux | Water Level | pH | MQ-135 | Fan | Pump | Valve |
Grow Light | Low Water | pH Alert | Air Quality Alert
Example row
2026-10-09T07:30:00Z | greenhouse-esp32-01 | 31.4 | 68 |
43 | 7200 | 1800 | 6.4 | 1150 | FALSE | FALSE |
FALSE | TRUE | FALSE | FALSE | FALSE
Recommended spreadsheet improvements
- Add conditional formatting for high temperature.
- Highlight soil moisture below the irrigation threshold.
- Highlight low-water rows in red.
- Add daily average formulas.
- Add a separate
Alertsworksheet. - Add a
Maintenanceworksheet for sensor calibration records.
18. ThingSpeak dashboard design
Configure one ThingSpeak channel with eight fields:
Field 1: Temperature
Field 2: Humidity
Field 3: Soil moisture
Field 4: Light intensity
Field 5: Water level
Field 6: pH
Field 7: MQ-135
Field 8: Pump status
ThingSpeak supports channel charts and REST API updates. The API update request should use the channel’s write API key, and field values can be passed using field1, field2, and similar parameters.[mathworks][mathworks]
19. Security design
Use these protections:
- Store Wi-Fi passwords and service keys outside public code repositories.
- Use HTTPS for ESP32-to-n8n communication.
- Validate a device API key in n8n.
- Use a unique device ID.
- Restrict Telegram commands to approved chat IDs.
- Require confirmation for actuator commands.
- Log every remote control action.
- Rate-limit webhook requests.
- Reject malformed JSON.
- Keep emergency safety logic local on the ESP32.
- Use TLS certificate validation in production instead of
setInsecure(). - Apply least-privilege credentials to Google Sheets and other services.
20. Testing procedure
Hardware testing
- Test the ESP32 without relay loads.
- Confirm each sensor reading in the Serial Monitor.
- Test the OLED separately.
- Test each relay with a low-risk load.
- Confirm relay ON/OFF polarity.
- Test the buzzer.
- Test pump and valve with water disconnected from plants.
- Verify the fuse and supply voltage.
- Confirm that low water disables the pump.
Software testing
- Upload the ESP32 code.
- Verify Wi-Fi connection.
- Confirm the n8n webhook receives JSON.
- Test invalid API-key rejection.
- Confirm Google Sheets receives one row per telemetry event.
- Confirm ThingSpeak fields update correctly.
- Trigger a low-water condition.
- Trigger a high-temperature condition.
- Verify Telegram text delivery.
- Verify Telegram voice delivery.
- Test Telegram voice-to-text if enabled.
- Test an AI summary.
- Test a denied unauthorized command.
- Disconnect Wi-Fi and verify local automation continues.
21. Troubleshooting
| Problem | Likely cause | Solution |
|---|---|---|
| OLED blank | Wrong I²C address or wiring | Scan I²C addresses; check SDA/SCL |
| Relay operates inversely | Active-HIGH/active-LOW mismatch | Swap RELAY_ON and RELAY_OFF |
| ESP32 resets when pump starts | Supply drop or electrical noise | Separate power paths, add protection, use adequate SMPS |
| pH value is incorrect | No calibration | Calibrate with standard buffer solutions |
| MQ-135 fluctuates | Sensor warm-up and environmental sensitivity | Allow warm-up and use calibrated thresholds |
| n8n receives no data | Wrong production URL or inactive workflow | Activate workflow and verify HTTPS URL |
| Telegram trigger fails | Bot webhook conflict or inaccessible n8n | Use one workflow per bot and public HTTPS |
| Google Sheets fails | Incorrect OAuth credential or sheet permissions | Reconnect Google account and verify access |
| ThingSpeak rejects data | Incorrect write API key or field mapping | Verify channel key and field parameters |
| Voice alert does not send | TTS output format or missing binary mapping | Confirm audio file and Telegram binary property |
| AI gives unsafe instruction | Poor prompt or unrestricted tool | Add authorization and hard safety rules |
22. Advantages
- Local automatic control continues during cloud failure.
- n8n provides visual, modular workflow automation.
- Telegram supports rapid text and voice notifications.
- Google Sheets provides a simple historical database.
- ThingSpeak provides IoT charts and cloud visualization.
- The AI agent provides natural-language analysis.
- The architecture is expandable to cameras, CO₂ sensors, weather APIs, and solar power.
- The project demonstrates embedded systems, IoT, cloud automation, APIs, AI, and smart agriculture in one platform.
23. Limitations
- Internet-dependent functions stop when Wi-Fi or n8n is unavailable.
- Low-cost pH sensors require frequent calibration.
- MQ-135 provides an indicative reading, not a laboratory-grade gas analysis.
- AI recommendations must not replace local safety logic.
- Telegram and cloud services may have rate limits or service interruptions.
- TTS services may require separate paid API credentials.
- Sensor placement and greenhouse conditions significantly affect accuracy.
- Relay modules are not a substitute for proper motor drivers in high-current systems.
24. Future scope
- Camera-based plant growth monitoring.
- AI disease and pest detection.
- CO₂ sensor integration.
- Automatic nutrient dosing.
- Weather-based irrigation.
- Solar power and battery backup.
- LoRaWAN communication for remote farms.
- Predictive irrigation using historical data.
- Crop-specific AI agents.
- Voice commands in multiple languages.
- Mobile application integration.
- Digital twin of the greenhouse.
- MQTT-based scalable deployment.
- Automatic report generation from Google Sheets.
25. Final conclusion
The proposed system combines ESP32 edge computing with n8n automation, AI assistance, Telegram notifications, Google Sheets logging, and ThingSpeak visualization. The ESP32 performs immediate sensing and actuator control, while n8n connects the greenhouse to cloud services and communication channels. Telegram provides practical text and voice alerts, Google Sheets offers accessible historical records, and ThingSpeak supplies live IoT visualization.
The most important design principle is to keep safety-critical functions local. The ESP32 must stop the pump during low-water conditions even if n8n, Telegram, the AI agent, or the internet is unavailable. The AI agent should interpret, summarize, and request authorized actions, but it should never bypass the embedded safety system.
Project Summary
The project is an AI-powered IoT greenhouse monitoring and control system using ESP32, n8n automation, Telegram alerts, Google Sheets, and ThingSpeak.
The ESP32 collects readings from temperature, humidity, soil-moisture, light, water-level, pH, and air-quality sensors. It locally controls the water pump, solenoid valve, ventilation fan, grow light, OLED display, and buzzer according to predefined thresholds.
Sensor data is sent through Wi-Fi to an n8n webhook. n8n processes the data, stores it in Google Sheets, updates ThingSpeak charts, evaluates abnormal conditions, and sends Telegram notifications. Critical alerts can be converted into Telegram voice messages using a text-to-speech service.
An AI agent can communicate through Telegram to:
- Explain current greenhouse conditions.
- Summarize sensor readings.
- Identify abnormal values.
- Recommend corrective actions.
- Process authorized control requests.
- Respond to text or voice messages.
System architecture
Sensors
│
▼
ESP32 Controller
│
├── OLED display
├── Buzzer
├── Relay module
│ ├── Pump
│ ├── Solenoid valve
│ ├── Ventilation fan
│ └── Grow light
│
└── Wi-Fi
│
▼
n8n
│
┌──────┼─────────┬──────────────┐
▼ ▼ ▼ ▼
Google ThingSpeak Telegram AI Agent
Sheets Dashboard Alerts Responses
Main software functions
- Sensor reading and calibration.
- Automatic irrigation using soil-moisture hysteresis.
- Fan control using temperature hysteresis.
- Grow-light control using light-intensity thresholds.
- Low-water safety shutdown.
- pH and MQ-135 warning detection.
- OLED local monitoring.
- HTTPS JSON telemetry to n8n.
- Google Sheets data logging.
- ThingSpeak cloud visualization.
- Telegram text alerts.
- Telegram voice alerts using text-to-speech.
- AI-based greenhouse analysis and authorized commands.
Key safety principle
The ESP32 must retain control of essential safety functions. For example, when the water tank is low, the ESP32 must switch off the pump and solenoid valve locally, even if the internet, n8n, Telegram, or the AI service is unavailable.
Final project title
AI-Powered Agentic IoT Greenhouse Monitoring and Automatic Control System Using ESP32, n8n Automation, Telegram Voice Alerts, Google Sheets, and ThingSpeak
mindmapAI-Powered IoT Greenhouse Mind Map
AI-POWERED AGENTIC IoT GREENHOUSE SYSTEM
USING ESP32, n8n, TELEGRAM, GOOGLE SHEETS & THINGSPEAK
│
┌─────────────────┬───────────────┼────────────────┬──────────────────┐
▼ ▼ ▼ ▼ ▼
Hardware ESP32 Edge n8n Automation IoT Services AI Agent
Control
│ │ │ │ │
├─ DHT22 ├─ Sensor read ├─ Webhook ├─ ThingSpeak ├─ Analyze data
├─ Soil sensor ├─ Thresholds ├─ Validation │ ├─ Live charts ├─ Summarize status
├─ BH1750 ├─ Hysteresis ├─ Routing │ └─ History ├─ Explain alerts
├─ Water level ├─ Relay logic ├─ Google Sheets ├─ Google Sheets ├─ Voice/text input
├─ pH sensor ├─ OLED output ├─ ThingSpeak │ └─ Data log ├─ Recommend actions
└─ MQ-135 ├─ Buzzer ├─ Alert rules └─ Telegram └─ Authorized commands
├─ Wi-Fi ├─ TTS voice ├─ Text alerts
└─ Safety └─ AI workflow └─ Voice alerts
Detailed mind map
AI-POWERED AGENTIC IoT GREENHOUSE SYSTEM
│
├── 1. Hardware
│ ├── ESP32 DevKit
│ │ ├── Main controller
│ │ ├── Wi-Fi connectivity
│ │ ├── ADC inputs
│ │ ├── Digital GPIO
│ │ └── I²C communication
│ │
│ ├── Sensors
│ │ ├── DHT22
│ │ │ ├── Temperature
│ │ │ └── Humidity
│ │ ├── Capacitive soil-moisture sensor
│ │ ├── BH1750 light sensor
│ │ ├── Water-level sensor
│ │ ├── pH sensor
│ │ └── MQ-135 air-quality sensor
│ │
│ ├── Actuators
│ │ ├── 12 V water pump
│ │ ├── 12 V solenoid valve
│ │ ├── Ventilation fan
│ │ └── LED grow light
│ │
│ ├── Indicators
│ │ ├── OLED display
│ │ └── Active buzzer
│ │
│ └── Power
│ ├── 12 V, 5 A SMPS
│ ├── Fuse
│ ├── Main switch
│ └── LM2596 buck converter
│
├── 2. ESP32 Edge Control
│ ├── Read sensor values
│ ├── Convert ADC values
│ ├── Apply calibration
│ ├── Compare thresholds
│ ├── Use hysteresis
│ ├── Control relays
│ ├── Update OLED
│ ├── Activate buzzer
│ ├── Create JSON telemetry
│ ├── Send data to n8n
│ └── Continue local operation during cloud failure
│
├── 3. Automatic Rules
│ ├── Irrigation
│ │ ├── Soil moisture below lower limit
│ │ ├── Check water level
│ │ ├── Pump ON
│ │ └── Valve ON
│ │
│ ├── Irrigation stop
│ │ ├── Soil moisture reaches upper limit
│ │ └── Pump and valve OFF
│ │
│ ├── Low-water protection
│ │ ├── Tank level below limit
│ │ ├── Pump OFF
│ │ ├── Valve OFF
│ │ └── Buzzer alert
│ │
│ ├── Ventilation
│ │ ├── Temperature above ON limit
│ │ └── Fan ON
│ │
│ ├── Lighting
│ │ ├── Light intensity below limit
│ │ └── Grow light ON
│ │
│ ├── pH warning
│ │ └── Alert if pH is outside configured range
│ │
│ └── Air-quality warning
│ └── Alert if MQ-135 reading exceeds limit
│
├── 4. n8n Automation
│ ├── Webhook
│ │ └── Receives ESP32 JSON data
│ ├── API-key validation
│ ├── Data normalization
│ ├── Timestamp generation
│ ├── Alert evaluation
│ ├── Google Sheets node
│ │ └── Appends telemetry row
│ ├── HTTP Request node
│ │ └── Updates ThingSpeak
│ ├── Telegram node
│ │ └── Sends text notification
│ ├── Text-to-speech node
│ │ └── Creates voice alert
│ ├── Telegram voice node
│ │ └── Sends audio message
│ └── AI Agent node
│ ├── Reads sensor data
│ ├── Interprets conditions
│ ├── Provides recommendations
│ └── Handles authorized commands
│
├── 5. IoT Cloud Services
│ ├── ThingSpeak
│ │ ├── Temperature field
│ │ ├── Humidity field
│ │ ├── Soil-moisture field
│ │ ├── Light field
│ │ ├── Water-level field
│ │ ├── pH field
│ │ ├── MQ-135 field
│ │ └── Pump-status field
│ │
│ ├── Google Sheets
│ │ ├── Timestamp
│ │ ├── Device ID
│ │ ├── Sensor readings
│ │ ├── Actuator status
│ │ └── Alert history
│ │
│ └── Telegram
│ ├── Text alerts
│ ├── Voice alerts
│ ├── User commands
│ └── AI conversation
│
├── 6. AI Agent
│ ├── Inputs
│ │ ├── Current readings
│ │ ├── Historical data
│ │ ├── ThingSpeak data
│ │ └── Telegram messages
│ │
│ ├── Processing
│ │ ├── Detect abnormal conditions
│ │ ├── Summarize greenhouse state
│ │ ├── Identify trends
│ │ ├── Explain alerts
│ │ └── Recommend actions
│ │
│ ├── Outputs
│ │ ├── Text response
│ │ ├── Voice response
│ │ ├── Alert message
│ │ └── Authorized control request
│ │
│ └── Safety restrictions
│ ├── No unsafe relay commands
│ ├── No pump operation during low water
│ ├── Confirmation for manual control
│ └── Local ESP32 rules have priority
│
├── 7. Communication
│ ├── Sensor-to-ESP32
│ │ ├── Analog
│ │ ├── Digital
│ │ └── I²C
│ ├── ESP32-to-n8n
│ │ └── HTTPS POST with JSON
│ ├── n8n-to-Google Sheets
│ │ └── Authenticated API
│ ├── n8n-to-ThingSpeak
│ │ └── REST API
│ ├── n8n-to-Telegram
│ │ └── Bot API
│ └── Telegram-to-AI Agent
│ ├── Text input
│ └── Voice input
│
├── 8. Security
│ ├── HTTPS
│ ├── API-key validation
│ ├── Telegram chat-ID validation
│ ├── Secure n8n credentials
│ ├── Authorized control commands
│ ├── Command logging
│ ├── Rate limiting
│ └── Local safety override
│
├── 9. Testing
│ ├── Sensor testing
│ ├── OLED testing
│ ├── Relay testing
│ ├── Pump and valve testing
│ ├── Low-water test
│ ├── n8n webhook test
│ ├── Google Sheets test
│ ├── ThingSpeak test
│ ├── Telegram text test
│ ├── Telegram voice test
│ └── AI command authorization test
│
└── 10. Future Scope
├── CO₂ sensor
├── Camera monitoring
├── AI disease detection
├── Automatic nutrient dosing
├── Solar power
├── Weather-based irrigation
├── Predictive maintenance
├── Multilingual voice control
├── LoRaWAN connectivity
└── Crop-specific AI agents
Workflow mind map
ESP32 SENSOR DATA
│
▼
n8n WEBHOOK
│
▼
VALIDATE API KEY
│
▼
NORMALIZE DATA
│
┌──────┼─────────┬─────────────┐
▼ ▼ ▼ ▼
Sheets ThingSpeak Alert Check AI Agent
│ │ │ │
▼ ▼ ▼ ▼
History Charts Telegram Analysis
┌─────┴─────┐
▼ ▼
Recommendation Command
│
▼
Authorization check
│
▼
ESP32 control
Core concept
ESP32 = Real-time local controller
n8n = Automation and integration layer
AI = Interpretation and decision-support layer
Telegram = Human interaction and alert channel
Google Sheets = Historical record
ThingSpeak = IoT dashboard
The most important design rule is that the ESP32 retains authority over safety-critical actions. Cloud automation and AI can analyze data and request an action, but they must not override local protections such as low-water pump shutdown.






No comments:
Post a Comment