Akıllı Çöp Kutusu Arduino Projesi: HC-SR04, Servo ve Kod
Arduino akıllı çöp kutusu nasıl yapılır? HC-SR04 ile el algılama, servo ile kapak açma ve yavaş kapanma, doluluk LED'i, pil hesabı ve ESP32 Telegram bildirimi.
Akıllı çöp kutusu Arduino projesi, çöp kutusunun kapağına dokunmadan açılmasını sağlayan ve okul projelerinin en sevilenlerinden biri olan basit ama öğretici bir otomasyon devresidir. Kutunun önündeki HC-SR04 ultrasonik sensör yaklaşan eli algılar, Arduino bir servo motor ile kapağı açar ve elini çektikten birkaç saniye sonra kapağı çarpmadan, yavaşça kapatır. Kapağın altına koyacağın ikinci bir HC-SR04 ise çöpün yüksekliğini ölçer; kutu doldukça yeşil, sarı ve kırmızı LED'ler sırayla yanar, kutu tamamen dolunca buzzer öter.
Bu rehberde parça listesinden başlayıp sensörlerin nereye takılacağını, servo torkunun nasıl hesaplanacağını, kapağın menteşeye nasıl bağlanacağını, kalibrasyonu ve bekleme ile yavaş kapanmayı aynı anda yöneten tam kodu adım adım anlatıyoruz. Ardından projeyi pille çalıştırmanın gerçek süresini hesaplıyor, ESP32 ile doluluk bildirimini telefonuna Telegram mesajı olarak gönderiyor ve aynı projeyi Raspberry Pi Pico ile MicroPython'da yazıyoruz. Yazının sonunda okul projesi sunumu için bir sunum akışı ve kendi ölçümlerinle doldurabileceğin bir test tablosu da var.

Akıllı çöp kutusu nasıl çalışır?
Proje, saniyede onlarca kez tekrarlanan bir döngüdür. Arduino el sensörüyle 60 milisaniyede bir ölçüm yapar. Sensöre 20 cm'den yakın bir nesne art arda iki ölçümde görünürse bunun bir el olduğuna karar verir ve servoyu açık konuma gönderir. El orada kaldıkça bekleme süresi her ölçümde yeniden başlar; el çekildikten 4 saniye sonra kapak saniyede yaklaşık 50 derece hızla, yavaşça kapanır. Kapanma sırasında el yeniden gelirse kapak hemen geri açılır. Kapak kapandıktan bir saniye sonra doluluk sensörü 5 ölçüm alır, ortancasını çöpün yüzeyine olan uzaklık kabul eder ve bunu yüzdeye çevirir. Kodda kapağın her an üç durumdan birinde olduğu küçük bir durum makinesi kullanılır: KAPALI (CLOSED), AÇIK (OPEN) ve KAPANIYOR (CLOSING).
| Parça | Görevi | Not |
|---|---|---|
| Arduino Uno (ya da Nano) | Beyin: ölçer, karar verir, servoya ve LED'lere komut verir | ESP32 ve Pico da kullanılabilir |
| 2 adet HC-SR04 | Biri eli, diğeri doluluğu ölçer | 5 V, ölçüm sırasında yaklaşık 15 mA |
| SG90, MG90S ya da MG996R servo | Kapağı açar ve kapatır | Kapağın ağırlığına göre seçilir (hesabı aşağıda) |
| 3 LED (yeşil, sarı, kırmızı) ve 3 adet 220 Ω | Doluluk göstergesi | Her LED'e bir direnç |
| Aktif buzzer | Dolu uyarısı | Pasif buzzer kullanırsan tone() gerekir |
| Menteşe, vida, sıcak silikon | Kapağı taşır, servo kolunu sabitler | Kapağın ağırlığını servo değil menteşe taşımalı |
| 470 µF kondansatör | Servonun kalkış akımını yumuşatır | Servonun + ve − uçlarına, kutup yönüne dikkat |
| Güç kaynağı | 5 V adaptör, powerbank ya da pil | Süre hesabı aşağıda |
| ESP32 (isteğe bağlı) | Telefona doluluk bildirimi | Wi-Fi gerekir |




