To read a soil humidity sensor with an Arduino, use a capacitive sensor module wired to an analog pin (A0), power it with 5V, and map the raw 10-bit ADC values to a 0-100% moisture scale. Unlike cheap resistive forks that destroy themselves via electrolysis within a week, capacitive sensors measure the dielectric permittivity of the soil, providing stable, corrosion-free readings for years. This guide targets the Arduino Uno R3 and Nano v3 (ATmega328P) board variants, utilizing their native 10-bit analog-to-digital converter (ADC).

Project Difficulty Rating: Beginner to Intermediate
Time to Complete: 30 minutes for hardware, 15 minutes for calibration
Target Board: Arduino Uno R3 / Nano v3 (5V logic, 10-bit ADC)

Parts List & Spec Sheet

Do not buy the dual-pronged nickel-plated resistive sensors. They act as an anode and cathode, rapidly corroding when wet and skewing your data. Stick to capacitive modules.

Component Exact Variant / Model Specs & Notes Est. Cost (2026)
Microcontroller Arduino Uno R3 or Nano v3 ATmega328P, 5V logic, 10-bit ADC $15 - $24
Soil Sensor (Standard) Capacitive Soil Moisture Sensor v1.2 Analog out, 3.3V-5V, built-in 555 timer oscillator $3 - $5
Soil Sensor (Premium) Adafruit STEMMA Soil Sensor (I2C) I2C interface, built-in ATTiny85, no ADC noise $12 - $15
Wiring 22 AWG solid core or silicone jumper wires Keep analog signal wires under 12 inches $4
Waterproofing MG Chemicals 419D Acrylic Conformal Coating Protects exposed copper traces at the top edge $18

For a deeper look into how capacitive sensing avoids the pitfalls of resistive probes, review the Adafruit STEMMA Soil Sensor overview, which details the I2C alternative if your Arduino's ADC is too noisy.

Wiring & Pin Mapping

The standard v1.2 capacitive sensor has three pins: VCC, GND, and AOUT. Because it outputs an analog voltage that varies inversely with moisture (higher voltage = drier soil), it must connect to one of the Arduino's ADC pins.

Sensor Pin (v1.2) Arduino Uno R3 Pin Wire Color (Standard) Function
VCC 5V Red Power (3.3V also works, but shifts output range)
GND GND Black Common Ground
AOUT A0 Blue / Green Analog Signal Output (0V to ~3.0V)
Callout Tip: Route the AOUT wire away from digital PWM pins and the USB cable. The Arduino Uno's unshielded ADC traces are highly susceptible to 50/60Hz mains hum and digital switching noise, which will cause your moisture readings to jitter by ±5%.

Complete Arduino Code with Error Handling

The code below targets the ATmega328P (Uno/Nano). It implements a moving average filter to smooth out ADC noise, maps the raw values to a percentage, and includes explicit error handling for disconnected wires or shorted sensors. For more on the smoothing algorithm, refer to the official Arduino Smoothing Example.

#include <Arduino.h>

// --- PIN DEFINITIONS ---
#define SENSOR_PIN A0
#define LED_PIN 13 // Built-in LED for dry soil alert

// --- CALIBRATION CONSTANTS ---
// Measure these values in your specific setup! 
// Typical v1.2 at 5V: Air ~ 580, Water ~ 290
#define AIR_VALUE 580   
#define WATER_VALUE 290 

// --- FILTER SETTINGS ---
const int numReadings = 10;
int readings[numReadings];
int readIndex = 0;
long total = 0;

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  
  // Initialize ring buffer
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
  
  // Allow sensor and ADC to stabilize
  delay(500); 
  Serial.println("Soil Humidity Sensor Initialized.");
}

void loop() {
  // Subtract the last reading
  total = total - readings[readIndex];
  
  // Read from the sensor using analogRead (docs: https://docs.arduino.cc/language-reference/en/functions/analog-io/analogRead/)
  int rawValue = analogRead(SENSOR_PIN);
  
  // --- ERROR HANDLING & BOUNDS CHECKING ---
  if (rawValue >= 1020) {
    Serial.println("ERROR: Sensor reading out of bounds (Raw: 1023). Check AOUT wire.");
    return; // Skip rest of loop
  }
  
  if (rawValue <= 2) {
    Serial.println("ERROR: Value stuck at 0. Check VCC connection or sensor short.");
    return; // Skip rest of loop
  }
  
  // Add new reading to buffer
  readings[readIndex] = rawValue;
  total = total + readings[readIndex];
  readIndex = (readIndex + 1) % numReadings;
  
  // Calculate average
  int averageRaw = total / numReadings;
  
  // Variance check for extreme noise
  int variance = abs(rawValue - averageRaw);
  if (variance > 50) {
    Serial.println("WARNING: High variance detected in readings. Check for wire noise.");
  }
  
  // Map to percentage (Constrain ensures we don't get -10% or 110%)
  // Note: Capacitive sensors output HIGHER voltage when DRY
  int moisturePercent = map(averageRaw, AIR_VALUE, WATER_VALUE, 0, 100);
  moisturePercent = constrain(moisturePercent, 0, 100);
  
  // Output data
  Serial.print("Raw Avg: ");
  Serial.print(averageRaw);
  Serial.print(" | Moisture: ");
  Serial.print(moisturePercent);
  Serial.println("%");
  
  // Dry soil alert threshold (< 30%)
  if (moisturePercent < 30) {
    digitalWrite(LED_PIN, HIGH);
  } else {
    digitalWrite(LED_PIN, LOW);
  }
  
  delay(1000); // 1 second sample rate
}

