Skip to Content
Project ListIntermediate2. Smart Plant Watering

2. Smart Plant Watering

Run a pump briefly when calibrated soil moisture falls below a threshold. A remote mode, maximum run time, and cooldown make the prototype safer and easier to observe.

Level: Intermediate · Time: 50 minutes

Safety and wiring

Use only a low-voltage DC mini pump. Never power it from an ESP32 pin. Use a suitable relay or MOSFET driver, external supply, shared GND, and inductive-load protection. Never switch mains voltage.

PartESP32
Soil sensor AOUTGPIO 34
Soil sensor VCC / GND3V3 / GND
Driver INGPIO 23
Driver GNDCommon GND

The example assumes an active-low relay.

Complete sketch

#include <PxServ.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 SOIL_PIN = 34; const int PUMP_PIN = 23; const int DRY_VALUE = 3000; // Calibrate const int WET_VALUE = 1400; // Calibrate const int START_THRESHOLD = 35; const unsigned long MAX_PUMP_MS = 5000; const unsigned long COOLDOWN_MS = 30000; PxServ client(PXSERV_API_KEY); bool pumpRunning = false; unsigned long pumpStartedAt = 0; unsigned long lastPumpStoppedAt = 0; unsigned long lastCycleAt = 0; void setPump(bool enabled) { pumpRunning = enabled; digitalWrite(PUMP_PIN, enabled ? LOW : HIGH); client.setData("watering/pump-status", enabled ? "1" : "0"); if (enabled) pumpStartedAt = millis(); else lastPumpStoppedAt = millis(); } void setup() { Serial.begin(115200); pinMode(PUMP_PIN, OUTPUT); digitalWrite(PUMP_PIN, HIGH); analogReadResolution(12); PxServ::connectWifi(WIFI_SSID, WIFI_PASSWORD); } void loop() { unsigned long now = millis(); if (pumpRunning && now - pumpStartedAt >= MAX_PUMP_MS) setPump(false); if (now - lastCycleAt < 5000) { delay(20); return; } lastCycleAt = now; int raw = analogRead(SOIL_PIN); int moisture = constrain(map(raw, DRY_VALUE, WET_VALUE, 0, 100), 0, 100); client.setData("watering/moisture-percent", String(moisture)); String mode = "automatic"; PxServ::Callback modeResult = client.getData("watering/mode"); if (modeResult.status == 200) mode = modeResult.data; bool requestPump = false; if (mode == "manual") { PxServ::Callback manual = client.getData("watering/manual-pump"); requestPump = manual.status == 200 && manual.data == "1"; } else { requestPump = moisture < START_THRESHOLD; } bool cooldownFinished = now - lastPumpStoppedAt >= COOLDOWN_MS; if (requestPump && !pumpRunning && cooldownFinished) setPump(true); if (!requestPump && pumpRunning) setPump(false); }

Commissioning

Test with only the relay indicator first. Confirm calibration, manual control, the five-second shutoff, and cooldown before connecting the pump. A real system also needs reservoir level, leak/flow detection, and a hardware cutoff.

Last updated