Cestino intelligente con Arduino: sensore HC-SR04, servo e codice
Cestino intelligente con Arduino: rilevamento della mano con HC-SR04, coperchio a servo con chiusura lenta, LED di riempimento e avviso Telegram con ESP32.
Un cestino intelligente con Arduino (detto anche pattumiera automatica) è un progetto di automazione semplice ma molto istruttivo che apre il coperchio senza che tu debba toccarlo, ed è uno dei progetti scolastici più amati. Un sensore a ultrasuoni HC-SR04 sul davanti del cestino rileva la mano che si avvicina, Arduino apre il coperchio con un servomotore e, qualche secondo dopo che hai tolto la mano, lo richiude lentamente invece di lasciarlo sbattere. Un secondo HC-SR04 sotto il coperchio misura l'altezza dei rifiuti: man mano che il cestino si riempie si accendono in sequenza i LED verde, giallo e rosso, e quando è completamente pieno suona un buzzer.
In questa guida partiamo dall'elenco dei componenti e vediamo passo dopo passo dove montare i sensori, come calcolare la coppia del servo, come collegare il coperchio alla cerniera, la calibrazione e il codice completo che gestisce insieme il tempo di attesa e la chiusura lenta. Poi calcoliamo quanto dura davvero il progetto a batterie, inviamo un avviso di riempimento con l'ESP32 sul tuo telefono come messaggio Telegram e scriviamo lo stesso progetto per il Raspberry Pi Pico in MicroPython. Alla fine trovi anche una scaletta per presentare il progetto a scuola e una tabella di prova da compilare con le tue misure.

Come funziona un cestino intelligente?
Il progetto è un ciclo che si ripete decine di volte al secondo. Arduino misura con il sensore della mano ogni 60 millisecondi. Se in due letture consecutive compare un oggetto a meno di 20 cm, decide che è una mano e porta il servo in posizione aperta. Finché la mano resta lì, il tempo di attesa riparte a ogni lettura; 4 secondi dopo che la mano se n'è andata, il coperchio si chiude lentamente a circa 50 gradi al secondo. Se durante la chiusura la mano torna, il coperchio si riapre subito. Un secondo dopo la chiusura, il sensore di riempimento fa 5 letture, usa la mediana come distanza dalla superficie dei rifiuti e la converte in percentuale. Il codice usa una piccola macchina a stati in cui il coperchio è sempre in uno di tre stati: CHIUSO (CLOSED), APERTO (OPEN) e IN CHIUSURA (CLOSING).
| Componente | Compito | Nota |
|---|---|---|
| Arduino Uno (o Nano) | Il cervello: misura, decide e comanda servo e LED | Vanno bene anche ESP32 e Pico |
| 2 × HC-SR04 | Uno rileva la mano, l'altro misura il riempimento | 5 V, circa 15 mA durante la misura |
| Servo SG90, MG90S o MG996R | Apre e chiude il coperchio | Si sceglie in base al peso del coperchio (calcolo più sotto) |
| 3 LED (verde, giallo, rosso) e 3 × 220 Ω | Indicatore di riempimento | Una resistenza per ogni LED |
| Buzzer attivo | Avviso di cestino pieno | Un buzzer passivo richiede tone() |
| Cerniera, viti, colla a caldo | Sostiene il coperchio, fissa la squadretta del servo | Il peso del coperchio deve reggerlo la cerniera, non il servo |
| Condensatore da 470 µF | Smorza la corrente di spunto del servo | Tra + e − del servo, attenzione alla polarità |
| Alimentazione | Alimentatore da 5 V, power bank o batterie | Calcolo della durata più sotto |
| ESP32 (facoltativo) | Avviso di riempimento sul telefono | Serve il Wi-Fi |




