Skip to main content
Nastrotek
IoT SolutionsIoT SolutionsNew

Smart Garden: Soil, Light and Water Monitoring

How I built a small ESP32 smart garden to monitor soil moisture, light, and water, then irrigate a few balcony plants without turning the project into a tangle of wires.

Share

LinkedInFacebookX
Hệ thống vườn thông minh dùng ESP32 theo dõi đất, ánh sáng và tưới nước cho các chậu rau thơm trên ban công

If you build a similar system, I recommend calibrating it on your actual pots before letting the pump run unattended.

A few pots of herbs on a balcony make a surprisingly good IoT project. They are close enough to check every day, yet easy to forget for a few days when work gets busy. When I first thought about a “smart garden,” I wanted many sensors and a polished dashboard. After trying it, three questions mattered much more: is the soil actually dry, how much light does the plant receive, and did the last watering cycle finish safely?

This is how I put those three questions into one small ESP32 system. It suits a few pots on a balcony or windowsill, not a whole garden. The goal is to learn the plants' routine, water with limits, and keep a manual way to step in.

The MVP I would build

BlockPractical choice for 2–4 pots
ControllerESP32-C3 or ESP32 DevKit
SoilOne 3.3 V capacitive probe per pot
LightBH1750 over I2C, mounted near the leaves
Water levelMagnetic float switch or another switch-type level sensor
Watering5 V peristaltic or small submersible pump, switched by a MOSFET
ConnectionMQTT to Home Assistant, or a local web page
PowerIsolated DC adapter; suitable rails for pump and ESP32 with common ground

For a few small pots, I like a peristaltic pump because water touches only the tube, flow is easy to estimate, and it is less likely to keep siphoning after power is removed. A submersible pump is cheaper, but it needs a minimum water level and careful tube routing.

The BH1750 reports illuminance in lux and is easier to compare over time than a bare photoresistor. Lux follows human visual sensitivity, however, so it is not a replacement for a proper PAR sensor. In a small balcony garden, I use it to compare locations and hours of light across days, not to diagnose a plant from one magic number.

Calibrate soil before enabling the pump

A capacitive probe is preferable to two exposed metal prongs because it does not pass DC directly through the soil. It is still not a fit-and-forget sensor: the electronics at the top must remain above the soil, dry, and protected from rain.

Calibrating a capacitive soil-moisture sensor with dry and wet soil beside an ESP32 test board

Do not trust the percentage from a sample sketch. I record dry and wet endpoints using the same soil, pot, and sensor that will be used in the garden.

My calibration is deliberately simple:

  1. Insert the probe to its intended depth in dry soil, wait for the value to settle, then average 20–30 samples. Save this as rawDry.
  2. Water the same soil evenly until it is wet but not muddy or flooded. Let water spread, then record rawWet.
  3. Map ADC readings to a relative 0–100% scale and clamp the result.
  4. Put the probe in the real pot, watch several drying and watering cycles, then choose a threshold for that plant.
int soilPercent(int raw, int rawDry, int rawWet) {
  int value = map(raw, rawDry, rawWet, 0, 100);
  return constrain(value, 0, 100);
}

On the original Wi-Fi ESP32, I prefer an ADC1 pin. Espressif's hardware guidelines recommend ADC1 over ADC2 while Wi-Fi is active and suggest a 0.1 µF capacitor from the ADC input to ground to improve accuracy. Firmware should also take several samples, reject obvious outliers, and average the rest.

With multiple pots, every probe gets its own rawDry/rawWet pair. Two identical-looking sensors can differ substantially. Sharing calibration points makes the dashboard look consistent, not the measurement.

Watering logic: less clever, more dependable

I do not let one low sample start the pump. A small state machine is easier to trust:

MONITOR
  soil stays below its threshold for 3 readings
  + reservoir is not empty
  + current time is inside the watering window
  -> WATER

WATER
  run the pump for 5–10 seconds
  -> SOAK

