Guías · 22 min de lectura

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.

Sensor de distancia ultrasónico HC-SR04
El HC-SR04 cumple dos funciones en el basurero inteligente: el sensor del frente mide la mano que se acerca y el de debajo de la tapa mide la altura de la basura. Detecta de 2 a 400 cm dentro de un haz estrecho.

¿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).

PiezaFunciónNota
Arduino Uno (o Nano)El cerebro: mide, decide y controla el servo y los LEDTambién sirven ESP32 o Pico
2 × HC-SR04Uno detecta la mano, el otro mide el llenado5 V, unos 15 mA al medir
Servo SG90, MG90S o MG996RAbre y cierra la tapaSe elige según el peso de la tapa (cálculo más abajo)
3 LED (verde, amarillo, rojo) y 3 × 220 ΩIndicador de llenadoUna resistencia por LED
Zumbador activoAviso de llenoUn zumbador pasivo necesita tone()
Bisagra, tornillos, silicona calienteSostiene la tapa, fija el brazo del servoEl peso de la tapa debe soportarlo la bisagra, no el servo
Condensador de 470 µFSuaviza la corriente de arranque del servoEntre + y − del servo, cuidado con la polaridad
AlimentaciónAdaptador de 5 V, power bank o pilasCálculo de duración más abajo
ESP32 (opcional)Aviso de llenado en el móvilNecesita Wi-Fi
Arduino Uno
Arduino Uno
SG90 servo motor
SG90 servo motor
Buzzer
Buzzer
LED
LED

¿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:

SensorCómo detectaPara un basurero
HC-SR04 (ultrasónico)Eco de sonido, 2–400 cm, cono estrechoUmbral ajustable por código; la opción más equilibrada
Sensor IR de obstáculos (FC-51)Luz infrarroja reflejada, unos 2–30 cmBarato y rápido; la luz del sol y los guantes oscuros pueden confundirlo
PIR (HC-SR501)Movimiento del calor corporal, hasta 7 m, ángulo amplioSe activa con cualquiera que pase y se queda activo unos segundos; no es adecuado
APDS-9960Proximidad y gestos de la mano a corta distanciaUna 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.

Nunca dispares los dos HC-SR04 a la vez. Cada uno tomaría el eco del otro por el suyo y ambos leerían valores absurdos. En el código los dos sensores se miden uno tras otro y con al menos 60 ms de separación; mientras el sensor de llenado hace sus 5 lecturas, el de mano espera. El frente de los sensores también debe quedar libre: el borde de los agujeros que hagas en el basurero debe quedar a ras de la cara del sensor, y hasta un trozo fino de plástico o de cinta transparente delante bloquea el eco.

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ónArduino UnoESP32Raspberry Pi Pico
Sensor de mano TRIGD2GPIO 18GP2
Sensor de mano ECHOD3 (directo)GPIO 19, con divisor de 1 kΩ / 2 kΩGP3, con divisor de 1 kΩ / 2 kΩ
Sensor de llenado TRIGD4GPIO 25GP4
Sensor de llenado ECHOD5 (directo)GPIO 26, con divisorGP5, con divisor
VCC de los HC-SR04 (ambos)5V5V (VIN)VBUS
Señal del servo (naranja)D9GPIO 13GP15
Servo + (rojo)5V (SG90) o externo de 5–6 V5V (VIN) o externo de 5–6 VVBUS o externo de 5–6 V
LED verde, amarillo y rojo (con 220 Ω)D10, D11, D12GPIO 27, 32, 33GP10, GP11, GP12
Zumbador activo (+)D8GPIO 23GP13
Todos los GNDGNDGNDGND
El pin ECHO del HC-SR04 entrega un pulso de 5 V, mientras que los pines del ESP32 y de la Pico son de 3,3 V y no toleran 5 V. Monta un divisor de tensión independiente en la línea ECHO de cada sensor: ECHO → 1 kΩ → punto que va a la placa → 2 kΩ → GND. Así obtienes 5 V × 2 / 3 ≈ 3,3 V. TRIG no necesita divisor; un disparo de 3,3 V es suficiente para el sensor. El pin 5V (VIN) del ESP32 y el pin VBUS de la Pico solo dan 5 V cuando la placa se alimenta por USB.

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.

