ESP8266 Soil Moisture Sensor - Smart Irrigation Project
Monitor soil moisture levels in real-time using ESP8266 and capacitive soil moisture sensor. Perfect for automated irrigation, smart gardening, and agricultural IoT applications.
This complete guide includes circuit connections, tested Arduino code, calibration instructions, and practical applications.
How It Works
Soil moisture sensors come in two very different kinds, and confusing them causes most of the disappointment with this part. Resistive probes have two exposed metal prongs and measure the conductivity between them — wet soil conducts better, so resistance falls. Capacitive probes have no exposed metal and measure the dielectric constant of the surrounding soil instead.
The resistive type is cheaper and the one in most starter kits. It has a serious flaw: passing current through soil electrodes causes electrolysis, which corrodes the prongs within weeks and progressively falsifies readings. Keeping it powered continuously destroys it faster.
The capacitive type costs slightly more and lasts indefinitely because nothing conductive touches the soil. For any installation meant to run longer than a few weeks, it is the only sensible choice.
The ESP8266 (NodeMCU) samples with a 10-bit ADC, so analogRead() returns 0–1023 across 3.3 V at the NodeMCU A0 header (the bare chip reads 0–1 V). Both types output an analog voltage, usually inverted — dry soil gives a high reading and wet soil a low one. Readings are also heavily affected by soil type, compaction and temperature, so numbers are only meaningful once calibrated in the actual soil being used.
Components Needed
- ESP8266 (NodeMCU)
- Capacitive Soil Moisture Sensor (v1.2 recommended)
- Jumper Wires (Male-Female)
- ESP8266 (NodeMCU)
- USB Cable for programming
Wiring to the ESP8266 (NodeMCU)
Connect AOUT to A0, VCC to the 3.3 V rail and GND to ground. Capacitive probes sold for ESP boards output 0–3 V and suit this board directly; some 5 V-powered probes exceed the ADC range and need a divider.
For a resistive probe, power it from a GPIO pin rather than the supply rail and switch it on only while taking a reading. Cutting duty cycle from 100% to a fraction of a percent extends electrode life from weeks to many months.
Insert the probe to root depth, vertically, and firm the soil around it. An air gap next to the probe reads as permanently dry, and a probe lying horizontally samples a layer that dries much faster than the root zone.
| Module pin | ESP8266 (NodeMCU) pin | Function |
|---|---|---|
| AOUT / A0 | A0 | Analog moisture level (inverted) |
| DOUT / D0 | D5 | Threshold trip from the onboard pot |
| VCC | 3V3 | Supply |
| GND | GND | Common ground |
Example Code
Moisture reading with the probe powered only during measurement, plus calibration points. Upload it with the board set to ESP8266 (NodeMCU) and open the Serial Monitor at 115200 baud.
const int MOISTURE_PIN = A0;
const int PROBE_POWER = 5; // powers the probe on demand
// Calibrate these in YOUR soil: reading in air (dry) and in water (wet)
const int DRY_READING = 870;
const int WET_READING = 307;
int readMoisturePercent() {
digitalWrite(PROBE_POWER, HIGH);
delay(200); // let the probe settle
long total = 0;
for (int i = 0; i < 10; i++) { total += analogRead(MOISTURE_PIN); delay(10); }
digitalWrite(PROBE_POWER, LOW); // power down to limit corrosion
int raw = total / 10;
int pct = map(raw, DRY_READING, WET_READING, 0, 100);
return constrain(pct, 0, 100);
}
void setup() {
Serial.begin(115200);
pinMode(PROBE_POWER, OUTPUT);
digitalWrite(PROBE_POWER, LOW);
}
void loop() {
int pct = readMoisturePercent();
Serial.print("Soil moisture: ");
Serial.print(pct);
Serial.print("% ");
if (pct < 30) Serial.println("dry — needs watering");
else if (pct < 70) Serial.println("adequate");
else Serial.println("saturated");
delay(60000); // hourly is plenty for soil
}
Applications
A soil moisture sensor turns up in a wide range of projects. These are the uses where it is the right choice rather than a compromise:
- Automatic plant watering and irrigation control
- Greenhouse and polytunnel monitoring
- Lawn and garden scheduling that skips watering after rain
- Agricultural field logging across multiple depths
- Detecting leaks and flooding under floors
Working with the ESP8266 (NodeMCU)
The ESP8266 (NodeMCU) is built around the ESP8266 and runs on 3.3 V logic with roughly 80 KB usable of SRAM and 4 MB on most NodeMCU boards of program flash. These details change how this circuit is wired and what the sketch can do, so they are worth stating plainly before you build.
The ESP8266 is a 3.3 V part and is not 5 V tolerant — feeding a 5 V sensor output straight into a GPIO can damage the chip. Use a divider or level shifter.
There is only one ADC channel, so reading several analog sensors needs an external multiplexer such as a CD4051 or an ADS1115.
D3 (GPIO0), D4 (GPIO2) and D8 (GPIO15) are strapping pins sampled at boot; holding them at the wrong level stops the board starting.
Built-in WiFi means the same sketch can publish readings to a dashboard without extra hardware.
| ESP8266 (NodeMCU) characteristic | Value | Why it matters here |
|---|---|---|
| Logic voltage | 3.3 V | Sensor outputs above this level need a divider or level shifter |
| ADC resolution | 10-bit (0–1023) | Sets how finely an analog reading can be resolved |
| Analog inputs | a single analog channel, A0 | Determines how many analog sensors can share the board |
| PWM outputs | any GPIO via software PWM | Needed for brightness, speed and tone control |
| I²C pins | D2 (SDA, GPIO4) and D1 (SCL, GPIO5) by default | Fixed by hardware — wiring copied from another board may not match |
| Interrupt pins | any GPIO except D0 (GPIO16) | Required for counting fast or asynchronous events |
| Serial | one hardware UART plus a transmit-only second port | Monitor runs at 115200 baud by default |
Troubleshooting
Most problems with this module fall into a handful of categories. Work through these before suspecting the part itself:
- Readings drift steadily over weeks — the resistive probe is corroding. Switch to capacitive.
- The percentage never reaches 100 even in water — the calibration constants do not match your probe; measure them directly.
- Values swing with temperature — soil conductivity is temperature dependent. Log temperature alongside and compensate.
- The probe reads dry in visibly wet soil — there is an air gap around it, or it is not deep enough.
- The sensor died after a season outdoors — the PCB end was not protected. Seal the electronics with heatshrink or conformal coating.
- The sketch compiles but the board resets or behaves erratically — a 5 V module output is being driven into a 3.3 V pin. Measure the signal before connecting it.
- Readings differ from an Arduino tutorial for the same part — the 10-bit ADC returns 0–1023, not 0–1023, so any constant copied from an Uno example needs rescaling.
Taking It Further on the ESP8266 (NodeMCU)
Once the basic reading works, where you go next depends very much on which board you are using. These are the directions that suit the ESP8266 (NodeMCU) specifically:
The obvious extension on an ESP8266 is to stop printing to Serial and publish instead. A few lines using ESP8266WiFi and an HTTP client will push each reading to a dashboard such as ThingSpeak, or to an MQTT broker for home automation.
Running an onboard web server turns the board into its own display: serve a small HTML page that fetches the latest reading over AJAX, and any phone on the network becomes the instrument panel.
For battery operation, deep sleep is essential — the ESP8266 draws around 70 mA with WiFi active but under 20 µA asleep. Wire D0 (GPIO16) to RST so the board can wake itself, take a reading, publish and sleep again.
Notes and Practical Limits
Calibration is per soil, not per sensor. Sandy soil and clay give completely different readings at the same actual water content, so numbers from one garden do not transfer to another.
Sampling once a minute is already far more often than soil moisture changes. Reading hourly, as the sketch does, saves power, reduces corrosion and loses nothing.