June 24, 2026
From Moisture Sensor to Pump: a Maker’s Fully Automatic Smart Plant-Watering System

Manual watering requires remembering on a schedule, and it’s exactly the kind of thing that slips during travel or busy days. So I let the sensors decide instead, and built a three-pump system that waters itself based on soil moisture. In this post I’m sharing the parts, the wiring logic, how the software works, the safety mechanisms, and the problems I ran into along the way.
Parts
- Main control: an ESP32-S3 dev board (44/48-pin) + a screw-terminal shield + a 4-channel relay module (5V) + an LM2596 step-down regulator (with display).
- Power & lighting: a 12V 12.5A metal-case adapter; a 12V full-spectrum LED bar (5630 chip / 72 LEDs) — ordered for a future addition, not active yet.
- Sensors & actuators: 5× capacitive soil moisture sensors, 1× horizontal liquid-level float switch, 3× 12V peristaltic dosing pumps.
- Wiring: 10m of 8-core DT8 signal cable, a JST-XH 2.54mm connector kit (230 pieces), M3 heat-set inserts and screws.
- Fluid storage: a 20-liter Litolan jerrycan with a tap (DIN 61), 4×6mm silicone tubing, Y/T hose fittings, custom 3D-printed enclosures.

Wiring and Setup Logic
The hardware side is actually quite simple: 5 analog inputs (moisture sensors, GPIO1-5, all on the ADC1 channel that doesn’t conflict with WiFi), 3 digital outputs (pump relays, GPIO11-13), and 1 digital input (float switch, GPIO14, INPUT_PULLUP + inverted). The 4th relay channel, reserved for lighting, isn’t in use yet. The relays are active-LOW — meaning even if the pins sit at their default state while the ESP32 boots, the pumps stay off, so they never turn on by themselves after a restart. The ESP32-S3 itself is powered through an LM2596 regulator that steps the 12V main line down to 5V, so a single power supply can run both the pumps and the low-voltage electronics.

How Moisture Becomes a Percentage
Each sensor reads a raw voltage, and a template sensor converts it to a percentage. Calibration is based on two reference points: dry soil is treated as 2.71V / 0%, wet soil as 0.94V / 100% — moisture% = (2.71 − voltage) / (2.71 − 0.94) × 100, clamped to 0-100. I use a 3-sample median filter to cut down on noise; the raw voltage and percentage are both reported to Home Assistant hourly. All five sensors currently share the same calibration — calibrating each one individually could improve accuracy given differences in soil and sensor manufacturing.
Watering Logic: Who, When, How Much
- Pump 1 → Yuka + large Dracaena: runs for 20s if Yuka <20% or the Dracaena <35%.
- Pump 2 → Starlice + medium Dracaena: runs for 10s if Starlice <40% or the Dracaena <35%.
- Pump 3 → Cactus: runs for just 5s if moisture <10% (cacti need very little water, very rarely — overwatering causes root rot).
Every evening at 21:00 the system runs through its sequence: first it checks that the reservoir is full and the emergency stop is off, then Pump 1 → 10s wait → Pump 2 → 10s wait → Pump 3 fire in turn. The reason I run the pumps one after another rather than together is simple — avoid loading a single power/water line all at once. Each pump finishes its own job before the next starts (via `script.wait`), a pump below threshold is skipped and the reason is logged, and the watering counters persist even across an ESP32 restart.
Safety Mechanisms
- Reservoir protection: the moment the float switch detects an empty tank, all three pumps shut off immediately and a notification goes out; a separate check every 60 seconds re-verifies the reservoir state as a backup. The float switch itself runs through a 500ms debounce filter, so momentary sloshing doesn’t trigger a false alarm.
- Emergency stop switch: flipping it stops all three pumps and locks the system — no watering, automatic or manual, until the switch is turned off again.
- Manual pump protection: pumps can also be switched on by hand from Home Assistant, but the reservoir/lock check re-runs on every manual activation, preventing an accidental run when the tank is empty.
- Safe boot: all pumps start with `restore_mode: ALWAYS_OFF`, so after a power outage the system always comes back up in a safe, off state.
Problems I Ran Into
Siphoning: even after a pump stopped, water kept flowing on its own because of the height difference in the tubing — a classic siphon effect. I fixed it in two steps: drilling a small air hole in the tube to break the vacuum that drives the siphon, and routing the tube near the pump outlet higher than the reservoir’s water level so gravity can’t drain it on its own. Together, those two changes solved it completely.
Conclusion
Where I’ve landed: five plants, three pumps, a fully automatic system backed by several layers of safety. As long as I remember to refill the reservoir, the plants going thirsty is now close to impossible. The Moon Wall Lamp and the fully local Smart Home Automation System I built follow the same ESPHome/Home Assistant approach as this project. If you’re thinking about building your own, or get stuck somewhere, feel free to reach out through the Contact page.