Smart Irrigation with ESP8266: Full Build Log and Firmware

Cover image for the ESP8266 smart irrigation project build log from eARgle Innovation Labs.

A soil-moisture sensor, a relay and a Wi-Fi board make a demo. Making it survive a season outdoors is a different project — one where the capacitive sensor matters more than the code, the pump needs its own supply, and the firmware has to assume the Wi-Fi will disappear.

This is the full build log for the eARgle smart irrigation node: bill of materials with real part choices, the wiring that avoids the ESP8266 boot traps, calibration procedure, the complete firmware with fail-safes, and what actually went wrong across eight weeks of running it on a rooftop garden in Vellore.

  • Board ESP8266 (NodeMCU)
  • Sensor Capacitive v2.0
  • Pump 12 V, 5 W
  • Build time ~3 hours
  • Runtime 8 weeks tested
  • Level Intermediate

What the node has to do

Written as requirements before any hardware was bought — which is the step most project builds skip:

  • Water a 2 m × 1 m raised bed when the soil dries past a set point, not on a clock.
  • Never run the pump for more than 90 seconds in one go, and never more than twice an hour, even if the sensor fails.
  • Keep working with no internet: Wi-Fi is for telemetry and manual override, not for the control loop.
  • Report moisture, pump runtime and uptime so a dead sensor is visible remotely.
  • Survive 40 °C afternoons, monsoon humidity and the occasional direct splash.
The rule that shaped everything

The control loop runs locally. Every cloud-first irrigation project we have seen eventually floods a bed or kills a plant when the Wi-Fi drops mid-cycle. The node decides; the cloud only watches and can request.

Bill of materials — and the two parts not to cheap out on

PartChoiceNotes
ControllerNodeMCU ESP8266 (CP2102 version)Mains-powered node, single Wi-Fi connection, one analog input needed — the ESP8266’s limits do not bite here
Moisture sensorCapacitive v1.2 / v2.0Not the cheap two-prong resistive probe. See the warning below
Relay5 V single-channel with opto-isolationNon-isolated relay boards couple pump kickback straight into the GPIO
Pump12 V DC diaphragm, ~5 W, with check valveSelf-priming matters if the tank sits below the bed
Supply12 V 2 A adapter + LM2596 buck to 5 VOne adapter, two rails. Do not run the pump from the board’s 5 V
Protection1N5408 flyback diode across the pump, 470 µF on the 5 V railTwo components that stop most mystery reboots
EnclosureIP65 ABS box, cable glands, sensor on a 1 m leadElectronics out of the weather; only the probe in the soil
OptionalDHT22 for air temperature and humidityUseful context in the telemetry; also feeds evaporation estimates
Do not use a resistive moisture sensor

The two-prong resistive probe that ships in most starter kits passes DC current through the soil. Within two to six weeks the exposed traces corrode, the readings drift upward, and the node concludes the soil is permanently dry — so it waters permanently. Capacitive probes measure through an insulated PCB with no exposed copper, and ours read consistently through eight weeks buried. This is the single highest-value substitution in the whole build.

Wiring and power architecture

SignalESP8266 pinWhy this pin
Moisture sensor AOUTA0The ESP8266’s only ADC. NodeMCU’s on-board divider gives a 0–3.3 V range
Relay IND5 (GPIO14)Free of boot-strapping duty and idles LOW, so the pump cannot latch on at power-up
DHT22 DATAD6 (GPIO12)Also boot-safe; 10 kΩ pull-up to 3V3
Status LEDD4 (GPIO2, on-board)Already fitted, inverted logic
Manual override buttonD7 (GPIO13) to GNDInternal pull-up, debounced in software
ESP8266 pins to keep clear

GPIO0 (D3), GPIO2 (D4) and GPIO15 (D8) set the boot mode. Anything that pulls GPIO0 low or GPIO15 high at power-up stops the board from booting your sketch — a relay module’s input pin is a classic offender. Keep relays and sensors on D1, D2, D5, D6 and D7.

Power: the 12 V adapter feeds the pump side directly and an LM2596 buck module steps it to 5 V for the NodeMCU’s VIN. Grounds are joined at one point next to the buck module, not daisy-chained through the relay board. The flyback diode goes across the pump terminals, banded end to +12 V. With this arrangement the pump’s switch-off spike stops reaching the ESP8266 at all; without it, expect a reset every few cycles and hours of blaming the firmware.

