Basurero inteligente con Arduino: sensor HC-SR04, servo y código
Haz un basurero inteligente con Arduino: detección de mano con HC-SR04, tapa con servo y cierre lento, nivel de llenado con LED y aviso por Telegram con ESP32.
Un basurero inteligente con Arduino (también llamado papelera o cubo de basura automático) es un proyecto de automatización sencillo pero muy didáctico que abre la tapa sin que la toques, y es uno de los proyectos escolares más populares. Un sensor ultrasónico HC-SR04 en el frente del basurero detecta la mano que se acerca, Arduino abre la tapa con un servomotor y, unos segundos después de que retiras la mano, la cierra despacio en lugar de dejarla caer de golpe. Un segundo HC-SR04 bajo la tapa mide la altura de la basura: a medida que el basurero se llena se encienden por turnos los LED verde, amarillo y rojo, y cuando está completamente lleno suena un zumbador.
En esta guía partimos de la lista de materiales y vemos paso a paso dónde montar los sensores, cómo calcular el torque del servo, cómo unir la tapa a la bisagra, la calibración y el código completo que controla a la vez el tiempo de espera y el cierre lento. Después calculamos cuánto dura realmente el proyecto con pilas, enviamos un aviso de llenado con ESP32 a tu móvil como mensaje de Telegram y escribimos el mismo proyecto para la Raspberry Pi Pico en MicroPython. Al final también encontrarás un guion para presentar el proyecto escolar y una tabla de pruebas que puedes rellenar con tus propias mediciones.

¿Cómo funciona un basurero inteligente?
El proyecto es un bucle que se repite decenas de veces por segundo. Arduino mide con el sensor de mano cada 60 milisegundos. Si aparece un objeto a menos de 20 cm en dos lecturas seguidas, decide que es una mano y manda el servo a la posición abierta. Mientras la mano siga ahí, el tiempo de espera se reinicia en cada lectura; 4 segundos después de que la mano se va, la tapa se cierra despacio a unos 50 grados por segundo. Si la mano vuelve mientras se cierra, la tapa se abre de nuevo al instante. Un segundo después de cerrarse, el sensor de llenado toma 5 lecturas, usa la mediana como distancia a la superficie de la basura y la convierte en porcentaje. El código usa una pequeña máquina de estados en la que la tapa siempre está en uno de tres estados: CERRADA (CLOSED), ABIERTA (OPEN) y CERRÁNDOSE (CLOSING).
| Pieza | Función | Nota |
|---|---|---|
| Arduino Uno (o Nano) | El cerebro: mide, decide y controla el servo y los LED | También sirven ESP32 o Pico |
| 2 × HC-SR04 | Uno detecta la mano, el otro mide el llenado | 5 V, unos 15 mA al medir |
| Servo SG90, MG90S o MG996R | Abre y cierra la tapa | Se elige según el peso de la tapa (cálculo más abajo) |
| 3 LED (verde, amarillo, rojo) y 3 × 220 Ω | Indicador de llenado | Una resistencia por LED |
| Zumbador activo | Aviso de lleno | Un zumbador pasivo necesita tone() |
| Bisagra, tornillos, silicona caliente | Sostiene la tapa, fija el brazo del servo | El peso de la tapa debe soportarlo la bisagra, no el servo |
| Condensador de 470 µF | Suaviza la corriente de arranque del servo | Entre + y − del servo, cuidado con la polaridad |
| Alimentación | Adaptador de 5 V, power bank o pilas | Cálculo de duración más abajo |
| ESP32 (opcional) | Aviso de llenado en el móvil | Necesita Wi-Fi |




