Build a smart bin with a lid that opens by itself

Wave your hand over the bin and a servo lifts the lid, then lowers it gently when you are done. A button keeps it open for emptying, and the UNO counts how often it is used.
  • Level:Advanced
  • Duration: 2 h
  • Ages 12+
  • Platform: Arduino

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Build a smart bin with a lid that opens by itself – EEMENTOR STEM™ lesson

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

Breadboard wiring diagram: Build a smart bin with a lid that opens by itself
Breadboard view. Check each connection against the wiring table below.
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_CM smaller.
  • 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.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

UNO R3 Starter Kit | Arduino IDE Compatible | Free Tutorials $56.00

Shop the kit