Build a soil moisture sensor with an ESP32 and ESPHome
Capacitive soil moisture sensors cost about $2 and, unlike the older fork-shaped probes, don't corrode in the dirt. Wired to an ESP32 and calibrated properly, they tell Home Assistant exactly when each plant needs water — and can water it for you.
Choose the right sensor
Search for "capacitive soil moisture sensor v1.2" and you'll find dozens of near-identical boards. They're not all equal:
- Check the chip. Good boards use a TLC555 timer chip. Many clones use an NE555, which doesn't work properly at the ESP32's 3.3 V and gives stuck or drifting readings. The chip's marking is printed on top; zoom in on product photos before buying.
- Avoid resistive probes (two bare metal prongs, often labeled YL-69 or FC-28). They corrode within weeks.
Parts
- An ESP32 DevKit and USB power supply.
- One capacitive soil moisture sensor per pot.
- Clear nail polish or conformal coating, and heat-shrink tubing.
Wiring
| Sensor pin | ESP32 pin |
|---|---|
| VCC | 3.3V |
| GND | GND |
| AOUT | GPIO34 |
The signal has to go to an ADC1 pin: GPIO32 to 39 on a classic ESP32. The other analog converter, ADC2, stops working when Wi-Fi is on, which in ESPHome is always. The ESP32 safe pin finder lists them; choose "Analog input".
Waterproof the top
The probe's sensing area is coated, but the electronics at the top and the cut edges of the board aren't. Paint the components and edges with clear nail polish or conformal coating, then slide heat-shrink over the top. Push the probe into the soil only up to the line printed on it.
The ESPHome config
This reads the raw voltage and turns it into a 0–100% moisture value. Add it to an ESP32 config (the config generator makes the base):
sensor:
- platform: adc
pin: GPIO34
name: "Soil moisture raw"
id: soil_raw
attenuation: 12db
update_interval: 60s
unit_of_measurement: "V"
entity_category: diagnostic
- platform: copy
source_id: soil_raw
name: "Soil moisture"
unit_of_measurement: "%"
device_class: moisture
accuracy_decimals: 0
filters:
- calibrate_linear:
- 2.80 -> 0.0
- 1.20 -> 100.0
- clamp:
min_value: 0
max_value: 100
The 12db attenuation lets the ESP32 read the sensor's full voltage range. The raw sensor is kept as a diagnostic entity, which you need for calibration.
Calibrate it
Capacitive sensors read higher voltage when dry and lower when wet, and every probe is a little different. The two numbers in calibrate_linear are placeholders until you measure your own:
- Dry reading: hold the sensor in the air and note Soil moisture raw in Home Assistant. Replace
2.80with it. - Wet reading: stand the probe in a glass of water up to its line and note the reading again. Replace
1.20with it. - Install the updated config. The percentage now runs from 0 in air to 100 in water.
Real soil sits in between. Most houseplants want watering somewhere around 30–40% on this scale, but it depends on the plant and the soil mix. Watch the reading for a week: note the value the day you water and the value when the soil feels dry an inch down, and you'll have your plant's thresholds.
Automations
- Needs water: notify when moisture stays below your threshold for an hour. The hour stops a single low reading from setting it off.
- Automatic watering: add a relay and a small 5 V pump. Run the pump for a few seconds when moisture is low, then wait at least 30 minutes before checking again — water takes time to soak down to the sensor, and without the wait the pump runs until the pot overflows.
Several plants on one ESP32
A classic ESP32 has six ADC1 pins exposed on most DevKits (GPIO32–36 and 39), so one board can watch six pots within wire's reach. For plants scattered around the house, a Bluetooth plant sensor plus an ESPHome Bluetooth proxy is often tidier.