arduino-sistema-riego-automatico

Automatic Watering System with Arduino

  • 7 min

An automatic watering system is a setup that activates a pump based on soil moisture and stops it when the desired level is reached.

So far, in our “Learn by playing” series, we’ve turned on lights and emitted sounds. Today, we’re making the leap to moving fluids and making decisions based on a plant’s biological state.

This project has two major technical challenges that set it apart from previous ones:

  1. Analog reading is not binary (all or nothing), but a continuous range that we must interpret.
  2. The need to separate power (the water pump) from logic (the Arduino).

The goal is not just to water, but to water well. To do this, we will learn to prevent the pump from turning on and off frantically when the moisture is right at the boundary.

Resistive and Capacitive Sensors

To measure soil moisture, we need a hygrometer sensor. There are two main technologies, and it’s worth knowing the difference:

  • Resistive (Cheap metal fork ones): They work by passing current through the soil. Wet soil conducts better. The problem: They suffer from electrolysis. Literally, the sensor corrodes and dissolves into the soil within weeks.
  • Capacitive: They measure changes related to soil permittivity without exposed metal electrodes. They are usually more durable, although they also need calibration and protection against water in the electronics area.

In this tutorial, we will use logic that works for both, but a capacitive sensor is preferable if you want to reduce corrosion problems.

Hysteresis

Suppose we decide to water when humidity drops below 50%. What happens if the humidity is fluctuating between 49.9% and 50.1%? The pump would turn on and off hundreds of times per minute. That would burn the relay or the motor.

To avoid this, we use Hysteresis. We define two thresholds:

  1. Lower Threshold (e.g., 30%): If it drops below here, turn the pump ON.
  2. Upper Threshold (e.g., 70%): Keep the pump on until it exceeds this level, then turn it OFF.

Between 30% and 70%, we do nothing (maintain the previous state). This guarantees long, stable watering cycles.

Components

  • [1x|blue] Arduino Uno | The project’s brain
  • [1x|green] Soil Moisture Sensor | Preferably Capacitive v1.2
  • [1x|red] 5V Relay Module | Pump control {5V}
  • [1x|orange] Mini Submersible Water Pump | 3V to 12V
  • [1x|yellow] External Power Supply | Batteries or adapter for the pump
  • [Various|gray] Vinyl tubing and water container | Watering system

Do not power the pump from the Arduino’s 5V pin. Use a power source appropriate for its voltage and current, and keep water away from connections, power sources, and unprotected boards.

Connection Diagram

Here we have two coexisting circuits: the control circuit (5V) and the power circuit (the pump’s).

Moisture Sensor

The sensor provides an analog signal that we will read on A0. Depending on the model, it can be powered by 5V or 3.3V.

[ { “from”: “Sensor VCC”, “to”: “Arduino 5V”, “color”: “red” }, { “from”: “Sensor GND”, “to”: “Arduino GND”, “color”: “black” }, { “from”: “Sensor Aout”, “to”: “Pin A0”, “color”: “orange”, “note”: “Analog Input” } ]

Relay Module

The relay is the boundary between the Arduino’s logic and the pump’s power circuit.

[ { “from”: “Relay VCC”, “to”: “Arduino 5V”, “color”: “red” }, { “from”: “Relay GND”, “to”: “Arduino GND”, “color”: “black” }, { “from”: “Relay IN (Signal)”, “to”: “Pin 7”, “color”: “purple” } ]

Pump and External Power

The pump is not powered from the Arduino. The relay only opens or closes the positive line from the external source.

[ { “from”: “Battery (+) → Relay COM”, “to”: “Relay NO → Pump (+)”, “color”: “red”, “note”: “Power Circuit” }, { “from”: “Pump (-)”, “to”: “Battery (-)”, “color”: “black” } ]

If you use an external source for the pump and power the Arduino via USB, keep the power part separate from the logic in the relay contacts. If the relay module does not have real isolation and shares a signal with the Arduino, then it will need a common GND reference on the control side.

