Automatischer Mülleimer mit Arduino: Sensor, Servo und Füllstand
Automatischen Mülleimer mit Arduino bauen: HC-SR04 erkennt die Hand, ein Servo öffnet den Deckel und schließt ihn sanft, dazu Füllstand-LEDs und ESP32-Telegram.
Ein automatischer Mülleimer mit Arduino ist ein einfaches, aber lehrreiches Automatisierungsprojekt, bei dem sich der Deckel öffnet, ohne dass du ihn berührst – und eines der beliebtesten Schulprojekte überhaupt. Ein HC-SR04-Ultraschallsensor vorne am Eimer erkennt die sich nähernde Hand, der Arduino öffnet den Deckel mit einem Servomotor und schließt ihn ein paar Sekunden, nachdem du die Hand weggezogen hast, langsam statt mit einem Knall. Ein zweiter HC-SR04 unter dem Deckel misst, wie hoch der Müll liegt: Je voller der Eimer wird, desto weiter wandert die Anzeige von der grünen über die gelbe zur roten LED, und wenn er ganz voll ist, piept ein Summer.
In dieser Anleitung gehen wir Schritt für Schritt von der Teileliste aus durch, wo die Sensoren hinkommen, wie du das Servo-Drehmoment berechnest, wie der Deckel am Scharnier befestigt wird, wie die Kalibrierung funktioniert und wie der komplette Code Wartezeit und langsames Schließen gleichzeitig steuert. Danach rechnen wir aus, wie lange das Projekt wirklich mit Batterien läuft, schicken eine Füllstandsmeldung per ESP32 als Telegram-Nachricht aufs Handy und schreiben dasselbe Projekt für den Raspberry Pi Pico in MicroPython. Am Ende findest du außerdem einen Ablauf für die Präsentation eines Schulprojekts und eine Testtabelle, die du mit deinen eigenen Messwerten füllen kannst.

Wie funktioniert ein automatischer Mülleimer?
Das Projekt ist eine Schleife, die sich dutzende Male pro Sekunde wiederholt. Der Arduino misst alle 60 Millisekunden mit dem Handsensor. Taucht in zwei Messungen hintereinander ein Gegenstand auf, der näher als 20 cm ist, entscheidet er, dass es eine Hand ist, und schickt das Servo in die offene Stellung. Solange die Hand dort bleibt, beginnt die Wartezeit bei jeder Messung von vorn; 4 Sekunden nachdem die Hand weg ist, schließt sich der Deckel langsam mit etwa 50 Grad pro Sekunde. Kommt während des Schließens wieder eine Hand, öffnet er sich sofort erneut. Eine Sekunde nachdem der Deckel geschlossen ist, macht der Füllstandsensor 5 Messungen, nimmt den Median als Abstand zur Oberfläche des Mülls und rechnet ihn in Prozent um. Der Code verwendet einen kleinen Zustandsautomaten, in dem der Deckel immer in einem von drei Zuständen ist: GESCHLOSSEN (CLOSED), OFFEN (OPEN) und SCHLIESST (CLOSING).
| Bauteil | Aufgabe | Hinweis |
|---|---|---|
| Arduino Uno (oder Nano) | Das Gehirn: misst, entscheidet, steuert Servo und LEDs | ESP32 oder Pico gehen auch |
| 2 × HC-SR04 | Einer erkennt die Hand, der andere misst den Füllstand | 5 V, beim Messen etwa 15 mA |
| Servo SG90, MG90S oder MG996R | Öffnet und schließt den Deckel | Wird nach dem Gewicht des Deckels gewählt (Rechnung unten) |
| 3 LEDs (grün, gelb, rot) und 3 × 220 Ω | Füllstandsanzeige | Ein Widerstand pro LED |
| Aktiver Summer | Warnung bei vollem Eimer | Ein passiver Summer braucht tone() |
| Scharnier, Schrauben, Heißkleber | Trägt den Deckel, hält das Servohorn | Das Gewicht des Deckels soll das Scharnier tragen, nicht das Servo |
| 470-µF-Kondensator | Glättet den Anlaufstrom des Servos | An + und − des Servos, auf die Polung achten |
| Stromversorgung | 5-V-Netzteil, Powerbank oder Batterien | Laufzeitrechnung unten |
| ESP32 (optional) | Füllstandsmeldung aufs Handy | Braucht WLAN |




Welcher Sensor zur Handerkennung?
Zum Öffnen eines Mülleimers kommen mehrere Sensoren infrage, aber nicht alle sind gleich gut geeignet. Eigentlich musst du eine Hand bemerken, die sich dem Eimer auf 15–25 cm nähert, und dabei eine Person ignorieren, die nur vorbeiläuft. Weil der HC-SR04 die Entfernung in Zentimetern liefert, kannst du die Schwelle im Code genau so einstellen, wie du willst – deshalb setzen ihn die meisten Projekte für smarte Mülleimer ein. Stärken und Schwächen der Alternativen:
| Sensor | Wie er erkennt | Für den Mülleimer |
|---|---|---|
| HC-SR04 (Ultraschall) | Schallecho, 2–400 cm, schmaler Kegel | Schwelle per Code einstellbar; die ausgewogenste Wahl |
| IR-Hindernissensor (FC-51) | Reflektiertes Infrarotlicht, etwa 2–30 cm | Billig und schnell; lässt sich von Sonnenlicht und dunklen Handschuhen täuschen |
| PIR (HC-SR501) | Bewegung von Körperwärme, bis 7 m, weiter Winkel | Löst bei jedem aus, der vorbeigeht, und hält einige Sekunden; ungeeignet |
| APDS-9960 | Annäherung und Handgesten auf kurze Distanz | Schicke Version zum Öffnen per Wischgeste; braucht I2C und 3,3 V |
Wie ein PIR-Sensor arbeitet, erfährst du im Artikel PIR-Bewegungsmelder, eine gestengesteuerte Version in der Anleitung zum APDS-9960-Gestensensor. So funktioniert der HC-SR04 in Kürze: Er sendet einen kurzen Schallimpuls mit 40 kHz aus und gibt die Laufzeit des Echos als Puls am ECHO-Pin aus. Da Schall in Luft bei 20 °C mit etwa 343 m/s unterwegs ist und der Weg hin und zurück führt, ergibt die Zeit in Mikrosekunden geteilt durch 58 die Entfernung in Zentimetern. Die Details stehen im Artikel HC-SR04-Entfernungssensor.
Platzierung der Sensoren
Der Handsensor kommt an den Korpus des Eimers, oben an die Vorderseite, nicht auf den Deckel – denn beim Öffnen würde er sich mitdrehen und an die Decke zeigen. Wenn du den Sensor um 10–20 Grad nach oben neigst, erfasst er eine Hand über dem Eimer, aber keine Beine, die vorbeilaufen. Steht der Eimer neben einer Wand oder einem Schrank, darf in Blickrichtung des Sensors innerhalb von 30 cm keine feste Fläche sein; sonst hält er diese Fläche für eine Hand und der Deckel schließt nie.
Der Füllstandsensor wird unter den Deckel geklebt, nahe der Mitte und genau nach unten gerichtet. In dieser Position misst er nur bei geschlossenem Deckel sinnvoll; bei offenem Deckel zeigt er auf eine Wand oder an die Decke. Deshalb misst der Code den Füllstand erst eine Sekunde nachdem der Deckel geschlossen ist. Auch wenn der Schallkegel des Sensors schmal ist (15 Grad laut Datenblatt, in der Praxis etwas breiter), erfasst er in 35 cm Tiefe eine Fläche von 10–20 cm Durchmesser; in Eimern unter 20 cm Durchmesser kann der Schall von den Seitenwänden zurückkommen und falsche Werte verursachen. Schlag den Rand des Müllbeutels nach außen über den Eimer: Ein nach innen hängender Beutelrand gerät vor den Sensor und lässt den Eimer selbst leer voll aussehen.
Schaltplan: Arduino Uno, ESP32 und Pico
Die folgende Tabelle passt exakt zu jedem Code in diesem Artikel. Am Uno kommt der Handsensor an D2–D3, der Füllstandsensor an D4–D5, das Servo an D9, die LEDs an D10–D12 und der Summer an D8. Weil die Servo-Bibliothek am Uno Timer1 belegt, schaltet sie die PWM-Ausgabe an D9 und D10 ab; die LED an D10 schalten wir nur ein und aus, das betrifft uns also nicht. Setz vor jede LED einen 220-Ω-Widerstand; den passenden Wert für deine LED berechnest du mit dem Werkzeug LED-Vorwiderstand. Die Pinbelegung deines Boards siehst du im Werkzeug Pinout.
| Verbindung | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| Handsensor TRIG | D2 | GPIO 18 | GP2 |
| Handsensor ECHO | D3 (direkt) | GPIO 19, über 1-kΩ-/2-kΩ-Teiler | GP3, über 1-kΩ-/2-kΩ-Teiler |
| Füllstandsensor TRIG | D4 | GPIO 25 | GP4 |
| Füllstandsensor ECHO | D5 (direkt) | GPIO 26, über Teiler | GP5, über Teiler |
| HC-SR04 VCC (beide) | 5V | 5V (VIN) | VBUS |
| Servo-Signal (orange) | D9 | GPIO 13 | GP15 |
| Servo + (rot) | 5V (SG90) oder extern 5–6 V | 5V (VIN) oder extern 5–6 V | VBUS oder extern 5–6 V |
| Grüne, gelbe, rote LED (mit 220 Ω) | D10, D11, D12 | GPIO 27, 32, 33 | GP10, GP11, GP12 |
| Aktiver Summer (+) | D8 | GPIO 23 | GP13 |
| Alle GND | GND | GND | GND |
Servo auswählen und Drehmoment berechnen
Das häufigste Problem bei automatischen Mülleimern ist ein Deckel, der nur halb aufgeht und zittert oder sich gar nicht hebt. Die Ursache ist fast immer ein Servo, das für den Deckel zu schwach ist. Das nötige Drehmoment lässt sich leicht abschätzen: Drehmoment (kg·cm) = Gewicht des Deckels (kg) × Abstand des Schwerpunkts vom Scharnier (cm). Bei einem flachen, gleichmäßig dicken Deckel liegt der Schwerpunkt genau in der Mitte; bei einem 24 cm langen Deckel sind das 12 cm. Am schwersten ist der allererste Moment des Öffnens, wenn der Deckel waagerecht liegt; je steiler er steht, desto weniger Drehmoment braucht es, senkrecht ist es fast null. Der Drehmomentwert in Servo-Katalogen ist das Blockiermoment (Stall Torque), bei dem das Servo stehen bleibt; arbeitet es nahe an dieser Grenze, wird es heiß und das Getriebe verschleißt. Deshalb multipliziere dein Ergebnis mindestens mit 2.
| Deckel | Gewicht | Schwerpunkt | Nötiges Drehmoment | Mit 2-facher Reserve | Empfehlung |
|---|---|---|---|---|---|
| 20 × 20 cm Karton oder Schaumstoffplatte | 50 g | 10 cm | 0,5 kg·cm | 1,0 kg·cm | SG90 |
| 25-cm-Kunststoffdeckel eines Mülleimers | 120 g | 12,5 cm | 1,5 kg·cm | 3,0 kg·cm | MG996R (MG90S grenzwertig) |
| 30-cm-Deckel aus MDF oder Sperrholz | 300 g | 15 cm | 4,5 kg·cm | 9,0 kg·cm | MG996R plus Gegengewicht |
| Servo | Drehmoment (4,8 V / 6 V) | Getriebe | Strom | Hinweis |
|---|---|---|---|---|
| SG90 | 1,8 kg·cm (nur ein 4,8-V-Wert angegeben) | Kunststoff | 100–250 mA in Bewegung | Kann am 5V-Pin des Arduino hängen |
| MG90S | 1,8 / 2,2 kg·cm | Metall | 150–300 mA in Bewegung | Gleiche Größe wie SG90, robuster |
| MG996R | 9,4 / 11 kg·cm | Metall | 0,5–0,9 A in Bewegung, blockiert bis 2,5 A | Unbedingt aus eigener 5–6-V-Quelle versorgen |

