Back to Portfolio

Smart Plant Pot

A 3D printed plant pot that shows how damp the soil is and glows at the base when it needs watering.

A capacitive soil moisture sensor sits in the pot and is read once a second by a Wemos D1 mini. The moisture level is shown as a percentage on a small OLED screen at the top of the sensor column. When the soil gets too dry, a ring of 16 white LEDs under the pot slowly fades in and out until the plant is watered. Over Wi-Fi the pot also answers a simple API request with its current reading, so my homelab dashboard can keep an eye on it.

Breadboard test circuit with an ESP8266 board, a small OLED reading 10% and the capacitive moisture sensor wired in

Testing the circuit

I started with everything on a breadboard: an ESP8266 development board, a 0.96" OLED display and the capacitive sensor. Once the reading showed up on the screen I knew the parts would work together.

My first plan was to use an ESP-01 as the brain to keep it small. It turned out the ESP-01 does not break out the chip's analogue input, so it can't read the sensor without an extra ADC board. I switched to a D1 mini instead, which has the analogue pin available and is still small enough to fit in the pot.

Housing and waterproofing

The pot and the sensor column are printed in black PLA. The sensor slots into the column so its probe reaches down into the soil, with the electronics and display sitting above the soil line.

The sensor's probe is fine to sit in damp soil, but the components at the top of the board are not. I sealed them with UV curing epoxy so watering the plant can't short anything out.

The capacitive soil moisture sensor slotted into the black printed sensor column inside the pot
The pot's LED ring slowly fading in and out under the base

The breathing LED ring

Under the pot is a ring of 16 white SMD LEDs, the same type I used in my Helping Hand LED Ring. Each LED has its own resistor and they run from 5V, switched by a small MOSFET that the D1 mini drives with PWM.

The base that covers the ring is printed in white TPU. It spreads the light from the LEDs into an even glow and gives the pot a grippy rubber foot so it doesn't slide around.

The ring starts breathing when the moisture drops below 30% and stops once it climbs back above 40%. Having two thresholds stops it flickering on and off when the reading sits right on the limit. The fade follows a curve that lingers at the dim end and rises quickly through the bright peak, which looks more natural than a straight ramp.

One trace on the ring's PCB was damaged during assembly, so in the end only the LEDs along the back face light up. The glow still shows through the white base, as you can see in the clip, but it doesn't reach all the way round.

The finished pot

With a seedling planted, the screen shows the current moisture as a large percentage with a bar underneath and a short status line. Every sensor reads slightly differently, so I calibrated it by noting the raw reading in open air and in a glass of water, and the firmware maps everything in between to 0 to 100%.

For the next version I want to redesign the LED ring. A sturdier board with better protected traces should avoid the damage that cut this one short, and give the pot a full 360 degree glow.

The finished black pot with a seedling in the soil and the OLED on the sensor column reading 10%

How the API works

The pot joins my home Wi-Fi and announces itself on the network as plant-pot.local. It answers one request: ask it for its moisture and it replies with the current reading as JSON. There's no web page on the device itself, and it doesn't store any history. Anything that wants to track the plant over time just asks every few minutes and keeps the answers.

The API is read-only and only reachable on my home network. Firmware updates also happen over Wi-Fi, but those need a password.

$ curl http://plant-pot.local/api/moisture
{
  "moisture_pct": 54,
  "raw": 437,
  "needs_water": false
}
moisture_pctCalibrated moisture, 0 to 100
rawSmoothed sensor reading, used for calibration
needs_watertrue while the LED ring is breathing

plant-pot.local

Desk Plant

Happy

54%

Soil moisture

Last watered

2d 05h 12m ago

Dashboard widget

This is the widget I use to show the pot on my dashboard. The data here is simulated so you can try it: drag the slider up to "water" the plant, and leave it for a bit to watch the soil slowly dry out.

The drop fills up to the moisture level, with the top edge fading softly into the empty part, and changes colour as it goes, amber while the plant needs water, then light blue through to deep blue as the soil gets wetter. Below 30% the status changes to "Needs water" and the drop pulses, the same way the LED ring does on the real pot.

The pot itself doesn't know when it was last watered, so the dashboard works it out. Each time it polls the API it compares the new reading with the previous one. Soil only dries out slowly, so a sudden jump of 15 points or more can only mean the plant was watered, and the dashboard logs it as one. The timer counts up from there.