El algılama için hangi sensör?
Çöp kutusunu açmak için birkaç sensör kullanılabilir, ama hepsi bu işe aynı derecede uygun değildir. Asıl ihtiyacın, kutunun 15–25 cm yakınına uzanan bir eli fark etmek ve önünden yürüyüp geçen insanı görmezden gelmektir. HC-SR04 mesafeyi santimetre olarak verdiği için eşiği kodda istediğin gibi ayarlayabilirsin; akıllı çöp kutusu projelerinin çoğunda tercih edilmesinin nedeni budur. Diğer seçeneklerin güçlü ve zayıf yanları şöyle:
| Sensör | Nasıl algılar? | Çöp kutusu için |
|---|---|---|
| HC-SR04 (ultrasonik) | Ses yankısı, 2–400 cm, dar bir koni | Eşik kodla ayarlanır; en dengeli seçim |
| IR engel sensörü (FC-51) | Yansıyan kızılötesi ışık, yaklaşık 2–30 cm | Ucuz ve hızlı; güneş ışığında ve koyu renkli eldivende şaşırabilir |
| PIR (HC-SR501) | Vücut ısısının hareketi, 7 m'ye kadar, geniş açı | Önünden geçen herkeste tetiklenir ve birkaç saniye bekler; uygun değil |
| APDS-9960 | Kısa mesafede yakınlık ve el hareketi (jest) | El sallayınca açılan şık bir sürüm; I2C ve 3,3 V ister |
PIR sensörün çalışma mantığını PIR Hareket Sensörü yazısında, el hareketiyle çalışan sürümü ise APDS-9960 jest sensörü rehberinde bulabilirsin. HC-SR04'ün çalışma ilkesi kısaca şöyle: sensör 40 kHz'lik kısa bir ses patlaması gönderir ve yankının dönme süresini ECHO pininden bir darbe olarak verir. Ses havada 20 °C'de yaklaşık 343 m/s hızla gittiği ve yol gidiş-dönüş olduğu için mikrosaniye cinsinden süreyi 58'e bölmek mesafeyi santimetre olarak verir. Ayrıntılar HC-SR04 Mesafe Sensörü yazısında.
Sensörlerin yerleşimi
El sensörü kutunun gövdesine, ön yüzün üst kısmına takılır; kapağa takılmaz, çünkü kapak açılınca sensör de döner ve tavana bakar. Sensörü yukarıya doğru 10–20 derece eğik yerleştirmek, kutunun üstüne uzanan eli yakalamasını, önünden geçen bacakları ise kaçırmasını sağlar. Kutu bir duvarın ya da dolabın yanında duruyorsa sensörün baktığı yönde 30 cm içinde sabit bir yüzey olmamalı; aksi hâlde sensör o yüzeyi el sanar ve kapak hiç kapanmaz.
Doluluk sensörü kapağın altına, ortaya yakın bir yere ve tam aşağı bakacak şekilde yapıştırılır. Bu konumda sensör yalnızca kapak kapalıyken anlamlı ölçer; kapak açıkken duvara ya da tavana bakar. Bu yüzden kod doluluğu yalnızca kapak kapandıktan bir saniye sonra ölçer. Sensörün ses konisi dar olsa da (veri sayfasında 15 derece, pratikte biraz daha geniş) 35 cm derinlikte çapı 10–20 cm olan bir alanı görür; çapı 20 cm'den dar kutularda ses yan duvarlardan yankılanıp yanlış ölçüme yol açabilir. Poşetin kenarını kutunun dışına katla: içeri sarkan bir poşet kenarı sensörün önüne girer ve kutu boşken bile dolu görünür.
Bağlantı şeması: Arduino Uno, ESP32 ve Pico
Aşağıdaki tablo bu yazıdaki tüm kodlarla birebir aynıdır. Uno'da el sensörü D2–D3'e, doluluk sensörü D4–D5'e, servo D9'a, LED'ler D10–D12'ye ve buzzer D8'e bağlanır. Servo kütüphanesi Uno'da Timer1'i kullandığı için D9 ve D10'daki PWM çıkışını devre dışı bırakır; D10'daki LED'i yalnızca yakıp söndürdüğümüz için bu bizi etkilemez. Her LED'in önüne 220 Ω'luk bir direnç koy; değeri kendi LED'ine göre LED direnci aracıyla hesaplayabilirsin. Kartının pin düzenini Pinout aracında görebilirsin.
| Bağlantı | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| El sensörü TRIG | D2 | GPIO 18 | GP2 |
| El sensörü ECHO | D3 (doğrudan) | GPIO 19, 1 kΩ / 2 kΩ bölücüyle | GP3, 1 kΩ / 2 kΩ bölücüyle |
| Doluluk sensörü TRIG | D4 | GPIO 25 | GP4 |
| Doluluk sensörü ECHO | D5 (doğrudan) | GPIO 26, bölücüyle | GP5, bölücüyle |
| HC-SR04 VCC (ikisi de) | 5V | 5V (VIN) | VBUS |
| Servo sinyal (turuncu) | D9 | GPIO 13 | GP15 |
| Servo + (kırmızı) | 5V (SG90) ya da harici 5–6 V | 5V (VIN) ya da harici 5–6 V | VBUS ya da harici 5–6 V |
| Yeşil, sarı, kırmızı LED (220 Ω ile) | D10, D11, D12 | GPIO 27, 32, 33 | GP10, GP11, GP12 |
| Aktif buzzer (+) | D8 | GPIO 23 | GP13 |
| Tüm GND'ler | GND | GND | GND |
Servo seçimi ve tork hesabı
Akıllı çöp kutusu projelerinde en sık yaşanan sorun, kapağın yarıya kadar açılıp titremesi ya da hiç kalkmamasıdır. Bunun nedeni neredeyse her zaman servonun kapağa göre zayıf kalmasıdır. Gereken torku kabaca hesaplamak kolaydır: Tork (kg·cm) = kapağın ağırlığı (kg) × ağırlık merkezinin menteşeye uzaklığı (cm). Düz ve her yeri aynı kalınlıkta bir kapağın ağırlık merkezi tam ortasındadır; 24 cm uzunluğundaki bir kapak için bu uzaklık 12 cm'dir. En zor an, kapağın yatay durduğu ilk açılma anıdır; kapak dikleştikçe gereken tork azalır ve tam dikte neredeyse sıfıra iner. Servo kataloglarındaki tork değeri, servonun zorlanıp durduğu (stall) noktadaki değerdir; servo bu sınıra yakın çalışınca ısınır ve dişlileri aşınır. Bu yüzden hesapladığın değeri en az 2 ile çarp.
| Kapak | Ağırlık | Ağırlık merkezi | Gereken tork | 2 kat payla | Öneri |
|---|---|---|---|---|---|
| 20 × 20 cm karton ya da köpük levha | 50 g | 10 cm | 0,5 kg·cm | 1,0 kg·cm | SG90 |
| 25 cm'lik plastik kutu kapağı | 120 g | 12,5 cm | 1,5 kg·cm | 3,0 kg·cm | MG996R (MG90S sınırda) |
| 30 cm'lik MDF ya da kontrplak kapak | 300 g | 15 cm | 4,5 kg·cm | 9,0 kg·cm | MG996R ve karşı ağırlık |
| Servo | Tork (4,8 V / 6 V) | Dişli | Akım | Not |
|---|---|---|---|---|
| SG90 | 1,8 kg·cm (yalnız 4,8 V değeri verilir) | Plastik | Dönerken 100–250 mA | Arduino'nun 5V pininden beslenebilir |
| MG90S | 1,8 / 2,2 kg·cm | Metal | Dönerken 150–300 mA | SG90 ile aynı boyda, daha dayanıklı |
| MG996R | 9,4 / 11 kg·cm | Metal | Dönerken 0,5–0,9 A, sıkışınca 2,5 A'e kadar | Mutlaka ayrı 5–6 V kaynaktan beslenir |

