Rehberler · 15 dk okuma

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.

HC-SR04 ultrasonik mesafe sensörü
HC-SR04 akıllı çöp kutusunda iki görev üstlenir: kutunun önündeki sensör yaklaşan eli, kapağın altındaki sensör çöpün yüksekliğini ölçer. 2–400 cm arasını dar bir açıyla görür.

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çaGöreviNot
Arduino Uno (ya da Nano)Beyin: ölçer, karar verir, servoya ve LED'lere komut verirESP32 ve Pico da kullanılabilir
2 adet HC-SR04Biri eli, diğeri doluluğu ölçer5 V, ölçüm sırasında yaklaşık 15 mA
SG90, MG90S ya da MG996R servoKapağı açar ve kapatırKapağı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östergesiHer LED'e bir direnç
Aktif buzzerDolu uyarısıPasif buzzer kullanırsan tone() gerekir
Menteşe, vida, sıcak silikonKapağı taşır, servo kolunu sabitlerKapağın ağırlığını servo değil menteşe taşımalı
470 µF kondansatörServonun kalkış akımını yumuşatırServonun + ve − uçlarına, kutup yönüne dikkat
Güç kaynağı5 V adaptör, powerbank ya da pilSüre hesabı aşağıda
ESP32 (isteğe bağlı)Telefona doluluk bildirimiWi-Fi gerekir
Arduino Uno
Arduino Uno
SG90 servo motor
SG90 servo motor
Buzzer
Buzzer
LED
LED

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örNasıl algılar?Çöp kutusu için
HC-SR04 (ultrasonik)Ses yankısı, 2–400 cm, dar bir koniEş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 cmUcuz 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-9960Kı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.

İki HC-SR04'ü asla aynı anda tetikleme. Birinin gönderdiği sesin yankısını diğeri kendi yankısı sanar ve iki sensör de saçma değerler okur. Kodda iki sensör sırayla ve en az 60 ms arayla ölçülür; doluluk sensörü kendi 5 ölçümünü yaparken el sensörü bekler. Sensörlerin önü de açık kalmalı: kutunun duvarına açtığın deliklerin kenarı sensörün yüzeyiyle aynı hizada olsun, önüne yapıştırılan ince bir plastik ya da şeffaf bant bile yankıyı keser.

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 UnoESP32Raspberry Pi Pico
El sensörü TRIGD2GPIO 18GP2
El sensörü ECHOD3 (doğrudan)GPIO 19, 1 kΩ / 2 kΩ bölücüyleGP3, 1 kΩ / 2 kΩ bölücüyle
Doluluk sensörü TRIGD4GPIO 25GP4
Doluluk sensörü ECHOD5 (doğrudan)GPIO 26, bölücüyleGP5, bölücüyle
HC-SR04 VCC (ikisi de)5V5V (VIN)VBUS
Servo sinyal (turuncu)D9GPIO 13GP15
Servo + (kırmızı)5V (SG90) ya da harici 5–6 V5V (VIN) ya da harici 5–6 VVBUS ya da harici 5–6 V
Yeşil, sarı, kırmızı LED (220 Ω ile)D10, D11, D12GPIO 27, 32, 33GP10, GP11, GP12
Aktif buzzer (+)D8GPIO 23GP13
Tüm GND'lerGNDGNDGND
HC-SR04'ün ECHO pini 5 V'luk darbe verir; ESP32 ve Pico'nun pinleri ise 3,3 V'tur ve 5 V'a dayanıklı değildir. Her iki sensörün ECHO hattına ayrı bir gerilim bölücü kur: ECHO → 1 kΩ → karta giden nokta → 2 kΩ → GND. Böylece 5 V × 2 / 3 ≈ 3,3 V elde edilir. TRIG için bölücü gerekmez, 3,3 V'luk tetik sensöre yeter. ESP32'nin 5V (VIN) pini ve Pico'nun VBUS pini yalnızca kart USB'den beslenirken 5 V verir.

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.

