Built-in project · Arduino Uno R3

Automatic soap dispenser: ultrasonic on 8 and 7, pump on 2, on an Arduino Uno

An HC-SR04 (TRIG 8, ECHO 7) sees a hand under the spout and runs a mini water pump for a moment through an L293D whose 1A input is on pin 2. The whole build, an Arduino Uno and 3 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 20 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $39. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Measuring distance with an ultrasonic sensor
  • Driving a pump through a motor driver
  • Running once per event with a flag
  • Timing a pulse with pulseIn
  • Difficulty: Intermediate
  • Time: About 20 minutes
  • Board: Arduino Uno R3
  • Parts: 5

How it works

An HC-SR04 ultrasonic sensor, with TRIG on pin 8 and ECHO on pin 7, watches under the spout. When a hand is closer than HAND_CM, 10 cm, the Uno runs a small water pump.

A pump is a small motor, so it needs a driver. Pin 2 goes to the L293D's input 1A. When it is HIGH, the chip puts 5 V on the pump for half a second. Then the pump stops.

It will not run again until the hand goes away, so one wave is one dose.

Parts list

5 parts, plus the jumper wires. They retail for about $39; on Mokxi they're free.

Kits that can build it

This starter kit has every part in the circuit. Each kit's page lists what else it can build.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Ultrasonic ranger pin TRIGArduino Uno R3 pin 8
Ultrasonic ranger pin ECHOArduino Uno R3 pin 7
Motor driver, L293D pin IN1Arduino Uno R3 pin 2
Motor driver, L293D pin EN12Arduino Uno R3 pin 5V
Motor driver, L293D pin VSArduino Uno R3 pin 5V
Motor driver, L293D pin VSSArduino Uno R3 pin 5V
Ultrasonic ranger pin VCCArduino Uno R3 pin 5V
Motor driver, L293D pin GND1Arduino Uno R3 pin GND
Motor driver, L293D pin IN2Arduino Uno R3 pin GND
Mini water pump pin V-Arduino Uno R3 pin GND
Ultrasonic ranger pin GNDArduino Uno R3 pin GND
Mini water pump pin V+Motor driver, L293D pin OUT1

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// An automatic soap dispenser: a hand under the spout runs the pump.
//
//   HC-SR04 VCC -> 5V   TRIG -> pin 8   ECHO -> pin 7   GND -> GND
//   L293D 1,2EN, VCC1 and VCC2 -> 5V   GND -> GND
//   L293D 1A -> pin 2   1Y -> the pump's + lead; the pump's - lead -> GND
//
// A pump is a small motor, so like the fan it needs a driver: pin 2 HIGH
// puts 5 V on 1Y and the pump runs. It runs for half a second, then waits
// for the hand to go away before it will run again, so one wave is one dose.
//
// Drag the distance slider on the sensor below 10 cm.

const int TRIG = 8;
const int ECHO = 7;
const int PUMP = 2;
const int HAND_CM = 10;

bool served = false;

long distanceCm() {
  digitalWrite(TRIG, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG, LOW);
  long echo = pulseIn(ECHO, HIGH, 30000UL);
  return echo / 58;
}

void setup() {
  pinMode(TRIG, OUTPUT);
  pinMode(ECHO, INPUT);
  pinMode(PUMP, OUTPUT);
  digitalWrite(PUMP, LOW);
  Serial.begin(9600);
  Serial.println("Mokxi Uno: soap dispenser, sensor on 8 and 7, pump on 2");
}

void loop() {
  long cm = distanceCm();
  bool hand = cm > 0 && cm < HAND_CM;
  if (hand && !served) {
    Serial.println("hand: one dose of soap");
    digitalWrite(PUMP, HIGH);
    delay(500);
    digitalWrite(PUMP, LOW);
    served = true;
  } else if (!hand) {
    served = false;
  }
  delay(100);
}

Code walkthrough

The key functions of the sketch, and what each one does.

distanceCm sketch.ino

Sends a 10 microsecond pulse on TRIG, then uses pulseIn to time how long ECHO stays HIGH, giving up after 30000 microseconds. It divides by 58 to get centimeters. No echo gives 0.

long distanceCm() {
  digitalWrite(TRIG, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG, LOW);
  long echo = pulseIn(ECHO, HIGH, 30000UL);
  return echo / 58;
}

setup sketch.ino

Sets TRIG and PUMP as outputs, ECHO as an input, and holds the pump LOW. It opens serial at 9600 baud.

void setup() {
  pinMode(TRIG, OUTPUT);
  pinMode(ECHO, INPUT);
  pinMode(PUMP, OUTPUT);
  digitalWrite(PUMP, LOW);
  Serial.begin(9600);
  Serial.println("Mokxi Uno: soap dispenser, sensor on 8 and 7, pump on 2");
}

loop sketch.ino

Measures the distance and decides if a hand is there: more than 0 and under HAND_CM. If it is, and served is false, it runs the pump for 500 ms and sets served. When the hand leaves, served clears. Then it waits 100 ms.

void loop() {
  long cm = distanceCm();
  bool hand = cm > 0 && cm < HAND_CM;
  if (hand && !served) {
    Serial.println("hand: one dose of soap");
    digitalWrite(PUMP, HIGH);
    delay(500);
    digitalWrite(PUMP, LOW);
    served = true;
  } else if (!hand) {
    served = false;
  }
  delay(100);
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The pump never runs This project
    The L293D needs its enable and both supply pins on 5 V. Also check the IN2 pin is on ground.
  • It dispenses over and over This project
    served should stay set while the hand is there. Make sure it only clears when no hand is detected.
  • The motor is weak or does not turn, and the chip gets hot. Motor driver
    The L293D gives about 600 mA per channel and drops about 1.4 V per side. Use a motor that fits, and for bigger motors pick another driver.
  • Nothing moves even though the inputs are right. Motor driver
    Power both supply pins: VCC1 (pin 16) at 5 V for logic and VCC2 (pin 8) for the motor. Tie the enable pin high, and join all grounds.
  • A pump wired straight to a microcontroller pin does not run or resets the board. It draws far more than a pin can give. Mini water pump
    Switch it with a logic level MOSFET or a transistor with a base resistor, from its own supply of 3 to 6 V. Join the grounds.
  • The motor spikes damage the driver, or the pump runs dry and burns out. A coil load makes voltage kicks when it stops. Mini water pump
    Add a flyback diode across the pump, stripe to the positive side. Keep the pump under water, since a dry run overheats it and wears the seal.
  • Distance reads zero, or never changes. Ultrasonic ranger
    Power it from 5 V and share ground. Send a 10 microsecond HIGH pulse on TRIG, then time ECHO with pulseIn. Check TRIG and ECHO are not swapped.
  • Readings jump around or are wrong on a 3.3 V board. Ultrasonic ranger
    ECHO outputs 5 V, so divide it down for 3.3 V boards. Aim at flat, hard surfaces. The range is about 2 cm to 4 m, and soft or angled targets bounce the sound away.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.