ideas.
May 26, 2026 2 min read hardwareconsumerautomation

ESP32 plant moisture monitor

A cheap ESP32 and capacitive moisture sensor that logs soil conditions to a self-hosted dashboard and sends push alerts when your plants need watering.

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.simple for 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 via better-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.