Bins with lids are hygienic but you need a free hand or a foot to open them. This capstone turns an ordinary small bin with a hinged lid into a touch-free one. An ultrasonic sensor at the front watches for a hand. When it sees one, a servo inside lifts the lid with a printed arm, and three seconds after the hand has gone, it lowers it again, slowly, so it doesn't slam. If a hand comes back while it closes, the lid goes up again. A button keeps the lid open for emptying, and the UNO counts how many times the bin was used.
What you'll learn
- How to combine a sensor, a motor and a button into one device
- How to move a servo slowly and stop halfway
- How to keep a servo quiet when it doesn't need to move
- How to design a printed lever that gives a motor more reach
What you need
- UNO R3 board and USB cable (UNO R3 Starter Kit)
- Ultrasonic sensor HC-SR04 (UNO R3 Starter Kit)
- Servo motor SG90 with horns and screws (UNO R3 Starter Kit)
- Green LED and 1 resistor, 220 Ω (UNO R3 Starter Kit)
- Passive buzzer (UNO R3 Starter Kit)
- 1 push button (UNO R3 Starter Kit)
- A small bin with a light hinged lid (a desk or bathroom bin, up to about 10 litres)
- Strong double-sided tape
- The printed parts from the 3D printing box below (lid lifter and sensor mount), or the cardboard version described there
- Breadboard and jumper wires (UNO R3 Starter Kit)
Wiring

| From | To |
|---|---|
| Ultrasonic VCC | 5V |
| Ultrasonic Trig | UNO pin 9 |
| Ultrasonic Echo | UNO pin 10 |
| Ultrasonic GND | GND |
| Servo signal (orange) | UNO pin 6 |
| Servo power (red) | 5V |
| Servo GND (brown) | GND |
| UNO pin 5 | 220 Ω resistor, then the green LED's long leg |
| LED short leg | GND |
| Passive buzzer + | UNO pin 8 |
| Passive buzzer − | GND |
| Service button, one leg | UNO pin 2 |
| Service button, diagonally opposite leg | GND |
Use long jumper wires for the servo and the sensor, so the breadboard can sit outside the bin. Power the UNO from a USB charger, not a laptop, because the servo pulls a lot of current when it lifts.
Find your angles
Every bin is different. Start with OPEN_ANGLE at 60, upload, and wave your hand over the sensor. Raise the angle in steps of 10 until the lid opens far enough to drop rubbish in comfortably. Don't go further than you need: the servo works hardest when the lid is almost upright.
The sketch
#include <Servo.h>
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
const int SERVO_PIN = 6;
const int LED_PIN = 5;
const int BUZZER_PIN = 8;
const int BUTTON_PIN = 2;
const int CLOSED_ANGLE = 0;
const int OPEN_ANGLE = 90; // see "Find your angles"
const int NEAR_CM = 20; // a hand closer than this opens the lid
const unsigned long STAY_OPEN = 3000;
Servo lid;
int angle = CLOSED_ANGLE;
bool serviceMode = false;
unsigned long lastHand = 0;
unsigned long uses = 0;
long distanceCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long us = pulseIn(ECHO_PIN, HIGH, 25000); // no echo within 25 ms: nothing near
return us == 0 ? 999 : us / 58;
}
bool handNear() {
return distanceCm() < NEAR_CM;
}
void chirp() {
tone(BUZZER_PIN, 1800, 40);
}
// Move one degree at a time. When closing, stop and return false if a hand appears.
bool moveTo(int target, int stepDelay, bool watchForHand) {
lid.attach(SERVO_PIN);
while (angle != target) {
angle += (target > angle) ? 1 : -1;
lid.write(angle);
delay(stepDelay);
if (watchForHand && angle % 5 == 0 && handNear()) return false;
}
if (target == CLOSED_ANGLE) {
delay(300);
lid.detach(); // closed: switch the servo off, so it doesn't hum
}
return true;
}
void openLid() {
if (angle == OPEN_ANGLE) return;
digitalWrite(LED_PIN, HIGH);
chirp();
moveTo(OPEN_ANGLE, 4, false); // quick to open
uses++;
Serial.print("Opened ");
Serial.print(uses);
Serial.println(" times");
}
void closeLid() {
if (moveTo(CLOSED_ANGLE, 15, true)) { // slow to close...
digitalWrite(LED_PIN, LOW);
} else {
openLid(); // ...and back up if a hand appears
lastHand = millis();
}
}
void setup() {
Serial.begin(9600);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
lid.attach(SERVO_PIN);
lid.write(CLOSED_ANGLE);
delay(500);
lid.detach();
Serial.println("Smart bin ready");
}
void loop() {
if (digitalRead(BUTTON_PIN) == LOW) { // service mode: open for emptying
serviceMode = !serviceMode;
tone(BUZZER_PIN, serviceMode ? 1200 : 600, 200);
if (serviceMode) openLid();
else closeLid();
while (digitalRead(BUTTON_PIN) == LOW) delay(10);
}
if (serviceMode) {
digitalWrite(LED_PIN, (millis() / 300) % 2); // blink while in service mode
return;
}
if (handNear()) {
lastHand = millis();
openLid();
} else if (angle != CLOSED_ANGLE && millis() - lastHand > STAY_OPEN) {
closeLid();
}
delay(60);
}
How it works
The sensor measures the time an ultrasonic "ping" needs to bounce back from your hand. Sound needs about 58 microseconds to travel one centimetre there and back, so the time divided by 58 is the distance. pulseIn() gives up after 25 ms: then nothing is within about 4 metres, and the sketch reports 999.
Moving the servo in one go would make the lid jump open and slam shut. moveTo() moves one degree at a time, with a short pause for opening and a longer one for closing. While closing, it checks the sensor every 5°, so the lid can't come down on a hand. Once closed, detach() stops the control pulses: the servo goes quiet and stops pushing, and the lid's own weight keeps it shut.
The servo turns only about 90° and is weak far from its shaft. The printed arm is a lever: it lifts the lid close to the hinge, where a small movement of the arm makes a big movement at the front of the lid. That's why the lifter sits just under the hinge.
Try this next
- Add a second ultrasonic sensor inside the lid that looks down and warns you with the LED when the bin is full.
- Show the number of uses on the 1602 LCD.
- Play a little "thank you" tune every tenth time someone uses the bin.
Troubleshooting
- The board restarts when the servo moves: servos draw bursts of current. Use a good USB port or a powered hub, and check that the servo's red wire is on 5V.
-
The lid opens by itself: the sensor sees something within 20 cm, like the wall or the lid. Point it straight up into the open, or make
NEAR_CMsmaller. - The servo buzzes and can't lift the lid: the lid is too heavy, or the lifter sits too far from the hinge. Move it closer to the hinge, or use a lighter bin.
- The lid doesn't close fully: unscrew the horn, push it back on one notch lower so the arm lies flat at 0°, and screw it tight again.
- A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.