Lixeira inteligente com Arduino: HC-SR04, tampa com servo e alerta de cheia
Monte uma lixeira inteligente com Arduino: detecção da mão com HC-SR04, tampa com servo e fechamento lento, LEDs de nível e buzzer, cálculo das pilhas e alertas no Telegram com ESP32.
Uma lixeira inteligente com Arduino é um projeto de automação simples, mas muito instrutivo, que abre a tampa sem você encostar nela, e é um dos projetos escolares mais populares. Um sensor ultrassônico HC-SR04 na frente da lixeira detecta a mão que se aproxima, o Arduino abre a tampa com um servo motor e, alguns segundos depois de você afastar a mão, fecha a tampa devagar em vez de deixá-la bater. Um segundo HC-SR04 embaixo da tampa mede a altura do lixo: conforme a lixeira enche, os LEDs verde, amarelo e vermelho acendem um após o outro e, quando ela fica totalmente cheia, um buzzer apita.
Neste guia começamos pela lista de peças e vamos passo a passo por onde montar os sensores, como calcular o torque do servo, como prender a tampa na dobradiça, a calibração e o código completo que cuida do tempo de espera e do fechamento lento ao mesmo tempo. Depois calculamos quanto tempo o projeto realmente funciona com pilhas, enviamos um alerta de nível com ESP32 para o seu celular como mensagem no Telegram e escrevemos o mesmo projeto para o Raspberry Pi Pico em MicroPython. No final você também encontra um roteiro de apresentação para um projeto escolar e uma tabela de testes para preencher com as suas próprias medições.

Como funciona uma lixeira inteligente?
O projeto é um loop que se repete dezenas de vezes por segundo. O Arduino faz uma leitura com o sensor da mão a cada 60 milissegundos. Se um objeto a menos de 20 cm aparecer em duas leituras seguidas, ele conclui que é uma mão e manda o servo para a posição aberta. Enquanto a mão continuar ali, o tempo de espera recomeça a cada leitura; 4 segundos depois que a mão sai, a tampa fecha devagar, a cerca de 50 graus por segundo. Se uma mão voltar enquanto a tampa está fechando, ela abre de novo na hora. Um segundo depois de a tampa fechar, o sensor de nível faz 5 leituras, usa a mediana como distância até a superfície do lixo e a converte em porcentagem. O código usa uma pequena máquina de estados em que a tampa está sempre em um de três estados: CLOSED (fechada), OPEN (aberta) e CLOSING (fechando).
| Peça | Função | Observação |
|---|---|---|
| Arduino Uno (ou Nano) | O cérebro: mede, decide e comanda o servo e os LEDs | Um ESP32 ou um Pico também servem |
| 2 × HC-SR04 | Um detecta a mão, o outro mede o nível de enchimento | 5 V, cerca de 15 mA durante a medição |
| Servo SG90, MG90S ou MG996R | Abre e fecha a tampa | Escolhido pelo peso da tampa (cálculo abaixo) |
| 3 LEDs (verde, amarelo, vermelho) e 3 × 220 Ω | Indicador de nível | Um resistor por LED |
| Buzzer ativo | Aviso de lixeira cheia | Um buzzer passivo precisa de tone() |
| Dobradiça, parafusos, cola quente | Sustenta a tampa e fixa o braço do servo | Quem deve sustentar o peso da tampa é a dobradiça, não o servo |
| Capacitor de 470 µF | Suaviza a corrente de partida do servo | Entre o + e o − do servo; atenção à polaridade |
| Fonte de alimentação | Fonte de 5 V, power bank ou pilhas | Cálculo da autonomia abaixo |
| ESP32 (opcional) | Alerta de nível no seu celular | Precisa de Wi-Fi |