Daha güçlü bir servo almak yerine kapağı dengelemek de çoğu zaman işe yarar. Kapağın menteşenin arkasına uzanan kısa bir kolu varsa oraya yapıştıracağın birkaç cıvata ya da madeni para kapağın ağırlığının bir kısmını dengeler; bu karşı ağırlık gereken torku yarıya kadar indirebilir. Servo kolunun kapağı menteşeden uzak bir noktadan itmesi de düşünülebilir, ama kaldıraç sana bedava tork vermez: menteşeden uzaklaştıkça gereken kuvvet azalır, fakat itilen nokta daha uzun bir yol gittiği için daha uzun bir servo kolu gerekir ve kol uzadıkça servodan istenen tork yeniden artar. Kapağı 90 dereceye yakın açmak istiyorsan gereken servo torku, servonun menteşeye doğrudan bağlandığı durumdakiyle hemen hemen aynı kalır. Servo tiplerinin ayrıntılı karşılaştırması Servo Motor Nedir? ve MG996R servo yazılarında.
Montaj ipuçları: menteşe, servo kolu ve kablolar
- Ağırlığı menteşe taşısın: Kapak gerçek bir menteşeye (kutunun kendi menteşesi ya da küçük bir dolap menteşesi) bağlı olmalı. Kapağı yalnızca servo milinin üzerine oturtursan bütün ağırlık servonun yatağına biner ve SG90'ın plastik dişlileri kısa sürede kırılır.
- Servo mili menteşe ekseniyle aynı hizada olsun: En kolay yöntem, servoyu kutunun arka kenarına, mili menteşe çizgisinin tam ucuna gelecek şekilde vidalamak ve servo kolunu kapağa vida ya da sıcak silikonla sabitlemektir. Eksenler kayıksa servo her açılışta kapağı yana zorlar.
- Servo gövdesini sağlam sabitle: Servoyu yalnızca çift taraflı bantla tutturma; kapağı kaldırırken gövde döner. Servonun kulaklarından iki vida ya da küçük bir L köşebent kullan.
- Kapağı kenara bastırma: Kapalı açıyı, kapağın kenara oturduğu ama servonun ona bastırmadığı değerde ayarla. Servo kapalı kapağı zorlamaya devam ederse vızıldar, ısınır ve boşuna akım çeker.
- Kablolara pay bırak: Doluluk sensörünün kabloları kapakla birlikte hareket eder. Kabloyu menteşenin yanından, kapak tam açıkken bile gerilmeyecek uzunlukta geçir ve bir kablo bağıyla sabitle.
- Elektroniği çöpten ayır: Arduino'yu, pilleri ve breadboard'u kutunun arkasına ya da altına yapıştıracağın küçük bir kutuya koy. Çöp poşetinden sızan sıvı ya da nem devreye ulaşmamalı.
- Karton maketle başla: Okul projesi için ayakkabı kutusundan ya da köpük levhadan yapılmış hafif bir kapak SG90 ile bile sorunsuz çalışır ve tork hesabını sunumda göstermek için iyi bir örnektir.
Adım adım kurulum ve kalibrasyon
Ana koda geçmeden önce aşağıdaki küçük kalibrasyon kodunu yükle. Bu kod, seri monitöre yazdığın açıya servoyu götürür ve yarım saniyede bir iki sensörün okuduğu mesafeyi ekrana yazar. Böylece kapağın kapalı ve açık açılarını, boş kutunun derinliğini ve el algılama eşiğini kendi kutuna göre ölçersin.
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}- Servo kolunu takmadan kodu yükle ve seri monitörü 9600 baud ile aç; satır sonu ayarını Newline yap. Servo 90 dereceye gider.
- 10 yazıp Enter'a bas. Kapağı kapalı tut, servo kolunu bu konumda mile tak ve sonra kapağa sabitle.
- 20, 40, 60 diye açıyı artırarak kapağın tam açıldığı değeri bul; genellikle 90–110 derecedir. Kapağı dikeyi geçecek kadar açma. Bulduğun değer LID_OPEN olur. Servon ters yöne dönüyorsa kapalı ve açık değerlerini buna göre seç; kod iki yönde de çalışır.
- Yeniden küçük açılara in. Kapağın kenara tam oturduğu açıdan 2–3 derece açılma yönüne geri git; bu değer LID_CLOSED olur. Servo kapalıyken vızıldıyorsa kapağa bastırıyordur.
- Poşet takılı ve kutu boşken doluluk sensörünün okuduğu değeri not et; bu EMPTY_CM olur. FULL_CM için 6–10 cm seç: çöp bu yüksekliğe ulaştığında kapak hâlâ rahat kapanmalı ve sensör güvenle ölçebilmeli, çünkü HC-SR04 2–3 cm'den yakını ölçemez.
- Elini el sensörünün önünde 5, 10, 20 ve 30 cm'ye tut ve okunan değerlere bak. Sonra kutunun önünden yürüyerek geç ve okunan en yakın değeri not et. HAND_CM bu değerin altında kalmalı; çoğu kutu için 15–25 cm iyi çalışır.
- Bulduğun değerleri ana kodun en üstündeki ayarlara yaz ve kodu yükle.
Ana kod: el algılama, bekleme süresi ve yavaş kapanma
Ana kodda uzun bir delay() yoktur; her iş millis() ile tutulan zaman damgalarıyla yapılır. Böylece kapak kapanırken bile el sensörü okunmaya devam eder ve el yeniden yaklaşırsa kapak hemen geri açılır. Bu, kapağın bir çocuğun eline kapanmasını önleyen basit ama önemli bir güvenlik önlemidir. Bekleme mantığının ayrıntılarını millis() ve delay(), durumlarla düşünme yaklaşımını ise Arduino durum makinesi yazısında bulabilirsin.
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}| Fonksiyon ya da ayar | Ne yapar? |
|---|---|
| pingCm() | Tek ölçüm yapar; 30 ms içinde yankı gelmezse 999 döndürür |
| medianCm() | 60 ms arayla 5 ölçüm alır, sıralar ve ortadakini verir |
| toPercent() | Mesafeyi EMPTY_CM ve FULL_CM'ye göre 0–100 arasında bir yüzdeye çevirir |
| handHits | Art arda kaç ölçümde el görüldüğünü sayar; tek bir hatalı ölçüm kapağı açmaz |
| OPEN_TIME | Elin son görüldüğü andan kapanmaya başlamaya kadar geçen süre (4000 ms) |
| CLOSE_STEP_MS | Kapanırken derece başına süre: 100 derece × 20 ms = 2 saniyelik kapanma |
| lid.detach() | Kapak kapandıktan 0,4 s sonra servoya darbe göndermeyi keser |
| fullWarned | Dolu uyarısının yalnızca bir kez çalmasını sağlar; doluluk %70'in altına inince sıfırlanır |
Yavaş kapanmanın püf noktası, açının her adımda bir derece artırılması yerine geçen süreden hesaplanmasıdır. Kapanma başladıktan 600 ms sonra kapak 600 / 20 = 30 derece yol almış olmalıdır; kod, döngü bir an doluluk ölçümü ya da buzzer yüzünden gecikse bile kapağı doğru açıya götürür ve hareket kesik kesik olmaz. Kapanmayı daha da yavaşlatmak için CLOSE_STEP_MS değerini büyüt: 30 ms ile 100 derece 3 saniyede kapanır. Kapak kapandıktan sonra servo detach() ile serbest bırakılır; kapak zaten kenara oturduğu için servonun onu tutmasına gerek yoktur, servo vızıldamaz ve pil daha az harcanır. Kodda write() fonksiyonunun attach()'tan önce çağrılması da bilinçli bir seçimdir: Arduino açıldığında servo önce 90 dereceye sıçramaz, doğrudan kapalı konumda başlar.
Doluluk ölçümü ve LED, buzzer uyarısı
Doluluk sensörü kapağın altından çöpün en yüksek noktasına olan uzaklığı ölçer. Kutu boşken bu uzaklık en büyük (EMPTY_CM), kutu doluyken en küçüktür (FULL_CM). Yüzde şu formülle bulunur: Doluluk (%) = (EMPTY_CM − ölçülen) / (EMPTY_CM − FULL_CM) × 100. Örneğin EMPTY_CM = 35 cm, FULL_CM = 8 cm ve ölçülen mesafe 20 cm ise doluluk (35 − 20) / 27 × 100 ≈ %55 olur ve sarı LED yanar. Ölçülen değer 35 cm'den büyük çıkarsa (ses dibe çapraz gidip döndüyse) sonuç %0'a, 8 cm'den küçük çıkarsa %100'e sabitlenir.
| Doluluk | Yanan LED | Buzzer | Anlamı |
|---|---|---|---|
| %0–49 | Yeşil | Sessiz | Rahat, yer var |
| %50–79 | Sarı | Sessiz | Yarıdan fazla dolu, yakında boşalt |
| %80–89 | Kırmızı | Sessiz | Neredeyse dolu |
| %90–100 | Kırmızı | Dolduğu an 3 kısa bip, her açılışta 2 bip | Hemen boşalt |
Çöpün yüzeyi düz değildir: tek bir şişe sensörün tam altında dikilirse ölçüm o noktayı görür ve doluluğu olduğundan yüksek gösterir. Ortanca (medyan) filtre tek tük sıçrayan ölçümleri eler, ama çöpün şeklini düzeltemez; bu yüzden %80 ve %90 gibi eşikleri kesin bir ölçüm değil, bir "boşaltma zamanı" uyarısı olarak düşün. Uyarının sürekli öterek can sıkmaması için kod histerezis kullanır: dolu uyarısı %90'da bir kez çalar ve kutu boşaltılıp doluluk %70'in altına inmeden yeniden çalmaz. Filtreler arasındaki farkı sensör verisi filtreleme yazısında ayrıntılı anlatıyoruz.
Pil ile besleme: ne kadar dayanır?
Akıllı çöp kutusunu kablosuz kullanmak cazip görünür, ama Arduino Uno pil dostu bir kart değildir. Kart; USB yongası, regülatörü ve güç LED'iyle birlikte hiçbir şey yapmadan yaklaşık 45–50 mA çeker. Buna iki HC-SR04'ün bekleme akımı, yanan bir LED ve serbest bırakılmış servonun elektroniği eklenince ortalama akım 70–80 mA civarına çıkar. Servonun açılıp kapanırken çektiği 100–250 mA ise yalnızca birkaç saniye sürdüğü için günde 50 kez açılan bir kutuda ortalamayı ancak 1 mA civarında artırır. Pil ömrü kabaca kapasite (mAh) / ortalama akım (mA) ile bulunur; kendi değerlerinle hesaplamak için Pil ömrü aracını kullanabilirsin.
| Güç kaynağı | Bağlantı | Yaklaşık süre (75 mA) | Değerlendirme |
|---|---|---|---|
| 9 V kare pil (yaklaşık 500 mAh) | VIN | 5–6 saat | Önerilmez: servo kalkışında gerilim çöker, kart yeniden başlar |
| 6 adet AA alkalin pil (9 V) | DC jak ya da VIN | Yaklaşık 1 gün | Sunum günü için yeterli |
| 2 adet 18650 (7,4 V, 2500 mAh) | VIN; servo için 5–6 V'a ayarlı LM2596 | Yaklaşık 1,5 gün | Şarj edilebilir; 2S şarj ve koruma devresi gerekir |
| 10 000 mAh powerbank | USB | 3–4 gün | Bazı powerbank'ler 50–100 mA'in altındaki yükte kendini kapatır |
| 5 V 2 A adaptör | USB ya da 5V pini | Sınırsız | Evde kalıcı kullanım için en iyi seçim |