KapakAğırlıkAğırlık merkeziGereken tork2 kat paylaÖneri
20 × 20 cm karton ya da köpük levha50 g10 cm0,5 kg·cm1,0 kg·cmSG90
25 cm'lik plastik kutu kapağı120 g12,5 cm1,5 kg·cm3,0 kg·cmMG996R (MG90S sınırda)
30 cm'lik MDF ya da kontrplak kapak300 g15 cm4,5 kg·cm9,0 kg·cmMG996R ve karşı ağırlık
ServoTork (4,8 V / 6 V)DişliAkımNot
SG901,8 kg·cm (yalnız 4,8 V değeri verilir)PlastikDönerken 100–250 mAArduino'nun 5V pininden beslenebilir
MG90S1,8 / 2,2 kg·cmMetalDönerken 150–300 mASG90 ile aynı boyda, daha dayanıklı
MG996R9,4 / 11 kg·cmMetalDönerken 0,5–0,9 A, sıkışınca 2,5 A'e kadarMutlaka ayrı 5–6 V kaynaktan beslenir
MG996R metal dişli servo motor
MG996R: 6 V'ta yaklaşık 11 kg·cm tork veren metal dişli servo. Gerçek bir çöp kutusunun plastik kapağını rahatça kaldırır, ama sıkıştığında 2,5 A'e kadar akım çektiği için Arduino'dan değil, ayrı bir kaynaktan beslenmelidir.

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.

Arduino (C++)
1#include <Servo.h>
2
3const int HAND_TRIG = 2, HAND_ECHO = 3;   // el sensörü / hand sensor
4const int FILL_TRIG = 4, FILL_ECHO = 5;   // doluluk sensörü / fill sensor
5const int SERVO_PIN = 9;
6
7Servo lid;
8unsigned long lastPrint = 0;
9
10int pingCm(int trig, int echo) {
11  digitalWrite(trig, LOW);  delayMicroseconds(2);
12  digitalWrite(trig, HIGH); delayMicroseconds(10);   // 10 µs tetik darbesi / 10 µs trigger pulse
13  digitalWrite(trig, LOW);
14  unsigned long t = pulseIn(echo, HIGH, 30000UL);    // en fazla 30 ms bekle / wait at most 30 ms
15  return t == 0 ? 999 : (int)(t / 58);               // 999 = yankı yok / 999 = no echo
16}
17
18void setup() {
19  Serial.begin(9600);
20  pinMode(HAND_TRIG, OUTPUT); pinMode(HAND_ECHO, INPUT);
21  pinMode(FILL_TRIG, OUTPUT); pinMode(FILL_ECHO, INPUT);
22  lid.write(90);                                     // başlangıçta orta konum / start at the middle position
23  lid.attach(SERVO_PIN);
24  Serial.println(F("Açı yaz (0-180) ve Enter'a bas / Type an angle (0-180) and press Enter"));
25}
26
27void loop() {
28  if (Serial.available()) {
29    String s = Serial.readStringUntil('\n');
30    s.trim();
31    if (s.length() > 0) {                            // boş satırı yok say / ignore empty lines
32      int a = constrain(s.toInt(), 0, 180);
33      lid.write(a);
34      Serial.print(F("Servo: "));
35      Serial.println(a);
36    }
37  }
38
39  if (millis() - lastPrint >= 500) {                 // yarım saniyede bir yaz / print every half second
40    lastPrint = millis();
41    int hand = pingCm(HAND_TRIG, HAND_ECHO);
42    delay(60);                                       // iki sensör arasında bekle / wait between the two sensors
43    int fill = pingCm(FILL_TRIG, FILL_ECHO);
44    Serial.print(F("El / Hand: "));
45    Serial.print(hand);
46    Serial.print(F(" cm   Doluluk / Fill: "));
47    Serial.print(fill);
48    Serial.println(F(" cm"));
49  }
50}
  1. 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.
  2. 10 yazıp Enter'a bas. Kapağı kapalı tut, servo kolunu bu konumda mile tak ve sonra kapağa sabitle.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.

