ESP32 UNO - Soil Moisture Sensor

Does your plant need water? Connect a soil moisture sensor to the ESP32 Uno form board and let the Arduino code tell you. The ESP32 Uno reads the sensor as a number from 0 to 4095, and you can use that number to decide if the soil is wet or dry.

By the end, you can:

  1. Choose between a resistive and a capacitive moisture sensor
  2. Wire a capacitive soil moisture sensor to the ESP32 Uno
  3. Read the moisture level with analogRead() in Arduino IDE
  4. Calibrate the sensor for your own soil
  5. Print "wet" or "dry" from a threshold value
ESP32 Uno soil moisture sensor

Components You Need

1×ESP32 Uno-form Board
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×Capacitive Soil Moisture Sensor
1×Jumper Wires

Or you can buy the following kits:

1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

Buy Note: Many soil moisture sensors available in the market are unreliable, regardless of their version. We strongly recommend buying the sensor with TLC555I Chip from the DIYables brand using the link provided above. We tested it, and it worked reliably.

Two Kinds of Soil Moisture Sensor

By the end of this part, you know which sensor type to buy and why.

capacitive moisture sensor vs resistive moisture sensor
Feature Resistive Sensor Capacitive Sensor
How it measures Current flows between two bare probes Probe measures a change in capacitance
Probe surface Bare metal Covered with a coating
Wear Corrodes after some time in wet soil Does not corrode
Our advice Avoid Use this one

The resistive probes send a small current through the soil. That current causes electrochemical corrosion, so the metal wears away and the readings drift. The photo below shows a probe that corroded this way.

resistive soil moisture sensor corroded

The rest of this page uses the capacitive sensor.

Capacitive Soil Moisture Sensor Pinout

The sensor has three pins.

Pin Role
GND Ground (0V)
VCC Power, 3.3V to 5V. Use 3.3V with the ESP32 Uno
AOUT Analog signal that changes with the water in the soil
capacitive soil moisture sensor pinout

Good to know: the AOUT voltage goes DOWN when the soil gets wetter. So a small number from the ESP32 Uno means wet soil, and a big number means dry soil.

How the Reading Changes

More water around the probe gives a lower voltage on AOUT. The ESP32 Uno ADC turns that voltage into a number from 0 to 4095 (12-bit).

ESP32 Uno Pinout

Every Uno header pin on the ESP32 Uno form board has its own GPIO number, and the code uses that number. Look at the diagram to find A0 and its GPIO before you wire the sensor.

ESP32 Uno pinout diagram

How to Connect the Sensor

By the end of this part, the sensor is powered and its signal goes to the ESP32 Uno.

The wiring diagram between ESP32 Uno soil moisture sensor

This image is created using Fritzing. Click to enlarge image

Sensor Pin ESP32 Uno Pin
VCC 3.3V
GND GND
AOUT A0 (GPIO2)

WARNING

A0 (GPIO2) is a boot strapping pin. If the sensor holds this pin at a strong level while the board starts or while you upload code, the upload can fail. Unplug the AOUT wire during upload if this happens. Power the sensor from 3.3V so the AOUT signal never goes above what the ESP32 Uno pin can take.

Good to know: ESP32 Uno pins are not 5V tolerant. A0 (GPIO2) is an ADC2 pin, which is fine here because this sketch does not use WiFi.

ESP32 Uno Code - Read the Moisture Value

The sketch reads A0 (GPIO2) twice a second and prints the raw value.

/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-soil-moisture-sensor */ #define AOUT_PIN 2 // A0 (GPIO2) is connected to the sensor AOUT wire void setup() { Serial.begin(9600); // start the serial link at 9600 baud // set the ADC attenuation to 11 dB (up to ~3.3V input) analogSetAttenuation(ADC_11db); } void loop() { int value = analogRead(AOUT_PIN); // take one ADC sample, 0 to 4095 Serial.print("Moisture: "); // label for the number Serial.println(value); // show the raw sample delay(500); // wait before the next sample }
Code Part What It Does
AOUT_PIN 2 Selects GPIO2, which is the A0 hole on the Uno header
analogSetAttenuation(ADC_11db) Widens the ADC input range to about 0-3.3V
analogRead(AOUT_PIN) Returns the moisture level as 0 to 4095
Serial.println(value) Sends the number to the Serial Monitor
delay(500) Gives two readings per second

