Community project

Automatic Spray Cleaning Robot

Arduino
Photo of Automatic Spray Cleaning Robot
Generated with AI

MJB Aeriest

Published October 28, 2026

This guide builds an autonomous cleaning robot that navigates spaces while spraying water. The robot uses three ultrasonic distance sensors to detect obstacles and avoid collisions, then drives forward on two independently controlled 12V motors. A submersible pump sprays water on a timed cycle, controlled by a relay module, making it useful for cleaning floors, patios, or garden areas.

The project runs on an Arduino Uno powered by a protected 18650 Li-ion battery pack, with a buck converter stepping voltage down for the microcontroller and sensors. This guide provides a complete wiring diagram, parts list, Arduino firmware with obstacle avoidance logic, and step-by-step assembly instructions including battery safety, motor control setup, and pump testing.

Wiring diagram

Wiring diagram for Automatic Spray Cleaning Robot
Loading diagram…

Gather all the parts

QtyComponent
1

Standard L298N dual H-bridge motor driver module

A dual motor driver board that switches the left and right TT wheel motors from the battery pack.

1

HC-SR04

HC-SR04 ultrasonic distance sensor module with separate 40 kHz transmitter and receiver transducers, measuring 2 to 400 cm. A 10 us pulse on TRIG starts a measurement and ECHO stays high for the echo's round-trip time. This HC-SR04+ board runs from 3.3 V or 5 V.

1

HC-SR04

HC-SR04 ultrasonic distance sensor module with separate 40 kHz transmitter and receiver transducers, measuring 2 to 400 cm. A 10 us pulse on TRIG starts a measurement and ECHO stays high for the echo's round-trip time. This HC-SR04+ board runs from 3.3 V or 5 V.

1

HC-SR04

HC-SR04 ultrasonic distance sensor module with separate 40 kHz transmitter and receiver transducers, measuring 2 to 400 cm. A 10 us pulse on TRIG starts a measurement and ECHO stays high for the echo's round-trip time. This HC-SR04+ board runs from 3.3 V or 5 V.

1

12V DC Submersible Mini Water Pump

Two-wire brushed DC submersible water pump for small irrigation or circulation projects. It is an external load and must be driven through a relay, MOSFET, or motor driver; do not connect directly to an MCU GPIO.

1

MP1584 Buck Converter

adjusted to 5.0 V

Adjustable buck (step-down) DC-DC converter, 4.5-28 V in -> 0.8-20 V out, ~3 A. Configured to 5 V to step a 9 V / 12 V supply or battery pack down to the board's 5 V rail.

1

18650 Li-ion Cell

Molicel 2600 mAh

18650 lithium-ion cell, nominal 3.7 V, ~2500 mAh. Common for higher-capacity portable / battery-bank style projects; needs a holder and protection / charger circuit.

1

18650 Li-ion Cell

Molicel 2600 mAh

18650 lithium-ion cell, nominal 3.7 V, ~2500 mAh. Common for higher-capacity portable / battery-bank style projects; needs a holder and protection / charger circuit.

1

18650 Li-ion Cell

Molicel 2600 mAh

18650 lithium-ion cell, nominal 3.7 V, ~2500 mAh. Common for higher-capacity portable / battery-bank style projects; needs a holder and protection / charger circuit.

1

3S Li-ion BMS protection board

A protection board that disconnects the three-cell battery pack if a cell is overcharged, over-discharged, or shorted.

1

12 V TT gear motor — left wheel

The small geared DC motor that turns the robot's left wheel.

1

12 V TT gear motor — right wheel

The small geared DC motor that turns the robot's right wheel.

1

KY-019 5 V single-channel relay module

A small 5 V relay board that lets the Arduino turn the 12 V water pump on and off.

Assemble it in 7 steps

1. Keep the electronics dry

Mount the Arduino, L298N motor driver, 5 V buck converter, BMS, relay, and pump where splashed water cannot reach them. Keep the 5 V buck adjustment screw reachable.

  • Water touching these boards can short the battery and damage them.

2. Prepare the protected battery pack

Wire the three 18650 cells in series and connect the BMS leads: B- to the first cell negative, B1 to the first cell join, B2 to the second cell join, and B+ to the last cell positive. Use P+ as the protected positive supply and P- as the common negative supply for the robot.

  • Do not solder directly onto loose 18650 cells; heat can damage a cell and create a fire risk.
  • There is no fuse in this version, so disconnect the battery whenever you change a wire.

3. Set the 5 V supply before connecting it

Connect BMS P+ to buck_5v VIN (input power) and BMS P- to buck_5v GND (ground). With a meter, adjust VOUT to exactly 5.0 V before connecting the Arduino, relay, sensors, or L298N logic terminal.

  • If the buck gets hot with its output disconnected, disconnect the battery immediately and replace the buck converter.
  • More than 5 V on the Arduino 5V pin can damage it.

4. Wire the Arduino and distance sensors