Arduino (C++)
1#include <Servo.h>
2
3// Pinler / Pins
4const int HAND_TRIG = 2, HAND_ECHO = 3;    // el sensörü, kutunun önünde / hand sensor, front of the bin
5const int FILL_TRIG = 4, FILL_ECHO = 5;    // doluluk sensörü, kapağın altında / fill sensor, under the lid
6const int SERVO_PIN = 9;
7const int LED_GREEN = 10, LED_YELLOW = 11, LED_RED = 12;
8const int BUZZER = 8;                      // aktif buzzer / active buzzer
9
10// Ayarlar: kalibrasyon kodunda bulduğun değerleri yaz / Settings: use the values from the calibration sketch
11const int HAND_CM = 20;                    // bundan yakın el kapağı açar / a hand closer than this opens the lid
12const int LID_CLOSED = 10;                 // kapak kenara oturuyor, bastırmıyor / lid rests on the rim, not pressing
13const int LID_OPEN = 110;                  // kapak tam açık / lid fully open
14const unsigned long OPEN_TIME = 4000;      // son el algılamadan sonra açık kalma (ms) / stay open after the last hand (ms)
15const unsigned long CLOSE_STEP_MS = 20;    // kapanırken derece başına ms / ms per degree while closing
16const int EMPTY_CM = 35;                   // boş kutuda sensörden dibe / sensor to bottom when empty
17const int FULL_CM = 8;                     // bu mesafede kutu %100 dolu / the bin is 100% full at this distance
18
19enum LidState { CLOSED, OPEN, CLOSING };
20LidState state = CLOSED;
21Servo lid;
22
23unsigned long lastHand = 0, lastHandPing = 0, closeStart = 0, closedAt = 0, lastFill = 0;
24int fillPercent = 0;
25byte handHits = 0;
26bool fullWarned = false;
27
28int pingCm(int trig, int echo) {
29  digitalWrite(trig, LOW);  delayMicroseconds(2);
30  digitalWrite(trig, HIGH); delayMicroseconds(10);  // 10 µs tetik darbesi / 10 µs trigger pulse
31  digitalWrite(trig, LOW);
32  unsigned long t = pulseIn(echo, HIGH, 30000UL);   // en fazla 30 ms bekle / wait at most 30 ms
33  return t == 0 ? 999 : (int)(t / 58);              // 999 = yankı yok / 999 = no echo
34}
35
36int medianCm(int trig, int echo) {                  // 5 ölçümün ortancası / median of 5 readings
37  int v[5];
38  delay(60);                                        // önce el sensörünün yankısı sönsün / first let the hand sensor's echo die out
39  for (int i = 0; i < 5; i++) {
40    v[i] = pingCm(trig, echo);
41    delay(60);                                      // yankılar sönsün / let the echoes die out
42  }
43  for (int i = 0; i < 4; i++)                       // küçükten büyüğe sırala / sort ascending
44    for (int j = i + 1; j < 5; j++)
45      if (v[j] < v[i]) { int tmp = v[i]; v[i] = v[j]; v[j] = tmp; }
46  return v[2];
47}
48
49int toPercent(int cm) {                             // mesafe → doluluk yüzdesi / distance → fill percent
50  long p = (long)(EMPTY_CM - cm) * 100 / (EMPTY_CM - FULL_CM);
51  if (p < 0) p = 0;
52  if (p > 100) p = 100;
53  return (int)p;
54}
55
56void showLevel() {
57  digitalWrite(LED_GREEN, fillPercent < 50);
58  digitalWrite(LED_YELLOW, fillPercent >= 50 && fillPercent < 80);
59  digitalWrite(LED_RED, fillPercent >= 80);
60}
61
62void beep(int n) {
63  for (int i = 0; i < n; i++) {
64    digitalWrite(BUZZER, HIGH); delay(120);
65    digitalWrite(BUZZER, LOW);  delay(120);
66  }
67}
68
69void openLid() {
70  if (!lid.attached()) lid.attach(SERVO_PIN);
71  lid.write(LID_OPEN);                              // hızlı aç / open quickly
72  state = OPEN;
73  if (fillPercent >= 90) beep(2);                   // açılırken dolu uyarısı / full warning on opening
74}
75
76void setup() {
77  Serial.begin(9600);
78  pinMode(HAND_TRIG, OUTPUT); pinMode(HAND_ECHO, INPUT);
79  pinMode(FILL_TRIG, OUTPUT); pinMode(FILL_ECHO, INPUT);
80  pinMode(LED_GREEN, OUTPUT); pinMode(LED_YELLOW, OUTPUT); pinMode(LED_RED, OUTPUT);
81  pinMode(BUZZER, OUTPUT);
82  lid.write(LID_CLOSED);                            // attach'tan önce: açılışta 90°'ye sıçramasın / before attach: no jump to 90° at power-up
83  lid.attach(SERVO_PIN);
84  delay(500);
85  lid.detach();                                     // kapalıyken servoyu bırak / release the servo while closed
86  closedAt = millis();
87  showLevel();
88}
89
90void loop() {
91  unsigned long now = millis();
92
93  // 1) El algılama, 60 ms'de bir / Hand detection, every 60 ms
94  if (now - lastHandPing >= 60) {
95    lastHandPing = now;
96    int d = pingCm(HAND_TRIG, HAND_ECHO);
97    if (d >= 2 && d <= HAND_CM) {
98      if (handHits < 2) handHits++;
99    } else {
100      handHits = 0;
101    }
102    if (handHits >= 2) {                            // art arda 2 ölçüm / 2 readings in a row
103      lastHand = now;                               // bekleme süresi yeniden başlar / the wait time restarts
104      if (state != OPEN) openLid();                 // kapanırken el gelirse yeniden aç / reopen if a hand appears while closing
105    }
106  }
107
108  // 2) Bekleme süresi doldu mu? / Has the wait time passed?
109  if (state == OPEN && now - lastHand >= OPEN_TIME) {
110    state = CLOSING;
111    closeStart = now;
112  }
113
114  // 3) Yavaş kapanma: açı geçen süreden hesaplanır / Slow close: angle is computed from elapsed time
115  if (state == CLOSING) {
116    int moved = (now - closeStart) / CLOSE_STEP_MS;
117    int range = abs(LID_OPEN - LID_CLOSED);
118    if (moved >= range) {
119      lid.write(LID_CLOSED);
120      state = CLOSED;
121      closedAt = now;
122    } else {
123      lid.write(LID_OPEN > LID_CLOSED ? LID_OPEN - moved : LID_OPEN + moved);
124    }
125  }
126
127  // 4) Kapandıktan 0,4 s sonra servoyu bırak / Release the servo 0.4 s after closing
128  if (state == CLOSED && lid.attached() && now - closedAt > 400) lid.detach();
129
130  // 5) Doluluk: yalnızca kapak kapalı ve durgunken / Fill level: only while the lid is closed and still
131  if (state == CLOSED && now - closedAt > 1000 && now - lastFill >= 3000) {
132    lastFill = now;
133    int cm = medianCm(FILL_TRIG, FILL_ECHO);
134    if (cm < 999) fillPercent = toPercent(cm);     // yankı yoksa eski değeri koru / keep the old value if there is no echo
135    showLevel();
136    Serial.print(F("Doluluk / Fill: "));
137    Serial.print(cm);
138    Serial.print(F(" cm = %"));
139    Serial.println(fillPercent);
140    if (fillPercent >= 90 && !fullWarned) {         // doldu: bir kez öt / just filled: beep once
141      fullWarned = true;
142      beep(3);
143    }
144    if (fillPercent < 70) fullWarned = false;       // boşaltıldı / emptied
145  }
146}
Fonksiyon ya da ayarNe 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
handHitsArt arda kaç ölçümde el görüldüğünü sayar; tek bir hatalı ölçüm kapağı açmaz
OPEN_TIMEElin son görüldüğü andan kapanmaya başlamaya kadar geçen süre (4000 ms)
CLOSE_STEP_MSKapanı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
fullWarnedDolu 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.