TapaPesoCentro de masaTorque necesarioCon margen ×2Recomendación
Cartón o cartón pluma de 20 × 20 cm50 g10 cm0,5 kg·cm1,0 kg·cmSG90
Tapa de plástico de basurero de 25 cm120 g12,5 cm1,5 kg·cm3,0 kg·cmMG996R (MG90S justo)
Tapa de MDF o contrachapado de 30 cm300 g15 cm4,5 kg·cm9,0 kg·cmMG996R y contrapeso
ServoTorque (4,8 V / 6 V)EngranajesCorrienteNota
SG901,8 kg·cm (solo se indica el valor a 4,8 V)Plástico100–250 mA en movimientoPuede alimentarse del pin 5V del Arduino
MG90S1,8 / 2,2 kg·cmMetal150–300 mA en movimientoMismo tamaño que el SG90, más resistente
MG996R9,4 / 11 kg·cmMetal0,5–0,9 A en movimiento, hasta 2,5 A bloqueadoSiempre con una fuente propia de 5–6 V
Servomotor MG996R con engranajes metálicos
MG996R: servo con engranajes metálicos y unos 11 kg·cm de torque a 6 V. Levanta sin esfuerzo la tapa de plástico de un basurero real, pero como bloqueado puede consumir hasta 2,5 A, hay que alimentarlo con una fuente aparte y no desde el Arduino.

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.

Arduino (C++)
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}
  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.

Arduino (C++)
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 ajusteQué 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
handHitsCuenta en cuántas lecturas seguidas se vio una mano; una sola lectura falsa no abre la tapa
OPEN_TIMETiempo desde la última vez que se vio la mano hasta que empieza el cierre (4000 ms)
CLOSE_STEP_MSTiempo 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
fullWarnedHace 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 %.

LlenadoLEDZumbadorSignificado
0–49 %VerdeEn silencioHay sitio de sobra
50–79 %AmarilloEn silencioMás de la mitad lleno, vacíalo pronto
80–89 %RojoEn silencioCasi lleno
90–100 %Rojo3 pitidos cortos al llenarse, 2 pitidos cada vez que se abreVací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.

La velocidad del sonido cambia con la temperatura: aproximadamente v = 331,3 + 0,606 × T (m/s). El número 58 es correcto para 20 °C; en un balcón a 5 °C el sonido va más despacio, así que las medidas salen alrededor de un 3 % más largas. En un basurero de 35 cm es un error de menos de 1 cm y no importa para el llenado, pero si el jurado lo pregunta, saber el porqué causa muy buena impresión.

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ónConexiónDuración aprox. (75 mA)Valoración
Pila de 9 V (unos 500 mAh)VIN5–6 horasNo recomendada: la tensión cae cuando arranca el servo y la placa se reinicia
6 pilas AA alcalinas (9 V)Conector DC o VINAprox. 1 díaSuficiente para el día de la presentación
2 × 18650 (7,4 V, 2500 mAh)VIN; LM2596 ajustado a 5–6 V para el servoAprox. 1,5 díasRecargable; necesita cargador 2S y placa de protección
Power bank de 10 000 mAhUSB3–4 díasAlgunos power banks se apagan con cargas por debajo de 50–100 mA
Adaptador de 5 V y 2 AUSB o pin 5VIlimitadaLa mejor opción para uso permanente en casa
Portapilas para baterías 18650 de iones de litio
Portapilas 18650: dos celdas en serie dan 7,4 V. Esa tensión sirve para alimentar el Arduino por VIN; para el servo se coloca en medio un módulo reductor ajustado a 5–6 V.

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.

Todos los GND del servo, los sensores y el Arduino deben estar en común; si el GND de un servo alimentado por una fuente aparte no está unido al Arduino, el servo no entiende la señal y tiembla. Pon un condensador de 470 µF en los terminales de alimentación del servo. Si el Arduino se reinicia cada vez que se abre la tapa, es que el servo hunde la línea de 5 V al arrancar; las causas y soluciones están en el artículo El Arduino se reinicia solo.

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.

Placa de desarrollo ESP32
ESP32: gracias a su Wi-Fi envía un mensaje de Telegram al móvil cuando el basurero se llena. Las líneas ECHO de los HC-SR04 no deben ir a sus pines sin reducirlas antes a 3,3 V.
Arduino (C++)
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.

MicroPython
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:

  1. 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.
  2. Objetivo: Un basurero que se abre sin tocarlo, avisa cuando está lleno y, si quieres, notifica a tu móvil.
  3. 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.
  4. Materiales y coste: Presenta la lista de piezas y la elección del servo en una tabla, junto con el cálculo de torque.
  5. 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.
  6. Resultados de las pruebas: Rellena la tabla de abajo con tus propias mediciones y pasa los resultados a gráficos.
  7. Conclusión y mejoras: Cuenta con sinceridad qué funcionó bien, qué no y qué cambiarías en la próxima versión.