Statt ein stärkeres Servo zu kaufen, hilft es oft, den Deckel auszubalancieren. Hat der Deckel einen kurzen Arm, der hinter das Scharnier reicht, gleichen ein paar dort festgeklebte Schrauben oder Münzen einen Teil seines Gewichts aus; dieses Gegengewicht kann das nötige Drehmoment bis auf die Hälfte senken. Du könntest das Servohorn den Deckel auch weit vom Scharnier entfernt drücken lassen, aber der Hebel schenkt dir kein Drehmoment: Je weiter weg vom Scharnier, desto weniger Kraft braucht es, doch der gedrückte Punkt legt einen längeren Weg zurück, also brauchst du ein längeres Servohorn – und mit dem längeren Horn steigt das vom Servo geforderte Drehmoment wieder. Soll der Deckel fast 90 Grad aufgehen, bleibt das nötige Servo-Drehmoment nahezu gleich wie bei einem direkt am Scharnier sitzenden Servo. Einen ausführlichen Vergleich der Servotypen findest du in den Artikeln Was ist ein Servomotor? und MG996R-Servo.
Montagetipps: Scharnier, Servohorn und Kabel
- Lass das Scharnier das Gewicht tragen: Der Deckel sollte an einem echten Scharnier hängen (dem eigenen Scharnier des Eimers oder einem kleinen Möbelscharnier). Setzt du den Deckel nur auf die Servowelle, liegt das ganze Gewicht auf dem Lager des Servos, und die Kunststoffzahnräder des SG90 brechen bald.
- Richte die Servowelle auf die Scharnierachse aus: Am einfachsten schraubst du das Servo so an die Hinterkante des Eimers, dass seine Welle genau am Ende der Scharnierlinie sitzt, und befestigst das Servohorn mit einer Schraube oder Heißkleber am Deckel. Sind die Achsen versetzt, drückt das Servo den Deckel bei jedem Öffnen zur Seite.
- Befestige das Servogehäuse stabil: Halte das Servo nicht nur mit doppelseitigem Klebeband; beim Anheben des Deckels verdreht sich das Gehäuse. Nimm zwei Schrauben durch die Befestigungslaschen des Servos oder einen kleinen Winkel.
- Drück den Deckel nicht auf den Rand: Stell den Geschlossen-Winkel so ein, dass der Deckel auf dem Rand aufliegt, das Servo aber nicht darauf drückt. Presst das Servo weiter gegen den geschlossenen Deckel, brummt es, wird warm und verbraucht unnötig Strom.
- Lass den Kabeln Spiel: Die Leitungen des Füllstandsensors bewegen sich mit dem Deckel. Führe das Kabel neben dem Scharnier entlang, so lang, dass es selbst bei ganz offenem Deckel nicht spannt, und sichere es mit einem Kabelbinder.
- Halte die Elektronik vom Müll fern: Steck Arduino, Batterien und Steckbrett in eine kleine Box, die du hinten oder unten an den Eimer klebst. Flüssigkeit oder Feuchtigkeit aus dem Müllbeutel darf die Schaltung nicht erreichen.
- Fang mit einem Pappmodell an: Für ein Schulprojekt funktioniert ein leichter Deckel aus einem Schuhkarton oder einer Schaumstoffplatte selbst mit einem SG90 problemlos und ist ein gutes Beispiel, um die Drehmomentrechnung in der Präsentation zu zeigen.
Aufbau und Kalibrierung Schritt für Schritt
Bevor du zum Hauptcode kommst, lade den kleinen Kalibrier-Sketch unten hoch. Er fährt das Servo auf den Winkel, den du in den seriellen Monitor tippst, und gibt jede halbe Sekunde die Entfernung beider Sensoren aus. So misst du für deinen eigenen Eimer den Geschlossen- und den Offen-Winkel des Deckels, die Tiefe des leeren Eimers und die Schwelle für die Handerkennung.
1#include <Servo.h>
2
3const int HAND_TRIG = 2, HAND_ECHO = 3; // el sensörü / hand sensor
4const int FILL_TRIG = 4, FILL_ECHO = 5; // doluluk sensörü / fill sensor
5const int SERVO_PIN = 9;
6
7Servo lid;
8unsigned long lastPrint = 0;
9
10int pingCm(int trig, int echo) {
11 digitalWrite(trig, LOW); delayMicroseconds(2);
12 digitalWrite(trig, HIGH); delayMicroseconds(10); // 10 µs tetik darbesi / 10 µs trigger pulse
13 digitalWrite(trig, LOW);
14 unsigned long t = pulseIn(echo, HIGH, 30000UL); // en fazla 30 ms bekle / wait at most 30 ms
15 return t == 0 ? 999 : (int)(t / 58); // 999 = yankı yok / 999 = no echo
16}
17
18void setup() {
19 Serial.begin(9600);
20 pinMode(HAND_TRIG, OUTPUT); pinMode(HAND_ECHO, INPUT);
21 pinMode(FILL_TRIG, OUTPUT); pinMode(FILL_ECHO, INPUT);
22 lid.write(90); // başlangıçta orta konum / start at the middle position
23 lid.attach(SERVO_PIN);
24 Serial.println(F("Açı yaz (0-180) ve Enter'a bas / Type an angle (0-180) and press Enter"));
25}
26
27void loop() {
28 if (Serial.available()) {
29 String s = Serial.readStringUntil('\n');
30 s.trim();
31 if (s.length() > 0) { // boş satırı yok say / ignore empty lines
32 int a = constrain(s.toInt(), 0, 180);
33 lid.write(a);
34 Serial.print(F("Servo: "));
35 Serial.println(a);
36 }
37 }
38
39 if (millis() - lastPrint >= 500) { // yarım saniyede bir yaz / print every half second
40 lastPrint = millis();
41 int hand = pingCm(HAND_TRIG, HAND_ECHO);
42 delay(60); // iki sensör arasında bekle / wait between the two sensors
43 int fill = pingCm(FILL_TRIG, FILL_ECHO);
44 Serial.print(F("El / Hand: "));
45 Serial.print(hand);
46 Serial.print(F(" cm Doluluk / Fill: "));
47 Serial.print(fill);
48 Serial.println(F(" cm"));
49 }
50}- Lade den Code ohne aufgestecktes Servohorn hoch und öffne den seriellen Monitor mit 9600 Baud; stell das Zeilenende auf Neue Zeile (Newline). Das Servo fährt auf 90 Grad.
- Tipp 10 ein und drück Enter. Halte den Deckel geschlossen, steck das Servohorn in dieser Stellung auf die Welle und befestige es dann am Deckel.
- Erhöhe den Winkel in Schritten wie 20, 40, 60, bis der Deckel ganz offen ist; meist sind das 90–110 Grad. Öffne den Deckel nicht über die Senkrechte hinaus. Dieser Wert wird zu LID_OPEN. Dreht dein Servo andersherum, wähle die Werte für geschlossen und offen entsprechend; der Code funktioniert in beide Richtungen.
- Geh wieder zu kleinen Winkeln zurück. Vom Winkel, bei dem der Deckel ganz auf dem Rand aufliegt, gehst du 2–3 Grad in Öffnungsrichtung zurück; das wird LID_CLOSED. Brummt das Servo im geschlossenen Zustand, drückt es auf den Deckel.
- Notiere bei eingesetztem Beutel und leerem Eimer den Wert des Füllstandsensors; das ist EMPTY_CM. Für FULL_CM wählst du 6–10 cm: Wenn der Müll diese Höhe erreicht, muss der Deckel noch leicht schließen und der Sensor noch sicher messen können, denn der HC-SR04 misst nichts, was näher als 2–3 cm ist.
- Halte die Hand in 5, 10, 20 und 30 cm Abstand vor den Handsensor und sieh dir die Werte an. Geh dann am Eimer vorbei und notiere den kleinsten Wert, der dabei gemessen wird. HAND_CM muss darunter liegen; für die meisten Eimer funktionieren 15–25 cm gut.
- Trag die ermittelten Werte oben im Hauptcode bei den Einstellungen ein und lade ihn hoch.
Hauptcode: Handerkennung, Wartezeit und langsames Schließen
Im Hauptcode gibt es kein langes delay(); alles läuft über Zeitstempel, die mit millis() festgehalten werden. So wird der Handsensor selbst während des Schließens weiter abgefragt, und kommt eine Hand wieder näher, öffnet sich der Deckel sofort. Das ist eine einfache, aber wichtige Sicherheitsfunktion, die verhindert, dass der Deckel auf die Hand eines Kindes fällt. Die Details zum blockierungsfreien Timing findest du in millis() und delay(), das Denken in Zuständen im Artikel Arduino-Zustandsautomat.
1#include <Servo.h>
2
3// Pinler / Pins
4const int HAND_TRIG = 2, HAND_ECHO = 3; // el sensörü, kutunun önünde / hand sensor, front of the bin
5const int FILL_TRIG = 4, FILL_ECHO = 5; // doluluk sensörü, kapağın altında / fill sensor, under the lid
6const int SERVO_PIN = 9;
7const int LED_GREEN = 10, LED_YELLOW = 11, LED_RED = 12;
8const int BUZZER = 8; // aktif buzzer / active buzzer
9
10// Ayarlar: kalibrasyon kodunda bulduğun değerleri yaz / Settings: use the values from the calibration sketch
11const int HAND_CM = 20; // bundan yakın el kapağı açar / a hand closer than this opens the lid
12const int LID_CLOSED = 10; // kapak kenara oturuyor, bastırmıyor / lid rests on the rim, not pressing
13const int LID_OPEN = 110; // kapak tam açık / lid fully open
14const unsigned long OPEN_TIME = 4000; // son el algılamadan sonra açık kalma (ms) / stay open after the last hand (ms)
15const unsigned long CLOSE_STEP_MS = 20; // kapanırken derece başına ms / ms per degree while closing
16const int EMPTY_CM = 35; // boş kutuda sensörden dibe / sensor to bottom when empty
17const int FULL_CM = 8; // bu mesafede kutu %100 dolu / the bin is 100% full at this distance
18
19enum LidState { CLOSED, OPEN, CLOSING };
20LidState state = CLOSED;
21Servo lid;
22
23unsigned long lastHand = 0, lastHandPing = 0, closeStart = 0, closedAt = 0, lastFill = 0;
24int fillPercent = 0;
25byte handHits = 0;
26bool fullWarned = false;
27
28int pingCm(int trig, int echo) {
29 digitalWrite(trig, LOW); delayMicroseconds(2);
30 digitalWrite(trig, HIGH); delayMicroseconds(10); // 10 µs tetik darbesi / 10 µs trigger pulse
31 digitalWrite(trig, LOW);
32 unsigned long t = pulseIn(echo, HIGH, 30000UL); // en fazla 30 ms bekle / wait at most 30 ms
33 return t == 0 ? 999 : (int)(t / 58); // 999 = yankı yok / 999 = no echo
34}
35
36int medianCm(int trig, int echo) { // 5 ölçümün ortancası / median of 5 readings
37 int v[5];
38 delay(60); // önce el sensörünün yankısı sönsün / first let the hand sensor's echo die out
39 for (int i = 0; i < 5; i++) {
40 v[i] = pingCm(trig, echo);
41 delay(60); // yankılar sönsün / let the echoes die out
42 }
43 for (int i = 0; i < 4; i++) // küçükten büyüğe sırala / sort ascending
44 for (int j = i + 1; j < 5; j++)
45 if (v[j] < v[i]) { int tmp = v[i]; v[i] = v[j]; v[j] = tmp; }
46 return v[2];
47}
48
49int toPercent(int cm) { // mesafe → doluluk yüzdesi / distance → fill percent
50 long p = (long)(EMPTY_CM - cm) * 100 / (EMPTY_CM - FULL_CM);
51 if (p < 0) p = 0;
52 if (p > 100) p = 100;
53 return (int)p;
54}
55
56void showLevel() {
57 digitalWrite(LED_GREEN, fillPercent < 50);
58 digitalWrite(LED_YELLOW, fillPercent >= 50 && fillPercent < 80);
59 digitalWrite(LED_RED, fillPercent >= 80);
60}
61
62void beep(int n) {
63 for (int i = 0; i < n; i++) {
64 digitalWrite(BUZZER, HIGH); delay(120);
65 digitalWrite(BUZZER, LOW); delay(120);
66 }
67}
68
69void openLid() {
70 if (!lid.attached()) lid.attach(SERVO_PIN);
71 lid.write(LID_OPEN); // hızlı aç / open quickly
72 state = OPEN;
73 if (fillPercent >= 90) beep(2); // açılırken dolu uyarısı / full warning on opening
74}
75
76void setup() {
77 Serial.begin(9600);
78 pinMode(HAND_TRIG, OUTPUT); pinMode(HAND_ECHO, INPUT);
79 pinMode(FILL_TRIG, OUTPUT); pinMode(FILL_ECHO, INPUT);
80 pinMode(LED_GREEN, OUTPUT); pinMode(LED_YELLOW, OUTPUT); pinMode(LED_RED, OUTPUT);
81 pinMode(BUZZER, OUTPUT);
82 lid.write(LID_CLOSED); // attach'tan önce: açılışta 90°'ye sıçramasın / before attach: no jump to 90° at power-up
83 lid.attach(SERVO_PIN);
84 delay(500);
85 lid.detach(); // kapalıyken servoyu bırak / release the servo while closed
86 closedAt = millis();
87 showLevel();
88}
89
90void loop() {
91 unsigned long now = millis();
92
93 // 1) El algılama, 60 ms'de bir / Hand detection, every 60 ms
94 if (now - lastHandPing >= 60) {
95 lastHandPing = now;
96 int d = pingCm(HAND_TRIG, HAND_ECHO);
97 if (d >= 2 && d <= HAND_CM) {
98 if (handHits < 2) handHits++;
99 } else {
100 handHits = 0;
101 }
102 if (handHits >= 2) { // art arda 2 ölçüm / 2 readings in a row
103 lastHand = now; // bekleme süresi yeniden başlar / the wait time restarts
104 if (state != OPEN) openLid(); // kapanırken el gelirse yeniden aç / reopen if a hand appears while closing
105 }
106 }
107
108 // 2) Bekleme süresi doldu mu? / Has the wait time passed?
109 if (state == OPEN && now - lastHand >= OPEN_TIME) {
110 state = CLOSING;
111 closeStart = now;
112 }
113
114 // 3) Yavaş kapanma: açı geçen süreden hesaplanır / Slow close: angle is computed from elapsed time
115 if (state == CLOSING) {
116 int moved = (now - closeStart) / CLOSE_STEP_MS;
117 int range = abs(LID_OPEN - LID_CLOSED);
118 if (moved >= range) {
119 lid.write(LID_CLOSED);
120 state = CLOSED;
121 closedAt = now;
122 } else {
123 lid.write(LID_OPEN > LID_CLOSED ? LID_OPEN - moved : LID_OPEN + moved);
124 }
125 }
126
127 // 4) Kapandıktan 0,4 s sonra servoyu bırak / Release the servo 0.4 s after closing
128 if (state == CLOSED && lid.attached() && now - closedAt > 400) lid.detach();
129
130 // 5) Doluluk: yalnızca kapak kapalı ve durgunken / Fill level: only while the lid is closed and still
131 if (state == CLOSED && now - closedAt > 1000 && now - lastFill >= 3000) {
132 lastFill = now;
133 int cm = medianCm(FILL_TRIG, FILL_ECHO);
134 if (cm < 999) fillPercent = toPercent(cm); // yankı yoksa eski değeri koru / keep the old value if there is no echo
135 showLevel();
136 Serial.print(F("Doluluk / Fill: "));
137 Serial.print(cm);
138 Serial.print(F(" cm = %"));
139 Serial.println(fillPercent);
140 if (fillPercent >= 90 && !fullWarned) { // doldu: bir kez öt / just filled: beep once
141 fullWarned = true;
142 beep(3);
143 }
144 if (fillPercent < 70) fullWarned = false; // boşaltıldı / emptied
145 }
146}| Funktion oder Einstellung | Was sie macht |
|---|---|
| pingCm() | Macht eine Messung; liefert 999, wenn innerhalb von 30 ms kein Echo kommt |
| medianCm() | Macht 5 Messungen im Abstand von 60 ms, sortiert sie und liefert die mittlere |
| toPercent() | Rechnet die Entfernung anhand von EMPTY_CM und FULL_CM in 0–100 Prozent um |
| handHits | Zählt, in wie vielen Messungen hintereinander eine Hand zu sehen war; eine einzelne Fehlmessung öffnet den Deckel nicht |
| OPEN_TIME | Zeit vom letzten Erkennen der Hand bis zum Beginn des Schließens (4000 ms) |
| CLOSE_STEP_MS | Zeit pro Grad beim Schließen: 100 Grad × 20 ms = 2 Sekunden Schließzeit |
| lid.detach() | Hört 0,4 s nach dem Schließen auf, Pulse an das Servo zu senden |
| fullWarned | Sorgt dafür, dass die Voll-Warnung nur einmal ertönt; wird zurückgesetzt, wenn der Füllstand unter 70 % fällt |
Der Trick beim langsamen Schließen: Der Winkel wird aus der verstrichenen Zeit berechnet, statt Grad für Grad hochgezählt zu werden. 600 ms nach Beginn des Schließens sollte der Deckel 600 / 20 = 30 Grad zurückgelegt haben; selbst wenn die Schleife kurz durch eine Füllstandsmessung oder den Summer aufgehalten wird, bringt der Code den Deckel auf den richtigen Winkel, und die Bewegung ruckelt nicht. Für ein noch langsameres Schließen vergrößerst du CLOSE_STEP_MS: Mit 30 ms dauern 100 Grad 3 Sekunden. Ist der Deckel zu, wird das Servo mit detach() freigegeben; der Deckel liegt ohnehin auf dem Rand, das Servo muss ihn nicht halten, es brummt nicht und verbraucht weniger Akku. Dass im Code write() vor attach() aufgerufen wird, ist ebenfalls Absicht: Beim Einschalten springt das Servo nicht erst auf 90 Grad, sondern startet direkt in der geschlossenen Stellung.
Füllstand messen, LED- und Summerwarnung
Der Füllstandsensor misst den Abstand von der Deckelunterseite bis zum höchsten Punkt des Mülls. Bei leerem Eimer ist dieser Abstand am größten (EMPTY_CM), bei vollem am kleinsten (FULL_CM). Der Prozentwert ergibt sich aus dieser Formel: Füllstand (%) = (EMPTY_CM − Messwert) / (EMPTY_CM − FULL_CM) × 100. Bei EMPTY_CM = 35 cm, FULL_CM = 8 cm und einem Messwert von 20 cm beträgt der Füllstand zum Beispiel (35 − 20) / 27 × 100 ≈ 55 %, und die gelbe LED leuchtet. Ist der Messwert größer als 35 cm (der Schall lief schräg über den Boden zurück), wird das Ergebnis auf 0 % begrenzt, ist er kleiner als 8 cm, auf 100 %.
| Füllstand | LED | Summer | Bedeutung |
|---|---|---|---|
| 0–49 % | Grün | Still | Genug Platz |
| 50–79 % | Gelb | Still | Mehr als halb voll, bald leeren |
| 80–89 % | Rot | Still | Fast voll |
| 90–100 % | Rot | 3 kurze Pieptöne beim Vollwerden, 2 Pieptöne bei jedem Öffnen | Jetzt leeren |
Die Oberfläche des Mülls ist nicht eben: Steht eine einzelne Flasche aufrecht genau unter dem Sensor, sieht die Messung diesen Punkt, und der Eimer wirkt voller, als er ist. Der Medianfilter entfernt einzelne Ausreißer, kann aber die Form des Mülls nicht korrigieren; betrachte Schwellen wie 80 % und 90 % deshalb als Hinweis „bald leeren“ und nicht als exakte Messung. Damit die Warnung nicht ständig piept, nutzt der Code eine Hysterese: Die Voll-Warnung ertönt einmal bei 90 % und erst wieder, nachdem der Eimer geleert wurde und der Füllstand unter 70 % gefallen ist. Die Unterschiede zwischen Filtern erklären wir ausführlich im Artikel Sensordaten filtern.
Batteriebetrieb: Wie lange hält er durch?
Den automatischen Mülleimer ohne Kabel zu betreiben, ist verlockend, aber der Arduino Uno ist kein batteriefreundliches Board. Mit USB-Chip, Spannungsregler und Power-LED zieht er schon im Leerlauf etwa 45–50 mA. Kommen der Ruhestrom von zwei HC-SR04, eine leuchtende LED und die Elektronik des freigegebenen Servos dazu, steigt der Durchschnittsstrom auf rund 70–80 mA. Die 100–250 mA, die das Servo beim Öffnen und Schließen zieht, dauern nur ein paar Sekunden; bei 50 Öffnungen am Tag erhöhen sie den Durchschnitt nur um etwa 1 mA. Die Laufzeit ergibt sich grob aus Kapazität (mAh) / Durchschnittsstrom (mA); mit deinen eigenen Werten rechnest du sie mit dem Werkzeug Akkulaufzeit aus.
| Stromquelle | Anschluss | Ungefähre Laufzeit (75 mA) | Bewertung |
|---|---|---|---|
| 9-V-Blockbatterie (etwa 500 mAh) | VIN | 5–6 Stunden | Nicht empfohlen: Die Spannung bricht beim Anlaufen des Servos ein, das Board startet neu |
| 6 × AA-Alkaline (9 V) | Hohlstecker oder VIN | Etwa 1 Tag | Reicht für den Präsentationstag |
| 2 × 18650 (7,4 V, 2500 mAh) | VIN; LM2596 auf 5–6 V für das Servo | Etwa 1,5 Tage | Wiederaufladbar; braucht ein 2S-Ladegerät und eine Schutzschaltung |
| Powerbank mit 10 000 mAh | USB | 3–4 Tage | Manche Powerbanks schalten bei weniger als 50–100 mA Last ab |
| 5-V-Netzteil mit 2 A | USB oder 5V-Pin | Unbegrenzt | Beste Wahl für den Dauerbetrieb zu Hause |