Debugging: First Three Things to Check

When your serial monitor spits out errors or garbage data, do not immediately rewrite your code. Hardware faults cause 90% of soil sensor failures. Here is the exact diagnostic path based on the error strings generated by the code above.

  1. "ERROR: Sensor reading out of bounds (Raw: 1023)"
    Cause: The AOUT pin is floating. The Arduino's internal pull-up resistors or stray capacitance are pulling the ADC pin to VCC.
    Fix: Set your digital multimeter (DMM) to DC Voltage. Probe the AOUT pin on the sensor itself. If it reads ~2.5V but the Arduino reads 1023, your jumper wire is broken or the breadboard contact is dead. Swap the wire.
  2. "ERROR: Value stuck at 0"
    Cause: The sensor is unpowered, or the AOUT line is shorted to ground.
    Fix: Probe the VCC and GND pins on the sensor module. You must read exactly 4.8V to 5.1V. If you read 0V, check your USB power or breadboard power rails. If VCC is 5V but AOUT is 0V, the sensor's internal 555 timer chip has failed (common with cheap clones exposed to condensation).
  3. "WARNING: High variance detected in readings"
    Cause: Electromagnetic interference (EMI) coupling into the high-impedance analog trace.
    Fix: If your AOUT wire is longer than 6 inches, it is acting as an antenna. Shorten the wire, or add a 0.1µF ceramic capacitor between the AOUT pin and GND right at the Arduino header to filter high-frequency noise.

Extending and Simplifying the Build

Once you have stable serial data, you can adapt this circuit for real-world automation.

To Simplify (Visual Indicator Only):
Strip out the Serial prints and the moving average ring buffer. Wire a standard 5mm red LED (with a 220Ω current-limiting resistor) to Pin 8, and a green LED to Pin 9. Use a simple if/else block based on a single analogRead() to toggle the LEDs. This reduces code footprint to under 2KB and is ideal for ATtiny85 deployments.

To Extend (Automated Irrigation):
Do not wire a water pump directly to the Arduino's 5V pin; it will fry the voltage regulator. Instead, use a logic-level N-channel MOSFET like the IRLZ44N. Connect the Arduino's Pin 8 to the MOSFET gate (via a 100Ω gate resistor), the source to ground, and the drain to the low side of a 12V solenoid water valve. Add a flyback diode (1N4007) across the solenoid coils to protect the MOSFET from inductive kickback when the valve closes.

Frequently Asked Questions

Why is my resistive soil humidity sensor Arduino reading drifting over time?

Resistive sensors pass a direct current through the soil between two exposed metal prongs. This causes rapid electrolysis, where metal ions are stripped from one prong and deposited into the soil. Within 48 to 72 hours of continuous power, the prongs corrode, increasing the baseline electrical resistance. The Arduino interprets this higher resistance as "drier soil," causing your readings to artificially drift upward even if the soil remains wet. Always switch to a capacitive sensor to measure the dielectric constant instead of electrical conductivity.

How do I waterproof the exposed copper contacts on a capacitive soil humidity sensor Arduino build?

While the sensing area of a capacitive v1.2 module is coated, the top edge where the components and header pins are located is usually bare PCB. Before inserting it into the soil, apply two coats of acrylic conformal coating (like MG Chemicals 419D) or a thick layer of marine-grade epoxy over the top 1.5 inches of the board, covering the solder joints and the 555 timer IC. Do not use standard hot glue, as it peels off when exposed to soil microbes and constant moisture, eventually allowing water to wick under the glue and short the header pins.

Can I power a soil humidity sensor Arduino project directly from a 3.7V LiPo battery?

Yes, but with caveats regarding the analog reference voltage. If you power an Arduino Nano directly via the 3.3V pin (bypassing the onboard regulator) using a boosted LiPo, the ADC reference defaults to VCC. As the battery voltage sags from 4.2V down to 3.3V, your "dry" and "wet" calibration values will shift, ruining your percentage mapping. To fix this, you must either use a dedicated 3.3V LDO voltage regulator to provide a rock-solid VCC, or use the Arduino's internal 1.1V reference (analogReference(INTERNAL)) and use a voltage divider on the sensor's AOUT pin to scale the signal down below 1.1V.