Quale sensore per rilevare la mano?
Diversi sensori possono aprire un cestino, ma non tutti sono adatti allo stesso modo. Quello che ti serve davvero è accorgerti di una mano che si avvicina a 15–25 cm dal cestino e ignorare una persona che passa soltanto davanti. Dato che l'HC-SR04 restituisce la distanza in centimetri, puoi impostare la soglia nel codice esattamente come vuoi; per questo lo usa la maggior parte dei progetti di cestino intelligente. Ecco punti di forza e debolezze delle alternative:
| Sensore | Come rileva | Per un cestino |
|---|---|---|
| HC-SR04 (ultrasuoni) | Eco sonora, 2–400 cm, cono stretto | Soglia regolabile nel codice; la scelta più equilibrata |
| Sensore IR di ostacoli (FC-51) | Luce infrarossa riflessa, circa 2–30 cm | Economico e veloce; il sole e i guanti scuri possono ingannarlo |
| PIR (HC-SR501) | Movimento del calore corporeo, fino a 7 m, angolo ampio | Scatta per chiunque passi e resta attivo alcuni secondi; non adatto |
| APDS-9960 | Prossimità e gesti della mano a breve distanza | Versione elegante che si apre con un gesto; richiede I2C e 3,3 V |
Come funziona un sensore PIR lo trovi nell'articolo Sensore di movimento PIR, mentre una versione comandata a gesti è nella guida al sensore di gesti APDS-9960. Ecco in breve come funziona l'HC-SR04: emette un breve impulso sonoro a 40 kHz e restituisce il tempo di andata e ritorno dell'eco come impulso sul pin ECHO. Poiché il suono nell'aria viaggia a circa 343 m/s a 20 °C e il percorso è di andata e ritorno, dividere il tempo in microsecondi per 58 dà la distanza in centimetri. I dettagli sono nell'articolo Sensore di distanza HC-SR04.
Posizione dei sensori
Il sensore della mano va sul corpo del cestino, nella parte alta del frontale, non sul coperchio: quando il coperchio si apre, il sensore ruoterebbe con lui e guarderebbe il soffitto. Inclinarlo di 10–20 gradi verso l'alto gli permette di cogliere una mano che si allunga sopra il cestino e di ignorare le gambe di chi passa. Se il cestino è vicino a un muro o a un mobile, non deve esserci nessuna superficie fissa entro 30 cm nella direzione in cui guarda il sensore; altrimenti la scambia per una mano e il coperchio non si chiude mai.
Il sensore di riempimento si incolla sotto il coperchio, vicino al centro, rivolto dritto verso il basso. In questa posizione misura qualcosa di utile solo a coperchio chiuso; a coperchio aperto punta verso un muro o il soffitto. Per questo il codice misura il riempimento solo un secondo dopo la chiusura del coperchio. Anche se il cono sonoro del sensore è stretto (15 gradi secondo il datasheet, un po' di più nella pratica), a 35 cm di profondità copre un'area di 10–20 cm di diametro; nei cestini più stretti di 20 cm il suono può rimbalzare sulle pareti laterali e dare letture sbagliate. Ripiega il bordo del sacchetto verso l'esterno del cestino: un bordo che pende all'interno finisce davanti al sensore e fa sembrare il cestino pieno anche quando è vuoto.
Schema di collegamento: Arduino Uno, ESP32 e Pico
La tabella seguente corrisponde esattamente a tutto il codice di questo articolo. Sull'Uno, il sensore della mano va su D2–D3, quello di riempimento su D4–D5, il servo su D9, i LED su D10–D12 e il buzzer su D8. Poiché la libreria Servo usa il Timer1 sull'Uno, disattiva l'uscita PWM su D9 e D10; il LED su D10 lo accendiamo e spegniamo soltanto, quindi non ci riguarda. Metti una resistenza da 220 Ω davanti a ogni LED; puoi calcolare il valore per il tuo LED con lo strumento resistenza per LED. La piedinatura della tua scheda la trovi nello strumento Pinout.
| Collegamento | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| Sensore della mano TRIG | D2 | GPIO 18 | GP2 |
| Sensore della mano ECHO | D3 (diretto) | GPIO 19, con partitore 1 kΩ / 2 kΩ | GP3, con partitore 1 kΩ / 2 kΩ |
| Sensore di riempimento TRIG | D4 | GPIO 25 | GP4 |
| Sensore di riempimento ECHO | D5 (diretto) | GPIO 26, con partitore | GP5, con partitore |
| VCC degli HC-SR04 (entrambi) | 5V | 5V (VIN) | VBUS |
| Segnale del servo (arancione) | D9 | GPIO 13 | GP15 |
| Servo + (rosso) | 5V (SG90) o esterno 5–6 V | 5V (VIN) o esterno 5–6 V | VBUS o esterno 5–6 V |
| LED verde, giallo, rosso (con 220 Ω) | D10, D11, D12 | GPIO 27, 32, 33 | GP10, GP11, GP12 |
| Buzzer attivo (+) | D8 | GPIO 23 | GP13 |
| Tutti i GND | GND | GND | GND |
Scegliere il servo e calcolare la coppia
Il problema più comune nei cestini intelligenti è un coperchio che si apre a metà e trema, o che non si solleva proprio. La causa è quasi sempre un servo troppo debole per il coperchio. Stimare la coppia necessaria è facile: Coppia (kg·cm) = peso del coperchio (kg) × distanza del baricentro dalla cerniera (cm). Un coperchio piatto e di spessore uniforme ha il baricentro esattamente al centro; per un coperchio lungo 24 cm questa distanza è di 12 cm. Il momento più difficile è l'inizio dell'apertura, quando il coperchio è orizzontale; man mano che si alza la coppia necessaria diminuisce, e in verticale è quasi zero. La coppia indicata nei cataloghi dei servo è quella di stallo, il punto in cui il servo si sforza e si ferma; se lavora vicino a quel limite si scalda e consuma gli ingranaggi. Per questo moltiplica il risultato almeno per 2.
| Coperchio | Peso | Baricentro | Coppia necessaria | Con margine ×2 | Consiglio |
|---|---|---|---|---|---|
| Cartone o pannello in schiuma 20 × 20 cm | 50 g | 10 cm | 0,5 kg·cm | 1,0 kg·cm | SG90 |
| Coperchio di plastica di un cestino da 25 cm | 120 g | 12,5 cm | 1,5 kg·cm | 3,0 kg·cm | MG996R (MG90S al limite) |
| Coperchio in MDF o compensato da 30 cm | 300 g | 15 cm | 4,5 kg·cm | 9,0 kg·cm | MG996R con contrappeso |
| Servo | Coppia (4,8 V / 6 V) | Ingranaggi | Corrente | Nota |
|---|---|---|---|---|
| SG90 | 1,8 kg·cm (si indica solo il valore a 4,8 V) | Plastica | 100–250 mA in movimento | Può essere alimentato dal pin 5V di Arduino |
| MG90S | 1,8 / 2,2 kg·cm | Metallo | 150–300 mA in movimento | Stesse dimensioni dell'SG90, più robusto |
| MG996R | 9,4 / 11 kg·cm | Metallo | 0,5–0,9 A in movimento, fino a 2,5 A in stallo | Da alimentare sempre con una sorgente separata da 5–6 V |

Invece di comprare un servo più potente, spesso basta bilanciare il coperchio. Se il coperchio ha un piccolo braccio che sporge dietro la cerniera, qualche bullone o moneta incollati lì compensano parte del suo peso; questo contrappeso può ridurre la coppia necessaria fino alla metà. Potresti anche far spingere il coperchio dalla squadretta del servo in un punto lontano dalla cerniera, ma la leva non ti regala coppia: più ti allontani dalla cerniera, meno forza serve, però il punto spinto percorre più strada, quindi ti serve una squadretta più lunga, e con una squadretta più lunga la coppia richiesta al servo torna a salire. Se vuoi aprire il coperchio fin quasi a 90 gradi, la coppia necessaria resta praticamente uguale a quella di un servo montato direttamente sulla cerniera. Un confronto dettagliato tra i tipi di servo è negli articoli Cos'è un servomotore? e servo MG996R.
Consigli di montaggio: cerniera, squadretta del servo e cavi
- Fai reggere il peso alla cerniera: Il coperchio deve essere fissato a una vera cerniera (quella del cestino o una piccola cerniera da mobile). Se appoggi il coperchio solo sull'albero del servo, tutto il peso grava sul suo cuscinetto e gli ingranaggi di plastica dell'SG90 si rompono presto.
- Allinea l'albero del servo con l'asse della cerniera: Il modo più semplice è avvitare il servo al bordo posteriore del cestino in modo che il suo albero si trovi esattamente alla fine della linea della cerniera, e fissare la squadretta al coperchio con una vite o con la colla a caldo. Se gli assi sono disallineati, il servo spinge il coperchio di lato a ogni apertura.
- Fissa bene il corpo del servo: Non tenerlo solo con il biadesivo; quando solleva il coperchio il corpo ruota. Usa due viti nelle alette di fissaggio del servo o una piccola squadretta a L.
- Non schiacciare il coperchio sul bordo: Imposta l'angolo di chiusura nel punto in cui il coperchio poggia sul bordo ma il servo non lo spinge. Se il servo continua a forzare il coperchio chiuso, ronza, si scalda e consuma corrente inutilmente.
- Lascia gioco ai cavi: I fili del sensore di riempimento si muovono con il coperchio. Fai passare il cavo accanto alla cerniera, abbastanza lungo da non tendersi neanche a coperchio tutto aperto, e fermalo con una fascetta.
- Tieni l'elettronica lontana dai rifiuti: Metti Arduino, batterie e breadboard in una scatolina incollata dietro o sotto il cestino. Liquidi o umidità che escono dal sacchetto non devono raggiungere il circuito.
- Inizia con un modellino di cartone: Per un progetto scolastico, un coperchio leggero ricavato da una scatola da scarpe o da un pannello in schiuma funziona senza problemi anche con un SG90 ed è un ottimo esempio per mostrare il calcolo della coppia nella presentazione.
Montaggio e calibrazione passo dopo passo
Prima di passare al codice principale, carica il piccolo sketch di calibrazione qui sotto. Porta il servo all'angolo che scrivi nel monitor seriale e ogni mezzo secondo stampa la distanza letta dai due sensori. In questo modo misuri, per il tuo cestino, gli angoli di coperchio chiuso e aperto, la profondità del cestino vuoto e la soglia di rilevamento della 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}- Carica il codice senza la squadretta montata e apri il monitor seriale a 9600 baud; imposta il fine riga su A capo (NL) (Newline). Il servo si porta a 90 gradi.
- Scrivi 10 e premi Invio. Tieni il coperchio chiuso, monta la squadretta sull'albero in questa posizione e poi fissala al coperchio.
- Aumenta l'angolo a passi di 20, 40, 60 finché trovi il valore in cui il coperchio è completamente aperto; di solito è tra 90 e 110 gradi. Non aprire il coperchio oltre la verticale. Questo valore diventa LID_OPEN. Se il tuo servo gira al contrario, scegli di conseguenza i valori di chiuso e aperto; il codice funziona in entrambi i versi.
- Torna agli angoli piccoli. Dall'angolo in cui il coperchio poggia completamente sul bordo, torna indietro di 2–3 gradi nel verso di apertura; questo diventa LID_CLOSED. Se il servo ronza a coperchio chiuso, lo sta premendo.
- Con il sacchetto montato e il cestino vuoto, annota il valore letto dal sensore di riempimento; è EMPTY_CM. Per FULL_CM scegli 6–10 cm: quando i rifiuti arrivano a quell'altezza, il coperchio deve ancora chiudersi senza fatica e il sensore misurare in modo affidabile, perché l'HC-SR04 non misura sotto i 2–3 cm.
- Tieni la mano davanti al sensore della mano a 5, 10, 20 e 30 cm e guarda i valori. Poi passa camminando davanti al cestino e annota il valore più vicino letto. HAND_CM deve restare sotto quel valore; per la maggior parte dei cestini vanno bene 15–25 cm.
- Inserisci i valori trovati nelle impostazioni in cima al codice principale e caricalo.
Codice principale: rilevamento della mano, tempo di attesa e chiusura lenta
Nel codice principale non c'è nessun delay() lungo; tutto si basa su marcatori temporali registrati con millis(). Così, anche mentre il coperchio si chiude, il sensore della mano continua a essere letto, e se una mano si riavvicina il coperchio si riapre subito. È una misura di sicurezza semplice ma importante che evita che il coperchio si chiuda sulla mano di un bambino. I dettagli della temporizzazione non bloccante sono in millis() e delay(), il ragionamento per stati nell'articolo macchina a stati con 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}| Funzione o impostazione | Cosa fa |
|---|---|
| pingCm() | Fa una lettura; restituisce 999 se entro 30 ms non arriva l'eco |
| medianCm() | Fa 5 letture a 60 ms l'una dall'altra, le ordina e restituisce quella centrale |
| toPercent() | Converte la distanza in una percentuale da 0 a 100 in base a EMPTY_CM e FULL_CM |
| handHits | Conta in quante letture consecutive si è vista una mano; una singola lettura errata non apre il coperchio |
| OPEN_TIME | Tempo dall'ultimo momento in cui si è vista la mano all'inizio della chiusura (4000 ms) |
| CLOSE_STEP_MS | Tempo per grado durante la chiusura: 100 gradi × 20 ms = chiusura in 2 secondi |
| lid.detach() | Smette di inviare impulsi al servo 0,4 s dopo la chiusura del coperchio |
| fullWarned | Fa suonare l'avviso di pieno una sola volta; si azzera quando il riempimento scende sotto il 70% |
Il trucco della chiusura lenta è che l'angolo viene calcolato dal tempo trascorso invece di essere aumentato di un grado alla volta. 600 ms dopo l'inizio della chiusura il coperchio deve aver percorso 600 / 20 = 30 gradi; anche se il ciclo viene rallentato per un attimo da una misura di riempimento o dal buzzer, il codice porta il coperchio all'angolo giusto e il movimento non va a scatti. Per una chiusura ancora più lenta aumenta CLOSE_STEP_MS: con 30 ms, 100 gradi richiedono 3 secondi. Quando il coperchio si è chiuso, il servo viene rilasciato con detach(); il coperchio poggia già sul bordo, il servo non deve tenerlo, non ronza e consuma meno batteria. Anche chiamare write() prima di attach() è voluto: all'accensione di Arduino il servo non salta prima a 90 gradi, ma parte direttamente in posizione chiusa.
Misura del riempimento e avvisi con LED e buzzer
Il sensore di riempimento misura la distanza tra il lato inferiore del coperchio e il punto più alto dei rifiuti. Questa distanza è massima con il cestino vuoto (EMPTY_CM) e minima quando è pieno (FULL_CM). La percentuale si ottiene con questa formula: Riempimento (%) = (EMPTY_CM − misura) / (EMPTY_CM − FULL_CM) × 100. Per esempio, con EMPTY_CM = 35 cm, FULL_CM = 8 cm e una misura di 20 cm, il riempimento è (35 − 20) / 27 × 100 ≈ 55% e si accende il LED giallo. Se la misura supera i 35 cm (il suono è rimbalzato in diagonale sul fondo) il risultato viene bloccato allo 0%, se è inferiore a 8 cm al 100%.
| Riempimento | LED | Buzzer | Significato |
|---|---|---|---|
| 0–49% | Verde | Silenzioso | C'è ancora spazio |
| 50–79% | Giallo | Silenzioso | Pieno per più di metà, da svuotare presto |
| 80–89% | Rosso | Silenzioso | Quasi pieno |
| 90–100% | Rosso | 3 bip brevi quando si riempie, 2 bip a ogni apertura | Da svuotare subito |
La superficie dei rifiuti non è piatta: se una sola bottiglia sta in piedi proprio sotto il sensore, la misura vede quel punto e il cestino sembra più pieno di quanto sia. Il filtro mediano elimina le singole letture anomale, ma non può correggere la forma dei rifiuti; considera quindi soglie come l'80% e il 90% un avviso di "da svuotare" più che una misura esatta. Perché l'avviso non suoni di continuo, il codice usa l'isteresi: l'avviso di pieno suona una volta al 90% e non suona più finché il cestino non viene svuotato e il riempimento non scende sotto il 70%. Spieghiamo in dettaglio le differenze tra i filtri nell'articolo filtrare i dati dei sensori.
Alimentazione a batterie: quanto dura?
Usare il cestino intelligente senza cavo è allettante, ma l'Arduino Uno non è una scheda adatta alle batterie. Con il suo chip USB, il regolatore e il LED di accensione assorbe circa 45–50 mA senza fare nulla. Aggiungendo la corrente a riposo di due HC-SR04, un LED acceso e l'elettronica del servo rilasciato, la corrente media sale a circa 70–80 mA. I 100–250 mA che il servo assorbe in apertura e chiusura durano solo pochi secondi, quindi in un cestino aperto 50 volte al giorno alzano la media soltanto di circa 1 mA. La durata si calcola approssimativamente come capacità (mAh) / corrente media (mA); per calcolarla con i tuoi valori usa lo strumento Durata della batteria.
| Alimentazione | Collegamento | Durata indicativa (75 mA) | Giudizio |
|---|---|---|---|
| Batteria da 9 V (circa 500 mAh) | VIN | 5–6 ore | Sconsigliata: la tensione crolla all'avvio del servo e la scheda si riavvia |
| 6 pile AA alcaline (9 V) | Jack DC o VIN | Circa 1 giorno | Sufficiente per il giorno della presentazione |
| 2 × 18650 (7,4 V, 2500 mAh) | VIN; LM2596 regolato a 5–6 V per il servo | Circa 1,5 giorni | Ricaricabile; servono un caricatore 2S e una scheda di protezione |
| Power bank da 10.000 mAh | USB | 3–4 giorni | Alcuni power bank si spengono con carichi sotto i 50–100 mA |
| Alimentatore da 5 V e 2 A | USB o pin 5V | Illimitata | La scelta migliore per l'uso fisso in casa |