DolulukYanan LEDBuzzerAnlamı
%0–49YeşilSessizRahat, yer var
%50–79SarıSessizYarıdan fazla dolu, yakında boşalt
%80–89KırmızıSessizNeredeyse dolu
%90–100KırmızıDolduğu an 3 kısa bip, her açılışta 2 bipHemen 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.

Ses hızı sıcaklıkla değişir: yaklaşık olarak v = 331,3 + 0,606 × T (m/s). 58 sayısı 20 °C için doğrudur; 5 °C'lik bir balkonda ses daha yavaş gittiği için ölçümler yaklaşık %3 uzun çıkar. 35 cm'lik bir kutuda bu 1 cm'den az bir hatadır ve doluluk için önemsizdir, ama sunumda jüri sorarsa nedenini bilmek iyi bir izlenim bırakır.

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)VIN5–6 saatÖnerilmez: servo kalkışında gerilim çöker, kart yeniden başlar
6 adet AA alkalin pil (9 V)DC jak ya da VINYaklaşık 1 günSunum günü için yeterli
2 adet 18650 (7,4 V, 2500 mAh)VIN; servo için 5–6 V'a ayarlı LM2596Yaklaşık 1,5 günŞarj edilebilir; 2S şarj ve koruma devresi gerekir
10 000 mAh powerbankUSB3–4 günBazı powerbank'ler 50–100 mA'in altındaki yükte kendini kapatır
5 V 2 A adaptörUSB ya da 5V piniSınırsızEvde kalıcı kullanım için en iyi seçim
18650 Li-ion pil yuvası
18650 pil yuvası: iki hücre seri bağlanınca 7,4 V elde edilir. Bu gerilim Arduino'yu VIN'den beslemeye uygundur; servo için araya 5–6 V'a ayarlanmış bir düşürücü modül konur.

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.

Servonun, sensörlerin ve Arduino'nun bütün GND'leri ortak olmalı; ayrı bir kaynaktan beslenen servonun GND'si Arduino'ya bağlı değilse servo sinyali anlamaz ve titrer. Servonun besleme uçlarına 470 µF'lık bir kondansatör koy. Kapak her açıldığında Arduino yeniden başlıyorsa servo kalkışta 5 V hattını çökertiyor demektir; nedenleri ve çözümleri Arduino kendi kendine resetleniyor 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.

