My soil moisture sensor said my basil was fine—then it wilted overnight. Turns out, analog readings lie without context.
My basil died, and I blamed the sun. Or maybe the pot. Definitely not me. After all, I had an ESP32 hooked up to a soil moisture sensor, and the numbers said everything was “fine.” Spoiler: they weren’t.
What I didn’t realize was that raw analog values from a soil moisture sensor can be misleading without proper calibration, grounding, and interpretation. That one plant—and my bruised ego—taught me more about voltage dividers and ADC noise than any datasheet ever did.
If you’ve ever tried to automate your garden with ESP32 and got readings that didn’t make sense, this post is for you. We’ll walk through how to wire, read, and rely on soil moisture data in MicroPython—without needing proprietary tools or cloud platforms.
Let’s debug the dirt together.
What Is a Soil Moisture Sensor and How Does It Work?
Soil moisture sensors are simple yet powerful tools that help you detect how “wet” or “dry” the soil is. At their core, they measure the electrical conductivity of the soil—which changes based on how much water is present. Wet soil conducts electricity better than dry soil, and the sensor translates that into a voltage (analog) reading that your ESP32 can understand.
There are two common types:
- Resistive sensors use two exposed metal probes. Water in the soil conducts electricity between them, and the resistance changes based on moisture. These are affordable but can corrode quickly if left powered.
- Capacitive sensors are a more durable upgrade. They measure changes in capacitance caused by soil moisture, offering better longevity and stability, especially in continuous monitoring setups.
In a typical MicroPython project, the ESP32 reads this value from one of its analog input pins. The result is a number (usually from 0 to 4095 on a 12-bit ADC), but that number only becomes meaningful once you calibrate it—by comparing it to known dry and wet conditions in your specific soil.
Whether you’re automating irrigation or just curious if your plant needs watering, soil moisture sensors bridge the gap between code and the natural world. They turn squishy, unpredictable dirt into reliable data—and that’s a little bit magical.
· · ─ ·𖥸· ─ · ·
Materials Needed
- ESP32 Development Board
We’ll be using the ESP32 to read data from the soil moisture sensor. It’s a powerful board with Wi-Fi and Bluetooth capabilities, perfect for IoT applications. - Soil Moisture Sensor
A common soil moisture sensor typically has two electrodes for measuring the soil’s conductivity, which varies with the moisture content. - Jumper Wires
For connecting the sensor to the ESP32. - Breadboard (Optional)
To make wiring simpler and more organized. - MicroPython installed on your ESP32.
· · ─ ·𖥸· ─ · ·
Wiring the Soil Moisture Sensor to the ESP32
- Connect the Soil Moisture Sensor to the ESP32:
- VCC (Sensor) → 3.3V (ESP32)
- GND (Sensor) → GND (ESP32)
- Analog Output (A0) → GPIO34 (ESP32) (Note: You can also use other GPIO pins with ADC functionality, but we’ll use GPIO34 here for simplicity.)
- The soil moisture sensor typically outputs an analog signal that represents the moisture level. This is read by the ESP32’s ADC (Analog-to-Digital Converter) pins.
Setting Up MicroPython on the ESP32
If you haven’t already, you’ll need to install MicroPython on your ESP32. Follow these steps to get it set up:
- Download and install Thonny or uPyCraft IDE for MicroPython programming.
- Flash the latest MicroPython firmware onto the ESP32 using the appropriate tool.
- Connect your ESP32 to the computer via USB and configure it to communicate with the IDE.
Writing the MicroPython Code
Now, let’s write a simple script to read the soil moisture level and print it to the console. We’ll also add a threshold and provide feedback via the serial monitor if the soil moisture is too low.
from machine import Pin, ADC
import time
# Set up the analog pin for the soil moisture sensor
moisture_sensor = ADC(Pin(34)) # Using GPIO34 for analog input
moisture_sensor.atten(ADC.ATTN_0DB) # Set the ADC input range to 0-3.3V
# Threshold value for soil moisture (this can be adjusted based on your sensor)
MOISTURE_THRESHOLD = 1000 # Values may range between 0 and 4095, higher values indicate more moisture
while True:
# Read the analog value from the sensor
moisture_level = moisture_sensor.read()
# Print the moisture level to the console
print("Soil Moisture Level:", moisture_level)
# Check if the moisture level is below the threshold
if moisture_level < MOISTURE_THRESHOLD:
print("Warning: Soil is too dry! Consider watering.")
else:
print("Soil moisture is sufficient.")
# Wait for 2 seconds before taking the next reading
time.sleep(2)
How the Code Works:
- Setup ADC Pin:
Themoisture_sensor = ADC(Pin(34))
line sets GPIO34 as an analog input. The ESP32 has several ADC pins; in this case, we use GPIO34, which is often free and accessible. - Reading Sensor Values:
Themoisture_sensor.read()
function reads the analog value from the soil moisture sensor. The sensor outputs a value between 0 and 4095, where a higher value typically indicates more moisture in the soil. - Moisture Level Check:
The code checks if the moisture level is below a set threshold (MOISTURE_THRESHOLD
). If the value is lower, it prints a warning message suggesting that the soil might need watering. - Delay:
Thetime.sleep(2)
command pauses the program for 2 seconds before reading the sensor again.
Testing and Calibration
- You can test the sensor by inserting it into a pot of soil. The reading will fluctuate as the moisture content of the soil changes.
- To calibrate, you may want to check the sensor’s readings in both dry and wet soil and adjust the
MOISTURE_THRESHOLD
accordingly.
Optional Enhancement
You can add an LED or a relay to trigger an alert when the soil moisture falls below the threshold. For example, you could connect an LED to GPIO2, which would light up when the soil is dry:
from machine import Pin
led = Pin(2, Pin.OUT) # GPIO2 for the LED
while True:
moisture_level = moisture_sensor.read()
if moisture_level < MOISTURE_THRESHOLD:
led.on() # Turn the LED on when the soil is too dry
else:
led.off() # Turn the LED off when the moisture level is sufficient
time.sleep(2)
· · ─ ·𖥸· ─ · ·
Understanding Soil Moisture Readings
Raw analog values from a soil moisture sensor mean nothing without calibration. In most setups, the ESP32’s ADC returns values from 0 to 4095, but what does that mean for your plant?
You’ll need to create a personal baseline:
# After uploading this, test your sensor dry and then wet
from machine import ADC, Pin
import time
adc = ADC(Pin(32)) # Use the same pin as your main script
adc.atten(ADC.ATTN_11DB)
while True:
moisture = adc.read()
print("Moisture level:", moisture)
time.sleep(2)
- Dry soil or air may read ~3000–4000
- Moist soil may read ~1000–2500
- Water saturated may go below ~1000
👉 Tip: Your values may vary. Calibrate by noting the ADC reading when the sensor is in completely dry soil, then in fully watered soil. Everything in between is your working range.
· · ─ ·𖥸· ─ · ·
Triggering a Watering Action (Or Just an LED)
Now that you know what moisture values mean in your context, let’s use them. This snippet turns on an LED if the soil is too dry:
from machine import ADC, Pin
import time
sensor = ADC(Pin(32))
sensor.atten(ADC.ATTN_11DB)
led = Pin(2, Pin.OUT)
# Define your own dry threshold based on calibration
DRY_THRESHOLD = 2500
while True:
moisture = sensor.read()
print("Soil:", moisture)
if moisture > DRY_THRESHOLD:
led.on() # Soil is too dry
else:
led.off() # Soil has enough moisture
time.sleep(2)
You can swap out the LED with a relay pin to trigger a small water pump—and you’ve got yourself a basic smart irrigation system.
· · ─ ·𖥸· ─ · ·
Conclusion: Plants Don’t Lie—But Sensors Might
Soil Moisture, Calibrated and Clean
Whether you’re building a DIY irrigation system or just trying to keep a single plant alive (no judgment), using a soil moisture sensor with ESP32 can be surprisingly powerful—if you understand the quirks. From proper analog pin selection to interpreting resistance values in real-world soil, getting reliable moisture data is about more than just wiring things up.
MicroPython makes experimentation accessible. Pair it with the FOSS mindset—question everything, document your edge cases, and share your fixes—and you’ll go far beyond blinking LEDs and into meaningful, sustainable automation.
Liked this guide? I share raw, hands-on insights like these in my newsletter—focused on low-cost builds, ethical hacking, and FOSS-powered learning.
Subscribe now: https://www.samgalope.dev/newsletter
Like what you’re reading?
Help keep DevDigest
free and caffeine-powered
—buy me a coffee on Ko-fi.
Leave a Reply