¿Qué sensor usar para detectar la mano?
Hay varios sensores que pueden abrir un basurero, pero no todos sirven igual de bien. Lo que realmente necesitas es notar una mano que se acerca a 15–25 cm del basurero e ignorar a una persona que solo pasa por delante. Como el HC-SR04 da la distancia en centímetros, puedes ajustar el umbral en el código exactamente como quieras; por eso lo usan la mayoría de los proyectos de basurero inteligente. Estos son los puntos fuertes y débiles de las alternativas:
| Sensor | Cómo detecta | Para un basurero |
|---|---|---|
| HC-SR04 (ultrasónico) | Eco de sonido, 2–400 cm, cono estrecho | Umbral ajustable por código; la opción más equilibrada |
| Sensor IR de obstáculos (FC-51) | Luz infrarroja reflejada, unos 2–30 cm | Barato y rápido; la luz del sol y los guantes oscuros pueden confundirlo |
| PIR (HC-SR501) | Movimiento del calor corporal, hasta 7 m, ángulo amplio | Se activa con cualquiera que pase y se queda activo unos segundos; no es adecuado |
| APDS-9960 | Proximidad y gestos de la mano a corta distancia | Una versión vistosa que se abre con un gesto; necesita I2C y 3,3 V |
Cómo funciona un sensor PIR lo explicamos en el artículo Sensor de movimiento PIR, y una versión controlada por gestos en la guía del sensor de gestos APDS-9960. Así funciona el HC-SR04 en pocas palabras: emite una breve ráfaga de sonido de 40 kHz y entrega el tiempo de vuelo del eco como un pulso en el pin ECHO. Como el sonido viaja por el aire a unos 343 m/s a 20 °C y el camino es de ida y vuelta, dividir el tiempo en microsegundos entre 58 da la distancia en centímetros. Los detalles están en el artículo Sensor de distancia HC-SR04.
Ubicación de los sensores
El sensor de mano va en el cuerpo del basurero, en la parte alta del frente, no en la tapa, porque al abrirse la tapa el sensor giraría con ella y apuntaría al techo. Inclinarlo 10–20 grados hacia arriba le permite captar una mano que se acerca por encima del basurero y no las piernas de quien pasa. Si el basurero está junto a una pared o un mueble, no debe haber ninguna superficie fija a menos de 30 cm en la dirección a la que mira el sensor; si no, la confundirá con una mano y la tapa nunca se cerrará.
El sensor de llenado se pega bajo la tapa, cerca del centro y apuntando justo hacia abajo. En esa posición solo mide algo útil con la tapa cerrada; con la tapa abierta apunta a una pared o al techo. Por eso el código mide el llenado solo un segundo después de que la tapa se ha cerrado. Aunque el cono de sonido del sensor es estrecho (15 grados según la hoja de datos, algo más ancho en la práctica), a 35 cm de profundidad cubre una zona de 10–20 cm de diámetro; en basureros de menos de 20 cm de diámetro el sonido puede rebotar en las paredes laterales y dar lecturas falsas. Dobla el borde de la bolsa hacia fuera del basurero: un borde que cuelga hacia dentro se pone delante del sensor y hace que el basurero parezca lleno incluso vacío.
Esquema de conexión: Arduino Uno, ESP32 y Pico
La tabla siguiente coincide exactamente con todo el código de este artículo. En el Uno, el sensor de mano va a D2–D3, el de llenado a D4–D5, el servo a D9, los LED a D10–D12 y el zumbador a D8. Como la librería Servo usa el Timer1 en el Uno, desactiva la salida PWM de D9 y D10; el LED de D10 solo lo encendemos y apagamos, así que no nos afecta. Pon una resistencia de 220 Ω delante de cada LED; puedes calcular el valor para tu LED con la herramienta resistencia para LED. La distribución de pines de tu placa la tienes en la herramienta Pinout.
| Conexión | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| Sensor de mano TRIG | D2 | GPIO 18 | GP2 |
| Sensor de mano ECHO | D3 (directo) | GPIO 19, con divisor de 1 kΩ / 2 kΩ | GP3, con divisor de 1 kΩ / 2 kΩ |
| Sensor de llenado TRIG | D4 | GPIO 25 | GP4 |
| Sensor de llenado ECHO | D5 (directo) | GPIO 26, con divisor | GP5, con divisor |
| VCC de los HC-SR04 (ambos) | 5V | 5V (VIN) | VBUS |
| Señal del servo (naranja) | D9 | GPIO 13 | GP15 |
| Servo + (rojo) | 5V (SG90) o externo de 5–6 V | 5V (VIN) o externo de 5–6 V | VBUS o externo de 5–6 V |
| LED verde, amarillo y rojo (con 220 Ω) | D10, D11, D12 | GPIO 27, 32, 33 | GP10, GP11, GP12 |
| Zumbador activo (+) | D8 | GPIO 23 | GP13 |
| Todos los GND | GND | GND | GND |
Elegir el servo y calcular el torque
El problema más habitual en los basureros inteligentes es una tapa que se abre a medias y tiembla, o que no se levanta en absoluto. Casi siempre la causa es un servo demasiado débil para la tapa. Estimar el torque necesario es fácil: Torque (kg·cm) = peso de la tapa (kg) × distancia del centro de masa a la bisagra (cm). Una tapa plana y de grosor uniforme tiene su centro de masa justo en el medio; para una tapa de 24 cm de largo esa distancia es de 12 cm. El momento más difícil es el inicio de la apertura, cuando la tapa está horizontal; a medida que se levanta, el torque necesario baja y en vertical es casi cero. El torque que dan los catálogos de servos es el de bloqueo (stall), el punto en que el servo se esfuerza y se detiene; si trabaja cerca de ese límite se calienta y desgasta los engranajes. Por eso multiplica tu resultado al menos por 2.
| Tapa | Peso | Centro de masa | Torque necesario | Con margen ×2 | Recomendación |
|---|---|---|---|---|---|
| Cartón o cartón pluma de 20 × 20 cm | 50 g | 10 cm | 0,5 kg·cm | 1,0 kg·cm | SG90 |
| Tapa de plástico de basurero de 25 cm | 120 g | 12,5 cm | 1,5 kg·cm | 3,0 kg·cm | MG996R (MG90S justo) |
| Tapa de MDF o contrachapado de 30 cm | 300 g | 15 cm | 4,5 kg·cm | 9,0 kg·cm | MG996R y contrapeso |
| Servo | Torque (4,8 V / 6 V) | Engranajes | Corriente | Nota |
|---|---|---|---|---|
| SG90 | 1,8 kg·cm (solo se indica el valor a 4,8 V) | Plástico | 100–250 mA en movimiento | Puede alimentarse del pin 5V del Arduino |
| MG90S | 1,8 / 2,2 kg·cm | Metal | 150–300 mA en movimiento | Mismo tamaño que el SG90, más resistente |
| MG996R | 9,4 / 11 kg·cm | Metal | 0,5–0,9 A en movimiento, hasta 2,5 A bloqueado | Siempre con una fuente propia de 5–6 V |