PruebaCómo se haceValor que anotar
Distancia de detecciónPon la mano a 5, 10, 15, 20 y 25 cm, 10 veces cada unaEn cuántos intentos se abrió en cada distancia (p. ej., 10/10)
Tiempo de respuestaGraba un vídeo a cámara lenta (240 fps) con el móvilTiempo entre la aparición de la mano y el primer movimiento de la tapa (ms)
Aperturas falsasPasa 20 veces caminando por delante del basureroNúmero de aperturas no deseadas
Precisión del llenadoLlena el basurero de papel hasta un cuarto, la mitad y tres cuartos, medidos con reglaDiferencia entre el llenado real y el porcentaje medido
Velocidad de cierrePon CLOSE_STEP_MS en 5, 20 y 40 msTiempo de cierre y nivel de ruido medido con el móvil
Duración de las pilasHazlo funcionar con pilas cargadas y mide la tensión cada horaCuántas horas funcionó el basurero
El jurado suele preguntar por los puntos débiles, así que prepara las respuestas. El HC-SR04 no ve bien las superficies blandas y porosas, por lo que con un guante grueso de tela la tapa puede tardar en abrirse. La medición de llenado ve el punto más alto de la basura, no su promedio. Un basurero con Uno solo dura 1–2 días con pilas. Demostrar que conoces estos límites y proponer una solución para cada uno te dará muchos más puntos que presentar el proyecto como si fuera perfecto.

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íntomaCausa probableSolución
La tapa se abre y se cierra solaEl sensor ve una pared cercana o a la gente que pasaBaja HAND_CM, inclina el sensor hacia arriba, deja 30 cm libres delante
El Arduino se reinicia al abrirse la tapaLa corriente de arranque del servo hunde la línea de 5 VCondensador de 470 µF, servo potente con fuente aparte, GND común
El servo zumba y se calienta con la tapa cerradaLID_CLOSED aprieta la tapa contra el bordeRetrocede el ángulo 2–3 grados; no borres la línea de detach()
La tapa se queda a medias o tiemblaTorque insuficiente o ejes desalineadosServo más fuerte, tapa más ligera, contrapeso; alinea el eje del servo con la bisagra
El llenado sale alto con el basurero vacíoBorde de la bolsa delante del sensor o ecos de las paredes en un basurero estrechoDobla la bolsa hacia fuera, centra el sensor, vuelve a medir EMPTY_CM
El llenado nunca cambiaEl sensor no recibe eco (999) o el cable ECHO se ha soltadoMira el valor en cm en el monitor serie y revisa las conexiones
En el ESP32 la distancia siempre es 999 o absurdaFalta el divisor de ECHO, está mal o los cables están cruzadosRevisa el divisor de 1 kΩ / 2 kΩ y el orden de TRIG y ECHO
El ESP32 se reinicia continuamenteEl Wi-Fi y el servo piden corriente a la vez, cable USB de mala calidadUsa un cable corto y bueno, alimenta el servo con una fuente aparte de 5 V
No llega el mensaje de TelegramToken o chat ID incorrectos, no se envió /start al botLee el código HTTP en el monitor serie (401, 400, 403)
Para buscar fallos, prueba las piezas de una en una: primero comprueba con el sketch de calibración que los dos sensores dan valores lógicos en el monitor serie, después que el servo va sin problemas a los ángulos de cerrado y abierto, y solo entonces sube el código principal. Si el ESP32 muestra el mensaje "Brownout detector was triggered", tienes las causas y soluciones en el artículo El ESP32 se reinicia continuamente.

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.

Ver todos los proyectos
HC-SR04 Medición de distancia (pantalla en serie)
#27Principiante

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

4.6(45)
68
Uso del zumbador
#13Principiante

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

4.7(89)
158
Uso del servomotor
#35Principiante
Premium

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

4.7(48)
67
Sensor de aparcamiento con HC-SR04
#28Intermedio
Premium

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

4.8(64)
84
ESP32 HC-SR04 Medidor de distancia por ultrasonidos
#116Intermedio

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**

4.9(28)
44
Servomotor ESP32 (control mediante potenciómetro)
#118Intermedio
Premium

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.

4.3(29)
42
Sensor de aparcamiento ESP32 (HC-SR04 + barra de 5 LED + zumbador + OLED)
#142Avanzado
Premium

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

0.0(0)
14
Sistema de Alarma de Movimiento ESP32 (PIR + Buzzer + LED + Telegram)
#145Avanzado
Premium

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

0.0(0)
8