Automatic Plant Watering
Intermediate50 minA system that waters a potted plant by itself once the soil dries out: the soil moisture sensor measures moisture as a percentage, and when it drops below 30% the Arduino runs the mini submersible pump for 3 seconds through a relay, waits for the water to soak in, measures again and repeats until moisture passes 50%. The pump runs on three AA batteries of its own; the relay keeps that circuit fully apart from the Arduino. If 5 waterings in a row leave the soil dry, the tank counts as empty and the pump is stopped so it never runs dry. It teaches calibrating a sensor into a percentage, switching a separately powered load with a relay, powering a sensor only while it reads to slow its corrosion, and guarding a system against faults. No extra library needed.
Build it step by step in the appWhat you need
1 × Arduino Uno R3
1 × Breadboard 830 Points (MB-102)
1 × Soil Moisture Sensor (YL-69 + LM393)
1 × 5V 1-Channel Relay Module
1 × 5V Mini Submersible Water Pump
1 × 3 x AA Battery Holder (4.5V)
3 × AA Alkaline Battery (1.5V)
3 × Jumper Wire - Male/Male
6 × Jumper Wire - Male/Female
Wiring steps
- 1
Connect the Arduino's power rails (5V and GND)

This project has two separate circuits: the Arduino's circuit uses the breadboard's bottom power rails, the pump's battery circuit uses the top rails, and the two are never connected. Plug a black male-male jumper wire into one of the GND pins on the Arduino's power header and connect its other end to the bottom blue (−) rail. Plug a red wire into the 5V pin on the same header and connect its other end to the bottom red (+) rail.
- Arduino 5V → the bottom red (+) rail (red wire)
- Arduino GND → the bottom blue (−) rail (black wire)
- 2
Connect the relay module

Push the female ends of male-female jumper wires onto the relay module's control pins; the pin names are printed on the board (some boards say DC+ for VCC and DC− for GND). Connect VCC to the bottom (+) rail with a red wire, GND to the bottom (−) rail with a black wire, and IN straight to the Arduino's pin 8 with an orange wire (RELAY_PIN 8). On boards with screw terminals instead of control pins, clamp the wire under the screw. If your board has an H/L (High/Low) trigger jumper, set it to L: the code is written for a relay that switches on LOW. The relay is a switch that opens and closes the pump's circuit on the Arduino's small signal; when it switches on you hear a click and its LED lights up.
- relay VCC → the bottom red (+) rail (red wire)
- relay GND → the bottom blue (−) rail (black wire)
- Arduino pin 8 → relay IN (orange wire)
- 3
Connect the soil moisture sensor

Connect the sensor's fork-shaped probe to the small LM393 board with the two-wire cable that comes with it (the probe's way round does not matter). Push the female ends of male-female wires onto the board's pins: VCC to the Arduino's pin 7 with a red wire (SENSOR_POWER 7), GND to the bottom (−) rail with a black wire, and AO to the Arduino's A0 pin with a yellow wire (SOIL_PIN A0). The DO pin stays empty. The sensor is powered from pin 7 instead of 5V: the code turns it on only for the few milliseconds it reads, so the probe's metal tracks corrode much more slowly in the wet soil. Push the probe into the pot's soil up to its line without hurting the plant's roots; keep the LM393 board dry.
- moisture probe POS → moisture sensor board POS (blue wire)
- moisture probe NEG → moisture sensor board NEG (blue wire)
- Arduino pin 7 → moisture sensor board VCC (red wire)
- moisture sensor board GND → the bottom blue (−) rail (black wire)
- Arduino A0 → moisture sensor board AO (yellow wire)
- 4
Wire the pump to the battery holder and the relay