SOAK
  wait 10–20 minutes for water to spread
  measure again; allow at most one extra cycle if still dry
  -> MONITOR or LOCKOUT

The SOAK delay matters. If the controller watches the probe while pumping, water may not have reached it yet, and the system can overshoot badly. I also cap the total pump time or cycle count per day. When that limit is reached, the device locks watering and sends an alert instead of trying to rescue a potentially bad reading.

Peristaltic pump, silicone tubing, and water reservoir mounted below an ESP32 control enclosure

The pump and reservoir stay below the electronics. A watering time limit and a low-water sensor provide simple protection when the reservoir runs dry.

A GPIO must not power the pump directly. For a small DC pump, I use a logic-level MOSFET, flyback diode, gate pulldown, and a supply that can handle startup current. A relay can work, but a mechanical relay is noisy, consumes more power, and can inject switching noise into nearby I2C and ADC wiring.

My non-negotiable stop conditions are:

  • Do not start if the low-water switch says the reservoir is empty.
  • Give every run a local timeout, even when the app or MQTT connection disappears.
  • Initialize the pump output to OFF immediately after reset.
  • Apply the timeout in manual mode too; an “ON” command never means forever.
  • Mount electronics above the reservoir, add drip loops, and avoid upward-facing connectors.

For indoor pots, I run the system in a catch tray for several days. A beautiful dashboard will not expose a loose tube as quickly as a dry tray after 20 test cycles.

Make the light reading useful

Place the BH1750 close to leaf height with a similar view of the sky or lamp. If the sensor sits under an awning while the plant reaches into direct sun, it measures the awning. Leaving a breakout board bare in the rain is no better; a small clear, ventilated hood is enough for a prototype.

Balcony light sensor and a phone dashboard showing soil, light, and water trends

Lux becomes useful as a multi-day trend. A single reading is rarely enough to decide that a plant needs more light.

Instead of sending a “low lux” alert whenever a cloud passes, I sample on a schedule and calculate a relative daily light total. The dashboard only needs three views:

  • Soil-moisture curves per pot with pump events marked on the timeline.
  • A lux curve that reveals which pot receives only morning sun.
  • Reservoir state, today's pump runtime, and the latest alert.

The MQTT payload can stay small:

{
  "soil": { "basil": 46, "mint": 58 },
  "light_lux": 12400,
  "tank_ok": true,
  "pump_ms_today": 8000,
  "state": "soak"
}

If the node loses its network, sensing and safety limits must continue locally. MQTT is for history, display, and requests; the cloud should never be in the pump's stop path.

A sensible sampling rhythm

Soil changes slowly, so one reading every 5–15 minutes is normally enough. With wall power, light can be sampled every minute and summarized before transmission. Watering is the exception: while the pump runs, a safety task must track its timeout continuously and must not block on Wi-Fi.

Deep sleep makes a large difference in a solar build such as Plantwatery. For my first prototype, I still use an isolated adapter and continuous logging. I optimize the battery or design a custom PCB only after the thresholds, pump flow, and sensor placement are stable.

Checklist before unattended use

  • Calibrate each probe in the same soil and pot it will monitor.
  • Drop Wi-Fi, reset the ESP32, and disconnect MQTT during tests; confirm the pump still stops.
  • Pump into a measuring cup ten times to estimate real milliliters per second.
  • Simulate an empty reservoir and a disconnected soil probe.
  • Keep the probe electronics dry, rain-covered, and ventilated.
  • Log for at least one week without automatic watering first.
  • Limit both the number of cycles and total pump runtime per day.

I consider the first version successful when it can answer “which pot dries fastest, and which corner gets too little sun,” even before it waters anything. Once the data agrees with what I see by hand, enabling the pump is a small next step. That order costs an extra afternoon, but it is less likely to turn a slightly dry plant into a flooded balcony.

References

Share

LinkedInFacebookX

Keep exploring

Read next

Related articles

View more in IoT Solutions

Nastrotek uses cookies for analytics and ad personalization to help us understand how the site is used. You can accept or decline non-essential cookies.