En lugar de comprar un servo más fuerte, muchas veces basta con equilibrar la tapa. Si la tapa tiene un pequeño brazo que sobresale por detrás de la bisagra, unos tornillos o monedas pegados ahí compensan parte de su peso; este contrapeso puede reducir el torque necesario hasta la mitad. También podrías hacer que el brazo del servo empuje la tapa en un punto lejos de la bisagra, pero la palanca no te regala torque: cuanto más lejos de la bisagra, menos fuerza hace falta, pero el punto empujado recorre más camino, así que necesitas un brazo de servo más largo, y con un brazo más largo vuelve a subir el torque que se le exige al servo. Si quieres que la tapa se abra cerca de 90 grados, el torque necesario queda prácticamente igual que con el servo montado directamente en la bisagra. Tienes una comparación detallada de tipos de servo en los artículos ¿Qué es un servomotor? y servo MG996R.
Consejos de montaje: bisagra, brazo del servo y cables
- Que el peso lo soporte la bisagra: La tapa debe ir unida a una bisagra de verdad (la del propio basurero o una pequeña bisagra de mueble). Si apoyas la tapa solo sobre el eje del servo, todo el peso carga sobre su rodamiento y los engranajes de plástico del SG90 se rompen pronto.
- Alinea el eje del servo con el de la bisagra: Lo más fácil es atornillar el servo al borde trasero del basurero de modo que su eje quede justo al final de la línea de la bisagra, y fijar el brazo del servo a la tapa con un tornillo o silicona caliente. Si los ejes no coinciden, el servo empuja la tapa de lado cada vez que se abre.
- Sujeta bien el cuerpo del servo: No lo fijes solo con cinta de doble cara; al levantar la tapa el cuerpo gira. Usa dos tornillos en las orejas de montaje del servo o una pequeña escuadra en L.
- No aprietes la tapa contra el borde: Ajusta el ángulo de cierre en el valor en que la tapa descansa sobre el borde pero el servo no la empuja. Si el servo sigue forzando la tapa cerrada, zumba, se calienta y consume corriente sin motivo.
- Deja holgura en los cables: Los cables del sensor de llenado se mueven con la tapa. Pásalos junto a la bisagra, con largo suficiente para que no se tensen ni con la tapa totalmente abierta, y sujétalos con una brida.
- Separa la electrónica de la basura: Coloca el Arduino, las pilas y la protoboard en una cajita pegada detrás o debajo del basurero. Los líquidos o la humedad que se escapen de la bolsa no deben llegar al circuito.
- Empieza con una maqueta de cartón: Para un proyecto escolar, una tapa ligera hecha con una caja de zapatos o cartón pluma funciona sin problemas incluso con un SG90, y es un buen ejemplo para mostrar el cálculo de torque en la presentación.
Montaje y calibración paso a paso
Antes de pasar al código principal, sube el pequeño sketch de calibración de abajo. Mueve el servo al ángulo que escribas en el monitor serie y muestra cada medio segundo la distancia que leen los dos sensores. Así mides, para tu propio basurero, los ángulos de tapa cerrada y abierta, la profundidad del basurero vacío y el umbral de detección de la mano.
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}- Sube el código sin el brazo del servo puesto y abre el monitor serie a 9600 baudios; pon el ajuste de fin de línea en Nueva línea (Newline). El servo va a 90 grados.
- Escribe 10 y pulsa Enter. Mantén la tapa cerrada, coloca el brazo del servo en el eje en esa posición y luego fíjalo a la tapa.
- Sube el ángulo en pasos de 20, 40, 60 hasta encontrar el valor en que la tapa queda totalmente abierta; suele estar entre 90 y 110 grados. No abras la tapa más allá de la vertical. Ese valor será LID_OPEN. Si tu servo gira en sentido contrario, elige los valores de cerrado y abierto según corresponda; el código funciona en ambos sentidos.
- Vuelve a ángulos pequeños. Desde el ángulo en que la tapa se apoya del todo en el borde, retrocede 2–3 grados hacia el sentido de apertura; ese será LID_CLOSED. Si el servo zumba con la tapa cerrada, la está empujando.
- Con la bolsa puesta y el basurero vacío, anota el valor que lee el sensor de llenado; ese es EMPTY_CM. Para FULL_CM elige 6–10 cm: cuando la basura llegue a esa altura, la tapa todavía debe cerrar con holgura y el sensor medir con fiabilidad, porque el HC-SR04 no mide por debajo de 2–3 cm.
- Pon la mano delante del sensor de mano a 5, 10, 20 y 30 cm y mira los valores. Después pasa caminando por delante del basurero y anota el valor más cercano que lee. HAND_CM debe quedar por debajo de ese valor; para la mayoría de los basureros funciona bien entre 15 y 25 cm.
- Escribe los valores que encontraste en los ajustes del principio del código principal y súbelo.
Código principal: detección de la mano, tiempo de espera y cierre lento
En el código principal no hay ningún delay() largo; todo se hace con marcas de tiempo guardadas con millis(). Así, incluso mientras la tapa se cierra, se sigue leyendo el sensor de mano, y si la mano se acerca otra vez la tapa se vuelve a abrir al instante. Es una medida de seguridad sencilla pero importante que evita que la tapa se cierre sobre la mano de un niño. Los detalles de la temporización sin bloqueo están en millis() y delay(), y la forma de pensar en estados en el artículo máquina de estados en Arduino.
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}| Función o ajuste | Qué hace |
|---|---|
| pingCm() | Hace una lectura; devuelve 999 si no llega eco en 30 ms |
| medianCm() | Hace 5 lecturas separadas 60 ms, las ordena y devuelve la del medio |
| toPercent() | Convierte la distancia en un porcentaje de 0 a 100 según EMPTY_CM y FULL_CM |
| handHits | Cuenta en cuántas lecturas seguidas se vio una mano; una sola lectura falsa no abre la tapa |
| OPEN_TIME | Tiempo desde la última vez que se vio la mano hasta que empieza el cierre (4000 ms) |
| CLOSE_STEP_MS | Tiempo por grado al cerrar: 100 grados × 20 ms = cierre de 2 segundos |
| lid.detach() | Deja de enviar pulsos al servo 0,4 s después de cerrar la tapa |
| fullWarned | Hace que el aviso de lleno suene una sola vez; se reinicia cuando el llenado baja del 70 % |
El truco del cierre lento es que el ángulo se calcula a partir del tiempo transcurrido en vez de aumentarse grado a grado. 600 ms después de empezar el cierre, la tapa debería haber recorrido 600 / 20 = 30 grados; aunque el bucle se retrase un momento por una medición de llenado o por el zumbador, el código lleva la tapa al ángulo correcto y el movimiento no da tirones. Para un cierre aún más lento, aumenta CLOSE_STEP_MS: con 30 ms, 100 grados tardan 3 segundos. Cuando la tapa se ha cerrado, el servo se libera con detach(); la tapa ya descansa sobre el borde, así que el servo no tiene que sostenerla, no zumba y gasta menos batería. Llamar a write() antes de attach() también es intencionado: al encender el Arduino, el servo no salta primero a 90 grados, sino que arranca directamente en la posición cerrada.
Medición del llenado y avisos con LED y zumbador
El sensor de llenado mide la distancia desde debajo de la tapa hasta el punto más alto de la basura. Esa distancia es máxima con el basurero vacío (EMPTY_CM) y mínima cuando está lleno (FULL_CM). El porcentaje sale de esta fórmula: Llenado (%) = (EMPTY_CM − medida) / (EMPTY_CM − FULL_CM) × 100. Por ejemplo, con EMPTY_CM = 35 cm, FULL_CM = 8 cm y una medida de 20 cm, el llenado es (35 − 20) / 27 × 100 ≈ 55 % y se enciende el LED amarillo. Si la medida sale mayor de 35 cm (el sonido rebotó en diagonal por el fondo), el resultado se fija en 0 %; si sale menor de 8 cm, en 100 %.
| Llenado | LED | Zumbador | Significado |
|---|---|---|---|
| 0–49 % | Verde | En silencio | Hay sitio de sobra |
| 50–79 % | Amarillo | En silencio | Más de la mitad lleno, vacíalo pronto |
| 80–89 % | Rojo | En silencio | Casi lleno |
| 90–100 % | Rojo | 3 pitidos cortos al llenarse, 2 pitidos cada vez que se abre | Vacíalo ya |
La superficie de la basura no es plana: si una sola botella queda de pie justo debajo del sensor, la medida ve ese punto y el basurero parece más lleno de lo que está. El filtro de mediana elimina las lecturas sueltas que se disparan, pero no puede corregir la forma de la basura; por eso, toma umbrales como el 80 % y el 90 % como un aviso de "hay que vaciarlo" y no como una medida exacta. Para que el aviso no esté pitando todo el rato, el código usa histéresis: el aviso de lleno suena una vez al 90 % y no vuelve a sonar hasta que el basurero se vacía y el llenado baja del 70 %. Explicamos en detalle las diferencias entre filtros en el artículo filtrar datos de sensores.
Alimentación con pilas: ¿cuánto dura?
Usar el basurero inteligente sin cable es tentador, pero el Arduino Uno no es una placa pensada para pilas. Con su chip USB, su regulador y su LED de encendido, consume unos 45–50 mA sin hacer nada. Si sumas la corriente en reposo de dos HC-SR04, un LED encendido y la electrónica del servo liberado, la corriente media sube a unos 70–80 mA. Los 100–250 mA que consume el servo al abrir y cerrar solo duran unos segundos, así que en un basurero que se abre 50 veces al día suben la media apenas alrededor de 1 mA. La duración de las pilas se calcula de forma aproximada con capacidad (mAh) / corriente media (mA); para calcularla con tus valores, usa la herramienta Duración de la batería.
| Fuente de alimentación | Conexión | Duración aprox. (75 mA) | Valoración |
|---|---|---|---|
| Pila de 9 V (unos 500 mAh) | VIN | 5–6 horas | No recomendada: la tensión cae cuando arranca el servo y la placa se reinicia |
| 6 pilas AA alcalinas (9 V) | Conector DC o VIN | Aprox. 1 día | Suficiente para el día de la presentación |
| 2 × 18650 (7,4 V, 2500 mAh) | VIN; LM2596 ajustado a 5–6 V para el servo | Aprox. 1,5 días | Recargable; necesita cargador 2S y placa de protección |
| Power bank de 10 000 mAh | USB | 3–4 días | Algunos power banks se apagan con cargas por debajo de 50–100 mA |
| Adaptador de 5 V y 2 A | USB o pin 5V | Ilimitada | La mejor opción para uso permanente en casa |

