Zyron
ZYRON
All Posts
ESP32AgricultureAutomationFirmwareIoTSensors

Greenhouse Climate Control with ESP32: Automating Humidity and Irrigation

8 min read18 March 2026Zyron Engineering

Greenhouse climate control is one of the clearest wins for embedded automation: the sensors are cheap, the actuators are simple, and the cost of failure — lost crops — is high and immediate. This post walks through an autonomous humidity control system we deployed across six greenhouse zones, built entirely on ESP32 hardware with no cloud dependency required for the control loop itself.

The control problem

Greenhouse humidity needs to stay within a defined band. Too high and you get fungal disease. Too low and transpiration stress reduces yield. The naive approach — turn the extractor on when humidity goes above threshold, off when it drops below — causes oscillation: the extractor overshoots, humidity drops below the lower limit, extractor off, humidity rises again, repeat. The right approach is a hysteresis loop with separate upper and lower thresholds and a minimum run time to prevent rapid cycling.

Hardware: what you need

Per greenhouse zone: one ESP32 development board, one DHT22 temperature and humidity sensor, one 2-channel relay module (one relay for the extractor fan, one for the irrigation solenoid), and a 12V or 24V power supply depending on your actuators. The ESP32 runs on 3.3V logic; relay modules typically want 5V for the coil but accept 3.3V trigger signals from the ESP32 GPIO pins. Confirm this for your specific module — some need a transistor driver.

For the solenoid valve, choose normally-closed: it only opens when energised. This means a power failure defaults to irrigation off, which is safer than defaulting to irrigation on.

Firmware: the hysteresis control loop

Read the DHT22 every 10 seconds — the sensor needs at least 2 seconds between readings and faster polling doesn't give you useful resolution. Store the last valid reading; if the sensor returns NaN (common on DHT22 under electrical noise), skip the control evaluation for that cycle and log the error.

The control logic is simple: if humidity > upper_threshold and extractor is off and extractor has been off for at least min_off_seconds, turn extractor on. If humidity < lower_threshold and extractor is on and extractor has been on for at least min_run_seconds, turn extractor off. The minimum run and off times are the key to preventing oscillation — tune them for your specific space volume and extractor airflow rate.

Same logic applies to irrigation: if humidity < lower_threshold and the last irrigation pulse was more than min_interval_minutes ago, pulse the solenoid open for pulse_duration_seconds. Avoid continuous irrigation — you want to raise humidity, not flood the root zone.

Persisting configuration to flash

Don't hardcode your thresholds. Use the ESP32's EEPROM or NVS (Non-Volatile Storage) to persist upper threshold, lower threshold, minimum run time, and other tunable parameters. On boot, load from NVS with sensible defaults if no stored value exists. Accept configuration updates over MQTT — publish to a config topic and have the firmware apply and persist the new values. This means you can retune the control loop remotely without a firmware update.

Connectivity: MQTT with offline resilience

The control loop must keep running even if the MQTT broker is unreachable. Structure your firmware so the WiFi and MQTT connection happen in a background task and the sensor/actuator loop runs regardless of connectivity state. Readings queue locally (a circular buffer of the last 100 readings is sufficient) and flush to the broker when connectivity restores.

For WiFi reconnection, use the ESP32's built-in reconnect events rather than polling. Register a WiFi event handler that sets a connection flag; your MQTT task checks this flag before attempting to connect.

Monitoring dashboard and alerts

All six zones publish to structured MQTT topics: greenhouse/zone1/humidity, greenhouse/zone1/temperature, greenhouse/zone1/extractor_state, greenhouse/zone1/solenoid_state. A central Raspberry Pi runs Mosquitto and a Python subscriber that writes to PostgreSQL. A Next.js dashboard shows live readings per zone with colour coding: green for in-range, amber for within 5% of threshold, red for out of range.

SMS alerts fire via an API when any zone stays out of range for more than 5 minutes — enough time to distinguish a genuine problem from a transient spike. The 5-minute delay eliminates false alarms from sensor glitches while still giving staff actionable lead time.

Lessons from deployment

DHT22 sensors in a humid greenhouse environment drift over time. Build a calibration offset into your NVS config and verify sensors against a reference hygrometer every three months. Relay contacts in humid environments corrode — use sealed relay modules or coat the PCB with conformal spray. Power your ESP32 nodes from a UPS-backed supply: a mains interruption that leaves extractors off during a hot afternoon can undo a week's work in hours.

Need this built, not just explained?

We build production systems around exactly this kind of engineering. Tell us what you're working on.

Start a conversation