Das Fazit aus der Tabelle ist klar: Ein batteriebetriebener Uno-Mülleimer ist ideal für Präsentationen und Vorführungen, aber für einen Eimer, der monatelang zu Hause läuft, nimm ein 5-V-Netzteil. Wie du den Abwärtswandler zwischen zwei 18650-Zellen und dem Servo einstellst, steht im Artikel LM2596. Willst du die Laufzeit wirklich verlängern, nimm statt des Uno einen Arduino Pro Mini ohne Power-LED, schick den Prozessor zwischen den Messungen in den Schlafmodus und lass die LEDs nur kurz leuchten, wenn der Deckel aufgeht; mit diesen Maßnahmen sinkt der Durchschnittsstrom auf 15–20 mA, und dieselben Batterien halten 4–5-mal so lange. Die Unterschiede der Versorgungswege (USB, Hohlstecker, VIN, 5V-Pin) erklärt der Artikel Arduino richtig mit Strom versorgen.
Füllstandsmeldung mit dem ESP32 (Telegram)
Um die LED zu sehen, wenn der Eimer voll ist, musst du hingehen; ein ESP32 dagegen kann diese Information direkt aufs Handy schicken. Am einfachsten geht das mit Telegram, weil es kostenlos ist und du in wenigen Minuten einen eigenen Bot einrichten kannst. Schreib in Telegram an @BotFather, leg mit dem Befehl /newbot einen Bot an und notiere den Token, den du bekommst. Schick deinem Bot dann einmal /start; ein Bot kann niemandem schreiben, der noch nie mit ihm gesprochen hat. Deine Chat-ID findest du heraus, indem du einem Bot wie @userinfobot schreibst oder die getUpdates-Anfrage der Bot-API nutzt. Der Code braucht keine zusätzliche Bibliothek: Er schickt mit dem eingebauten HTTPClient des ESP32 eine Anfrage an die sendMessage-Adresse von Telegram.
Der Code nutzt denselben Zustandsautomaten wie die Uno-Version; nur das Servosignal entsteht statt mit der Servo-Bibliothek mit LEDC, der PWM-Einheit des ESP32. Bei einem 50-Hz-Signal entspricht ein Puls von 0,544–2,4 ms den Winkeln 0–180 Grad, und das Abschalten der Pulse (ledcWrite mit 0) gibt das Servo frei. Der Code ist für Version 3.x des ESP32-Arduino-Cores geschrieben; Version 2.x kennt die Funktion ledcAttach() nicht, aktualisiere den Core also im Boardverwalter.