La conclusione della tabella è chiara: un cestino con Uno a batterie è ideale per presentazioni e dimostrazioni, ma per un cestino che deve funzionare per mesi in casa usa un alimentatore da 5 V. Come regolare il modulo step-down tra due 18650 e il servo lo spiega l'articolo LM2596. Se vuoi davvero allungare la durata, al posto dell'Uno puoi usare un Arduino Pro Mini senza LED di accensione, mettere il processore in sospensione tra una misura e l'altra e accendere i LED solo per un attimo quando il coperchio si apre; con questi accorgimenti la corrente media scende a 15–20 mA e le stesse batterie durano da 4 a 5 volte di più. Le differenze tra i modi di alimentare un Arduino (USB, jack DC, VIN, pin 5V) sono nell'articolo Come alimentare Arduino.
Avviso di riempimento con ESP32 (Telegram)
Per vedere il LED quando il cestino è pieno devi andarci vicino; un ESP32, invece, può mandare l'informazione direttamente sul tuo telefono. Il modo più semplice è Telegram, perché è gratuito e puoi creare il tuo bot in pochi minuti. Su Telegram scrivi a @BotFather, crea un bot con il comando /newbot e annota il token che ti fornisce. Poi invia una volta /start al tuo bot: un bot non può scrivere a chi non gli ha mai parlato. Per conoscere il tuo chat ID puoi scrivere a un bot come @userinfobot oppure usare la richiesta getUpdates della Bot API. Il codice non ha bisogno di librerie aggiuntive: invia una richiesta all'indirizzo sendMessage di Telegram con l'HTTPClient integrato dell'ESP32.
Il codice usa la stessa macchina a stati della versione per Uno; solo il segnale del servo viene generato con LEDC, l'unità PWM dell'ESP32, invece che con la libreria Servo. In un segnale a 50 Hz un impulso di 0,544–2,4 ms corrisponde a 0–180 gradi, e interrompere gli impulsi (scrivere 0 con ledcWrite) rilascia il servo. Il codice è per la versione 3.x del core ESP32 per Arduino; la versione 2.x non ha la funzione ledcAttach(), quindi aggiorna il core dal Gestore schede.

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}L'avviso viene inviato una sola volta quando il cestino arriva all'85% e viene riarmato con un messaggio di "svuotato" quando il riempimento scende sotto il 30%, così il telefono non vibra ogni 3 secondi. Se manca il Wi-Fi o l'invio non riesce, il codice riprova una volta al minuto e nel frattempo il cestino continua a funzionare normalmente. L'invio di un messaggio richiede 1–3 secondi per via della connessione cifrata (TLS), per questo il codice invia solo a coperchio chiuso. setInsecure() si collega senza verificare il certificato del server; per un progetto amatoriale va bene, ma ricorda che il token è un'informazione segreta e cancellalo prima di condividere il codice. Se nel monitor seriale vedi "Telegram HTTP 200" il messaggio è partito; 401 indica un token sbagliato, 400 un chat ID sbagliato e 403 che non hai ancora inviato /start al bot. Per un bot completo che risponde ai comandi, consulta la guida bot Telegram con ESP32.
Cestino intelligente con Raspberry Pi Pico (MicroPython)
Sul Raspberry Pi Pico la stessa logica si realizza in MicroPython. La funzione time_pulse_us() funziona come pulseIn() su Arduino; dato che restituisce un valore negativo quando non c'è eco, lo trattiamo come 999, cioè "nessuna eco". Per il servo si usano il PWM a 50 Hz e duty_ns(), che riceve la larghezza dell'impulso in nanosecondi; duty_ns(0) interrompe gli impulsi e rilascia il servo. I confronti di tempo si fanno con ticks_ms() e ticks_diff(); ticks_diff dà la differenza corretta anche quando il contatore ricomincia da capo. Se salvi il codice sul Pico come main.py con Thonny, parte da solo a ogni accensione della scheda. Con un Pico W puoi aggiungere il metodo dell'articolo web server con Pico W per mostrare il riempimento via 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)Consigli per presentare il progetto a scuola
Il cestino intelligente è un progetto molto frequente nelle fiere della scienza e nelle mostre scolastiche come TÜBİTAK 4006, quindi alla giuria interessa meno il coperchio che si apre e si chiude e più come hai definito il problema, come l'hai misurato e che conclusioni ne hai tratto. Ciò che trasforma il tuo progetto da semplice dimostrazione in un lavoro scientifico sono le misure: in quanti tentativi si è aperto, in quanto tempo, con quale precisione ha stimato il riempimento, quante ore sono durate le batterie. Puoi costruire la presentazione in questo ordine:
- Problema: Toccare il coperchio con la mano trasmette germi e i cestini che traboccano portano cattivi odori e insetti. Parti da dati reali contando quante persone toccano il coperchio di un cestino della tua scuola durante un intervallo.
- Obiettivo: Un cestino che si apre senza contatto, avvisa quando è pieno e, se vuoi, manda una notifica al telefono.
- Principio di funzionamento: Disegna un unico schema a blocchi con sensore, Arduino, servo, LED, buzzer, ESP32 e telefono. Spiega la velocità del suono e la formula "diviso 58" in una slide.
- Materiali e costi: Presenta l'elenco dei componenti e la scelta del servo in una tabella, insieme al calcolo della coppia.
- Dimostrazione dal vivo: Avvicina la mano e lascia che il coperchio si apra. Riempi il cestino di carta e mostra il LED che passa dal verde al giallo e al rosso; proietta sullo schermo il messaggio Telegram arrivato sul telefono.
- Risultati dei test: Compila la tabella qui sotto con le tue misure e trasforma i risultati in grafici.
- Conclusioni e miglioramenti: Racconta con sincerità cosa ha funzionato bene, cosa no e cosa cambieresti nella prossima versione.
| Test | Come si fa | Valore da annotare |
|---|---|---|
| Distanza di rilevamento | Tieni la mano a 5, 10, 15, 20 e 25 cm, 10 volte ciascuna | In quanti tentativi si è aperto a ogni distanza (es. 10/10) |
| Tempo di risposta | Riprendi al rallentatore (240 fps) con il telefono | Tempo tra la comparsa della mano e il primo movimento del coperchio (ms) |
| Aperture indesiderate | Passa davanti al cestino 20 volte camminando | Numero di aperture non volute |
| Precisione del riempimento | Riempi il cestino di carta a un quarto, a metà e a tre quarti, misurando con il righello | Differenza tra riempimento reale e percentuale misurata |
| Velocità di chiusura | Imposta CLOSE_STEP_MS a 5, 20 e 40 ms | Tempo di chiusura e livello di rumore misurato con il telefono |
| Durata delle batterie | Fallo funzionare con batterie cariche e misura la tensione ogni ora | Per quante ore ha funzionato il cestino |
Idee per migliorarlo
- Cestino con display: Mostra la percentuale di riempimento e una barra su un display OLED.
- Grazie a voce: Riproduci un breve "grazie" con un DFPlayer Mini ogni volta che il coperchio si apre.
- Misura del peso: Metti una cella di carico con HX711 sotto il cestino per misurare quanti chili di rifiuti contiene e valuta insieme volume e peso.
- Separatore per la raccolta differenziata: Usa un sensore di colore e un secondo servo per indirizzare i rifiuti in due scomparti.
- Avviso di cattivo odore: Rileva con un MQ-135 quando l'aria dentro il cestino peggiora.
- Cestino solare da esterno: Elimina del tutto il cavo con un piccolo pannello solare, una cella 18650, un modulo di ricarica TP4056 e un modulo step-up a 5 V.
- Registro del riempimento: Salva le misure su Fogli Google con l'ESP32 e mostra in un grafico in quali giorni il cestino si riempie più in fretta.
- Per tutta la scuola o il condominio: Controlla da centinaia di metri i cestini del cortile che il Wi-Fi non raggiunge, con un modulo LoRa.
Errori comuni e soluzioni
| Sintomo | Causa probabile | Soluzione |
|---|---|---|
| Il coperchio si apre e si chiude da solo | Il sensore vede un muro vicino o le persone che passano | Abbassa HAND_CM, inclina il sensore verso l'alto, lascia 30 cm liberi davanti |
| Arduino si riavvia quando il coperchio si apre | La corrente di spunto del servo fa crollare la linea dei 5 V | Condensatore da 470 µF, servo potente con alimentazione separata, GND in comune |
| Il servo ronza e si scalda a coperchio chiuso | LID_CLOSED schiaccia il coperchio sul bordo | Arretra l'angolo di 2–3 gradi; non cancellare la riga con detach() |
| Il coperchio si ferma a metà o trema | Coppia insufficiente o assi disallineati | Servo più potente, coperchio più leggero, contrappeso; allinea l'albero del servo alla cerniera |
| Il riempimento risulta alto anche a cestino vuoto | Bordo del sacchetto davanti al sensore o echi dalle pareti in un cestino stretto | Ripiega il sacchetto verso l'esterno, centra il sensore, misura di nuovo EMPTY_CM |
| Il riempimento non cambia mai | Il sensore non riceve l'eco (999) o il filo ECHO si è staccato | Osserva il valore grezzo in cm nel monitor seriale e controlla i collegamenti |
| Sull'ESP32 la distanza è sempre 999 o assurda | Partitore su ECHO mancante, sbagliato o fili invertiti | Controlla il partitore 1 kΩ / 2 kΩ e l'ordine di TRIG ed ECHO |
| L'ESP32 si riavvia di continuo | Wi-Fi e servo assorbono corrente insieme, cavo USB scadente | Usa un cavo corto e di qualità, alimenta il servo con una sorgente separata da 5 V |
| Il messaggio Telegram non arriva | Token o chat ID sbagliati, /start non inviato al bot | Leggi il codice HTTP nel monitor seriale (401, 400, 403) |
Poiché riunisce in un solo progetto la lettura dei sensori, il controllo di un servo, la temporizzazione e la pianificazione dell'alimentazione, il cestino intelligente è uno dei modi più divertenti per imparare Arduino. Una volta costruito, realizzare un sensore di parcheggio o una porta automatica con gli stessi componenti diventa molto più facile. Nell'app BlueGrays trovi passo dopo passo i progetti di misura della distanza con l'HC-SR04, controllo dei servo, uso del buzzer, un sensore di parcheggio con ESP32 e un allarme di movimento con notifiche Telegram, con schemi elettrici e codice pronto all'uso.
Domande frequenti
Quali componenti servono per un cestino intelligente con Arduino?
Per la versione base bastano un Arduino Uno o Nano, un sensore a ultrasuoni HC-SR04, un servo adatto al peso del coperchio (SG90 per un coperchio leggero, MG996R per il coperchio di un vero cestino), cavetti jumper e un alimentatore da 5 V. Per misurare il riempimento si aggiungono un secondo HC-SR04, tre LED con resistenze da 220 Ω e un buzzer attivo. Se vuoi le notifiche sul telefono, puoi usare un ESP32 al posto di Arduino.
Come funziona un cestino intelligente?
Il sensore a ultrasuoni sul davanti emette un'onda sonora e misura quanto tempo impiega l'eco a tornare; quando vede una mano a meno di 20 cm, Arduino apre il coperchio con un servomotore. Qualche secondo dopo che la mano se n'è andata, il coperchio si chiude lentamente. Un secondo sensore sotto il coperchio misura l'altezza dei rifiuti e mostra il riempimento con i LED e un buzzer.
Perché il mio servo non riesce a sollevare il coperchio del cestino?
Di solito la coppia del servo è troppo bassa per il coperchio. La coppia necessaria è il peso del coperchio moltiplicato per la distanza del suo baricentro dalla cerniera; un coperchio da 120 g e 25 cm richiede circa 1,5 kg·cm, al limite di un SG90. Scegli un servo per il doppio del tuo calcolo, alleggerisci il coperchio o aggiungi un contrappeso, e verifica che l'alimentazione del servo fornisca abbastanza corrente.
Si può usare un sensore IR al posto dell'HC-SR04?
Sì, un sensore IR di ostacoli come l'FC-51 rileva una mano tra 2 e 30 cm e il codice è ancora più semplice, perché indica solo se c'è qualcosa oppure no. Però risente della luce del sole e vede più tardi le superfici scure. Un sensore PIR non è adatto al coperchio di un cestino, perché scatta per chiunque passi davanti.
Quanto dura a batterie un cestino intelligente con Arduino?
Un cestino con Arduino Uno assorbe in media 70–80 mA, quindi dura circa un giorno con 6 pile AA, un giorno e mezzo con due celle 18650 e 3–4 giorni con un power bank da 10.000 mAh. Per l'uso fisso si consiglia un alimentatore da 5 V. Un Pro Mini, la modalità di sospensione e i LED accesi solo quando serve possono allungare la durata di 4–5 volte.
Come ricevo una notifica sul telefono quando il cestino è pieno?
L'ESP32 si collega al Wi-Fi e, quando il riempimento supera la percentuale che hai impostato, invia una richiesta alla Bot API di Telegram che ti recapita un messaggio sul telefono. Per farlo servono un token del bot ottenuto da BotFather e il tuo chat ID. Per non inviare lo stesso avviso più volte, il codice aspetta che il cestino venga svuotato.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
HC-SR04 Misura della distanza (display seriale)
Il sensore HC-SR04 funziona come un pipistrello che trova la strada al buio: manda un suono che non puoi sentire, ne ascolta l'eco e misura quanto è lontano l'oggetto davanti a sé. L'Arduino scrive qu