La conclusión de la tabla es clara: un basurero con Uno y pilas es ideal para presentaciones y demostraciones, pero para uno que va a funcionar meses en casa, usa un adaptador de 5 V. Cómo ajustar el módulo reductor que va entre dos 18650 y el servo lo explica el artículo LM2596. Si de verdad quieres alargar la duración, puedes usar en lugar del Uno un Arduino Pro Mini sin LED de encendido, poner el procesador en modo de reposo entre medidas y encender los LED solo un momento cuando se abre la tapa; con estas medidas la corriente media baja a 15–20 mA y las mismas pilas duran de 4 a 5 veces más. Las diferencias entre las formas de alimentar un Arduino (USB, conector DC, VIN, pin 5V) están en el artículo Cómo alimentar un Arduino.
Aviso de llenado con ESP32 (Telegram)
Para ver el LED cuando el basurero está lleno tienes que acercarte; un ESP32, en cambio, puede enviar esa información directamente a tu móvil. Lo más fácil es Telegram, porque es gratis y puedes crear tu propio bot en minutos. En Telegram escribe a @BotFather, crea un bot con el comando /newbot y apunta el token que te da. Después envía una vez /start a tu bot; un bot no puede escribir a alguien que nunca ha hablado con él. Para saber tu chat ID puedes escribir a un bot como @userinfobot o usar la petición getUpdates de la Bot API. El código no necesita librerías extra: envía una petición a la dirección sendMessage de Telegram con el HTTPClient propio del ESP32.
El código usa la misma máquina de estados que la versión para Uno; solo la señal del servo se genera con LEDC, la unidad PWM del ESP32, en lugar de con la librería Servo. En una señal de 50 Hz, un pulso de 0,544–2,4 ms corresponde a 0–180 grados, y cortar los pulsos (escribir 0 con ledcWrite) libera el servo. El código es para la versión 3.x del núcleo ESP32 para Arduino; la versión 2.x no tiene la función ledcAttach(), así que actualiza el núcleo en el Gestor de tarjetas.

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}El aviso se envía una sola vez cuando el basurero llega al 85 % y se rearma con un mensaje de "vaciado" cuando el llenado baja del 30 %, así tu móvil no vibra cada 3 segundos. Si no hay Wi-Fi o el envío falla, el código lo reintenta una vez por minuto y mientras tanto el basurero sigue funcionando con normalidad. Enviar un mensaje tarda 1–3 segundos por la conexión cifrada (TLS), por eso el código solo envía con la tapa cerrada. setInsecure() se conecta sin verificar el certificado del servidor; para un proyecto de aficionado es suficiente, pero recuerda que el token es un dato secreto y bórralo antes de compartir tu código. Si en el monitor serie ves "Telegram HTTP 200", el mensaje ha salido; 401 indica que el token está mal, 400 que el chat ID está mal y 403 que todavía no has enviado /start al bot. Para un bot completo que responda a comandos, consulta la guía bot de Telegram con ESP32.
Basurero inteligente con Raspberry Pi Pico (MicroPython)
En la Raspberry Pi Pico la misma lógica se monta con MicroPython. La función time_pulse_us() funciona como pulseIn() en Arduino; como devuelve un valor negativo si no hay eco, lo tratamos como 999, es decir, "sin eco". Para el servo se usa PWM a 50 Hz y duty_ns(), que recibe el ancho del pulso en nanosegundos; duty_ns(0) corta los pulsos y libera el servo. Las comparaciones de tiempo se hacen con ticks_ms() y ticks_diff(); ticks_diff da la diferencia correcta aunque el contador se desborde. Si guardas el código en la Pico como main.py con Thonny, se ejecuta solo cada vez que la placa se enciende. Si usas una Pico W, puedes añadir el método del artículo servidor web con Pico W para mostrar el llenado por Wi-Fi.
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)Consejos para presentar el proyecto escolar
El basurero inteligente es un proyecto muy frecuente en ferias de ciencias y exposiciones escolares como TÜBİTAK 4006, así que al jurado le interesa menos que la tapa se abra y se cierre que cómo planteaste el problema, cómo lo mediste y qué conclusiones sacaste. Lo que convierte tu proyecto de una simple demostración en un trabajo científico son las mediciones: en cuántos intentos se abrió, en cuánto tiempo, con qué precisión estimó el llenado, cuántas horas duraron las pilas. Puedes organizar la presentación en este orden:
- Problema: Tocar la tapa con la mano transmite gérmenes y los basureros que rebosan provocan olores e insectos. Empieza con datos reales contando cuántas personas tocan la tapa de un basurero de tu escuela durante un recreo.
- Objetivo: Un basurero que se abre sin tocarlo, avisa cuando está lleno y, si quieres, notifica a tu móvil.
- Funcionamiento: Dibuja un único diagrama de bloques con el sensor, el Arduino, el servo, los LED, el zumbador, el ESP32 y el móvil. Explica la velocidad del sonido y la fórmula de dividir entre 58 en una diapositiva.
- Materiales y coste: Presenta la lista de piezas y la elección del servo en una tabla, junto con el cálculo de torque.
- Demostración en vivo: Acerca la mano y deja que se abra la tapa. Llena el basurero de papel y muestra cómo el LED pasa de verde a amarillo y a rojo; proyecta en la pantalla el mensaje de Telegram que llega a tu móvil.
- Resultados de las pruebas: Rellena la tabla de abajo con tus propias mediciones y pasa los resultados a gráficos.
- Conclusión y mejoras: Cuenta con sinceridad qué funcionó bien, qué no y qué cambiarías en la próxima versión.
| Prueba | Cómo se hace | Valor que anotar |
|---|---|---|
| Distancia de detección | Pon la mano a 5, 10, 15, 20 y 25 cm, 10 veces cada una | En cuántos intentos se abrió en cada distancia (p. ej., 10/10) |
| Tiempo de respuesta | Graba un vídeo a cámara lenta (240 fps) con el móvil | Tiempo entre la aparición de la mano y el primer movimiento de la tapa (ms) |
| Aperturas falsas | Pasa 20 veces caminando por delante del basurero | Número de aperturas no deseadas |
| Precisión del llenado | Llena el basurero de papel hasta un cuarto, la mitad y tres cuartos, medidos con regla | Diferencia entre el llenado real y el porcentaje medido |
| Velocidad de cierre | Pon CLOSE_STEP_MS en 5, 20 y 40 ms | Tiempo de cierre y nivel de ruido medido con el móvil |
| Duración de las pilas | Hazlo funcionar con pilas cargadas y mide la tensión cada hora | Cuántas horas funcionó el basurero |
Ideas para mejorarlo
- Basurero con pantalla: Muestra el porcentaje de llenado y una barra en una pantalla OLED.
- Agradecimiento con voz: Reproduce un breve "gracias" con un DFPlayer Mini cada vez que se abre la tapa.
- Medición de peso: Coloca una celda de carga con HX711 bajo el basurero para medir cuántos kilos de basura tiene y combina volumen y peso.
- Separador de reciclaje: Usa un sensor de color y un segundo servo para dirigir los residuos a dos compartimentos.
- Aviso de mal olor: Detecta con un MQ-135 cuándo empeora el aire dentro del basurero.
- Basurero solar para exteriores: Elimina el cable por completo con un pequeño panel solar, una celda 18650, un módulo de carga TP4056 y un elevador a 5 V.
- Registro del llenado: Guarda las medidas en Google Sheets con el ESP32 y muestra en un gráfico qué días se llena más rápido.
- Para toda la escuela o el vecindario: Vigila desde cientos de metros los basureros del patio a los que no llega el Wi-Fi con un módulo LoRa.
Errores frecuentes y soluciones
| Síntoma | Causa probable | Solución |
|---|---|---|
| La tapa se abre y se cierra sola | El sensor ve una pared cercana o a la gente que pasa | Baja HAND_CM, inclina el sensor hacia arriba, deja 30 cm libres delante |
| El Arduino se reinicia al abrirse la tapa | La corriente de arranque del servo hunde la línea de 5 V | Condensador de 470 µF, servo potente con fuente aparte, GND común |
| El servo zumba y se calienta con la tapa cerrada | LID_CLOSED aprieta la tapa contra el borde | Retrocede el ángulo 2–3 grados; no borres la línea de detach() |
| La tapa se queda a medias o tiembla | Torque insuficiente o ejes desalineados | Servo más fuerte, tapa más ligera, contrapeso; alinea el eje del servo con la bisagra |
| El llenado sale alto con el basurero vacío | Borde de la bolsa delante del sensor o ecos de las paredes en un basurero estrecho | Dobla la bolsa hacia fuera, centra el sensor, vuelve a medir EMPTY_CM |
| El llenado nunca cambia | El sensor no recibe eco (999) o el cable ECHO se ha soltado | Mira el valor en cm en el monitor serie y revisa las conexiones |
| En el ESP32 la distancia siempre es 999 o absurda | Falta el divisor de ECHO, está mal o los cables están cruzados | Revisa el divisor de 1 kΩ / 2 kΩ y el orden de TRIG y ECHO |
| El ESP32 se reinicia continuamente | El Wi-Fi y el servo piden corriente a la vez, cable USB de mala calidad | Usa un cable corto y bueno, alimenta el servo con una fuente aparte de 5 V |
| No llega el mensaje de Telegram | Token o chat ID incorrectos, no se envió /start al bot | Lee el código HTTP en el monitor serie (401, 400, 403) |
Como el basurero inteligente reúne en un solo proyecto la lectura de sensores, el control de un servo, la temporización y la planificación de la alimentación, es una de las formas más divertidas de aprender Arduino. Una vez que lo hayas montado, hacer un sensor de aparcamiento o una puerta automática con las mismas piezas te resultará mucho más fácil. En la app BlueGrays encontrarás paso a paso proyectos de medición de distancia con el HC-SR04, control de servos, uso del zumbador, un sensor de aparcamiento con ESP32 y una alarma de movimiento con avisos por Telegram, con sus esquemas y el código listo para usar.
Preguntas frecuentes
¿Qué materiales necesito para un basurero inteligente con Arduino?
Para la versión básica bastan un Arduino Uno o Nano, un sensor ultrasónico HC-SR04, un servo adecuado al peso de la tapa (SG90 para una tapa ligera, MG996R para la tapa de un basurero real), cables jumper y una fuente de 5 V. Para medir el llenado se añaden un segundo HC-SR04, tres LED con resistencias de 220 Ω y un zumbador activo. Si quieres avisos en el móvil, puedes usar un ESP32 en lugar del Arduino.
¿Cómo funciona un basurero inteligente?
El sensor ultrasónico del frente emite una onda de sonido y mide cuánto tarda en volver el eco; cuando ve una mano a menos de 20 cm, Arduino abre la tapa con un servomotor. Unos segundos después de retirar la mano, la tapa se cierra despacio. Un segundo sensor bajo la tapa mide la altura de la basura y muestra el llenado con LED y un zumbador.
¿Por qué mi servo no levanta la tapa del basurero?
Normalmente el torque del servo es insuficiente para la tapa. El torque necesario es el peso de la tapa por la distancia de su centro de masa a la bisagra; una tapa de 120 g y 25 cm necesita unos 1,5 kg·cm, que está en el límite de un SG90. Elige un servo para el doble de tu cálculo, aligera la tapa o añade un contrapeso, y asegúrate de que la alimentación del servo da suficiente corriente.
¿Se puede usar un sensor IR en lugar del HC-SR04?
Sí, un sensor IR de obstáculos como el FC-51 detecta una mano a 2–30 cm y el código es aún más simple, porque solo indica si hay algo o no. Sin embargo, le afecta la luz del sol y ve más tarde las superficies oscuras. Un sensor PIR no sirve para la tapa de un basurero, porque se activa con cualquiera que pase por delante.
¿Cuánto dura un basurero inteligente con Arduino a pilas?
Un basurero con Arduino Uno consume de media 70–80 mA, así que dura aproximadamente un día con 6 pilas AA, un día y medio con dos celdas 18650 y 3–4 días con un power bank de 10 000 mAh. Para uso permanente se recomienda un adaptador de 5 V. Un Pro Mini, el modo de reposo y encender los LED solo cuando hace falta pueden alargar la duración de 4 a 5 veces.
¿Cómo recibo un aviso en el móvil cuando el basurero está lleno?
El ESP32 se conecta al Wi-Fi y, cuando el llenado supera el porcentaje que fijaste, envía una petición a la Bot API de Telegram que te hace llegar un mensaje al móvil. Para ello necesitas un token de bot de BotFather y tu propio chat ID. Para no enviar el mismo aviso una y otra vez, el código espera hasta que el basurero se vacía.
Proyectos para hacer después de leer esto
Ver todos los proyectos
HC-SR04 Medición de distancia (pantalla en serie)
El sensor HC-SR04 funciona igual que un murciélago que se orienta en la oscuridad: emite un sonido que no puedes oír, escucha el eco y mide a qué distancia está el objeto que tiene delante. El Arduino

Uso del zumbador
¿Alguna vez te has preguntado cómo suena un timbre o una alarma? El Arduino hace pitar un pequeño zumbador a intervalos cortos: pip, silencio, pip, silencio. Ves cómo se crea sonido con electricidad y

Uso del servomotor
El brazo de un pequeño servomotor gira por turnos a tres posiciones: al principio del todo, justo al centro y al final del todo, esperando dos segundos en cada parada. El Arduino le dice al motor en q

Sensor de aparcamiento con HC-SR04
Una versión en miniatura del sensor de aparcamiento de los coches: cuanto más se acerca algo, más rápidos son los pitidos. El sensor de distancia mide el objeto que tiene delante; si está a más de 20

ESP32 HC-SR04 Medidor de distancia por ultrasonidos
El sensor ultrasónico HC-SR04 calcula la distancia midiendo el tiempo de vuelo (Time of Flight) de la onda sonora que emite al chocar contra un objeto y rebotar. En este proyecto, enviaremos un pulso de 10 microsegundos desde el ESP32 al pin TRIG del sensor y leeremos el tiempo de eco procedente del pin ECHO mediante `pulseIn()`. Mostraremos la distancia calculada en centímetros en una pantalla LCD I2C de 16x2. Dado que el HC-SR04 funciona a 5 V, es obligatorio utilizar un **divisor de tensión**

Servomotor ESP32 (control mediante potenciómetro)
En este proyecto controlamos un servomotor SG90 mediante un potenciómetro. Ajustamos el ángulo del servo asignando el valor analógico leído en el potenciómetro al rango de 0 a 180 grados. La biblioteca ESP32Servo genera señales de servo en los GPIO adecuados del ESP32 que admiten salida; en este proyecto se utiliza GPIO 18. Este es un método de control básico para brazos robóticos, cardanes de cámara y mecanismos de puertas automáticas.

Sensor de aparcamiento ESP32 (HC-SR04 + barra de 5 LED + zumbador + OLED)
¡La versión ESP32 del sensor de aparcamiento para coches! Medimos la distancia al objeto que hay delante mediante el sensor ultrasónico HC-SR04. A medida que la distancia disminuye, la barra de 5 LED se va llenando (verde → amarillo → rojo) y la frecuencia de los pitidos del zumbador se acorta. Cuando se acerca demasiado (menos de 10 cm), suena una alarma continua y se encienden todos los LED. En la pantalla OLED SSD1306 se muestra, en letra grande, el valor actual en centímetros, un mensaje de

Sistema de Alarma de Movimiento ESP32 (PIR + Buzzer + LED + Telegram)
Un sistema inteligente de alarma de seguridad que activa el buzzer y el LED cuando el sensor PIR detecta movimiento, y envía simultáneamente una notificación instantánea al propietario vía Telegram. Cuenta con un tiempo de espera de 30 segundos para limitar la frecuencia de las alarmas repetidas; este mecanismo no comprueba la validez de las detecciones del sensor PIR ni elimina las falsas detecciones. Ideal para seguridad del hogar, vigilancia de garajes, almacenes o salas de trabajo. Con ESP32
Más artículos
App BlueGrays
Monta este circuito paso a paso en tu teléfono
Esquema, código y lista de piezas en una sola pantalla. Más de 160 proyectos de Arduino, ESP32 y Pico listos para montar.
Escanea con tu teléfono
Apunta la cámara al código y se abrirá la tienda de apps de tu teléfono.
o ábrela en la tienda