Qual sensor usar para detectar a mão?
Vários sensores conseguem abrir uma lixeira, mas nem todos são igualmente adequados para o trabalho. O que você realmente precisa é perceber uma mão que se aproxima a 15–25 cm da lixeira e ignorar uma pessoa que só passa na frente. Como o HC-SR04 dá a distância em centímetros, você pode ajustar o limite no código exatamente como quiser, e é por isso que a maioria dos projetos de lixeira inteligente usa esse sensor. Estes são os pontos fortes e fracos das alternativas:
| Sensor | Como detecta | Para uma lixeira |
|---|---|---|
| HC-SR04 (ultrassônico) | Eco do som, 2–400 cm, cone estreito | Limite ajustado no código; a escolha mais equilibrada |
| Sensor de obstáculo IR (FC-51) | Luz infravermelha refletida, cerca de 2–30 cm | Barato e rápido; pode ser enganado pela luz do sol e por luvas escuras |
| PIR (HC-SR501) | Movimento do calor do corpo, até 7 m, ângulo amplo | Dispara para todo mundo que passa e segura por alguns segundos; não é adequado |
| APDS-9960 | Proximidade de curto alcance e gestos com a mão | Uma versão elegante que abre com um aceno; precisa de I2C e 3,3 V |
Você descobre como funciona um sensor PIR no artigo Sensor de movimento PIR e encontra uma versão controlada por gestos no guia do sensor de gestos APDS-9960. Em resumo, o HC-SR04 funciona assim: ele emite um pulso curto de som de 40 kHz e entrega o tempo de ida e volta do eco como um pulso no pino ECHO. Como o som viaja a cerca de 343 m/s no ar a 20 °C e o caminho é de ida e volta, dividir o tempo em microssegundos por 58 dá a distância em centímetros. Os detalhes estão no artigo Sensor de distância HC-SR04.
Posição dos sensores
O sensor da mão vai no corpo da lixeira, na parte de cima da face frontal, e não na tampa, porque, quando a tampa abrisse, o sensor giraria junto e ficaria apontando para o teto. Inclinar o sensor 10–20 graus para cima faz com que ele pegue a mão que se estende sobre a lixeira e deixe passar as pernas de quem anda na frente. Se a lixeira fica ao lado de uma parede ou de um armário, não pode haver nenhuma superfície fixa a menos de 30 cm na direção para onde o sensor aponta; caso contrário, ele confunde essa superfície com uma mão e a tampa nunca fecha.
O sensor de nível é colado embaixo da tampa, perto do centro, apontando bem para baixo. Nessa posição ele só mede algo que faça sentido enquanto a tampa está fechada; com a tampa aberta, ele aponta para uma parede ou para o teto. É por isso que o código mede o nível só um segundo depois de a tampa fechar. Embora o cone de som do sensor seja estreito (15 graus no datasheet, um pouco mais largo na prática), a 35 cm de profundidade ele cobre uma área de 10–20 cm de diâmetro; em lixeiras com menos de 20 cm de largura, o som pode bater nas paredes laterais e causar leituras falsas. Dobre a borda do saco de lixo para o lado de fora da lixeira: uma borda de saco pendurada para dentro fica na frente do sensor e faz a lixeira parecer cheia mesmo vazia.
Esquema de ligação: Arduino Uno, ESP32 e Pico
A tabela abaixo corresponde exatamente a todos os códigos deste artigo. No Uno, o sensor da mão vai em D2–D3, o sensor de nível em D4–D5, o servo em D9, os LEDs em D10–D12 e o buzzer em D8. Como a biblioteca Servo usa o Timer1 no Uno, ela desativa a saída PWM em D9 e D10; como só ligamos e desligamos o LED de D10, isso não nos afeta. Coloque um resistor de 220 Ω na frente de cada LED; você pode calcular o valor para o seu LED com a calculadora de resistor para LED. A disposição dos pinos da sua placa está na ferramenta Pinagem.
| Ligação | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| TRIG do sensor da mão | D2 | GPIO 18 | GP2 |
| ECHO do sensor da mão | D3 (direto) | GPIO 19, por um divisor de 1 kΩ / 2 kΩ | GP3, por um divisor de 1 kΩ / 2 kΩ |
| TRIG do sensor de nível | D4 | GPIO 25 | GP4 |
| ECHO do sensor de nível | D5 (direto) | GPIO 26, pelo divisor | GP5, pelo divisor |
| VCC dos HC-SR04 (os dois) | 5V | 5V (VIN) | VBUS |
| Sinal do servo (laranja) | D9 | GPIO 13 | GP15 |
| + do servo (vermelho) | 5V (SG90) ou externo de 5–6 V | 5V (VIN) ou externo de 5–6 V | VBUS ou externo de 5–6 V |
| LED verde, amarelo, vermelho (com 220 Ω) | D10, D11, D12 | GPIO 27, 32, 33 | GP10, GP11, GP12 |
| Buzzer ativo (+) | D8 | GPIO 23 | GP13 |
| Todos os GNDs | GND | GND | GND |
Escolha do servo e cálculo do torque
O problema mais comum em projetos de lixeira inteligente é uma tampa que abre até a metade e fica tremendo, ou que nem levanta. A causa quase sempre é um servo fraco demais para a tampa. Estimar o torque necessário é fácil: Torque (kg·cm) = peso da tampa (kg) × distância da dobradiça até o centro de massa (cm). Uma tampa plana e de espessura uniforme tem o centro de massa bem no meio; para uma tampa de 24 cm de comprimento, essa distância é de 12 cm. O momento mais difícil é o início da abertura, quando a tampa está na horizontal; conforme a tampa sobe, o torque necessário cai e, na vertical, fica quase zero. O valor de torque nos catálogos de servo é o torque de travamento (stall), o ponto em que o servo força e para; um servo trabalhando perto desse limite esquenta e desgasta as engrenagens. Por isso, multiplique o resultado por pelo menos 2.
| Tampa | Peso | Centro de massa | Torque necessário | Com margem de 2× | Sugestão |
|---|---|---|---|---|---|
| Papelão ou placa de espuma de 20 × 20 cm | 50 g | 10 cm | 0,5 kg·cm | 1,0 kg·cm | SG90 |
| Tampa plástica de lixeira de 25 cm | 120 g | 12,5 cm | 1,5 kg·cm | 3,0 kg·cm | MG996R (o MG90S fica no limite) |
| Tampa de MDF ou compensado de 30 cm | 300 g | 15 cm | 4,5 kg·cm | 9,0 kg·cm | MG996R com contrapeso |
| Servo | Torque (4,8 V / 6 V) | Engrenagens | Corrente | Observação |
|---|---|---|---|---|
| SG90 | 1,8 kg·cm (só existe a especificação em 4,8 V) | Plástico | 100–250 mA em movimento | Pode ser alimentado pelo pino 5V do Arduino |
| MG90S | 1,8 / 2,2 kg·cm | Metal | 150–300 mA em movimento | Mesmo tamanho do SG90, mais durável |
| MG996R | 9,4 / 11 kg·cm | Metal | 0,5–0,9 A em movimento, até 2,5 A quando travado | Precisa ser alimentado por uma fonte separada de 5–6 V |