Tablodan çıkan sonuç açık: pille çalışan bir Uno kutusu sunum ve gösteri için idealdir, ama evde aylarca çalışacak bir kutu için 5 V'luk bir adaptör kullan. Servoyu 2 adet 18650 ile beslerken araya koyacağın düşürücü modülün ayarı LM2596 yazısında. Pil ömrünü gerçekten uzatmak istersen Uno yerine güç LED'i sökülmüş bir Arduino Pro Mini kullanabilir, ölçümler arasında işlemciyi uyku moduna alabilir ve LED'leri yalnızca kapak açılınca kısa süre yakabilirsin; bu önlemlerle ortalama akım 15–20 mA'e iner ve aynı pil 4–5 kat uzun dayanır. Arduino'yu besleme yollarının (USB, DC jak, VIN, 5V pini) farkları Arduino nasıl beslenir? yazısında.
ESP32 ile doluluk bildirimi (Telegram)
Kutu dolduğunda LED'i görmek için yanına gitmen gerekir; ESP32 ise bu bilgiyi doğrudan telefonuna gönderebilir. En kolay yol Telegram'dır, çünkü ücretsizdir ve kendi botunu dakikalar içinde açabilirsin. Telegram'da @BotFather'a yazıp /newbot komutuyla bir bot oluştur ve verdiği token'ı not et. Ardından kendi botuna bir kez /start yaz; bot, kendisiyle hiç konuşmamış birine mesaj gönderemez. Sohbet kimliğini (chat ID) öğrenmek için @userinfobot gibi bir bota yazabilir ya da Bot API'nin getUpdates isteğini kullanabilirsin. Kod ek kütüphane gerektirmez: ESP32'nin kendi HTTPClient'ı ile Telegram'ın sendMessage adresine bir istek gönderir.
Kod, Uno sürümüyle aynı durum makinesini kullanır; yalnızca servo sinyali Servo kütüphanesi yerine ESP32'nin PWM birimi LEDC ile üretilir. 50 Hz'lik sinyalde 0,544–2,4 ms'lik darbe 0–180 dereceye karşılık gelir ve darbeyi kesmek (ledcWrite ile 0 yazmak) servoyu serbest bırakır. Kod, ESP32 Arduino çekirdeğinin 3.x sürümü içindir; 2.x sürümünde ledcAttach() fonksiyonu yoktur, Kart Yöneticisi'nden çekirdeği güncelle.

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}Bildirim, kutu %85'e ulaşınca bir kez gönderilir ve doluluk %30'un altına inince "boşaltıldı" mesajıyla yeniden kurulur; böylece telefonun her 3 saniyede bir titremez. Wi-Fi yoksa ya da gönderim başarısız olursa kod dakikada bir yeniden dener ve kutu bu sırada normal çalışmaya devam eder. Bir mesajın gönderilmesi şifreli (TLS) bağlantı yüzünden 1–3 saniye sürer; kod bu yüzden yalnızca kapak kapalıyken gönderim yapar. setInsecure() sunucunun sertifikasını doğrulamadan bağlanır; hobi projesi için yeterlidir, ama token'ın gizli bir bilgi olduğunu unutma ve kodu paylaşırken sil. Seri monitörde "Telegram HTTP 200" görüyorsan mesaj gitmiştir; 401 token'ın, 400 chat ID'nin yanlış olduğunu, 403 ise bota henüz /start yazmadığını gösterir. Komutlarla çalışan tam bir bot için ESP32 Telegram bot rehberine bakabilirsin.
Raspberry Pi Pico (MicroPython) ile akıllı çöp kutusu
Raspberry Pi Pico'da aynı mantık MicroPython ile kurulur. time_pulse_us() fonksiyonu Arduino'daki pulseIn() gibi çalışır; yankı gelmezse negatif bir değer döndürdüğü için bunu 999, yani "yankı yok" sayıyoruz. Servo için 50 Hz'lik PWM ve darbe genişliğini nanosaniye olarak alan duty_ns() kullanılır; duty_ns(0) darbeyi keser ve servoyu serbest bırakır. Zaman karşılaştırmaları ticks_ms() ve ticks_diff() ile yapılır; ticks_diff sayaç başa dönse bile doğru farkı verir. Kodu Thonny ile Pico'ya main.py adıyla kaydedersen kart her açıldığında kendiliğinden çalışır. Pico W kullanıyorsan doluluğu Wi-Fi üzerinden göstermek için Pico W web sunucusu yazısındaki yöntemi ekleyebilirsin.
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)Okul projesi sunumu için ipuçları
Akıllı çöp kutusu, bilim fuarlarında ve TÜBİTAK 4006 sergilerinde çok sık görülen bir projedir; bu yüzden jüri kapağın açılıp kapanmasından çok, bir problemi nasıl tanımladığını, nasıl ölçtüğünü ve sonuçtan ne çıkardığını merak eder. Projeni sıradan bir gösteriden bilimsel bir çalışmaya dönüştüren şey ölçümdür: kaç denemede açıldı, ne kadar sürede açıldı, doluluğu ne kadar doğru tahmin etti, pil kaç saat dayandı. Sunumunu şu sırayla kurabilirsin:
- Problem: Kapağa elle dokunmak mikrop taşır, taşan kutular koku ve böcek yapar. Okulundaki bir çöp kutusunun kapağına bir teneffüste kaç kişinin dokunduğunu sayarak somut bir veriyle başla.
- Amaç: Dokunmadan açılan, dolunca uyaran ve isteğe bağlı olarak telefona haber veren bir kutu.
- Çalışma ilkesi: Sensör, Arduino, servo, LED, buzzer, ESP32 ve telefonu gösteren tek bir blok şema çiz. Ses hızını ve 58'e bölme formülünü bir slaytta açıkla.
- Malzeme ve maliyet: Parça listesini ve servo seçimini, tork hesabıyla birlikte tablo hâlinde ver.
- Canlı gösterim: Elini yaklaştır, kapak açılsın. Kutuya kâğıt doldurup LED'in yeşilden sarıya ve kırmızıya geçtiğini göster; telefona gelen Telegram mesajını perdeye yansıt.
- Test sonuçları: Aşağıdaki tabloyu kendi ölçümlerinle doldur ve sonuçları grafiğe dök.
- Sonuç ve geliştirme: Neyin iyi çalıştığını, neyin çalışmadığını ve bir sonraki sürümde neyi değiştireceğini dürüstçe anlat.
| Test | Nasıl yapılır? | Kaydedilecek değer |
|---|---|---|
| Algılama mesafesi | Elini 5, 10, 15, 20 ve 25 cm'de 10'ar kez tut | Her mesafede kaç denemede açıldığı (ör. 10/10) |
| Tepki süresi | Telefonla ağır çekim (240 fps) video çek | El ile kapağın kıpırdaması arasındaki süre (ms) |
| Yanlış açılma | Kutunun önünden 20 kez yürüyerek geç | Gereksiz açılma sayısı |
| Doluluk doğruluğu | Kutuyu cetvelle ölçerek dörtte bir, yarı ve dörtte üç oranında kâğıtla doldur | Gerçek doluluk ile ölçülen yüzde arasındaki fark |
| Kapanma hızı | CLOSE_STEP_MS değerini 5, 20 ve 40 ms yap | Kapanma süresi ve telefonla ölçülen ses seviyesi |
| Pil ömrü | Dolu pille çalıştır, saatte bir gerilimi ölç | Kutunun kaç saat çalıştığı |
Geliştirme fikirleri
- Ekranlı kutu: Doluluk yüzdesini ve bir çubuk grafiği OLED ekranda göster.
- Sesli teşekkür: Kapak her açıldığında DFPlayer Mini ile kısa bir "teşekkürler" sesi çal.
- Ağırlık ölçümü: Kutunun altına HX711 ve yük hücresi koyarak çöpün kaç kilo olduğunu da ölç; hacmi ve ağırlığı birlikte değerlendir.
- Geri dönüşüm ayırıcı: Bir renk sensörü ve ikinci bir servoyla atıkları iki bölmeye yönlendir.
- Koku uyarısı: MQ-135 ile kutunun içindeki havanın kötüleştiğini algıla.
- Güneş enerjili dış mekân kutusu: Küçük bir güneş paneli, 18650 pil, TP4056 şarj modülü ve 5 V yükseltici modülle kabloyu tamamen kaldır.
- Doluluk kaydı: ESP32 ile ölçümleri Google E-Tablolar'a yazarak kutunun hangi gün daha hızlı dolduğunu grafikle göster.
- Okul ya da site geneli: Wi-Fi'nin ulaşmadığı bahçe kutularını LoRa modülüyle yüzlerce metre öteden izle.
Sık yapılan hatalar ve çözümleri
| Belirti | Olası neden | Çözüm |
|---|---|---|
| Kapak kendi kendine açılıp kapanıyor | Sensör yakındaki bir duvarı ya da önünden geçen insanı görüyor | HAND_CM'i düşür, sensörü yukarı eğik tak, önünde 30 cm boşluk bırak |
| Kapak açılırken Arduino yeniden başlıyor | Servonun kalkış akımı 5 V hattını çökertiyor | 470 µF kondansatör, güçlü servoyu ayrı kaynaktan besle, GND'ler ortak |
| Kapak kapalıyken servo vızıldıyor ve ısınıyor | LID_CLOSED kapağı kenara bastırıyor | Açıyı 2–3 derece geri al; detach() satırını silme |
| Kapak yarıda kalıyor ya da titriyor | Tork yetersiz ya da eksenler kayık | Daha güçlü servo, hafif kapak, karşı ağırlık; servo milini menteşeyle hizala |
| Doluluk boş kutuda bile yüksek | Poşet kenarı sensörün önünde ya da dar kutuda duvar yankısı | Poşeti dışarı katla, sensörü ortala, EMPTY_CM'i yeniden ölç |
| Doluluk hiç değişmiyor | Sensör yankı alamıyor (999) ya da ECHO kablosu çıkmış | Seri monitörde ham cm değerini izle, bağlantıyı kontrol et |
| ESP32'de mesafe hep 999 ya da saçma | ECHO bölücüsü yok, yanlış ya da kablo ters | 1 kΩ / 2 kΩ bölücüyü ve TRIG-ECHO sırasını kontrol et |
| ESP32 sürekli yeniden başlıyor | Wi-Fi ve servo aynı anda akım çekiyor, USB kablosu zayıf | Kısa ve kaliteli kablo kullan, servoyu ayrı 5 V kaynaktan besle |
| Telegram mesajı gelmiyor | Token ya da chat ID yanlış, bota /start yazılmamış | Seri monitördeki HTTP kodunu oku (401, 400, 403) |
Akıllı çöp kutusu; sensör okumayı, servo kontrolünü, zamanlamayı ve güç planlamasını tek bir projede buluşturduğu için Arduino'yu öğrenmenin en eğlenceli yollarından biridir. Bu projeyi kurduktan sonra aynı parçalarla bir park sensörü ya da otomatik bir kapı yapmak çok kolaylaşır. BlueGrays uygulamasında HC-SR04 ile mesafe ölçme, servo kontrolü, buzzer kullanımı, ESP32 park sensörü ve Telegram bildirimli hareket alarmı projelerini devre şemaları ve hazır kodlarıyla adım adım bulabilirsin.
Sıkça sorulan sorular
Arduino akıllı çöp kutusu için hangi malzemeler gerekir?
Temel sürüm için bir Arduino Uno ya da Nano, bir HC-SR04 ultrasonik sensör, kapağın ağırlığına uygun bir servo (hafif kapakta SG90, gerçek kutu kapağında MG996R), jumper kablolar ve 5 V'luk bir güç kaynağı yeterlidir. Doluluk ölçmek için ikinci bir HC-SR04, üç LED ile 220 Ω dirençler ve aktif bir buzzer eklenir. Telefona bildirim istiyorsan Arduino yerine ESP32 kullanabilirsin.
Akıllı çöp kutusu nasıl çalışır?
Kutunun önündeki ultrasonik sensör ses dalgası gönderip yankının dönme süresini ölçer; 20 cm'den yakın bir el görünce Arduino servo motorla kapağı açar. El çekildikten birkaç saniye sonra kapak yavaşça kapanır. Kapağın altındaki ikinci sensör çöpün yüksekliğini ölçer ve doluluğu LED'ler ve buzzer ile gösterir.
Servo çöp kutusu kapağını neden kaldıramıyor?
Genellikle servonun torku kapağa göre yetersizdir. Gereken tork, kapağın ağırlığı ile ağırlık merkezinin menteşeye uzaklığının çarpımıdır; 120 g'lık, 25 cm'lik bir kapak yaklaşık 1,5 kg·cm ister ve bu SG90'ın sınırındadır. Servoyu hesabın iki katına göre seç, kapağı hafiflet ya da karşı ağırlık ekle ve servonun beslemesinin yeterli akım verdiğinden emin ol.
Akıllı çöp kutusunda HC-SR04 yerine IR sensör kullanılabilir mi?
Evet, FC-51 gibi bir IR engel sensörü 2–30 cm arasında eli algılayabilir ve yalnızca var ya da yok bilgisi verdiği için kodu daha da basittir. Ancak güneş ışığından etkilenir ve koyu renkli yüzeyleri daha geç görür. PIR sensör ise önünden geçen herkeste tetiklendiği için çöp kutusu kapağına uygun değildir.
Arduino akıllı çöp kutusu pille ne kadar çalışır?
Arduino Uno'lu bir kutu ortalama 70–80 mA çektiği için 6 adet AA pille yaklaşık bir gün, iki 18650 pille yaklaşık bir buçuk gün, 10 000 mAh'lik bir powerbank ile 3–4 gün çalışır. Kalıcı kullanım için 5 V'luk bir adaptör önerilir. Pro Mini, uyku modu ve LED'leri yalnızca gerektiğinde yakmak pil ömrünü 4–5 kat uzatabilir.
Çöp kutusu dolunca telefona nasıl bildirim gelir?
ESP32 Wi-Fi'ye bağlanır ve doluluk belirlediğin yüzdeyi geçince Telegram Bot API'sine bir istek göndererek telefonuna mesaj atar. Bunun için BotFather'dan alınan bir bot token'ı ve kendi chat ID'n gerekir. Kod, aynı uyarıyı tekrar tekrar göndermemek için kutu boşaltılana kadar bekler.
Bu yazıdan sonra yapabileceğin projeler
Tüm projeleri gör
HC-SR04 Mesafe Ölçme (Serial Ekran)
Yarasalar karanlıkta yollarını nasıl buluyorsa HC-SR04 sensörü de öyle çalışır: duyamadığın bir ses gönderir, yankısını dinler ve önündeki cismin ne kadar uzakta olduğunu ölçer. Arduino bu mesafeyi sa