ESP32 geliştirme kartı
ESP32: Wi-Fi'si sayesinde çöp kutusu dolduğunda telefona Telegram mesajı gönderir. HC-SR04'lerin ECHO hatları 3,3 V'a bölünmeden pinlerine bağlanmamalıdır.
Arduino (C++)
1// ESP32 Arduino çekirdeği 3.x / ESP32 Arduino core 3.x
2#include <WiFi.h>
3#include <WiFiClientSecure.h>
4#include <HTTPClient.h>
5
6const char* WIFI_SSID = "WIFI_ADIN";              // kendi ağın / your network
7const char* WIFI_PASS = "WIFI_SIFREN";
8const char* BOT_TOKEN = "123456789:ABCdefGhIJKlmNoPQRstuVWxyz";  // BotFather'dan / from BotFather
9const char* CHAT_ID   = "123456789";              // senin sohbet kimliğin / your chat ID
10
11// Pinler: ECHO'lar 1 kΩ / 2 kΩ bölücüyle / Pins: ECHOs via a 1 kΩ / 2 kΩ divider
12const int HAND_TRIG = 18, HAND_ECHO = 19;
13const int FILL_TRIG = 25, FILL_ECHO = 26;
14const int SERVO_PIN = 13;
15const int LED_GREEN = 27, LED_YELLOW = 32, LED_RED = 33;
16const int BUZZER = 23;
17
18const int HAND_CM = 20;
19const int LID_CLOSED = 10, LID_OPEN = 110;
20const unsigned long OPEN_TIME = 4000, CLOSE_STEP_MS = 20;
21const int EMPTY_CM = 35, FULL_CM = 8;
22const int NOTIFY_PCT = 85;                        // bu yüzdede bildir / notify at this percent
23const int RESET_PCT = 30;                         // bunun altına inince "boşaltıldı" / below this: "emptied"
24
25enum LidState { CLOSED, OPEN, CLOSING };
26LidState state = CLOSED;
27unsigned long lastHand = 0, lastHandPing = 0, closeStart = 0, closedAt = 0, lastFill = 0, lastTry = 0;
28int fillPercent = 0;
29byte handHits = 0;
30bool notified = false, servoOn = false, fullWarned = false;
31
32void servoWrite(int angle) {                      // 0–180° → 544–2400 µs, 50 Hz
33  uint32_t us = map(angle, 0, 180, 544, 2400);
34  ledcWrite(SERVO_PIN, us * 16384 / 20000);       // 14 bit, 20 ms periyot / 14-bit, 20 ms period
35  servoOn = true;
36}
37
38void servoRelease() {                             // darbe yok: servo serbest kalır / no pulses: the servo relaxes
39  ledcWrite(SERVO_PIN, 0);
40  servoOn = false;
41}
42
43int pingCm(int trig, int echo) {
44  digitalWrite(trig, LOW);  delayMicroseconds(2);
45  digitalWrite(trig, HIGH); delayMicroseconds(10);
46  digitalWrite(trig, LOW);
47  unsigned long t = pulseIn(echo, HIGH, 30000UL);
48  return t == 0 ? 999 : (int)(t / 58);            // 999 = yankı yok / 999 = no echo
49}
50
51int medianCm(int trig, int echo) {                // 5 ölçümün ortancası / median of 5 readings
52  int v[5];
53  delay(60);                                        // önce el sensörünün yankısı sönsün / first let the hand sensor's echo die out
54  for (int i = 0; i < 5; i++) {
55    v[i] = pingCm(trig, echo);
56    delay(60);
57  }
58  for (int i = 0; i < 4; i++)
59    for (int j = i + 1; j < 5; j++)
60      if (v[j] < v[i]) { int tmp = v[i]; v[i] = v[j]; v[j] = tmp; }
61  return v[2];
62}
63
64int toPercent(int cm) {
65  long p = (long)(EMPTY_CM - cm) * 100 / (EMPTY_CM - FULL_CM);
66  return (int)constrain(p, 0L, 100L);
67}
68
69void showLevel() {
70  digitalWrite(LED_GREEN, fillPercent < 50);
71  digitalWrite(LED_YELLOW, fillPercent >= 50 && fillPercent < 80);
72  digitalWrite(LED_RED, fillPercent >= 80);
73}
74
75void beep(int n) {
76  for (int i = 0; i < n; i++) {
77    digitalWrite(BUZZER, HIGH); delay(120);
78    digitalWrite(BUZZER, LOW);  delay(120);
79  }
80}
81
82String urlEncode(const String& s) {               // Türkçe harfler ve boşluk için / for Turkish letters and spaces
83  const char* hex = "0123456789ABCDEF";
84  String out;
85  for (size_t i = 0; i < s.length(); i++) {
86    uint8_t c = s[i];
87    if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
88      out += (char)c;
89    } else {
90      out += '%';
91      out += hex[c >> 4];
92      out += hex[c & 15];
93    }
94  }
95  return out;
96}
97
98bool sendTelegram(const String& text) {
99  if (WiFi.status() != WL_CONNECTED) return false;
100  WiFiClientSecure client;
101  client.setInsecure();                           // sertifika denetimi yok, hobi projesi için yeterli / no certificate check, fine for a hobby project
102  HTTPClient http;
103  String url = String("https://api.telegram.org/bot") + BOT_TOKEN +
104               "/sendMessage?chat_id=" + CHAT_ID + "&text=" + urlEncode(text);
105  if (!http.begin(client, url)) return false;
106  int code = http.GET();                          // 200 = gönderildi / 200 = sent
107  Serial.printf("Telegram HTTP %d\n", code);
108  http.end();
109  return code == 200;
110}
111
112void openLid() {
113  servoWrite(LID_OPEN);
114  state = OPEN;
115  if (fillPercent >= 90) beep(2);
116}
117
118void setup() {
119  Serial.begin(115200);
120  pinMode(HAND_TRIG, OUTPUT); pinMode(HAND_ECHO, INPUT);
121  pinMode(FILL_TRIG, OUTPUT); pinMode(FILL_ECHO, INPUT);
122  pinMode(LED_GREEN, OUTPUT); pinMode(LED_YELLOW, OUTPUT); pinMode(LED_RED, OUTPUT);
123  pinMode(BUZZER, OUTPUT);
124  ledcAttach(SERVO_PIN, 50, 14);                  // 50 Hz, 14 bit servo sinyali / 50 Hz, 14-bit servo signal
125  servoWrite(LID_CLOSED);
126  delay(500);
127  servoRelease();
128  WiFi.mode(WIFI_STA);
129  WiFi.begin(WIFI_SSID, WIFI_PASS);               // arka planda bağlanır; kutu Wi-Fi olmadan da çalışır / connects in the background; the bin works without Wi-Fi too
130  closedAt = millis();
131  showLevel();
132}
133
134void loop() {
135  unsigned long now = millis();
136
137  if (now - lastHandPing >= 60) {                 // el sensörü / hand sensor
138    lastHandPing = now;
139    int d = pingCm(HAND_TRIG, HAND_ECHO);
140    if (d >= 2 && d <= HAND_CM) {
141      if (handHits < 2) handHits++;
142    } else {
143      handHits = 0;
144    }
145    if (handHits >= 2) {
146      lastHand = now;
147      if (state != OPEN) openLid();
148    }
149  }
150
151  if (state == OPEN && now - lastHand >= OPEN_TIME) {
152    state = CLOSING;
153    closeStart = now;
154  }
155
156  if (state == CLOSING) {                         // yavaş kapanma / slow close
157    int moved = (now - closeStart) / CLOSE_STEP_MS;
158    int range = abs(LID_OPEN - LID_CLOSED);
159    if (moved >= range) {
160      servoWrite(LID_CLOSED);
161      state = CLOSED;
162      closedAt = now;
163    } else {
164      servoWrite(LID_OPEN > LID_CLOSED ? LID_OPEN - moved : LID_OPEN + moved);
165    }
166  }
167
168  if (state == CLOSED && servoOn && now - closedAt > 400) servoRelease();
169
170  if (state == CLOSED && now - closedAt > 1000 && now - lastFill >= 3000) {
171    lastFill = now;
172    int cm = medianCm(FILL_TRIG, FILL_ECHO);
173    if (cm < 999) fillPercent = toPercent(cm);
174    showLevel();
175    Serial.printf("Doluluk / Fill: %d cm = %%%d\n", cm, fillPercent);
176
177    if (fillPercent >= 90 && !fullWarned) { fullWarned = true; beep(3); }
178    if (fillPercent < 70) fullWarned = false;
179
180    // Bildirim: kapak kapalıyken, en fazla dakikada bir dene / Notify: only with the lid closed, retry at most once a minute
181    if (!notified && fillPercent >= NOTIFY_PCT && (lastTry == 0 || now - lastTry > 60000UL)) {
182      lastTry = now;
183      notified = sendTelegram("Çöp kutusu %" + String(fillPercent) + " dolu, boşaltma zamanı. / The bin is " +
184                              String(fillPercent) + "% full.");
185    }
186    if (notified && fillPercent < RESET_PCT) {   // boşaltıldı: yeniden kur / emptied: re-arm
187      notified = false;
188      lastTry = 0;
189      sendTelegram("Çöp kutusu boşaltıldı. / The bin has been emptied.");
190    }
191  }
192}

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.