The Code

The code has a particular feature: Calibration. Analog sensors return a value between 0 and 1023. But what is 0 and what is 1023?

  • In capacitive ones, it’s usually: Air (Dry) ≈ 600-800, Water (Wet) ≈ 200-300.
  • Note that it is inverse: the higher the moisture, the lower the voltage value.

We must experimentally measure what values our sensor gives in dry and wet soil and place them in the code. If you use a capacitive one, do not submerge the part containing the electronics; respect the insertion line indicated by the manufacturer.

Definitions and Calibration

const int pinSensor = A0;
const int pinBomba = 7;

// --- CALIBRATION (Change these values after your tests) ---
// Value read with the sensor in the air (completely dry)
const int VALOR_SECO = 590; 
// Value read in moist soil, without wetting the electronics
const int VALOR_MOJADO = 280;

// Irrigation thresholds (in Percentage %)
const int UMBRAL_ENCENDIDO = 30; // Water if it drops below 30%
const int UMBRAL_APAGADO = 70;   // Stop when reaching 70%

bool bombaEncendida = false; // Current pump state

void setup() {
  Serial.begin(9600);
  pinMode(pinBomba, OUTPUT);
  digitalWrite(pinBomba, HIGH); // Turn off relay (many modules work with inverse logic, HIGH=Off)
  
  // Note: If your relay activates with HIGH, set LOW here.
  // We assume a relay activated by LOW (common in modules).
}
Copied!

Reading and Mapping Function

We don’t want to work with strange numbers (280, 590), we want percentages (0% - 100%). We will use the map() function, but carefully, as the sensor might give values outside the theoretical range in extreme conditions.

int obtenerHumedad() {
  int lectura = analogRead(pinSensor);
  
  // Map the value: from (DRY, WET) to (0, 100)
  // By placing VALOR_SECO first, we automatically invert the logic
  int porcentaje = map(lectura, VALOR_SECO, VALOR_MOJADO, 0, 100);
  
  // Constrain prevents negative numbers or numbers > 100
  return constrain(porcentaje, 0, 100);
}
Copied!

The loop with Hysteresis

Here we apply the control logic. There is no else that forces a state in the intermediate zone: the two conditions create the hysteresis window.

void loop() {
  int humedadActual = obtenerHumedad();
  
  Serial.print("Humedad: ");
  Serial.print(humedadActual);
  Serial.println("%");
  
  // --- CONTROL LOGIC (HYSTERESIS) ---
  
  if (humedadActual <= UMBRAL_ENCENDIDO && !bombaEncendida) {
    Serial.println("-> Soil very dry. TURNING PUMP ON.");
    digitalWrite(pinBomba, LOW); // Activate Relay (inverse logic)
    bombaEncendida = true;
  }
  
  else if (humedadActual >= UMBRAL_APAGADO && bombaEncendida) {
    Serial.println("-> Soil watered. TURNING PUMP OFF.");
    digitalWrite(pinBomba, HIGH); // Deactivate Relay
    bombaEncendida = false;
  }
  
  // Important: We don't put an 'else' that turns off the pump if it's at 50%.
  // If it's at 50%, it stays as it was (on if it was rising, off if it was falling).
  
  delay(1000); // Read every second
}
Copied!

Safety and Improvements

Handling water and electricity requires precautions.

  1. Invalid readings: A disconnected sensor can leave the input floating and produce unpredictable values. It is advisable to detect readings outside the calibrated range, limit the maximum watering time, and require a rest period before reactivating the pump.
  2. Sensor corrosion: If you use a resistive sensor, power it only during the reading to reduce electrolysis. Check beforehand that its consumption is compatible with the chosen switching method.
  3. Interference: Motors generate electrical noise. If the Arduino resets when the pump turns on, you will need to add a capacitor or a flyback diode across the motor terminals.