Buzzer Kullanımı
Kapı zili ya da alarm sesi nasıl çıkar, hiç merak ettin mi? Arduino küçük bir buzzer'ı kısa aralıklarla öttürür: bip, sus, bip, sus. Sesin elektrikle nasıl üretildiğini görür, ileride melodi çalan pro

Servo Motor Kullanım
Küçük bir servo motorun kolu sırayla üç konuma döner: en başa, tam ortaya ve en sona, her durakta iki saniye bekleyerek. Arduino motora hangi açıda durması gerektiğini söyler, motor da oraya gidip ora

HC-SR04 ile Park Sensörü
Arabalardaki park sensörünün küçük bir benzeri: bir şey yaklaştıkça bip sesleri hızlanır. Mesafe sensörü önündeki nesneyi ölçer; 20 santimetreden uzaktaysa yeşil LED yanar, daha yakına gelince kırmızı

ESP32 HC-SR04 Ultrasonik Mesafe Ölçer
HC-SR04 ultrasonik sensör, gönderdiği ses dalgasının nesneye çarpıp geri dönme süresini (Time of Flight) ölçerek mesafe hesaplar. Bu projede ESP32 ile TRIG pinine 10 mikrosaniyelik darbe gönderip, ECHO pininden gelen yankı süresini `pulseIn()` ile okuyacağız. Hesaplanan mesafeyi cm cinsinden 16x2 I2C LCD'de göstereceğiz. HC-SR04 5V çalıştığı için ECHO pinine **gerilim bölücü** zorunludur — aksi halde ESP32'nin 3.3V GPIO'su zarar görür.