Em vez de comprar um servo mais forte, equilibrar a tampa costuma resolver. Se a tampa tem um braço curto que passa para trás da dobradiça, alguns parafusos ou moedas colados ali equilibram parte do peso dela; esse contrapeso pode reduzir o torque necessário pela metade. Você também pode fazer o braço do servo empurrar a tampa em um ponto longe da dobradiça, mas a alavanca não dá torque de graça: quanto mais longe da dobradiça, menos força é preciso, só que o ponto empurrado percorre um caminho maior, então você precisa de um braço de servo mais comprido, e um braço mais comprido aumenta de novo o torque exigido do servo. Se você quer que a tampa abra perto de 90 graus, o torque necessário fica praticamente o mesmo de quando o servo está preso direto na dobradiça. Uma comparação detalhada dos tipos de servo está nos artigos O que é um servo motor? e servo MG996R.
Dicas de montagem: dobradiça, braço do servo e cabos
- Deixe a dobradiça sustentar o peso: a tampa deve estar presa a uma dobradiça de verdade (a própria dobradiça da lixeira ou uma pequena dobradiça de armário). Se você apoiar a tampa só no eixo do servo, todo o peso vai para o mancal do servo e as engrenagens plásticas do SG90 logo quebram.
- Alinhe o eixo do servo com o eixo da dobradiça: o jeito mais fácil é parafusar o servo na borda traseira da lixeira, de modo que o eixo fique bem na ponta da linha da dobradiça, e fixar o braço do servo na tampa com um parafuso ou cola quente. Se os eixos estiverem desalinhados, o servo força a tampa para o lado toda vez que abre.
- Fixe bem o corpo do servo: não segure o servo só com fita dupla face; o corpo vai girar quando ele levantar a tampa. Use dois parafusos pelas orelhas de fixação do servo ou uma pequena cantoneira em L.
- Não pressione a tampa contra a borda: ajuste o ângulo de fechado no ponto em que a tampa se apoia na borda, mas o servo não a empurra. Se o servo continuar forçando a tampa fechada, ele zumbe, esquenta e gasta corrente à toa.
- Deixe folga nos cabos: os fios do sensor de nível se movem com a tampa. Passe o cabo ao lado da dobradiça, com comprimento suficiente para não esticar nem com a tampa totalmente aberta, e prenda-o com uma abraçadeira.
- Mantenha a eletrônica longe do lixo: coloque o Arduino, as pilhas e a protoboard em uma caixinha colada na parte de trás ou embaixo da lixeira. Líquido ou umidade que vaze do saco de lixo não pode chegar ao circuito.
- Comece com uma maquete de papelão: para um projeto escolar, uma tampa leve feita de caixa de sapato ou placa de espuma funciona bem até com um SG90, e é um bom exemplo para mostrar o cálculo do torque na apresentação.
Montagem e calibração passo a passo
Antes de passar para o código principal, carregue o pequeno sketch de calibração abaixo. Ele leva o servo ao ângulo que você digitar no Monitor Serial e mostra a distância lida pelos dois sensores a cada meio segundo. Assim você mede os ângulos de tampa fechada e aberta, a profundidade da lixeira vazia e o limite de detecção da mão para a sua própria lixeira.
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}- Carregue o código sem o braço do servo encaixado e abra o Monitor Serial a 9600 baud; configure o final de linha como Nova linha (Newline). O servo vai para 90 graus.
- Digite 10 e pressione Enter. Segure a tampa fechada, encaixe o braço do servo no eixo nessa posição e depois fixe-o na tampa.
- Aumente o ângulo em passos de 20, 40, 60 até achar o valor em que a tampa fica totalmente aberta; normalmente fica na faixa de 90–110 graus. Não abra a tampa além da vertical. Esse valor vira LID_OPEN. Se o seu servo gira para o outro lado, escolha os valores de fechado e aberto de acordo; o código funciona nos dois sentidos.
- Volte para ângulos pequenos. A partir do ângulo em que a tampa assenta totalmente na borda, volte 2–3 graus no sentido de abertura; esse valor vira LID_CLOSED. Se o servo zumbe com a tampa fechada, ele está pressionando a tampa.
- Com o saco colocado e a lixeira vazia, anote o valor lido pelo sensor de nível; esse é o EMPTY_CM. Escolha 6–10 cm para FULL_CM: quando o lixo chegar a essa altura, a tampa ainda precisa fechar com facilidade e o sensor ainda precisa medir com segurança, porque o HC-SR04 não mede a menos de 2–3 cm.
- Segure a mão na frente do sensor da mão a 5, 10, 20 e 30 cm e observe as leituras. Depois passe andando na frente da lixeira e anote o menor valor lido. O HAND_CM precisa ficar abaixo desse valor; 15–25 cm funciona bem para a maioria das lixeiras.
- Coloque os valores que você encontrou nas configurações do início do código principal e carregue-o.
Código principal: detecção da mão, tempo de espera e fechamento lento
Não há nenhum delay() longo no código principal; tudo é feito com marcas de tempo guardadas com millis(). Assim, mesmo enquanto a tampa está fechando, o sensor da mão continua sendo lido e, se uma mão se aproximar de novo, a tampa abre imediatamente. É um recurso de segurança simples, mas importante, que impede a tampa de fechar na mão de uma criança. Os detalhes da temporização sem bloqueio estão em millis() e delay(), e a ideia de pensar em estados está no artigo máquina de estados no 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}| Função ou configuração | O que faz |
|---|---|
| pingCm() | Faz uma leitura; retorna 999 se nenhum eco chegar em 30 ms |
| medianCm() | Faz 5 leituras com 60 ms de intervalo, ordena e retorna a do meio |
| toPercent() | Converte a distância em uma porcentagem de 0–100 com base em EMPTY_CM e FULL_CM |
| handHits | Conta em quantas leituras seguidas uma mão foi vista; uma única leitura errada não abre a tampa |
| OPEN_TIME | Tempo entre a última vez que a mão foi vista e o início do fechamento (4000 ms) |
| CLOSE_STEP_MS | Tempo por grau durante o fechamento: 100 graus × 20 ms = fechamento em 2 segundos |
| lid.detach() | Para de enviar pulsos ao servo 0,4 s depois que a tampa fechou |
| fullWarned | Faz o aviso de lixeira cheia tocar só uma vez; é zerado quando o nível cai abaixo de 70% |
O truque do fechamento lento é que o ângulo é calculado a partir do tempo decorrido, em vez de aumentar um grau por vez. 600 ms depois do início do fechamento, a tampa deve ter percorrido 600 / 20 = 30 graus; mesmo que o loop atrase por um instante por causa de uma medição de nível ou do buzzer, o código coloca a tampa no ângulo certo e o movimento não fica aos trancos. Para deixar o fechamento ainda mais lento, aumente CLOSE_STEP_MS: com 30 ms, os 100 graus levam 3 segundos. Depois que a tampa fecha, o servo é liberado com detach(); a tampa já está apoiada na borda, então o servo não precisa segurá-la, não zumbe e gasta menos pilha. Chamar write() antes de attach() no código também é proposital: quando o Arduino liga, o servo não pula primeiro para 90 graus, mas já começa na posição fechada.
Medição do nível e avisos com LED e buzzer
O sensor de nível mede a distância entre a parte de baixo da tampa e o ponto mais alto do lixo. Essa distância é maior quando a lixeira está vazia (EMPTY_CM) e menor quando está cheia (FULL_CM). A porcentagem sai desta fórmula: Nível (%) = (EMPTY_CM − medida) / (EMPTY_CM − FULL_CM) × 100. Por exemplo, com EMPTY_CM = 35 cm, FULL_CM = 8 cm e uma distância medida de 20 cm, o nível é (35 − 20) / 27 × 100 ≈ 55% e o LED amarelo acende. Se a leitura for maior que 35 cm (o som bateu na diagonal no fundo), o resultado é limitado a 0%; se for menor que 8 cm, é limitado a 100%.
| Nível | LED aceso | Buzzer | Significado |
|---|---|---|---|
| 0–49% | Verde | Silencioso | Ainda tem bastante espaço |
| 50–79% | Amarelo | Silencioso | Mais da metade cheia, esvazie em breve |
| 80–89% | Vermelho | Silencioso | Quase cheia |
| 90–100% | Vermelho | 3 bipes curtos quando enche, 2 bipes toda vez que abre | Esvazie agora |
A superfície do lixo não é plana: se uma única garrafa ficar em pé bem embaixo do sensor, a leitura enxerga esse ponto e mostra a lixeira mais cheia do que está. O filtro de mediana elimina a leitura isolada que destoa, mas não corrige o formato do lixo; por isso, trate limites como 80% e 90% como um aviso de “hora de esvaziar”, e não como uma medida exata. Para o aviso não ficar apitando o tempo todo, o código usa histerese: o aviso de cheia toca uma vez em 90% e não volta a tocar até a lixeira ser esvaziada e o nível cair abaixo de 70%. Explicamos em detalhes as diferenças entre os filtros no artigo filtragem de dados de sensores.
Alimentação com pilhas: quanto tempo dura?
Usar a lixeira inteligente sem cabo é tentador, mas o Arduino Uno não é uma placa amiga das pilhas. Com o chip USB, o regulador e o LED de alimentação, a placa puxa cerca de 45–50 mA sem fazer nada. Somando a corrente em repouso de dois HC-SR04, um LED aceso e a eletrônica do servo liberado, a corrente média sobe para cerca de 70–80 mA. Os 100–250 mA que o servo puxa ao abrir e fechar duram só alguns segundos, então, em uma lixeira aberta 50 vezes por dia, eles aumentam a média em apenas cerca de 1 mA. A duração da bateria é aproximadamente capacidade (mAh) / corrente média (mA); para calcular com os seus valores, use a ferramenta Duração da bateria.
| Fonte de energia | Ligação | Autonomia aprox. (75 mA) | Avaliação |
|---|---|---|---|
| Bateria de 9 V tipo PP3 (cerca de 500 mAh) | VIN | 5–6 horas | Não recomendada: a tensão despenca quando o servo arranca e a placa reinicia |
| 6 pilhas AA alcalinas (9 V) | Conector DC ou VIN | Cerca de 1 dia | Suficiente para o dia da apresentação |
| 2 × 18650 (7,4 V, 2500 mAh) | VIN; LM2596 ajustado em 5–6 V para o servo | Cerca de 1,5 dia | Recarregável; precisa de carregador 2S e placa de proteção |
| Power bank de 10.000 mAh | USB | 3–4 dias | Alguns power banks desligam sozinhos com cargas abaixo de 50–100 mA |
| Fonte de 5 V 2 A | USB ou pino 5V | Ilimitada | Melhor escolha para uso permanente em casa |