Calibrating the moisture sensor properly

A capacitive sensor gives you an arbitrary ADC number, not a percentage. Calibration is two readings and takes five minutes — and without it your threshold is a guess:

  1. Dry readingHold the probe in air, or in a cup of bone-dry soil. Log the raw ADC value. On our NodeMCU + capacitive v2.0: ~790.
  2. Wet readingInsert the probe into a glass of water up to the marked line only — never past it, the top of the board is not waterproof. Ours: ~370.
  3. Map, remembering the sensor is invertedHigher ADC means drier. moisture% = map(raw, dry, wet, 0, 100).
  4. Find the real set point in the bedWater thoroughly, then log moisture every 30 minutes for two days. The percentage at which the plants first look thirsty is your threshold — for our loam-and-compost mix it was 38 %, not the 50 % we had assumed.

Firmware, with the fail-safes that matter

The sketch below is the control core, trimmed of telemetry specifics so you can point it at whatever backend you use. The important parts are not the watering logic — they are the four guards: sensor sanity, maximum run time, minimum interval, and daily cap.

ESP8266 · irrigation control core

#include <ESP8266WiFi.h>

// ---- calibration (from the steps above) ----
const int RAW_DRY = 790;
const int RAW_WET = 370;

// ---- policy ----
const int           THRESHOLD_PCT   = 38;      // water below this
const int           TARGET_PCT      = 55;      // stop above this
const unsigned long MAX_RUN_MS      = 90UL * 1000UL;
const unsigned long MIN_GAP_MS      = 30UL * 60UL * 1000UL;
const uint8_t       MAX_CYCLES_DAY  = 6;

const uint8_t PIN_RELAY = 14;   // D5
const uint8_t PIN_SENSE = A0;

bool          pumping      = false;
unsigned long pumpStart    = 0;
unsigned long lastPumpEnd  = 0;
uint8_t       cyclesToday  = 0;
unsigned long dayStart     = 0;

int readMoisturePct() {
  long sum = 0;                          // 8 samples, 20 ms apart
  for (uint8_t i = 0; i < 8; i++) { sum += analogRead(PIN_SENSE); delay(20); }
  int raw = sum / 8;
  if (raw < 150 || raw > 1000) return -1;     // open circuit / shorted probe
  int pct = map(raw, RAW_DRY, RAW_WET, 0, 100);
  return constrain(pct, 0, 100);
}

void pumpOn()  { digitalWrite(PIN_RELAY, HIGH); pumping = true;  pumpStart = millis(); cyclesToday++; }
void pumpOff() { digitalWrite(PIN_RELAY, LOW);  pumping = false; lastPumpEnd = millis(); }

void setup() {
  pinMode(PIN_RELAY, OUTPUT);
  digitalWrite(PIN_RELAY, LOW);          // pump off before anything else
  Serial.begin(115200);
  dayStart = millis();
  // WiFi.begin(...) — telemetry only; the loop below never waits for it
}

void loop() {
  if (millis() - dayStart >= 24UL * 3600UL * 1000UL) { dayStart = millis(); cyclesToday = 0; }

  int pct = readMoisturePct();

  // GUARD 1 — a failed sensor must never water
  if (pct < 0) {
    if (pumping) pumpOff();
    Serial.println(F("SENSOR FAULT - pump inhibited"));
    delay(5000);
    return;
  }

  if (pumping) {
    // GUARD 2 — hard ceiling on a single run
    if (millis() - pumpStart >= MAX_RUN_MS) { pumpOff(); Serial.println(F("max run reached")); }
    else if (pct >= TARGET_PCT)             { pumpOff(); Serial.println(F("target reached")); }
  } else {
    bool dry      = pct < THRESHOLD_PCT;
    bool rested   = (lastPumpEnd == 0) || (millis() - lastPumpEnd >= MIN_GAP_MS);  // GUARD 3
    bool quotaOk  = cyclesToday < MAX_CYCLES_DAY;                                  // GUARD 4
    if (dry && rested && quotaOk) pumpOn();
  }

  Serial.printf("moisture %d%%  pump %s  cycles %u\n", pct, pumping ? "ON" : "off", cyclesToday);
  delay(2000);
}
Why the relay is driven LOW at the very top of setup()