Uso del cicalino
Ti sei mai chiesto come fa un campanello o un allarme a suonare? L'Arduino fa suonare un piccolo cicalino a intervalli brevi: bip, pausa, bip, pausa. Vedi come l'elettricità diventa suono e fai il pri

Utilizzo del servomotore
Il braccio di un piccolo servomotore ruota a turno in tre posizioni: tutto all'inizio, esattamente a metà e tutto alla fine, aspettando due secondi a ogni fermata. L'Arduino dice al motore a quale ang

Sensore di parcheggio con HC-SR04
Una versione in miniatura del sensore di parcheggio delle auto: più qualcosa si avvicina, più i bip accelerano. Il sensore di distanza misura l'oggetto davanti a sé; se è a più di 20 centimetri resta

ESP32 HC-SR04 Misuratore di distanza a ultrasuoni
Il sensore a ultrasuoni HC-SR04 calcola la distanza misurando il tempo di volo (Time of Flight) dell'onda sonora che emette dopo aver colpito un oggetto e essere rimbalzata indietro. In questo progetto invieremo un impulso di 10 microsecondi dall'ESP32 al pin TRIG del sensore e leggeremo il tempo di eco proveniente dal pin ECHO con `pulseIn()`. Visualizziamo la distanza calcolata in cm su un display LCD I2C 16x2. Poiché l'HC-SR04 funziona a 5V, è necessario un **divisore di tensione** sul pin EC

