The idea
A small circuit — an ESP32 microcontroller and a capacitive soil moisture sensor ($2 each) — reads moisture levels every 15 minutes and publishes readings to a local MQTT broker via Wi-Fi. A lightweight dashboard plots the history, and when a reading drops below a configured dry threshold it fires a push notification to your phone via Ntfy or Gotify.
The firmware is written in MicroPython and configured with a single JSON file on the device — SSID, password, MQTT host, and threshold value. The server side is two Docker containers: Mosquitto (MQTT broker) and a small Node.js bridge that subscribes, writes to SQLite, and triggers notifications.
Why build this
Most commercial plant monitors (Xiaomi Mi Flora, Parrot Pot) are overpriced, require a vendor cloud, and stop working when the company pivots or the app is sunset. A DIY version with off-the-shelf parts costs under $10 per plant and keeps all data local. The ESP32 and MQTT pattern is well-understood and the firmware can go from breadboard to permanent install in an afternoon.
The trend toward self-hosted home automation (Home Assistant has millions of active users) has created a large audience comfortable running a small server at home. This fits that ecosystem naturally and can publish to Home Assistant's MQTT discovery API with minimal extra work.
Stack sketch
- Firmware: MicroPython on ESP32;
umqtt.simplefor MQTT; capacitive moisture sensor — not resistive, which corrodes in weeks - Broker: Mosquitto in Docker, plain TCP port 1883 on the LAN
- Bridge: Node.js service subscribing to
plants/+/moisture; writes readings to SQLite viabetter-sqlite3 - Dashboard: Grafana reading from SQLite via the Grafana SQLite plugin; or a plain HTML page with Chart.js served by the Node.js process
- Notifications: Ntfy (self-hosted) or Gotify; one HTTP POST from the bridge when a threshold is crossed
Scope for v1
- Single-plant support: one sensor, one ESP32
- 15-minute polling interval, configurable in the JSON file
- SQLite history with 90-day rolling retention
- One alert channel: Ntfy or Gotify
- Grafana dashboard shipped as a pre-built JSON config
- Deliberately out: watering actuation, multiple sensors per device, battery-life optimization, OTA firmware updates
Where it could go
Once v1 is stable, multi-plant support is the natural next step. Each ESP32 gets a unique device ID and publishes to a namespaced topic (plants/bedroom-fern/moisture). The dashboard groups sensors by room and the alert logic can apply different thresholds per plant species. A minimal admin UI could replace manual JSON editing on the device.
Adding a small relay and a 5V pump turns a monitor into an automated waterer. That is a bigger hardware lift — power supply, tubing, reservoir — but the software side barely changes: add a MQTT command topic and a schedule in the bridge service, and the rest of the stack stays identical.
Watch out for
Capacitive sensors vary in accuracy between manufacturing batches — calibrate each unit by recording readings in completely dry soil and fully saturated soil, then map those raw values to 0–100. Without per-unit calibration, a threshold that works on one device will misfire on another.