Good to know: the 11 dB setting lets the analog pin read the full range, from 0V up to about 3.3V. Without it, the readings reach 4095 too early and the moisture values look wrong.

Detailed Instructions

  1. First time with this board? Set it up with ESP32 Uno - Getting Started before you continue.
  2. Build the circuit by following the wiring table above.
  3. Plug in the USB Type-C cable between the ESP32 Uno and your computer.
  4. Select the board in Arduino IDE: pick ESP32 Dev Module and the COM port of the board (CH340).
  5. Copy the code above into a new sketch.
  6. Click Upload and wait for the "Done uploading" message.
  7. Open the Serial Monitor at 9600 baud.
  8. Test it: push the sensor into dry soil, then slowly pour water next to it. You can also dip the sensor in a cup of salt water.
  9. Watch the values drop as the soil gets wet. Output looks like this:
  10. Pro tip: let the reading settle for about 10 seconds after you move the sensor. Water needs time to spread through the soil.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
Moisture: 2640 Moisture: 2633 Moisture: 2598 Moisture: 2541 Moisture: 2463 Moisture: 2380 Moisture: 2292 Moisture: 2204 Moisture: 2121 Moisture: 2049 Moisture: 1986 Moisture: 1934 Moisture: 1897 Moisture: 1879 Moisture: 1871
Ln 11, Col 1
ESP32 Dev Module on COM15
2

※ NOTE THAT:

  • Do not use pure water for the test. Pure water conducts almost no electricity, so it hardly changes the reading.
  • The value does not fall to zero. It stays in a band that depends on the soil, the depth of the probe and the supply voltage.
  • Keep the electronics at the top of the sensor dry. Push only the marked probe part into the soil.

Video Tutorial

See the ESP32 Uno build in action in the video below.

Calibrate the Sensor

By the end of this part, you have your own wet/dry threshold.

The reading depends on the soil type and its water content, so no fixed number works for everyone. Do this once:

Step 1 - Start the sketch

Run the code above and open the Serial Monitor.

Step 2 - Insert and water

Push the sensor into the soil, then add water a little at a time.

Step 3 - Note the switch point

Write down the value at the moment you think the soil goes from dry to wet. This number is your THRESHOLD.

Tell Wet Soil from Dry Soil

By the end of this part, the ESP32 Uno prints if the soil is wet or dry.

Put your THRESHOLD into the sketch below. The value 2000 is only an example, so replace it with the number you noted.

/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-soil-moisture-sensor */ #define AOUT_PIN 2 // A0 (GPIO2) reads the sensor AOUT wire #define THRESHOLD 2000 // replace with the value you found while calibrating void setup() { Serial.begin(9600); // open the serial link at 9600 baud // set the ADC attenuation to 11 dB (up to ~3.3V input) analogSetAttenuation(ADC_11db); } void loop() { int value = analogRead(AOUT_PIN); // sample the sensor, 0 to 4095 if (value > THRESHOLD) // a high value means little water Serial.print("The soil is DRY ("); // report dry soil else Serial.print("The soil is WET ("); // report wet soil Serial.print(value); // add the raw number Serial.println(")"); // close the bracket and end the line delay(500); // half a second between checks }
Code Part What It Does
THRESHOLD 2000 The border between dry and wet, set by you
value > THRESHOLD True when the soil holds little water
Serial.print(value) Shows the number next to the text

The Serial Monitor shows the result:

∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
The soil is DRY (2712) The soil is DRY (2588) The soil is DRY (2310) The soil is DRY (2105) The soil is WET (1942) The soil is WET (1856) The soil is WET (1790)
Ln 11, Col 1
ESP32 Dev Module on COM15
2

ESP32 Uno vs UNO R4 for This Project

By the end of this part, you know what to change when you move this project from the UNO R4 to the ESP32 Uno.

Item Arduino UNO R4 ESP32 Uno
Logic voltage 5V 3.3V
Sensor pin A0 A0 (GPIO2), written as 2 in code
ADC range 0-1023 0-4095
Typical wet/dry threshold about 530 about 2000, calibrate it yourself
Extra code line none analogSetAttenuation(ADC_11db)
Sensor power 5V 3.3V
Arduino IDE board Arduino UNO R4 WiFi ESP32 Dev Module