Servomotore ESP32 (controllo tramite potenziometro)
In questo progetto controlliamo un servomotore SG90 tramite un potenziometro. Regoliamo l'angolo del servomotore mappando il valore analogico letto dal potenziometro su un intervallo compreso tra 0 e 180 gradi. La libreria ESP32Servo genera segnali per servomotori sui GPIO adatti dell'ESP32 che possono funzionare come uscite; in questo progetto si utilizza GPIO 18. Si tratta di un metodo di controllo fondamentale per bracci robotici, gimbal per telecamere e meccanismi di porte automatiche.

Sensore di parcheggio ESP32 (HC-SR04 + barra a 5 LED + cicalino + OLED)
La versione ESP32 del sensore di parcheggio per auto! Grazie al sensore a ultrasuoni HC-SR04 misuriamo la distanza dall'oggetto davanti al veicolo. Man mano che la distanza diminuisce, la barra composta da 5 LED si riempie (da verde → giallo → rosso) e l'intervallo tra i bip del cicalino si accorcia. Quando ci si avvicina troppo (meno di 10 cm), l'allarme suona ininterrottamente e tutti i LED si accendono. Sul display OLED SSD1306 vengono visualizzati in caratteri grandi il valore istantaneo in

Sistema di Allarme Movimento ESP32 (PIR + Buzzer + LED + Telegram)
Un sistema di allarme di sicurezza intelligente che attiva buzzer e LED quando il sensore PIR rileva movimento, inviando contemporaneamente una notifica istantanea al proprietario tramite Telegram. Dispone di un tempo di attesa di 30 secondi per limitare la frequenza degli allarmi ripetuti; questo meccanismo non verifica la validità dei rilevamenti del sensore PIR né elimina le false attivazioni. Adatto a sicurezza domestica, monitoraggio di garage, magazzini o studi. Con l'ESP32-CAM è possibile
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 160 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

