Automatic garden watering with Arduino

We combine two things we already know: the soil moisture sensor measures how dry the soil is, and the relay and pump bring the water. The Arduino makes the decision: when the soil is dry – water it. This is the first real automatic system: sensor → decision → actuator, without a human.

What you need

  • Arduino Uno and a USB cable
  • a soil moisture sensor – a capacitive one is recommended (it does not corrode)
  • a relay module (5 V), a small 3–6 V submersible pump, a tube (silicone, about 5 mm diameter)
  • 4 AA batteries or a 5–6 V adapter for the pump
  • a water container and a pot with soil (or a plant)

Wiring

The wiring is the sum of the two previous lessons:

MOISTURE SENSOR      ARDUINO
VCC  --------------> 5V
GND  --------------> GND
AOUT --------------> A0

RELAY MODULE         ARDUINO
VCC  --------------> 5V
GND  --------------> GND
IN   --------------> D7

BATTERY (+) -> relay COM,  relay NO -> pump (+),  pump (-) -> BATTERY (-)
Placement: put the sensor into the soil between the plant and the place where the tube pours water – not right next to the tube, because it would immediately “see” wet soil and stop the watering too early. The electronics go away from the water and above the container.

Step 1: calibrating the sensor

Every sensor gives slightly different numbers. So we measure them first:

// Soil moisture sensor calibration: prints the raw value twice per second
const int SENSOR_PIN = A0;

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.println(analogRead(SENSOR_PIN));   // write down the value in air (DRY) and in a glass of water (WET)
  delay(500);
}
  1. Hold the sensor in the air and write down the value – this is DRY (usually about 500–600 for a capacitive sensor).
  2. Put the sensor into a glass of water (only up to the mark on the board!) and write down the value – this is WET (usually about 250–300).

With a capacitive sensor, dry soil gives a larger number and wet soil a smaller one. The function map(raw, DRY, WET, 0, 100) “turns it around” by itself, so we get a moisture percentage from 0 to 100.

Step 2: watering in portions

Why not simply keep the pump on until the sensor says “wet”? Because the water needs time to soak in and reach the sensor. In that time the pump would flood the pot. So we water in portions: 3 seconds of water, then 30 seconds of waiting, then a new measurement.

// Automatic watering: when the soil is dry, the pump runs briefly,
// then we wait for the water to soak in before the next measurement (watering “in portions”)
const int SENSOR_PIN = A0;
const int RELAY_PIN  = 7;
const int LED_PIN    = 13;        // the built-in LED is on while the pump runs
const int RELAY_ON  = LOW;        // for a HIGH-trigger module swap LOW and HIGH
const int RELAY_OFF = HIGH;

// values from the calibration – replace them with your numbers
const int DRY = 600;              // sensor in air
const int WET = 270;              // sensor in a glass of water

const int THRESHOLD = 35;                     // below 35 % the soil is dry
const unsigned long PORTION_MS = 3000;        // the pump runs for 3 seconds
const unsigned long SOAK_MS = 30000;          // then we wait 30 s for the water to soak in
const int MAX_PORTIONS = 5;                   // protection: at most 5 portions in a row

int portionsInRow = 0;

int readMoisture() {
  long sum = 0;
  for (int i = 0; i < 10; i++) {              // average of 10 readings – a calmer value
    sum += analogRead(SENSOR_PIN);
    delay(10);
  }
  int raw = sum / 10;
  int percent = map(raw, DRY, WET, 0, 100);
  return constrain(percent, 0, 100);          // no -5 % or 110 %
}

void setup() {
  Serial.begin(115200);
  pinMode(RELAY_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, RELAY_OFF);
}

void loop() {
  int moisture = readMoisture();
  Serial.print("Moisture: ");
  Serial.print(moisture);
  Serial.println(" %");

  if (moisture < THRESHOLD && portionsInRow < MAX_PORTIONS) {
    Serial.println("The soil is dry - watering...");
    digitalWrite(RELAY_PIN, RELAY_ON);
    digitalWrite(LED_PIN, HIGH);
    delay(PORTION_MS);
    digitalWrite(RELAY_PIN, RELAY_OFF);
    digitalWrite(LED_PIN, LOW);
    portionsInRow++;
    delay(SOAK_MS);                           // the water needs time to reach the sensor
  } else {
    if (moisture >= THRESHOLD) {
      portionsInRow = 0;                      // the soil is moist again – reset the counter
    } else {
      Serial.println("WARNING: the soil is still dry - check the water tank and the tube!");
    }
    delay(5000);                              // next measurement in 5 seconds
  }
}

How the program works

  • readMoisture() calculates the average of 10 readings and returns a percentage. constrain prevents values below 0 and above 100.
  • If the moisture is below the threshold (35 %), the pump runs one portion, and then the program waits for the water to soak in.
  • Protection: after 5 portions in a row, if the soil is still dry, the program stops watering and prints a warning. This happens when the water container is empty, the tube has fallen out or the sensor was pulled out of the soil – without this protection the pump would run dry or flood the table. When you fix the problem, press the RESET button on the Arduino.
The threshold and the portion depend on the plant and the pot. A cactus likes dry soil (threshold 15–20 %), a tomato wetter soil (40–50 %). For a small pot a portion of 2–3 s is enough.

Ideas for improvement

  1. Hysteresis: use two thresholds – watering starts below 30 %, and “the soil is moist enough” only above 45 %. This way the system does not keep jumping between two states when the moisture is right around the threshold.
  2. Add a water level sensor in the container (or a float switch) and do not start the pump when there is no water.
  3. Send the moisture to Processing and draw a graph of how it changes during the day (as in the soil moisture lesson).
  4. Water only in the morning: add an LDR sensor and water only when it is light.
  5. Move the project to the ESP32 and watch the garden from your phone: automatic watering with ESP32.

Check your knowledge

Open the quiz

Quiz: automatic watering

1. Why do we water in portions instead of until the sensor says “wet”?




2. What does sensor calibration do?




3. What is the limit of 5 portions in a row for?




4. What is hysteresis?