MicroPython
1from machine import Pin, PWM, time_pulse_us
2import time
3
4# Pinler: ECHO'lar 1 kΩ / 2 kΩ bölücüyle / Pins: ECHOs via a 1 kΩ / 2 kΩ divider
5hand_trig, hand_echo = Pin(2, Pin.OUT), Pin(3, Pin.IN)
6fill_trig, fill_echo = Pin(4, Pin.OUT), Pin(5, Pin.IN)
7green, yellow, red = Pin(10, Pin.OUT), Pin(11, Pin.OUT), Pin(12, Pin.OUT)
8buzzer = Pin(13, Pin.OUT)
9servo = PWM(Pin(15))
10servo.freq(50)                                  # servo için 50 Hz / 50 Hz for the servo
11
12HAND_CM = 20
13LID_CLOSED, LID_OPEN = 10, 110
14OPEN_MS, CLOSE_STEP_MS = 4000, 20
15EMPTY_CM, FULL_CM = 35, 8
16
17def ping_cm(trig, echo):
18    trig.low(); time.sleep_us(2)
19    trig.high(); time.sleep_us(10)              # 10 µs tetik / 10 µs trigger
20    trig.low()
21    t = time_pulse_us(echo, 1, 30000)           # yankı yoksa negatif / negative if there is no echo
22    return 999 if t < 0 else t // 58
23
24def median_cm(trig, echo):                      # 5 ölçümün ortancası / median of 5 readings
25    v = []
26    time.sleep_ms(60)                           # önce el sensörünün yankısı sönsün / first let the hand sensor's echo die out
27    for _ in range(5):
28        v.append(ping_cm(trig, echo))
29        time.sleep_ms(60)
30    v.sort()
31    return v[2]
32
33def servo_write(angle):                         # 0–180° → 544–2400 µs
34    servo.duty_ns((544 + angle * 1856 // 180) * 1000)
35
36def servo_release():                            # darbe yok: servo serbest / no pulses: servo relaxes
37    servo.duty_ns(0)
38
39def show_level(p):
40    green.value(p < 50)
41    yellow.value(50 <= p < 80)
42    red.value(p >= 80)
43
44def beep(n):
45    for _ in range(n):
46        buzzer.on(); time.sleep_ms(120)
47        buzzer.off(); time.sleep_ms(120)
48
49state = "closed"
50fill, hits, warned, released = 0, 0, False, False
51last_hand = last_ping = close_start = last_fill = time.ticks_ms()
52servo_write(LID_CLOSED)
53time.sleep_ms(500)
54servo_release()
55released = True
56closed_at = time.ticks_ms()
57show_level(fill)
58
59while True:
60    now = time.ticks_ms()
61
62    if time.ticks_diff(now, last_ping) >= 60:   # el sensörü, 60 ms'de bir / hand sensor, every 60 ms
63        last_ping = now
64        d = ping_cm(hand_trig, hand_echo)
65        hits = min(hits + 1, 2) if 2 <= d <= HAND_CM else 0
66        if hits >= 2:
67            last_hand = now
68            if state != "open":                 # kapanırken de yeniden açar / also reopens while closing
69                servo_write(LID_OPEN)
70                state, released = "open", False
71                if fill >= 90:
72                    beep(2)
73
74    if state == "open" and time.ticks_diff(now, last_hand) >= OPEN_MS:
75        state, close_start = "closing", now
76
77    if state == "closing":                      # yavaş kapanma / slow close
78        moved = time.ticks_diff(now, close_start) // CLOSE_STEP_MS
79        if moved >= abs(LID_OPEN - LID_CLOSED):
80            servo_write(LID_CLOSED)
81            state, closed_at = "closed", now
82        else:
83            servo_write(LID_OPEN - moved if LID_OPEN > LID_CLOSED else LID_OPEN + moved)
84
85    if state == "closed" and not released and time.ticks_diff(now, closed_at) > 400:
86        servo_release()
87        released = True
88
89    if state == "closed" and time.ticks_diff(now, closed_at) > 1000 and time.ticks_diff(now, last_fill) >= 3000:
90        last_fill = now
91        cm = median_cm(fill_trig, fill_echo)
92        if cm < 999:                            # yankı yoksa eski değer kalır / keep the old value if no echo
93            fill = max(0, min(100, (EMPTY_CM - cm) * 100 // (EMPTY_CM - FULL_CM)))
94        show_level(fill)
95        print("Doluluk / Fill:", cm, "cm =", fill, "%")
96        if fill >= 90 and not warned:
97            warned = True
98            beep(3)
99        if fill < 70:
100            warned = False
101
102    time.sleep_ms(5)

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:

  1. 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.
  2. Amaç: Dokunmadan açılan, dolunca uyaran ve isteğe bağlı olarak telefona haber veren bir kutu.
  3. Ç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.
  4. Malzeme ve maliyet: Parça listesini ve servo seçimini, tork hesabıyla birlikte tablo hâlinde ver.
  5. 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.
  6. Test sonuçları: Aşağıdaki tabloyu kendi ölçümlerinle doldur ve sonuçları grafiğe dök.
  7. 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.
TestNasıl yapılır?Kaydedilecek değer
Algılama mesafesiElini 5, 10, 15, 20 ve 25 cm'de 10'ar kez tutHer mesafede kaç denemede açıldığı (ör. 10/10)
Tepki süresiTelefonla ağır çekim (240 fps) video çekEl ile kapağın kıpırdaması arasındaki süre (ms)
Yanlış açılmaKutunun önünden 20 kez yürüyerek geçGereksiz açılma sayısı
Doluluk doğruluğuKutuyu cetvelle ölçerek dörtte bir, yarı ve dörtte üç oranında kâğıtla doldurGerçek doluluk ile ölçülen yüzde arasındaki fark
Kapanma hızıCLOSE_STEP_MS değerini 5, 20 ve 40 ms yapKapanma süresi ve telefonla ölçülen ses seviyesi
Pil ömrüDolu pille çalıştır, saatte bir gerilimi ölçKutunun kaç saat çalıştığı
Jüri en çok zayıf noktaları sorar; cevaplarını önceden hazırla. HC-SR04 yumuşak ve gözenekli yüzeyleri iyi göremez, bu yüzden kalın kumaş bir eldivenle kapak geç açılabilir. Doluluk ölçümü çöpün ortalamasını değil, en yüksek noktasını görür. Uno'lu bir kutu pille yalnızca 1–2 gün dayanır. Bu sınırları bildiğini göstermek ve her biri için bir çözüm önermek, projeni kusursuzmuş gibi anlatmaktan çok daha fazla puan kazandırır.

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

BelirtiOlası nedenÇözüm
Kapak kendi kendine açılıp kapanıyorSensör yakındaki bir duvarı ya da önünden geçen insanı görüyorHAND_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ıyorServonun kalkış akımı 5 V hattını çökertiyor470 µF kondansatör, güçlü servoyu ayrı kaynaktan besle, GND'ler ortak
Kapak kapalıyken servo vızıldıyor ve ısınıyorLID_CLOSED kapağı kenara bastırıyorAçıyı 2–3 derece geri al; detach() satırını silme
Kapak yarıda kalıyor ya da titriyorTork yetersiz ya da eksenler kayıkDaha güçlü servo, hafif kapak, karşı ağırlık; servo milini menteşeyle hizala
Doluluk boş kutuda bile yüksekPoş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şmiyorSensö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çmaECHO bölücüsü yok, yanlış ya da kablo ters1 kΩ / 2 kΩ bölücüyü ve TRIG-ECHO sırasını kontrol et
ESP32 sürekli yeniden başlıyorWi-Fi ve servo aynı anda akım çekiyor, USB kablosu zayıfKısa ve kaliteli kablo kullan, servoyu ayrı 5 V kaynaktan besle
Telegram mesajı gelmiyorToken ya da chat ID yanlış, bota /start yazılmamışSeri monitördeki HTTP kodunu oku (401, 400, 403)
Hata ararken parçaları tek tek dene: önce kalibrasyon koduyla iki sensörün seri monitörde mantıklı değerler verdiğini, sonra servonun kapalı ve açık açılara sorunsuz gittiğini gör, en son ana kodu yükle. ESP32'de "Brownout detector was triggered" mesajı görüyorsan nedenleri ve çözümleri ESP32 sürekli yeniden başlıyor yazısında.

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.

Tüm projeleri gör
HC-SR04 Mesafe Ölçme (Serial Ekran)
#27Başlangıç

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

4.6(45)
68
Buzzer Kullanımı
#13Başlangıç

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

4.7(89)
158
Servo Motor Kullanım
#35Başlangıç
Premium

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

4.7(48)
67
HC-SR04 ile Park Sensörü
#28Orta
Premium

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ı

4.8(64)
84
ESP32 HC-SR04 Ultrasonik Mesafe Ölçer
#116Orta

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.

4.9(28)
44
ESP32 Servo Motor (Potansiyometre Kontrolü)
#118Orta
Premium

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.

4.3(29)
42
ESP32 Park Sensörü (HC-SR04 + 5 LED Bar + Buzzer + OLED)
#142İleri
Premium

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

0.0(0)
14
ESP32 Hareket Alarm Sistemi (PIR + Buzzer + LED + Telegram)
#145İleri
Premium

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

0.0(0)
8