A conclusão da tabela é clara: uma lixeira com Uno a pilhas é ideal para apresentações e demonstrações, mas, para uma lixeira que vai funcionar em casa por meses, use uma fonte de 5 V. Como ajustar o módulo abaixador que vai entre as duas 18650 e o servo está explicado no artigo sobre o LM2596. Se você quer mesmo esticar a duração das pilhas, pode usar um Arduino Pro Mini sem o LED de alimentação no lugar do Uno, colocar o processador para dormir entre as leituras e acender os LEDs só por um instante quando a tampa abre; com essas medidas, a corrente média cai para 15–20 mA e as mesmas pilhas duram 4–5 vezes mais. As diferenças entre as formas de alimentar um Arduino (USB, conector DC, VIN, pino 5V) estão no artigo Como alimentar um Arduino.
Alerta de nível com ESP32 (Telegram)
Para ver o LED quando a lixeira está cheia, você precisa ir até ela; já um ESP32 pode mandar essa informação direto para o seu celular. O jeito mais fácil é o Telegram, porque é grátis e você cria o seu próprio bot em minutos. No Telegram, mande uma mensagem para o @BotFather, crie um bot com o comando /newbot e anote o token que ele fornecer. Depois envie /start uma vez para o seu bot; um bot não consegue mandar mensagem para quem nunca falou com ele. Para descobrir o seu chat ID, você pode mandar mensagem para um bot como o @userinfobot ou usar a requisição getUpdates da Bot API. O código não precisa de nenhuma biblioteca extra: ele envia uma requisição para o endereço sendMessage do Telegram com o próprio HTTPClient do ESP32.
O código usa a mesma máquina de estados da versão para Uno; só o sinal do servo é gerado pela unidade PWM do ESP32, o LEDC, em vez da biblioteca Servo. Em um sinal de 50 Hz, um pulso de 0,544–2,4 ms corresponde a 0–180 graus, e parar os pulsos (escrevendo 0 com ledcWrite) libera o servo. O código é para a versão 3.x do core Arduino do ESP32; a versão 2.x não tem a função ledcAttach(), então atualize o core no Gerenciador de Placas.

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}O alerta é enviado uma vez quando a lixeira chega a 85% e é rearmado com uma mensagem de “esvaziada” quando o nível cai abaixo de 30%, para o seu celular não vibrar a cada 3 segundos. Se não houver Wi-Fi ou o envio falhar, o código tenta de novo uma vez por minuto e, enquanto isso, a lixeira continua funcionando normalmente. Enviar uma mensagem leva 1–3 segundos por causa da conexão criptografada (TLS), e é por isso que o código só envia com a tampa fechada. setInsecure() conecta sem verificar o certificado do servidor; para um projeto de hobby tudo bem, mas lembre que o token é uma informação secreta e apague-o antes de compartilhar o código. Se aparecer “Telegram HTTP 200” no Monitor Serial, a mensagem foi enviada; 401 significa token errado, 400 significa chat ID errado e 403 significa que você ainda não enviou /start para o bot. Para um bot completo que responde a comandos, veja o guia Bot do Telegram com ESP32.
Lixeira inteligente com Raspberry Pi Pico (MicroPython)
No Raspberry Pi Pico, a mesma lógica é montada com MicroPython. A função time_pulse_us() funciona como o pulseIn() do Arduino; como ela retorna um valor negativo quando não há eco, tratamos isso como 999, ou seja, “sem eco”. O servo usa PWM de 50 Hz e duty_ns(), que recebe a largura do pulso em nanossegundos; duty_ns(0) para os pulsos e libera o servo. As comparações de tempo são feitas com ticks_ms() e ticks_diff(); ticks_diff dá a diferença correta mesmo quando o contador dá a volta. Se você salvar o código no Pico como main.py pelo Thonny, ele roda sozinho toda vez que a placa liga. Se estiver usando um Pico W, pode acrescentar o método do artigo servidor web no Pico W para mostrar o nível pelo 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)Dicas para a apresentação de um projeto escolar
A lixeira inteligente é um projeto muito comum em feiras de ciências e mostras escolares, como as exposições TÜBİTAK 4006 na Turquia; por isso, os avaliadores se interessam menos pela tampa abrindo e fechando e mais por como você definiu um problema, como o mediu e o que concluiu. O que transforma o seu projeto de uma demonstração comum em um estudo científico é a medição: em quantas tentativas ela abriu, com que rapidez abriu, com que precisão estimou o nível, quantas horas as pilhas duraram. Você pode organizar a apresentação nesta ordem:
- Problema: tocar a tampa com a mão espalha germes, e lixeiras transbordando causam mau cheiro e atraem insetos. Comece com dados reais, contando quantas pessoas tocam a tampa de uma lixeira da sua escola durante um intervalo.
- Objetivo: uma lixeira que abre sem ser tocada, avisa quando está cheia e, opcionalmente, notifica o seu celular.
- Como funciona: desenhe um único diagrama de blocos com o sensor, o Arduino, o servo, os LEDs, o buzzer, o ESP32 e o celular. Explique a velocidade do som e a fórmula de dividir por 58 em um slide.
- Materiais e custo: apresente a lista de peças e a escolha do servo em uma tabela, junto com o cálculo do torque.
- Demonstração ao vivo: aproxime a mão e deixe a tampa abrir. Encha a lixeira de papel e mostre o LED passando de verde para amarelo e vermelho; projete na tela a mensagem do Telegram que chega no seu celular.
- Resultados dos testes: preencha a tabela abaixo com as suas próprias medições e transforme os resultados em gráficos.
- Conclusão e melhorias: explique com sinceridade o que funcionou bem, o que não funcionou e o que você mudaria na próxima versão.
| Teste | Como fazer | Valor a registrar |
|---|---|---|
| Distância de detecção | Segure a mão a 5, 10, 15, 20 e 25 cm, 10 vezes em cada uma | Em quantas tentativas a tampa abriu em cada distância (ex.: 10/10) |
| Tempo de resposta | Filme em câmera lenta (240 fps) com o celular | Tempo entre a mão aparecer e a tampa começar a se mover (ms) |
| Aberturas falsas | Passe andando na frente da lixeira 20 vezes | Número de aberturas indesejadas |
| Precisão do nível | Encha a lixeira de papel até um quarto, metade e três quartos, medindo com uma régua | Diferença entre o nível real e a porcentagem medida |
| Velocidade de fechamento | Ajuste CLOSE_STEP_MS em 5, 20 e 40 ms | Tempo de fechamento e nível de ruído medido com o celular |
| Duração das pilhas | Ligue com as pilhas carregadas e meça a tensão a cada hora | Quantas horas a lixeira funcionou |
Ideias de melhorias
- Lixeira com display: mostre a porcentagem do nível e um gráfico de barras em um display OLED.
- Agradecimento falado: toque um som curto de “obrigado” com um DFPlayer Mini toda vez que a tampa abrir.
- Medição de peso: coloque um HX711 e uma célula de carga embaixo da lixeira para medir quantos quilos de lixo ela tem e combine volume e peso.
- Separador de recicláveis: use um sensor de cor e um segundo servo para direcionar os resíduos para dois compartimentos.
- Alerta de mau cheiro: detecte com um MQ-135 quando o ar dentro da lixeira fica ruim.
- Lixeira externa com energia solar: elimine o cabo de vez com um pequeno painel solar, uma célula 18650, um módulo carregador TP4056 e um módulo elevador de 5 V.
- Registro do nível: grave as leituras no Google Sheets com o ESP32 e mostre em gráfico em quais dias a lixeira enche mais rápido.
- Escola ou condomínio inteiro: monitore, a centenas de metros de distância, lixeiras em áreas externas onde o Wi-Fi não chega, com um módulo LoRa.
Erros comuns e soluções
| Sintoma | Causa provável | Solução |
|---|---|---|
| A tampa abre e fecha sozinha | O sensor está vendo uma parede próxima ou pessoas passando na frente | Diminua HAND_CM, incline o sensor para cima, deixe 30 cm livres na frente dele |
| O Arduino reinicia quando a tampa abre | A corrente de partida do servo derruba a linha de 5 V | Capacitor de 470 µF, servo forte com alimentação separada, GND comum |
| O servo zumbe e esquenta com a tampa fechada | LID_CLOSED pressiona a tampa contra a borda | Recue o ângulo 2–3 graus; não apague a linha do detach() |
| A tampa para no meio do caminho ou treme | Torque insuficiente ou eixos desalinhados | Servo mais forte, tampa mais leve, contrapeso; alinhe o eixo do servo com a dobradiça |
| O nível aparece alto mesmo com a lixeira vazia | Borda do saco na frente do sensor ou eco nas paredes de uma lixeira estreita | Dobre o saco para fora, centralize o sensor, meça o EMPTY_CM de novo |
| O nível nunca muda | O sensor não recebe eco (999) ou o fio do ECHO se soltou | Acompanhe o valor bruto em cm no Monitor Serial e confira a ligação |
| No ESP32, a distância é sempre 999 ou sem sentido | Divisor do ECHO faltando ou errado, ou fios trocados | Confira o divisor de 1 kΩ / 2 kΩ e a ordem TRIG-ECHO |
| O ESP32 fica reiniciando | O Wi-Fi e o servo puxam corrente ao mesmo tempo; cabo USB fraco | Use um cabo curto e de boa qualidade e alimente o servo por uma fonte separada de 5 V |
| Nenhuma mensagem do Telegram chega | Token ou chat ID errado, /start não enviado ao bot | Leia o código HTTP no Monitor Serial (401, 400, 403) |
Como reúne leitura de sensores, controle de servo, temporização e planejamento de energia em um só projeto, a lixeira inteligente é uma das formas mais divertidas de aprender Arduino. Depois de montá-la, fazer um sensor de estacionamento ou uma porta automática com as mesmas peças fica muito mais fácil. No app BlueGrays você encontra, passo a passo e com esquemas do circuito e códigos prontos, projetos de medição de distância com o HC-SR04, controle de servo, uso de buzzer, sensor de estacionamento com ESP32 e alarme de movimento com alertas no Telegram.
Perguntas frequentes
Quais materiais preciso para uma lixeira inteligente com Arduino?
Para a versão básica, bastam um Arduino Uno ou Nano, um sensor ultrassônico HC-SR04, um servo adequado ao peso da tampa (um SG90 para uma tampa leve, um MG996R para a tampa de uma lixeira de verdade), jumpers e uma fonte de 5 V. Para medir o nível, acrescente um segundo HC-SR04, três LEDs com resistores de 220 Ω e um buzzer ativo. Se quiser alertas no celular, você pode usar um ESP32 no lugar do Arduino.
Como funciona uma lixeira inteligente?
O sensor ultrassônico na frente da lixeira emite uma onda sonora e mede quanto tempo o eco leva para voltar; quando ele vê uma mão a menos de 20 cm, o Arduino abre a tampa com um servo motor. Alguns segundos depois que a mão sai, a tampa fecha devagar. Um segundo sensor embaixo da tampa mede a altura do lixo e mostra o nível com LEDs e um buzzer.
Por que o meu servo não consegue levantar a tampa da lixeira?
Normalmente o torque do servo é baixo demais para a tampa. O torque necessário é o peso da tampa multiplicado pela distância da dobradiça até o centro de massa; uma tampa de 120 g e 25 cm precisa de cerca de 1,5 kg·cm, o que está no limite de um SG90. Escolha um servo especificado para o dobro do seu cálculo, deixe a tampa mais leve ou acrescente um contrapeso e confira se a alimentação do servo fornece corrente suficiente.
Posso usar um sensor IR em vez do HC-SR04 em uma lixeira inteligente?
Sim, um sensor de obstáculo IR como o FC-51 detecta uma mão de 2 a 30 cm, e o código fica ainda mais simples, porque ele só informa se há algo ali ou não. Porém, ele é afetado pela luz do sol e demora mais para ver superfícies escuras. Um sensor PIR não é adequado para a tampa de uma lixeira, porque dispara para todo mundo que passa na frente.
Quanto tempo uma lixeira inteligente com Arduino funciona com pilhas?
Uma lixeira com Arduino Uno puxa 70–80 mA em média, então funciona cerca de um dia com 6 pilhas AA, cerca de um dia e meio com duas células 18650 e 3–4 dias com um power bank de 10.000 mAh. Para uso permanente, recomenda-se uma fonte de 5 V. Um Pro Mini, o modo de suspensão e acender os LEDs só quando necessário podem fazer as pilhas durarem 4–5 vezes mais.
Como recebo uma notificação no celular quando a lixeira enche?
O ESP32 se conecta ao Wi-Fi e, quando o nível passa da porcentagem que você definiu, envia uma requisição à Bot API do Telegram, que entrega uma mensagem no seu celular. Para isso, você precisa de um token de bot do BotFather e do seu próprio chat ID. Para não mandar o mesmo alerta várias vezes, o código espera até a lixeira ser esvaziada.
Projetos para montar depois desta leitura
Ver todos os projetos
Medição de distância com HC-SR04 (Monitor Serial)
O sensor HC-SR04 funciona como um morcego que se orienta no escuro: emite um som que você não consegue ouvir, escuta o eco e mede a distância até o objeto à sua frente. O Arduino escreve essa distânci

Uso do buzzer
Já se perguntou como uma campainha ou um alarme produz som? O Arduino faz um pequeno buzzer emitir bipes em intervalos curtos: bipe, pausa, bipe, pausa. Você vê como a eletricidade produz som e dá o p

Uso de servo motor
O braço de um pequeno servo motor gira para três posições em sequência: o início, o meio e o fim do percurso, esperando dois segundos em cada parada. O Arduino informa ao motor em qual ângulo ficar, e

Sensor de estacionamento com HC-SR04
Uma versão em miniatura do sensor de estacionamento de um carro: quanto mais perto um objeto chega, mais rápidos ficam os bipes. O sensor de distância mede a distância até o objeto à sua frente; se el

Medidor ultrassônico de distância HC-SR04 com ESP32
O sensor ultrassônico HC-SR04 calcula a distância medindo o tempo que a onda sonora emitida leva para atingir um objeto e retornar (tempo de voo). Neste projeto, enviaremos um pulso de 10 microssegundos do ESP32 ao pino TRIG do sensor e leremos a duração do eco no pino ECHO com pulseIn(). Exibiremos a distância calculada em centímetros em um LCD I2C 16x2. Como o HC-SR04 opera a 5V, é preciso usar um divisor de tensão no pino ECHO; caso contrário, o GPIO de 3,3V do ESP32 será danificado.

Servo motor com ESP32 (controle por potenciômetro)
Neste projeto, controlamos um servo motor SG90 com um potenciômetro. Ajustamos o ângulo do servo mapeando o valor analógico lido do potenciômetro para a faixa de 0 a 180 graus. A biblioteca ESP32Servo gera sinais para o servo nos GPIOs do ESP32 adequados e capazes de operar como saída; este projeto usa o GPIO 18. Esse é um método básico de controle para braços robóticos, estabilizadores de câmera e mecanismos de portas automáticas.

Sensor de estacionamento com ESP32 (HC-SR04 + barra de 5 LEDs + buzzer + OLED)
A versão com ESP32 do sensor de estacionamento de carro! Usamos o sensor ultrassônico HC-SR04 para medir a distância até o objeto à sua frente. Conforme a distância diminui, a barra de 5 LEDs vai se preenchendo (verde → amarelo → vermelho) e o intervalo entre os bipes do buzzer diminui. Quando o objeto chega muito perto (menos de 10 cm), o alarme soa continuamente e todos os LEDs acendem.

Alarme de movimento com ESP32 (PIR + buzzer + LED + Telegram)
Um sistema de alarme de segurança inteligente que aciona o buzzer e o LED quando o sensor PIR detecta movimento e, ao mesmo tempo, envia uma notificação instantânea ao proprietário pelo Telegram. Inclui um intervalo de espera de 30 segundos para limitar a frequência dos alarmes repetidos; esse mecanismo não valida as detecções do PIR nem elimina disparos falsos. Pode ser usado na segurança de casas, no monitoramento de garagens, depósitos ou salas de trabalho.
Mais artigos
App BlueGrays
Monte este circuito passo a passo pelo celular
Esquema do circuito, código e lista de componentes em uma só tela. Mais de 200 projetos com Arduino, ESP32 e Pico prontos para montar.
Escaneie com seu celular
Aponte a câmera para o código para abrir a loja de aplicativos do seu celular.
ou abrir a loja