Put the three AA batteries in the holder following its + / − marks (keep its switch off if it has one). Plug the holder's red wire into the breadboard's top red (+) rail and its black wire into the top blue (−) rail. Connect the top (+) rail to the relay's COM screw with a male-male wire: loosen the screw with a small screwdriver, push the wire in, tighten it. Connect the pump's red wire to the relay's NO (normally open) screw and its black wire to the top (−) rail. When the relay switches on, COM and NO join and the pump runs on the batteries. Do not connect the top rails to the bottom ones or to the Arduino. Fit the hose onto the pump and fix its end pointing at the pot's soil; put the pump at the bottom of the tank, fully under water. The hose's end in the pot must sit higher than the water in the tank, otherwise water can keep flowing after the pump stops.
- battery holder red wire (+) → the top red (+) rail (red wire)
- battery holder black wire (−) → the top blue (−) rail (black wire)
- relay COM → the top red (+) rail (orange wire)
- relay NO → pump red wire (red wire)
- pump black wire → the top blue (−) rail (black wire)
Arduino sketch
// The soil sensor's analog output and
// its supply, on only while it reads
const int SOIL_PIN = A0;
const int SENSOR_POWER = 7;
// The relay module's IN pin
const int RELAY_PIN = 8;
// Most relay modules switch on LOW; if
// yours switches on HIGH, swap the two
const int RELAY_ON = LOW;
const int RELAY_OFF = HIGH;
// Calibration: raw readings with the
// sensor in dry air and in water
const int DRY_RAW = 1023;
const int WET_RAW = 300;
// Watering starts below this and stops
// once past this (percent)
const int DRY_LIMIT = 30;
const int WET_LIMIT = 50;
// How long the pump runs per watering
const unsigned long PUMP_TIME = 3000;
// Wait for the water to soak in
const unsigned long SOAK_TIME = 30000;
// Time between readings (ms)
const unsigned long CHECK_EVERY = 10000;
// If the soil is still dry after this
// many waterings, the tank is empty
const int MAX_PUMPS = 5;
bool watering = false;
int pumps = 0;
void setup() {
// Write "off" first, so the pump never
// runs even for a moment at start-up
digitalWrite(RELAY_PIN, RELAY_OFF);
pinMode(RELAY_PIN, OUTPUT);
pinMode(SENSOR_POWER, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
Serial.println("Automatic watering started.");
}
// Sensor on, mean of 5 readings, off:
// this way the probes corrode slowly
int readRaw() {
digitalWrite(SENSOR_POWER, HIGH);
delay(100);
long total = 0;
for (int i = 0; i < 5; i++) {
total += analogRead(SOIL_PIN);
delay(10);
}
digitalWrite(SENSOR_POWER, LOW);
return total / 5;
}
void runPump() {
digitalWrite(RELAY_PIN, RELAY_ON);
delay(PUMP_TIME);
digitalWrite(RELAY_PIN, RELAY_OFF);
}
// The tank may be empty: the pump stays
// off and the on-board LED blinks
void stopForGood() {
Serial.print(MAX_PUMPS);
Serial.println(" waterings and the soil is still dry: the tank may be empty or the hose out of the pot.");
Serial.println("Pump stopped. Check it, then press reset.");
while (true) {
digitalWrite(LED_BUILTIN, HIGH);
delay(300);
digitalWrite(LED_BUILTIN, LOW);
delay(300);
}
}
void loop() {
int raw = readRaw();
// Close to 0: the sensor has no power
if (raw < 50) {
Serial.print("Cannot read the sensor (raw ");
Serial.print(raw);
Serial.println("), check the wires.");
delay(CHECK_EVERY);
return;
}
// Dry soil reads high, wet soil low
int moisture = constrain(map(raw, DRY_RAW, WET_RAW, 0, 100), 0, 100);
Serial.print("Moisture: ");
Serial.print(moisture);
Serial.print("% (raw ");
Serial.print(raw);
Serial.println(")");
// Two thresholds: no flicker at edge
if (!watering && moisture < DRY_LIMIT) {
watering = true;
pumps = 0;
Serial.println("Soil is dry, watering starts.");
}
if (watering && moisture >= WET_LIMIT) {
watering = false;
Serial.println("Soil is moist, watering done.");
}
if (watering) {
if (pumps >= MAX_PUMPS) {
stopForGood();
}
runPump();
pumps++;
Serial.print("Pump ran (");
Serial.print(pumps);
Serial.println("), water soaking in...");
delay(SOAK_TIME);
return;
}
delay(CHECK_EVERY);
}
What the Serial Monitor shows
Automatic watering started. Moisture: 36% (raw 759) Moisture: 32% (raw 789) Moisture: 29% (raw 809) Soil is dry, watering starts. Pump ran (1), water soaking in... Moisture: 41% (raw 721) Pump ran (2), water soaking in... Moisture: 54% (raw 630) Soil is moist, watering done. Moisture: 52% (raw 642)
If a wire is loose
Cannot read the sensor (raw 0), check the wires.
What you will see
First calibrate the sensor: open the Serial Monitor, note the raw value it prints with the probe in dry air, then with only its fork dipped in a glass of water; put these in DRY_RAW and WET_RAW in the code and upload it again. Then push the probe into the pot's soil: every 10 seconds the moisture is printed as a percentage. When it drops below 30% the relay clicks, the pump pumps water for 3 seconds, the code waits 30 seconds for it to soak in and measures again; once moisture passes 50% the watering stops. Try it with a dry pot and you see the moisture climb over a few waterings. If the tank is empty or the hose is out of the pot, the pump stops after 5 waterings and the small L LED on the Arduino blinks: refill the tank and press the reset button. If the pump runs straight away at start-up or never stops, your relay switches on HIGH: swap the values of RELAY_ON and RELAY_OFF in the code. If the relay clicks but the pump does not run, check the batteries and the COM / NO wires. If it says "Cannot read the sensor", check the sensor board's VCC (pin 7), GND and AO wires. Never run the pump dry; it soon breaks.
In the app, tick off every wire, match the parts to your inventory and pick up where you left off.
Build it step by step in the app