ESP32 Servo Motor (Potansiyometre Kontrolü)
Bu projede potansiyometre ile SG90 servo motoru kontrol ediyoruz. Potansiyometreden okunan analog değeri 0-180 derece aralığına map'leyerek servo açısını ayarlıyoruz. ESP32Servo kütüphanesi, ESP32'nin çıkış verebilen uygun GPIO'larında servo sinyali üretir; bu projede GPIO 18 kullanılır. Bu, robotik kollar, kamera gimbal'ları ve otomatik kapı mekanizmaları için temel bir kontrol yöntemidir.

ESP32 Park Sensörü (HC-SR04 + 5 LED Bar + Buzzer + OLED)
Otomobil park sensörünün ESP32 versiyonu! HC-SR04 ultrasonik sensör ile önündeki nesneye olan mesafeyi ölçüyoruz. Mesafe azaldıkça 5 LED'lik bar dolar (yeşil → sarı → kırmızı) ve buzzer bip aralığı kısalır. Çok yakınlaşınca (10 cm altında) sürekli alarm çalar ve tüm LED'ler yanar. SSD1306 OLED ekranda büyük fontla anlık cm değeri, durum metni (SAKIN / DIKKAT / YAKIN / DUR! / TEHLIKE) ve LED bar simülasyonu gösterilir. Klasik ve öğretici bir proje — HC-SR04 pulseIn, digital multi-output, buzzer P

ESP32 Hareket Alarm Sistemi (PIR + Buzzer + LED + Telegram)
PIR sensörü hareket algıladığında buzzer ve LED'i aktif eden, aynı anda Telegram üzerinden sahibine anlık bildirim gönderen akıllı bir güvenlik alarm sistemidir. Tekrarlayan alarmların sıklığını sınırlamak için 30 saniyelik cooldown (bekleme süresi) mekanizması vardır; bu mekanizma PIR sensörünün yanlış algılamalarını doğrulamaz veya ortadan kaldırmaz. Ev güvenliği, garaj izleme, depo veya çalışma odası gibi alanlarda kullanılabilir. Telegram entegrasyonu sayesinde nerede olursanız olun anında h
Diğer yazılar
BlueGrays uygulaması
Bu devreyi telefonunda adım adım kur
Devre şeması, kod ve parça listesi tek ekranda. 160'tan fazla Arduino, ESP32 ve Pico projesi seni bekliyor.
Telefonunla tara
Kamerayı koda tut; telefonunun uygulama mağazası açılır.
ya da mağazada aç