Connect buck_5v VOUT to Arduino 5V (power) and buck_5v GND to Arduino GND (ground). For all three sensors, join VCC to this 5 V output (power) and GND to common GND (ground). Left sensor: TRIG to A0 (signal), ECHO to A1 (signal). Center sensor: TRIG to A2 (signal), ECHO to A3 (signal). Right sensor: TRIG to A4 (signal), ECHO to A5 (signal).

  • Do not join any TRIG or ECHO wires together; each sensor needs its own two signal wires.
  • Do not use the Arduino 3.3 V pin for these sensors.

5. Wire the L298N wheel driver

Remove the small 5V-EN jumper from the L298N, then connect buck_5v VOUT to L298N 5V (logic power), buck_5v GND to L298N GND (ground), and BMS P+ to L298N 12V (motor power). Leave the ENA and ENB jumpers fitted. Connect IN1 to Arduino D6 (left direction), IN2 to D9 (left direction), IN3 to D11 (right direction), and IN4 to D10 (right direction). Connect the left motor to OUT1 and OUT2; connect the right motor to OUT3 and OUT4.

  • If the right wheel runs backward, disconnect the battery and swap only its two wires at OUT3 and OUT4.
  • Keep the battery disconnected while tightening terminals; a loose copper strand can short the battery.
  • The 5V-EN jumper must be removed before feeding the L298N 5V terminal from the buck converter.

6. Wire the pump relay

Connect relay VCC to the regulated 5 V output (power), relay GND to common GND (ground), and relay IN to Arduino D4 (control). Connect BMS P+ to relay COM (pump power); connect relay NO to pump PUMP+ (switched power); connect pump PUMP- to BMS P- (ground).

  • There is no flyback diode across the pump in this version, so the relay contacts may wear sooner and electrical noise can reset the Arduino.
  • Never connect the pump directly to an Arduino pin.

7. Test the wheels and pump safely

Raise both wheels off the floor. Plug the Arduino into USB and press Deploy, then connect the battery briefly. The relay on D4 should click once every 5 seconds and power the pump for about 1 second. If the L298N status LEDs change but the right wheel does not turn, disconnect the battery and swap the right motor's two wires with the left motor's two output terminals for a short test; this tells you whether the motor/wire or the L298N right output is at fault.

  • Keep fingers and loose wires away from the wheels.
  • Without a fuse, disconnect the battery immediately if any wire, BMS, relay, motor driver, or buck converter gets warm.

Review all connections

1. Connections between "cell_1" and "Arduino"

Functioncell_1Arduino
groundGND → 3S Li-ion BMS protection board B-EXT
power+V → 18650 Li-ion Cell GNDEXT
power+V → 3S Li-ion BMS protection board B1EXT

2. Connections between "cell_2" and "Arduino"

Functioncell_2Arduino
power+V → 18650 Li-ion Cell GNDEXT
power+V → 3S Li-ion BMS protection board B2EXT

3. Connections between "cell_3" and "Arduino"

Functioncell_3Arduino
power+V → 3S Li-ion BMS protection board B+EXT

4. Connections between "bms_3s" and "Arduino"

Functionbms_3sArduino
groundP-GND

5. Connections between "buck_5v" and "Arduino"

Functionbuck_5vArduino
powerVOUT5V
groundGNDGND
powerVIN → 3S Li-ion BMS protection board P+EXT

6. Connections between "sonar_front" and "Arduino"

Functionsonar_frontArduino
powerVCC5V
groundGNDGND
digitalTRIGGPIO 16
digitalECHOGPIO 17

7. Connections between "sonar_left" and "Arduino"

Functionsonar_leftArduino
powerVCC5V
groundGNDGND
digitalTRIGGPIO 14
digitalECHOGPIO 15

8. Connections between "sonar_right" and "Arduino"

Functionsonar_rightArduino
powerVCC5V
groundGNDGND
digitalTRIGGPIO 18
digitalECHOGPIO 19

9. Connections between "pump_relay" and "Arduino"

Functionpump_relayArduino
powerVCC5V
groundGNDGND
powerNO → 12V DC Submersible Mini Water Pump PUMP+EXT
powerCOM → 3S Li-ion BMS protection board P+EXT
digitalIN → Arduino control signal that turns the pump relay on and offGPIO 4

10. Connections between "pump" and "Arduino"

FunctionpumpArduino
groundPUMP-GND

11. Connections between "motor_driver" and "Arduino"

Functionmotor_driverArduino
power12V → 3S Li-ion BMS protection board P+EXT
groundGND → Common battery and Arduino groundGND
power5V → 5 V logic supply from buck converter; remove the L298N 5V-EN jumper first5V
digitalIN1 → Left motor direction signal 1GPIO 6
digitalIN2 → Left motor direction signal 2GPIO 9
digitalIN3 → Right motor direction signal 1GPIO 11
digitalIN4 → Right motor direction signal 2GPIO 10
powerOUT1 → 12 V TT gear motor — left wheel M+EXT
powerOUT2 → 12 V TT gear motor — left wheel M-EXT
powerOUT3 → 12 V TT gear motor — right wheel M+EXT
powerOUT4 → 12 V TT gear motor — right wheel M-EXT

Deploy the firmware

#include <Arduino.h>
#include <NewPing.h>

