Skip to Content
Project ListAdvanced1. Greenhouse Automation

1. Greenhouse Automation

Combine a DHT22 and capacitive soil sensor. Temperature hysteresis controls the fan; short watering pulses and a cooldown protect the pump. PxServ enables the system and exposes every decision.

Level: Advanced · Time: 70 minutes

Design goals

  • Schedule multiple sensor and actuator tasks without long blocking delays
  • Prevent relay chatter around a threshold with hysteresis
  • Limit pump run time and repeat frequency
  • Stop outputs on invalid sensor data or configuration

Safety and wiring

Use only low-voltage DC loads, suitable relay/MOSFET drivers, external power, common GND, and inductive-load protection. Keep water physically separated from electronics. The sketch assumes active-low drivers.

PartESP32
DHT22 DATAGPIO 4
Soil sensor AOUTGPIO 34
Fan driver INGPIO 25
Pump driver INGPIO 26

Create greenhouse/system-enabled=1, greenhouse/fan-on=30, greenhouse/fan-off=28, and greenhouse/watering-threshold=30.

Complete sketch

#include <PxServ.h> #include <DHT.h> const char* WIFI_SSID = "your_wifi_ssid"; const char* WIFI_PASSWORD = "your_wifi_password"; const char* PXSERV_API_KEY = "your_pxserv_api_key"; const int DHT_PIN = 4, SOIL_PIN = 34, FAN_PIN = 25, PUMP_PIN = 26; const int DRY_VALUE = 3000, WET_VALUE = 1400; // Calibrate const unsigned long PUMP_RUN_MS = 5000; const unsigned long PUMP_COOLDOWN_MS = 60000; PxServ client(PXSERV_API_KEY); DHT dht(DHT_PIN, DHT22); bool systemEnabled = true, fanOn = false, pumpOn = false; float fanOnAt = 30.0, fanOffAt = 28.0; int wateringThreshold = 30; unsigned long pumpStartedAt = 0, pumpStoppedAt = 0; unsigned long lastSensorAt = 0, lastConfigAt = 0; void setOutput(int pin, bool enabled) { digitalWrite(pin, enabled ? LOW : HIGH); } void setPump(bool enabled) { pumpOn = enabled; setOutput(PUMP_PIN, enabled); if (enabled) pumpStartedAt = millis(); else pumpStoppedAt = millis(); } void readConfig() { PxServ::Callback active = client.getData("greenhouse/system-enabled"); if (active.status == 200) systemEnabled = active.data == "1"; PxServ::Callback onValue = client.getData("greenhouse/fan-on"); if (onValue.status == 200) fanOnAt = onValue.data.toFloat(); PxServ::Callback offValue = client.getData("greenhouse/fan-off"); if (offValue.status == 200) fanOffAt = offValue.data.toFloat(); PxServ::Callback soilValue = client.getData("greenhouse/watering-threshold"); if (soilValue.status == 200) { wateringThreshold = constrain(soilValue.data.toInt(), 0, 100); } } void safeStop(String reason) { fanOn = false; setOutput(FAN_PIN, false); if (pumpOn) setPump(false); client.setData("greenhouse/status", reason); } void setup() { Serial.begin(115200); pinMode(FAN_PIN, OUTPUT); pinMode(PUMP_PIN, OUTPUT); setOutput(FAN_PIN, false); setOutput(PUMP_PIN, false); analogReadResolution(12); dht.begin(); PxServ::connectWifi(WIFI_SSID, WIFI_PASSWORD); } void loop() { unsigned long now = millis(); if (pumpOn && now - pumpStartedAt >= PUMP_RUN_MS) setPump(false); if (now - lastConfigAt >= 30000) { lastConfigAt = now; readConfig(); } if (now - lastSensorAt < 10000) { delay(20); return; } lastSensorAt = now; float temperature = dht.readTemperature(); int soil = constrain(map(analogRead(SOIL_PIN), DRY_VALUE, WET_VALUE, 0, 100), 0, 100); if (!systemEnabled) { safeStop("remote-disabled"); return; } if (isnan(temperature)) { safeStop("sensor-error"); return; } if (fanOffAt >= fanOnAt) { safeStop("configuration-error"); return; } if (!fanOn && temperature >= fanOnAt) fanOn = true; if (fanOn && temperature <= fanOffAt) fanOn = false; setOutput(FAN_PIN, fanOn); bool cooldownReady = now - pumpStoppedAt >= PUMP_COOLDOWN_MS; if (!pumpOn && soil < wateringThreshold && cooldownReady) setPump(true); client.setData("greenhouse/temperature", String(temperature, 1)); client.setData("greenhouse/soil-moisture", String(soil)); client.setData("greenhouse/fan-status", fanOn ? "1" : "0"); client.setData("greenhouse/pump-status", pumpOn ? "1" : "0"); client.setData("greenhouse/status", "normal"); }

Commissioning

Validate sensors first, then test driver outputs with LEDs. Confirm hysteresis, five-second watering, one-minute cooldown, remote disable, and sensor-error shutdown before connecting loads. A field system also needs reservoir, flow, relay-feedback, and independent hardware protection.

Last updated