The wiring stays the same on the Uno header, but power the sensor from 3.3V. Your old threshold must be calibrated again because the ADC range is four times bigger.

Go Wireless: Check Your Plant from the Couch

By the end of this part, you know how to read soil moisture on your phone.

The ESP32 Uno has WiFi and Bluetooth built in. Send the moisture value to a web page or to a phone app, and you see the soil state without walking to the pot. Note that the ESP32 Uno cannot read A0 (GPIO2) with analogRead() while WiFi is on, so for WiFi projects move the AOUT wire to A2 (GPIO35) and change AOUT_PIN in the code.

Function References

Troubleshooting

Problem Possible Cause Solution
No COM port in Arduino IDE CH340 driver missing or charge-only USB cable Install the CH340 driver and use a USB cable that carries data
Upload fails with "Failed to connect" Board is not in download mode Hold the BOOT button when the upload starts
Upload fails only with the sensor wired A0 (GPIO2) is a boot strapping pin and the sensor pulls it Unplug the AOUT wire during upload, then plug it back
Reading is always 0 VCC or GND not connected Check the wires and power the sensor from 3.3V
Reading is always 4095 AOUT floating or wrong pin Connect AOUT to A0 (GPIO2) and check the pin number in code
Values hit 4095 too early Attenuation line missing Keep analogSetAttenuation(ADC_11db) in setup()
Values jump around Loose jumper wire or electrical noise Re-seat the wires and average a few samples
Wet soil gives a high number Reading is reversed in your mind On this sensor a lower number means wetter soil
Sensor stops working after some weeks Water got into the electronics Keep the top of the sensor dry and seal the edge

FAQ

How does a capacitive soil moisture sensor work?

It measures how the water in the soil changes the capacitance around its probe. The sensor turns this change into a voltage on the AOUT pin. More water gives a lower voltage.

Should I power the sensor with 3.3V or 5V on the ESP32 Uno?

Use 3.3V. The ESP32 Uno pins are not 5V tolerant, and a 5V supply can push the AOUT signal above what the analog pin can take.

Why do I get numbers up to 4095 and not 1023?

The ESP32 Uno has a 12-bit ADC, so analogRead() returns 0 to 4095. A UNO R4 sketch with a threshold of 530 needs a new threshold here.

Why does the code call analogSetAttenuation(ADC_11db)?

It sets the ESP32 Uno ADC input range to about 0-3.3V, so the full sensor signal maps to 0-4095. With a smaller range, readings saturate early. The UNO R4 does not need this line, because it is an ESP32-only call. Keep the sensor output at 3.3V or less.

Can I connect the sensor to a different pin?

Yes. Use A2 to A5 (GPIO35, 34, 36, 39) for input-only analog reads, which are safe for a sensor. Change AOUT_PIN to the new GPIO number.

Why should I avoid A0 (GPIO2) for permanent builds?

GPIO2 is a boot strapping pin, and it is an ADC2 pin. ADC2 stops working for analogRead() when WiFi is on. A2 (GPIO35) is a better choice for a WiFi project.

How do I find the right THRESHOLD value?

Read the raw value in dry soil and again in wet soil. Pick a number between the two, then test it with your plant and adjust it.

Can I use two soil moisture sensors?

Yes. Put the second AOUT on another analog pin such as A2 (GPIO35), add a second #define and call analogRead() for each pin.

Can I see the soil moisture on my phone?

Yes. The ESP32 Uno has WiFi and Bluetooth. The Go Wireless section above lists tutorials that show a value on a web page or in a phone app.

What if my project needs more pins than the Uno headers give?

The ESP32 Uno has a few extra holes near the power header: GPIO32 and GPIO33 (I/O, PWM, analog on ADC1, so they also work while WiFi is on) and GPIO15 (I/O, PWM, analog on ADC2). Solder pin headers to use them and write the GPIO number in code. For a second soil sensor in a WiFi project, GPIO32 or GPIO33 fit best. Two warnings: (1) GPIO15 and GPIO0 (the IO0 pin at the unused Uno NC position) are boot strapping pins, so a wrong connection can stop the board from booting or uploading code. Use GPIO32 and GPIO33 first. (2) Never use the six holes under the digital header (SD0-SD3, CMD, CLK = GPIO6-GPIO11). They are wired to the flash chip and using them crashes the board.

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!