1// ESP32 Arduino çekirdeği 3.x / ESP32 Arduino core 3.x
2#include <WiFi.h>
3#include <WiFiClientSecure.h>
4#include <HTTPClient.h>
5
6const char* WIFI_SSID = "WIFI_ADIN"; // kendi ağın / your network
7const char* WIFI_PASS = "WIFI_SIFREN";
8const char* BOT_TOKEN = "123456789:ABCdefGhIJKlmNoPQRstuVWxyz"; // BotFather'dan / from BotFather
9const char* CHAT_ID = "123456789"; // senin sohbet kimliğin / your chat ID
10
11// Pinler: ECHO'lar 1 kΩ / 2 kΩ bölücüyle / Pins: ECHOs via a 1 kΩ / 2 kΩ divider
12const int HAND_TRIG = 18, HAND_ECHO = 19;
13const int FILL_TRIG = 25, FILL_ECHO = 26;
14const int SERVO_PIN = 13;
15const int LED_GREEN = 27, LED_YELLOW = 32, LED_RED = 33;
16const int BUZZER = 23;
17
18const int HAND_CM = 20;
19const int LID_CLOSED = 10, LID_OPEN = 110;
20const unsigned long OPEN_TIME = 4000, CLOSE_STEP_MS = 20;
21const int EMPTY_CM = 35, FULL_CM = 8;
22const int NOTIFY_PCT = 85; // bu yüzdede bildir / notify at this percent
23const int RESET_PCT = 30; // bunun altına inince "boşaltıldı" / below this: "emptied"
24
25enum LidState { CLOSED, OPEN, CLOSING };
26LidState state = CLOSED;
27unsigned long lastHand = 0, lastHandPing = 0, closeStart = 0, closedAt = 0, lastFill = 0, lastTry = 0;
28int fillPercent = 0;
29byte handHits = 0;
30bool notified = false, servoOn = false, fullWarned = false;
31
32void servoWrite(int angle) { // 0–180° → 544–2400 µs, 50 Hz
33 uint32_t us = map(angle, 0, 180, 544, 2400);
34 ledcWrite(SERVO_PIN, us * 16384 / 20000); // 14 bit, 20 ms periyot / 14-bit, 20 ms period
35 servoOn = true;
36}
37
38void servoRelease() { // darbe yok: servo serbest kalır / no pulses: the servo relaxes
39 ledcWrite(SERVO_PIN, 0);
40 servoOn = false;
41}
42
43int pingCm(int trig, int echo) {
44 digitalWrite(trig, LOW); delayMicroseconds(2);
45 digitalWrite(trig, HIGH); delayMicroseconds(10);
46 digitalWrite(trig, LOW);
47 unsigned long t = pulseIn(echo, HIGH, 30000UL);
48 return t == 0 ? 999 : (int)(t / 58); // 999 = yankı yok / 999 = no echo
49}
50
51int medianCm(int trig, int echo) { // 5 ölçümün ortancası / median of 5 readings
52 int v[5];
53 delay(60); // önce el sensörünün yankısı sönsün / first let the hand sensor's echo die out
54 for (int i = 0; i < 5; i++) {
55 v[i] = pingCm(trig, echo);
56 delay(60);
57 }
58 for (int i = 0; i < 4; i++)
59 for (int j = i + 1; j < 5; j++)
60 if (v[j] < v[i]) { int tmp = v[i]; v[i] = v[j]; v[j] = tmp; }
61 return v[2];
62}
63
64int toPercent(int cm) {
65 long p = (long)(EMPTY_CM - cm) * 100 / (EMPTY_CM - FULL_CM);
66 return (int)constrain(p, 0L, 100L);
67}
68
69void showLevel() {
70 digitalWrite(LED_GREEN, fillPercent < 50);
71 digitalWrite(LED_YELLOW, fillPercent >= 50 && fillPercent < 80);
72 digitalWrite(LED_RED, fillPercent >= 80);
73}
74
75void beep(int n) {
76 for (int i = 0; i < n; i++) {
77 digitalWrite(BUZZER, HIGH); delay(120);
78 digitalWrite(BUZZER, LOW); delay(120);
79 }
80}
81
82String urlEncode(const String& s) { // Türkçe harfler ve boşluk için / for Turkish letters and spaces
83 const char* hex = "0123456789ABCDEF";
84 String out;
85 for (size_t i = 0; i < s.length(); i++) {
86 uint8_t c = s[i];
87 if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
88 out += (char)c;
89 } else {
90 out += '%';
91 out += hex[c >> 4];
92 out += hex[c & 15];
93 }
94 }
95 return out;
96}
97
98bool sendTelegram(const String& text) {
99 if (WiFi.status() != WL_CONNECTED) return false;
100 WiFiClientSecure client;
101 client.setInsecure(); // sertifika denetimi yok, hobi projesi için yeterli / no certificate check, fine for a hobby project
102 HTTPClient http;
103 String url = String("https://api.telegram.org/bot") + BOT_TOKEN +
104 "/sendMessage?chat_id=" + CHAT_ID + "&text=" + urlEncode(text);
105 if (!http.begin(client, url)) return false;
106 int code = http.GET(); // 200 = gönderildi / 200 = sent
107 Serial.printf("Telegram HTTP %d\n", code);
108 http.end();
109 return code == 200;
110}
111
112void openLid() {
113 servoWrite(LID_OPEN);
114 state = OPEN;
115 if (fillPercent >= 90) beep(2);
116}
117
118void setup() {
119 Serial.begin(115200);
120 pinMode(HAND_TRIG, OUTPUT); pinMode(HAND_ECHO, INPUT);
121 pinMode(FILL_TRIG, OUTPUT); pinMode(FILL_ECHO, INPUT);
122 pinMode(LED_GREEN, OUTPUT); pinMode(LED_YELLOW, OUTPUT); pinMode(LED_RED, OUTPUT);
123 pinMode(BUZZER, OUTPUT);
124 ledcAttach(SERVO_PIN, 50, 14); // 50 Hz, 14 bit servo sinyali / 50 Hz, 14-bit servo signal
125 servoWrite(LID_CLOSED);
126 delay(500);
127 servoRelease();
128 WiFi.mode(WIFI_STA);
129 WiFi.begin(WIFI_SSID, WIFI_PASS); // arka planda bağlanır; kutu Wi-Fi olmadan da çalışır / connects in the background; the bin works without Wi-Fi too
130 closedAt = millis();
131 showLevel();
132}
133
134void loop() {
135 unsigned long now = millis();
136
137 if (now - lastHandPing >= 60) { // el sensörü / hand sensor
138 lastHandPing = now;
139 int d = pingCm(HAND_TRIG, HAND_ECHO);
140 if (d >= 2 && d <= HAND_CM) {
141 if (handHits < 2) handHits++;
142 } else {
143 handHits = 0;
144 }
145 if (handHits >= 2) {
146 lastHand = now;
147 if (state != OPEN) openLid();
148 }
149 }
150
151 if (state == OPEN && now - lastHand >= OPEN_TIME) {
152 state = CLOSING;
153 closeStart = now;
154 }
155
156 if (state == CLOSING) { // yavaş kapanma / slow close
157 int moved = (now - closeStart) / CLOSE_STEP_MS;
158 int range = abs(LID_OPEN - LID_CLOSED);
159 if (moved >= range) {
160 servoWrite(LID_CLOSED);
161 state = CLOSED;
162 closedAt = now;
163 } else {
164 servoWrite(LID_OPEN > LID_CLOSED ? LID_OPEN - moved : LID_OPEN + moved);
165 }
166 }
167
168 if (state == CLOSED && servoOn && now - closedAt > 400) servoRelease();
169
170 if (state == CLOSED && now - closedAt > 1000 && now - lastFill >= 3000) {
171 lastFill = now;
172 int cm = medianCm(FILL_TRIG, FILL_ECHO);
173 if (cm < 999) fillPercent = toPercent(cm);
174 showLevel();
175 Serial.printf("Doluluk / Fill: %d cm = %%%d\n", cm, fillPercent);
176
177 if (fillPercent >= 90 && !fullWarned) { fullWarned = true; beep(3); }
178 if (fillPercent < 70) fullWarned = false;
179
180 // Bildirim: kapak kapalıyken, en fazla dakikada bir dene / Notify: only with the lid closed, retry at most once a minute
181 if (!notified && fillPercent >= NOTIFY_PCT && (lastTry == 0 || now - lastTry > 60000UL)) {
182 lastTry = now;
183 notified = sendTelegram("Çöp kutusu %" + String(fillPercent) + " dolu, boşaltma zamanı. / The bin is " +
184 String(fillPercent) + "% full.");
185 }
186 if (notified && fillPercent < RESET_PCT) { // boşaltıldı: yeniden kur / emptied: re-arm
187 notified = false;
188 lastTry = 0;
189 sendTelegram("Çöp kutusu boşaltıldı. / The bin has been emptied.");
190 }
191 }
192}Die Meldung wird einmal verschickt, wenn der Eimer 85 % erreicht, und mit einer „geleert“-Nachricht wieder scharf geschaltet, sobald der Füllstand unter 30 % fällt – so vibriert dein Handy nicht alle 3 Sekunden. Gibt es kein WLAN oder schlägt das Senden fehl, versucht es der Code einmal pro Minute erneut, und der Eimer arbeitet in der Zwischenzeit normal weiter. Das Senden einer Nachricht dauert wegen der verschlüsselten Verbindung (TLS) 1–3 Sekunden, deshalb sendet der Code nur bei geschlossenem Deckel. setInsecure() verbindet sich, ohne das Zertifikat des Servers zu prüfen; für ein Hobbyprojekt reicht das, aber denk daran, dass der Token geheim ist, und lösch ihn, bevor du deinen Code teilst. Siehst du im seriellen Monitor „Telegram HTTP 200“, ist die Nachricht raus; 401 bedeutet einen falschen Token, 400 eine falsche Chat-ID und 403, dass du dem Bot noch kein /start geschickt hast. Einen vollständigen Bot, der auf Befehle reagiert, findest du in der Anleitung ESP32-Telegram-Bot.
Automatischer Mülleimer mit dem Raspberry Pi Pico (MicroPython)
Auf dem Raspberry Pi Pico baust du dieselbe Logik mit MicroPython. Die Funktion time_pulse_us() arbeitet wie pulseIn() beim Arduino; da sie ohne Echo einen negativen Wert zurückgibt, werten wir das als 999, also „kein Echo“. Für das Servo werden 50-Hz-PWM und duty_ns() verwendet, das die Pulsbreite in Nanosekunden erwartet; duty_ns(0) stoppt die Pulse und gibt das Servo frei. Zeitvergleiche laufen über ticks_ms() und ticks_diff(); ticks_diff liefert auch dann die richtige Differenz, wenn der Zähler überläuft. Speicherst du den Code mit Thonny als main.py auf dem Pico, startet er bei jedem Einschalten von selbst. Mit einem Pico W kannst du die Methode aus dem Artikel Pico-W-Webserver ergänzen, um den Füllstand über WLAN anzuzeigen.
1from machine import Pin, PWM, time_pulse_us
2import time
3
4# Pinler: ECHO'lar 1 kΩ / 2 kΩ bölücüyle / Pins: ECHOs via a 1 kΩ / 2 kΩ divider
5hand_trig, hand_echo = Pin(2, Pin.OUT), Pin(3, Pin.IN)
6fill_trig, fill_echo = Pin(4, Pin.OUT), Pin(5, Pin.IN)
7green, yellow, red = Pin(10, Pin.OUT), Pin(11, Pin.OUT), Pin(12, Pin.OUT)
8buzzer = Pin(13, Pin.OUT)
9servo = PWM(Pin(15))
10servo.freq(50) # servo için 50 Hz / 50 Hz for the servo
11
12HAND_CM = 20
13LID_CLOSED, LID_OPEN = 10, 110
14OPEN_MS, CLOSE_STEP_MS = 4000, 20
15EMPTY_CM, FULL_CM = 35, 8
16
17def ping_cm(trig, echo):
18 trig.low(); time.sleep_us(2)
19 trig.high(); time.sleep_us(10) # 10 µs tetik / 10 µs trigger
20 trig.low()
21 t = time_pulse_us(echo, 1, 30000) # yankı yoksa negatif / negative if there is no echo
22 return 999 if t < 0 else t // 58
23
24def median_cm(trig, echo): # 5 ölçümün ortancası / median of 5 readings
25 v = []
26 time.sleep_ms(60) # önce el sensörünün yankısı sönsün / first let the hand sensor's echo die out
27 for _ in range(5):
28 v.append(ping_cm(trig, echo))
29 time.sleep_ms(60)
30 v.sort()
31 return v[2]
32
33def servo_write(angle): # 0–180° → 544–2400 µs
34 servo.duty_ns((544 + angle * 1856 // 180) * 1000)
35
36def servo_release(): # darbe yok: servo serbest / no pulses: servo relaxes
37 servo.duty_ns(0)
38
39def show_level(p):
40 green.value(p < 50)
41 yellow.value(50 <= p < 80)
42 red.value(p >= 80)
43
44def beep(n):
45 for _ in range(n):
46 buzzer.on(); time.sleep_ms(120)
47 buzzer.off(); time.sleep_ms(120)
48
49state = "closed"
50fill, hits, warned, released = 0, 0, False, False
51last_hand = last_ping = close_start = last_fill = time.ticks_ms()
52servo_write(LID_CLOSED)
53time.sleep_ms(500)
54servo_release()
55released = True
56closed_at = time.ticks_ms()
57show_level(fill)
58
59while True:
60 now = time.ticks_ms()
61
62 if time.ticks_diff(now, last_ping) >= 60: # el sensörü, 60 ms'de bir / hand sensor, every 60 ms
63 last_ping = now
64 d = ping_cm(hand_trig, hand_echo)
65 hits = min(hits + 1, 2) if 2 <= d <= HAND_CM else 0
66 if hits >= 2:
67 last_hand = now
68 if state != "open": # kapanırken de yeniden açar / also reopens while closing
69 servo_write(LID_OPEN)
70 state, released = "open", False
71 if fill >= 90:
72 beep(2)
73
74 if state == "open" and time.ticks_diff(now, last_hand) >= OPEN_MS:
75 state, close_start = "closing", now
76
77 if state == "closing": # yavaş kapanma / slow close
78 moved = time.ticks_diff(now, close_start) // CLOSE_STEP_MS
79 if moved >= abs(LID_OPEN - LID_CLOSED):
80 servo_write(LID_CLOSED)
81 state, closed_at = "closed", now
82 else:
83 servo_write(LID_OPEN - moved if LID_OPEN > LID_CLOSED else LID_OPEN + moved)
84
85 if state == "closed" and not released and time.ticks_diff(now, closed_at) > 400:
86 servo_release()
87 released = True
88
89 if state == "closed" and time.ticks_diff(now, closed_at) > 1000 and time.ticks_diff(now, last_fill) >= 3000:
90 last_fill = now
91 cm = median_cm(fill_trig, fill_echo)
92 if cm < 999: # yankı yoksa eski değer kalır / keep the old value if no echo
93 fill = max(0, min(100, (EMPTY_CM - cm) * 100 // (EMPTY_CM - FULL_CM)))
94 show_level(fill)
95 print("Doluluk / Fill:", cm, "cm =", fill, "%")
96 if fill >= 90 and not warned:
97 warned = True
98 beep(3)
99 if fill < 70:
100 warned = False
101
102 time.sleep_ms(5)Tipps für die Präsentation als Schulprojekt
Der automatische Mülleimer ist auf Wissenschaftsmessen und Schulausstellungen wie TÜBİTAK 4006 ein sehr häufiges Projekt; die Jury interessiert sich deshalb weniger dafür, dass der Deckel auf- und zugeht, als dafür, wie du ein Problem beschrieben, wie du gemessen und was du daraus geschlossen hast. Was dein Projekt von einer gewöhnlichen Vorführung zu einer wissenschaftlichen Arbeit macht, sind Messungen: bei wie vielen Versuchen er sich geöffnet hat, wie schnell, wie genau er den Füllstand geschätzt hat, wie viele Stunden die Batterien gehalten haben. Du kannst deine Präsentation in dieser Reihenfolge aufbauen:
- Problem: Den Deckel mit der Hand anzufassen überträgt Keime, überquellende Eimer sorgen für Geruch und Insekten. Starte mit echten Daten, indem du zählst, wie viele Leute in einer Pause den Deckel eines Mülleimers in deiner Schule anfassen.
- Ziel: Ein Eimer, der sich berührungslos öffnet, warnt, wenn er voll ist, und auf Wunsch dein Handy benachrichtigt.
- Funktionsprinzip: Zeichne ein einziges Blockschaltbild mit Sensor, Arduino, Servo, LEDs, Summer, ESP32 und Handy. Erkläre die Schallgeschwindigkeit und die Formel „geteilt durch 58“ auf einer Folie.
- Material und Kosten: Zeig Teileliste und Servoauswahl als Tabelle, zusammen mit der Drehmomentrechnung.
- Live-Vorführung: Halt die Hand hin und lass den Deckel aufgehen. Füll den Eimer mit Papier und zeig, wie die LED von Grün über Gelb zu Rot wechselt; wirf die Telegram-Nachricht auf deinem Handy an die Leinwand.
- Testergebnisse: Füll die Tabelle unten mit deinen eigenen Messwerten und stell die Ergebnisse als Diagramm dar.
- Fazit und Verbesserungen: Sag ehrlich, was gut funktioniert hat, was nicht und was du in der nächsten Version ändern würdest.
| Test | Durchführung | Zu notierender Wert |
|---|---|---|
| Erkennungsentfernung | Halte die Hand in 5, 10, 15, 20 und 25 cm Abstand, je 10-mal | Bei wie vielen Versuchen sich der Deckel je Entfernung geöffnet hat (z. B. 10/10) |
| Reaktionszeit | Mit dem Handy in Zeitlupe (240 fps) filmen | Zeit zwischen Erscheinen der Hand und erster Bewegung des Deckels (ms) |
| Fehlauslösungen | 20-mal am Eimer vorbeigehen | Anzahl ungewollter Öffnungen |
| Genauigkeit des Füllstands | Eimer mit dem Lineal abgemessen zu einem Viertel, zur Hälfte und zu drei Vierteln mit Papier füllen | Unterschied zwischen echtem Füllstand und gemessenem Prozentwert |
| Schließgeschwindigkeit | CLOSE_STEP_MS auf 5, 20 und 40 ms setzen | Schließzeit und mit dem Handy gemessene Lautstärke |
| Batterielaufzeit | Mit vollen Batterien betreiben, stündlich die Spannung messen | Wie viele Stunden der Eimer lief |
Ideen zur Erweiterung
- Eimer mit Display: Zeig den Füllstand in Prozent und als Balken auf einem OLED-Display.
- Gesprochenes Dankeschön: Spiel bei jedem Öffnen mit einem DFPlayer Mini ein kurzes „Danke“ ab.
- Gewichtsmessung: Setz eine Wägezelle mit HX711 unter den Eimer, miss, wie viele Kilo Müll drin sind, und kombiniere Volumen und Gewicht.
- Recycling-Sortierer: Leite den Abfall mit einem Farbsensor und einem zweiten Servo in zwei Fächer.
- Geruchswarnung: Erkenne mit einem MQ-135, wenn die Luft im Eimer schlecht wird.
- Solarbetriebener Eimer für draußen: Verzichte ganz auf das Kabel – mit einem kleinen Solarpanel, einer 18650-Zelle, einem TP4056-Lademodul und einem 5-V-Aufwärtswandler.
- Füllstandsprotokoll: Schreib die Messwerte mit dem ESP32 in Google Tabellen und zeig im Diagramm, an welchen Tagen der Eimer am schnellsten voll wird.
- Für Schule oder Wohnanlage: Überwache Eimer auf dem Gelände, die das WLAN nicht erreicht, mit einem LoRa-Modul aus Hunderten Metern Entfernung.
Häufige Fehler und Lösungen
| Symptom | Mögliche Ursache | Lösung |
|---|---|---|
| Der Deckel geht von selbst auf und zu | Der Sensor sieht eine nahe Wand oder vorbeigehende Menschen | HAND_CM verringern, Sensor nach oben neigen, 30 cm davor frei lassen |
| Der Arduino startet beim Öffnen neu | Der Anlaufstrom des Servos zieht die 5-V-Leitung herunter | 470-µF-Kondensator, starkes Servo separat versorgen, gemeinsamer GND |
| Das Servo brummt und wird bei geschlossenem Deckel warm | LID_CLOSED drückt den Deckel auf den Rand | Winkel um 2–3 Grad zurücknehmen; die Zeile mit detach() nicht löschen |
| Der Deckel bleibt auf halbem Weg stehen oder zittert | Zu wenig Drehmoment oder versetzte Achsen | Stärkeres Servo, leichterer Deckel, Gegengewicht; Servowelle auf das Scharnier ausrichten |
| Füllstand hoch, obwohl der Eimer leer ist | Beutelrand vor dem Sensor oder Wandechos in einem schmalen Eimer | Beutel nach außen umschlagen, Sensor mittig setzen, EMPTY_CM neu messen |
| Der Füllstand ändert sich nie | Der Sensor bekommt kein Echo (999) oder das ECHO-Kabel hat sich gelöst | Rohwert in cm im seriellen Monitor beobachten, Verkabelung prüfen |
| Am ESP32 ist die Entfernung immer 999 oder unsinnig | ECHO-Teiler fehlt, ist falsch oder Leitungen vertauscht | 1-kΩ-/2-kΩ-Teiler und Reihenfolge von TRIG und ECHO prüfen |
| Der ESP32 startet ständig neu | WLAN und Servo ziehen gleichzeitig Strom, schwaches USB-Kabel | Kurzes, gutes Kabel verwenden, Servo aus eigener 5-V-Quelle versorgen |
| Keine Telegram-Nachricht kommt an | Falscher Token oder falsche Chat-ID, /start nicht an den Bot geschickt | HTTP-Code im seriellen Monitor lesen (401, 400, 403) |
Weil ein automatischer Mülleimer Sensorabfrage, Servosteuerung, Timing und Stromplanung in einem Projekt vereint, ist er einer der unterhaltsamsten Wege, Arduino zu lernen. Hast du ihn einmal gebaut, wird es viel leichter, mit denselben Teilen einen Parksensor oder eine automatische Tür zu bauen. In der BlueGrays-App findest du Schritt-für-Schritt-Projekte zur Entfernungsmessung mit dem HC-SR04, zur Servosteuerung, zum Summer, einen ESP32-Parksensor und eine Bewegungsalarmanlage mit Telegram-Benachrichtigung – jeweils mit Schaltplan und fertigem Code.
Häufige Fragen
Welche Bauteile brauche ich für einen automatischen Mülleimer mit Arduino?
Für die Grundversion reichen ein Arduino Uno oder Nano, ein HC-SR04-Ultraschallsensor, ein zum Deckelgewicht passendes Servo (SG90 für einen leichten Deckel, MG996R für einen echten Eimerdeckel), Jumperkabel und ein 5-V-Netzteil. Für die Füllstandsmessung kommen ein zweiter HC-SR04, drei LEDs mit 220-Ω-Widerständen und ein aktiver Summer dazu. Willst du Benachrichtigungen aufs Handy, nimm einen ESP32 statt des Arduino.
Wie funktioniert ein Mülleimer mit Sensor?
Der Ultraschallsensor vorne am Eimer sendet eine Schallwelle aus und misst, wie lange das Echo zurück braucht; erkennt er eine Hand näher als 20 cm, öffnet der Arduino den Deckel mit einem Servomotor. Ein paar Sekunden nachdem die Hand weg ist, schließt sich der Deckel langsam. Ein zweiter Sensor unter dem Deckel misst die Höhe des Mülls und zeigt den Füllstand mit LEDs und einem Summer an.
Warum hebt mein Servo den Mülleimerdeckel nicht an?
Meist ist das Drehmoment des Servos für den Deckel zu gering. Das nötige Drehmoment ist das Gewicht des Deckels mal der Abstand seines Schwerpunkts vom Scharnier; ein 120 g schwerer, 25 cm langer Deckel braucht etwa 1,5 kg·cm, was an der Grenze eines SG90 liegt. Wähl ein Servo für das Doppelte deiner Rechnung, mach den Deckel leichter oder bau ein Gegengewicht an und sorg dafür, dass die Servoversorgung genug Strom liefert.
Kann ich statt des HC-SR04 einen IR-Sensor verwenden?
Ja, ein IR-Hindernissensor wie der FC-51 erkennt eine Hand auf 2–30 cm, und der Code ist sogar einfacher, weil er nur „da“ oder „nicht da“ meldet. Allerdings wird er von Sonnenlicht beeinflusst und erkennt dunkle Oberflächen später. Ein PIR-Sensor eignet sich nicht für einen Mülleimerdeckel, weil er bei jedem auslöst, der vorbeigeht.
Wie lange läuft ein Arduino-Mülleimer mit Batterien?
Ein Mülleimer mit Arduino Uno zieht im Schnitt 70–80 mA und läuft daher mit 6 AA-Batterien etwa einen Tag, mit zwei 18650-Zellen etwa anderthalb Tage und mit einer 10 000-mAh-Powerbank 3–4 Tage. Für den Dauerbetrieb wird ein 5-V-Netzteil empfohlen. Ein Pro Mini, der Schlafmodus und LEDs, die nur bei Bedarf leuchten, können die Laufzeit um das 4- bis 5-Fache verlängern.
Wie bekomme ich eine Nachricht aufs Handy, wenn der Mülleimer voll ist?
Der ESP32 verbindet sich mit dem WLAN und schickt, sobald der Füllstand den eingestellten Prozentwert überschreitet, eine Anfrage an die Telegram-Bot-API, die dir eine Nachricht aufs Handy liefert. Dafür brauchst du einen Bot-Token von BotFather und deine eigene Chat-ID. Damit dieselbe Warnung nicht immer wieder kommt, wartet der Code, bis der Eimer geleert wurde.
Projekte, die du danach bauen kannst
Alle Projekte ansehen
HC-SR04 Abstandsmessung (serielle Anzeige)
Der Sensor HC-SR04 funktioniert wie eine Fledermaus, die im Dunkeln ihren Weg findet: Er sendet einen Ton, den du nicht hörst, lauscht auf das Echo und misst, wie weit der Gegenstand vor ihm entfernt

Verwendung des Summers
Hast du dich mal gefragt, wie eine Türklingel oder ein Alarm seinen Ton erzeugt? Der Arduino lässt einen kleinen Summer in kurzen Abständen piepen: piep, Pause, piep, Pause. Du siehst, wie Strom zu Kl

Verwendung von Servomotoren
Der Arm eines kleinen Servomotors dreht sich nacheinander in drei Stellungen: ganz an den Anfang, genau in die Mitte und ganz ans Ende, mit jeweils zwei Sekunden Pause. Der Arduino sagt dem Motor, bei

Parksensor mit HC-SR04
Eine Mini-Ausgabe der Einparkhilfe im Auto: Je näher etwas kommt, desto schneller piept es. Der Abstandssensor misst das Hindernis vor ihm; ist es weiter als 20 Zentimeter entfernt, leuchtet die grüne

ESP32 HC-SR04 Ultraschall-Entfernungsmesser
Der Ultraschallsensor HC-SR04 berechnet die Entfernung, indem er die Zeit misst, die die von ihm ausgesendete Schallwelle benötigt, um auf ein Objekt zu treffen und zurückzukehren (Time of Flight). In diesem Projekt senden wir mit dem ESP32 einen 10-Mikrosekunden-Impuls an den TRIG-Pin und lesen die vom ECHO-Pin kommende Echozeit mit `pulseIn()`. Die berechnete Entfernung geben wir in cm auf einem 16x2-I2C-LCD aus. Da der HC-SR04 mit 5 V betrieben wird, ist ein **Spannungsteiler** am ECHO-Pin zw

ESP32-Servomotor (Steuerung über Potentiometer)
In diesem Projekt steuern wir einen SG90-Servomotor mit einem Potentiometer. Wir stellen den Servowinkel ein, indem wir den vom Potentiometer gelesenen Analogwert auf den Bereich von 0 bis 180 Grad abbilden. Die ESP32Servo-Bibliothek erzeugt Servosignale an geeigneten GPIO-Pins des ESP32, die als Ausgang verwendet werden können; in diesem Projekt wird GPIO 18 verwendet. Dies ist eine grundlegende Steuerungsmethode für Roboterarme, Kamera-Gimbals und automatische Türmechanismen.

ESP32-Parksensor (HC-SR04 + 5-LED-Leiste + Summer + OLED)
Die ESP32-Version des Parkhilfesensors für Autos! Mit dem Ultraschallsensor HC-SR04 messen wir den Abstand zum Objekt vor dem Fahrzeug. Je weiter sich das Objekt nähert, desto mehr leuchten die 5 LEDs der Leiste auf (grün → gelb → rot) und die Intervalle der Summer-Signaltöne werden kürzer. Wenn das Objekt sehr nahe kommt (unter 10 cm), ertönt ein Daueralarm und alle LEDs leuchten. Auf dem SSD1306-OLED-Display werden der aktuelle Wert in cm in großer Schrift, ein Statustext (NICHT / VORSICHT / N

ESP32 Bewegungsalarmsystem (PIR + Buzzer + LED + Telegram)
Ein intelligentes Sicherheitsalarmsystem, das Buzzer und LED aktiviert, sobald der PIR-Sensor Bewegung erkennt, und gleichzeitig eine sofortige Benachrichtigung per Telegram an den Besitzer sendet. Ein 30-sekündiger Cooldown begrenzt die Häufigkeit wiederholter Alarme; dieser Mechanismus überprüft weder die Gültigkeit der PIR-Erkennungen noch verhindert er Fehlauslösungen. Geeignet für Heimsicherheit, Garagenüberwachung, Lagerräume oder Arbeitszimmer. Mit ESP32-CAM sind Foto-Benachrichtigungen m
Weitere Artikel
BlueGrays-App
Diese Schaltung Schritt für Schritt auf dem Handy nachbauen
Schaltplan, Code und Teileliste auf einem Bildschirm. Über 160 Arduino-, ESP32- und Pico-Projekte zum Nachbauen.
Mit dem Handy scannen
Richte die Kamera auf den Code, dann öffnet sich der App-Store deines Handys.
oder im Store öffnen