On power-up, GPIO float briefly before setup() runs. With an active-HIGH relay that is a short click — harmless. With an active-LOW relay board (many are) it is the pump running from the instant power is applied until your sketch takes over, and again on every crash-reboot. Check which polarity your module is, write the safe state first, and add a pull resistor that holds the relay off with the ESP absent.

Eight weeks of field results

What happenedCauseFix applied
Node rebooted on almost every pump switch-off in week 1No flyback diode; inductive spike through the shared 5 V rail1N5408 across the pump, 470 µF on the 5 V rail. Zero reboots afterwards
Moisture readings drifted 6–8 % over an afternoonCapacitive sensors are temperature-sensitive; the enclosure hit 48 °CMoved the box into shade; added DHT22 temperature to the telemetry so drift is visible rather than mysterious
One 90-second cycle at 2 a.m. every night during monsoonSoil near the surface dried while the root zone stayed wet — probe sat too shallowBuried the probe to root depth (~15 cm) and angled it; night cycles stopped
Wi-Fi dropped for 11 hours on day 26Router firmware update at the houseNothing needed — the local control loop watered correctly throughout, which was the point of the architecture
Telemetry gaps whenever the pump ranBlocking delay() plus TLS handshake during the pump cycleBuffered readings and posted them after the cycle rather than during it

Water use over the eight weeks came out roughly 30–40 % below the previous fixed-timer schedule for the same bed, almost entirely because the node skips watering after rain — the case a timer can never handle.

What we would change next

  • Move to an ESP32-C3 and a battery. The node is mains-powered only because the ESP8266 dev board’s sleep current makes battery operation pointless. A C3 sleeping at microamps between 15-minute checks would run for a season on a cell — see the board comparison for the numbers.
  • Two probes, not one. One at 5 cm and one at 15 cm tells you whether water is reaching the roots. That needs a second ADC channel — an ADS1115, or simply the ESP32.
  • A flow sensor on the outlet. Runtime is a proxy for water delivered; a blocked dripper or an empty tank looks identical to a healthy cycle without one.
  • Rain forecast as a veto. Cheap to add once telemetry exists, and the safest kind of cloud dependency: it can only prevent watering, never cause it.

Frequently asked questions

Can I run the pump straight from the NodeMCU’s 5 V pin?

No. A small 12 V diaphragm pump draws several hundred milliamps to over an amp at start-up, well past what the board’s regulator and USB supply can provide. The rail collapses, the ESP8266 browns out and reboots, and the cycle repeats. Give the pump its own supply, switch it with an opto-isolated relay, and join the grounds at a single point.

How long do capacitive soil sensors last buried?

Ours read consistently through eight weeks, and the usual failure mode is not the sensing area but water creeping into the header pins at the top of the board. Seal the top 2 cm and the cable entry with hot glue or heat-shrink and epoxy, keep the marked waterline above soil level, and a season is realistic. Resistive probes, by contrast, degrade in weeks.

Do I need the cloud for this project at all?

Not for control, and designing it that way is the main lesson of this build. The cloud earns its place for three things: seeing that a sensor has failed while you are away, a manual “water now” button, and vetoing a cycle when rain is forecast. All three are optional extras on top of a loop that works offline.

What moisture threshold should I use?

There is no universal number — it depends on your soil mix, the plant and the probe depth. Calibrate the sensor’s dry and wet ends, then log moisture for two days after a thorough watering and note the percentage at which the plants first show stress. Set the threshold a few points above that. Ours landed at 38 % for a loam-and-compost bed; sandy soil will sit far lower.

Will this work with a 220 V AC mains pump?

Electrically yes, with a relay rated for the load — but mains wiring is not a hobby-bench task. Use a properly enclosed, earthed contactor or a commercial smart plug switched by the ESP over Wi-Fi, keep all mains wiring inside a sealed enclosure, and have it checked by a qualified electrician. The low-voltage side of the design stays exactly as described above.

Build it with the kit

Every part in the bill of materials — capacitive sensor, opto-isolated relay, pump and wiring — ships in the eARgle irrigation kit, with the course module that walks through it.

Get the irrigation kit More project builds
AR
Arun Roshan

Founder of eARgle Innovation Labs and eARgle Technologies, and a PhD scholar at VIT Vellore. He builds and field-tests IoT hardware for assistive and agricultural applications, and writes up the failures as carefully as the results.

1 Comment.

Comments are closed.