const uint8_t FRONT_TRIG_PIN = A2;
const uint8_t FRONT_ECHO_PIN = A3;
const uint8_t LEFT_IN1_PIN = 6;
const uint8_t LEFT_IN2_PIN = 9;
const uint8_t RIGHT_IN3_PIN = 11;
const uint8_t RIGHT_IN4_PIN = 10;
const uint8_t PUMP_PIN = 4;
// This KY-019 is active-low: its relay energizes when IN is LOW.
const uint8_t RELAY_ON_LEVEL = LOW;
const uint8_t LEFT_TRIG_PIN = A0;
const uint8_t LEFT_ECHO_PIN = A1;
const uint8_t RIGHT_TRIG_PIN = A4;
const uint8_t RIGHT_ECHO_PIN = A5;

const uint16_t MAX_DISTANCE_CM = 200;
const uint16_t STOP_DISTANCE_CM = 30;
const uint8_t DRIVE_SPEED = 185;
const uint8_t TURN_SPEED = 170;
const uint16_t BACKUP_TIME_MS = 350;
const uint16_t TURN_TIME_MS = 500;
const unsigned long PUMP_INTERVAL_MS = 5000;
const unsigned long PUMP_ON_TIME_MS = 1000;
unsigned long lastPumpCycleMs = 0;

NewPing frontSonar(FRONT_TRIG_PIN, FRONT_ECHO_PIN, MAX_DISTANCE_CM);
NewPing leftSonar(LEFT_TRIG_PIN, LEFT_ECHO_PIN, MAX_DISTANCE_CM);
NewPing rightSonar(RIGHT_TRIG_PIN, RIGHT_ECHO_PIN, MAX_DISTANCE_CM);

uint16_t readDistanceCm(NewPing &sonar) {
  const uint16_t distance = sonar.ping_cm();
  return distance == 0 ? MAX_DISTANCE_CM : distance;
}

void setPump(bool on) {
  digitalWrite(PUMP_PIN, on ? RELAY_ON_LEVEL : !RELAY_ON_LEVEL);
}

void stopRobot() {
  digitalWrite(LEFT_IN1_PIN, LOW);
  digitalWrite(LEFT_IN2_PIN, LOW);
  digitalWrite(RIGHT_IN3_PIN, LOW);
  digitalWrite(RIGHT_IN4_PIN, LOW);
}

void driveForward(uint8_t speed) {
  (void)speed;
  digitalWrite(LEFT_IN1_PIN, HIGH);
  digitalWrite(LEFT_IN2_PIN, LOW);
  digitalWrite(RIGHT_IN3_PIN, HIGH);
  digitalWrite(RIGHT_IN4_PIN, LOW);
}

void driveBackward(uint8_t speed) {
  (void)speed;
  digitalWrite(LEFT_IN1_PIN, LOW);
  digitalWrite(LEFT_IN2_PIN, HIGH);
  digitalWrite(RIGHT_IN3_PIN, LOW);
  digitalWrite(RIGHT_IN4_PIN, HIGH);
}

void turnLeft() {
  digitalWrite(LEFT_IN1_PIN, LOW);
  digitalWrite(LEFT_IN2_PIN, HIGH);
  digitalWrite(RIGHT_IN3_PIN, HIGH);
  digitalWrite(RIGHT_IN4_PIN, LOW);
}

void turnRight() {
  digitalWrite(LEFT_IN1_PIN, HIGH);
  digitalWrite(LEFT_IN2_PIN, LOW);
  digitalWrite(RIGHT_IN3_PIN, LOW);
  digitalWrite(RIGHT_IN4_PIN, HIGH);
}

void avoidObstacle() {
  stopRobot();
  delay(120);

  driveBackward(DRIVE_SPEED);
  delay(BACKUP_TIME_MS);
  stopRobot();
  delay(120);

  const uint16_t leftDistance = readDistanceCm(leftSonar);
  delay(60);
  const uint16_t rightDistance = readDistanceCm(rightSonar);
  delay(60);

  if (leftDistance >= rightDistance) {
    turnLeft();
  } else {
    turnRight();
  }
  delay(TURN_TIME_MS);
  stopRobot();
}

void setup() {
  pinMode(LEFT_IN1_PIN, OUTPUT);
  pinMode(LEFT_IN2_PIN, OUTPUT);
  pinMode(RIGHT_IN3_PIN, OUTPUT);
  pinMode(RIGHT_IN4_PIN, OUTPUT);
  pinMode(PUMP_PIN, OUTPUT);
  setPump(false);
  stopRobot();
  lastPumpCycleMs = millis();
  delay(1000);
}

void loop() {
  const unsigned long now = millis();
  const unsigned long cycleElapsed = now - lastPumpCycleMs;

  if (cycleElapsed >= PUMP_INTERVAL_MS) {
    lastPumpCycleMs = now;
    setPump(true);
  } else if (cycleElapsed >= PUMP_ON_TIME_MS) {
    setPump(false);
  }

  const uint16_t frontDistance = readDistanceCm(frontSonar);
  delay(60);

  if (frontDistance <= STOP_DISTANCE_CM) {
    avoidObstacle();
  } else {
    driveForward(DRIVE_SPEED);
  }

  delay(40);
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace, diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik