Rehberler · 11 dk okuma

Arduino Trafik Lambası Yapımı: Devre, Kod, Yaya Butonu ve Kavşak

Arduino trafik lambası yapımı: 3 LED ve 220 Ω ile devre, delay ve millis kodu, yaya butonu, iki yönlü kavşak, Tinkercad, ESP32 ve Pico sürümleri, ödev ipuçları.

Arduino trafik lambası, kırmızı, sarı ve yeşil üç LED'i gerçek bir trafik ışığı gibi sırayla yakıp söndüren, elektroniğe başlayanların ilk yaptığı klasik projedir. Kurmak için bir Arduino Uno, üç LED, üç adet 220 Ω direnç ve birkaç jumper kablo yeter; ilk çalışan sürümü yarım saatte bitirirsin. Ama bu basit proje aslında çok şey öğretir: dijital çıkışlar, LED ve direnç hesabı, zamanlama, buton okuma ve kodu düzenli yazmanın en temiz yolu olan durum makinesi, hepsi bu birkaç LED'in içindedir.

Bu rehberde önce gerçek trafik ışıklarının hangi sırayla ve ne kadar süre yandığını göreceksin, sonra devreyi kurup delay() ile en basit kodu yazacaksın. Ardından yaya butonlu bir sürüm yapacak, aynı mantığı millis() ve durum makinesiyle hiç bekletmeden çalışır hale getirecek ve iki yönlü bir kavşağı kontrol edeceksin. Son bölümlerde projeyi Tinkercad'de kurulum yapmadan denemeyi, ESP32 ve Raspberry Pi Pico (MicroPython) sürümlerini ve okul ödevi ya da proje raporu hazırlarken işine yarayacak ipuçlarını bulacaksın.

Trafik lambası için kırmızı, sarı ve yeşil 5 mm LED'ler
5 mm LED'ler: trafik lambası için kırmızı, sarı ve yeşilden birer tane yeter; yaya ışığı ve kavşak için birkaç tane daha ayır.

Gerçek trafik lambası nasıl çalışır?

Trafik lambası, araçlara sırayla geçiş hakkı veren bir zamanlayıcıdır. Türkiye'de, Almanya'da ve İngiltere'de döngü şöyledir: kırmızı → kırmızı + sarı → yeşil → sarı → kırmızı. Kırmızı ile sarının birlikte yanması "yeşil yanmak üzere, harekete hazırlan ama henüz geçme" anlamına gelir. Tek başına yanan sarı ise "ışık kırmızıya dönüyor, durmaya hazırlan" demektir. ABD, Fransa, İtalya ve İspanya gibi ülkelerde kırmızı + sarı fazı yoktur; kırmızıdan doğrudan yeşile geçilir. Sarının süresi de rastgele seçilmez: Almanya'daki kurallar hız sınırı 50 km/sa olan yollarda 3 saniye, 60 km/sa olanlarda 4 saniye, 70 km/sa olanlarda 5 saniye sarı öngörür. Çünkü sarı yandığında duramayacak kadar yakın olan sürücünün kavşağı güvenle terk edebilmesi gerekir.

FazYanan lambaAnlamıBu yazıdaki süre
1KırmızıDur5 sn
2Kırmızı + sarıYeşil yanacak, hazırlan1 sn
3YeşilGeç5 sn
4SarıKırmızı yanacak, dur3 sn
KavşaktaTüm yönler kırmızıKavşağın boşalması için güvenlik payı1 sn

Malzemeler ve 220 Ω direnç hesabı

MalzemeAdetNot
Arduino Uno (ya da Nano)1ESP32 ya da Raspberry Pi Pico da olur
5 mm LED: kırmızı, sarı, yeşilHer renkten 1Yaya ışığı için +1 kırmızı ve +1 yeşil, kavşak için +1 takım
220 Ω direnç3Her LED için bir tane; tüm sürümler için 8
Push buton (6×6 mm)1Yalnızca yaya butonlu sürümde
Breadboard1Yarım boy (400 delikli) yeterli
Jumper kablo10–20Erkek-erkek
USB kablo1Kodu yüklemek ve kartı beslemek için

LED'ler akımı kendi kendine sınırlamaz; doğrudan 5 V'a bağlanan bir LED, hem kendisinin hem de pinin kaldırabileceğinden fazla akım çeker. Bu yüzden her LED'e seri bir direnç takarsın. Değeri Ohm Kanunu ile bulunur: R = (besleme gerilimi − LED ileri gerilimi) / akım. Kırmızı bir LED için (5 V − 2,0 V) / 0,015 A ≈ 200 Ω çıkar; en yakın standart değer olan 220 Ω ile akım yaklaşık 13,6 mA olur. Bu değer LED'i yeterince parlak yakar ve Arduino pini için önerilen 20 mA sınırının altında kalır. 220 Ω'luk direnç dört bantlıysa kırmızı-kırmızı-kahverengi-altın, beş bantlıysa kırmızı-kırmızı-siyah-siyah-kahverengi renkleriyle işaretlidir; emin olmak için direnç renk kodu aracını, farklı renk ve gerilimler için LED direnci hesaplayıcı aracını kullanabilirsin.

LED rengiİleri gerilim (yaklaşık)5 V'ta akım (220 Ω)3,3 V'ta akım (220 Ω)
Kırmızı1,8–2,2 V≈ 13,6 mA≈ 5,9 mA
Sarı1,9–2,2 V≈ 13,2 mA≈ 5,5 mA
Yeşil (sarımsı yeşil)2,0–2,4 V≈ 13,2 mA≈ 5,5 mA
Yeşil (parlak, saf yeşil)2,8–3,4 V≈ 9,1 mA≈ 1,4 mA (çok soluk)

Devre bağlantısı ve pin tablosu

Her LED'in uzun bacağı (anot, +) bir direnç üzerinden Arduino'nun dijital pinine, kısa bacağı (katot, −) ise GND'ye gider. Direncin anot tarafında ya da katot tarafında olması fark etmez; seri bağlı olması yeterlidir. Breadboard üzerinde en temiz yöntem, Arduino'nun GND pinini breadboard'un mavi (−) hattına bağlamak ve üç LED'in kısa bacaklarını bu hatta toplamaktır. Aşağıdaki tablo bu yazıdaki tüm kodların kullandığı pinleri gösterir; ilk kod yalnızca ilk üç satırı kullanır. Kartının pin dizilişini Pinout aracında görebilirsin.

GörevArduino UnoESP32Raspberry Pi Pico
Araç kırmızı (A yönü)D10GPIO 5GP13
Araç sarı (A yönü)D9GPIO 18GP12
Araç yeşil (A yönü)D8GPIO 19GP11
Yaya kırmızıD7GPIO 21GP15
Yaya yeşilD6GPIO 22GP14
Yaya butonu (diğer ucu GND)D2GPIO 23GP4
B yönü kırmızı / sarı / yeşilD5 / D4 / D3GPIO 25 / 26 / 27GP8 / GP7 / GP6
LED katotları ve butonGNDGNDGND
LED'i ters takarsan yanmaz ama bozulmaz; direnci unutursan ise LED ve pin zorlanır, LED kısa sürede kararabilir. Arduino Uno'da bir pinden en fazla 20 mA çekmeyi hedefle (mutlak sınır 40 mA) ve tüm pinlerin toplamını 200 mA'nın altında tut. LED'lerin hiçbiri yanmıyorsa ilk bakacağın yer GND bağlantısıdır: breadboard'un mavi hattı Arduino'nun GND pinine bağlı değilse devre tamamlanmaz.

1. adım: delay() ile en basit trafik lambası kodu

Arduino (C++)
1// Arduino trafik lambası: delay() ile en basit sürüm / simplest version with delay()
2const int KIRMIZI = 10;  // kırmızı LED / red LED
3const int SARI = 9;      // sarı LED / yellow LED
4const int YESIL = 8;     // yeşil LED / green LED
5
6// Üç LED'i tek satırda ayarla / set all three LEDs in one line
7void lambalar(int k, int s, int y) {
8  digitalWrite(KIRMIZI, k);
9  digitalWrite(SARI, s);
10  digitalWrite(YESIL, y);
11}
12
13void setup() {
14  pinMode(KIRMIZI, OUTPUT);
15  pinMode(SARI, OUTPUT);
16  pinMode(YESIL, OUTPUT);
17}
18
19void loop() {
20  lambalar(HIGH, LOW, LOW);   // DUR: kırmızı / STOP: red
21  delay(5000);
22  lambalar(HIGH, HIGH, LOW);  // HAZIRLAN: kırmızı + sarı / GET READY: red + yellow
23  delay(1000);
24  lambalar(LOW, LOW, HIGH);   // GEÇ: yeşil / GO: green
25  delay(5000);
26  lambalar(LOW, HIGH, LOW);   // YAVAŞLA: sarı / SLOW DOWN: yellow
27  delay(3000);
28}

Kod, üç pini setup() içinde çıkış (OUTPUT) yapar ve loop() içinde dört fazı sırayla uygular. Üç digitalWrite satırını tekrar tekrar yazmak yerine lambalar() adında küçük bir fonksiyon kullandık; böylece her faz tek satırda okunur ve bir hata yaptığında nerede olduğu hemen görünür. Kodu yüklemek için Araçlar menüsünden kart olarak Arduino Uno'yu ve doğru portu seç, ardından Yükle düğmesine bas. Yükleme hata verirse Arduino kod yüklenmiyor yazısındaki kontrol listesine bak. Süreleri değiştirmek için delay() içindeki milisaniye değerlerini düzenlemen yeterli: 5000, 5 saniye demektir.

2. adım: yaya butonlu trafik lambası

Şehirlerde gördüğün yaya butonlu ışıklar araçlara sürekli yeşil yakar ve yalnızca biri butona bastığında yayalara geçiş verir. Bu sürümde araç lambasına iki LED'lik bir yaya lambası (kırmızı ve yeşil) ve D2'ye bir buton ekliyoruz. Butonun bir bacağı D2'ye, çapraz köşedeki bacağı GND'ye gider; kodda INPUT_PULLUP kullandığımız için harici direnç gerekmez ve butona basıldığında pin LOW okunur. Dört bacaklı butonun iç bağlantılarını ve titreşim (debounce) konusunu Arduino'ya Buton Nasıl Bağlanır? yazısında ayrıntılı bulabilirsin.

6×6 mm dört bacaklı push buton
6×6 mm push buton: dört bacağı ikişer ikişer içeriden bağlıdır; çapraz köşedeki iki bacağı kullanırsan her zaman doğru bağlamış olursun.
Arduino (C++)
1// Yaya butonlu trafik lambası (delay ile) / pedestrian push-button light (with delay)
2const int KIRMIZI = 10, SARI = 9, YESIL = 8;  // araç lambası / vehicle light
3const int YAYA_KIRMIZI = 7, YAYA_YESIL = 6;   // yaya lambası / pedestrian light
4const int BUTON = 2;                          // diğer ucu GND / other leg to GND
5
6void setup() {
7  pinMode(KIRMIZI, OUTPUT);
8  pinMode(SARI, OUTPUT);
9  pinMode(YESIL, OUTPUT);
10  pinMode(YAYA_KIRMIZI, OUTPUT);
11  pinMode(YAYA_YESIL, OUTPUT);
12  pinMode(BUTON, INPUT_PULLUP);               // basılınca LOW okunur / reads LOW when pressed
13  digitalWrite(YESIL, HIGH);                  // araçlar geçer / cars go
14  digitalWrite(YAYA_KIRMIZI, HIGH);           // yayalar bekler / pedestrians wait
15}
16
17void loop() {
18  if (digitalRead(BUTON) == LOW) {            // yaya butona bastı / pedestrian pressed
19    delay(2000);                              // araçlara kısa süre tanı / short grace time for cars
20    digitalWrite(YESIL, LOW);
21    digitalWrite(SARI, HIGH);                 // sarı / yellow
22    delay(3000);
23    digitalWrite(SARI, LOW);
24    digitalWrite(KIRMIZI, HIGH);              // araçlar durur / cars stop
25    delay(1000);                              // tüm kırmızı güvenlik payı / all-red safety gap
26    digitalWrite(YAYA_KIRMIZI, LOW);
27    digitalWrite(YAYA_YESIL, HIGH);           // yayalar geçer / pedestrians cross
28    delay(6000);
29    for (int i = 0; i < 6; i++) {             // yaya yeşili 3 kez yanıp söner / green man flashes 3 times
30      digitalWrite(YAYA_YESIL, i % 2 == 0 ? LOW : HIGH);
31      delay(500);
32    }
33    digitalWrite(YAYA_YESIL, LOW);
34    digitalWrite(YAYA_KIRMIZI, HIGH);
35    delay(1000);                              // tüm kırmızı / all red
36    digitalWrite(SARI, HIGH);                 // kırmızı + sarı / red + yellow
37    delay(1000);
38    digitalWrite(KIRMIZI, LOW);
39    digitalWrite(SARI, LOW);
40    digitalWrite(YESIL, HIGH);                // araçlar yeniden geçer / cars go again
41    delay(8000);                              // en kısa yeşil süresi / minimum green time
42  }
43}

Bu kod çalışır, ama önemli bir zayıflığı var: butona basıldıktan sonra Arduino yaklaşık 25 saniye boyunca delay() satırlarında bekler. Bu sürede butona tekrar basılırsa fark edilmez, seri porttan veri okunamaz, bir ekran güncellenemez. Kodun sonundaki 8 saniyelik bekleme sırasında gelen basışlar da tamamen kaybolur; yaya butona basıp bırakır, hiçbir şey olmaz ve tekrar basmak zorunda kalır. Projeye ses ya da geri sayım eklemek istediğinde bu yapı daha da zorlaşır. Çözüm, beklemeyi bırakıp zamanı ölçmektir.

3. adım: millis() ve durum makinesiyle bekletmeden çalışma

Durum makinesi (state machine), sistemin her an tam olarak tek bir durumda bulunduğu ve belirli koşullar oluşunca bir sonraki duruma geçtiği bir tasarım yöntemidir. Trafik lambası buna mükemmel bir örnektir: "araçlara yeşil", "araçlara sarı" ve "yayalar geçiyor" gibi her faz bir durumdur. Kod artık delay() ile beklemez; loop() her turda yalnızca iki soru sorar: Şu an hangi durumdayım? Bu durumda ne kadar zaman geçti? Geçen süre millis() - durumBaslangic ile ölçülür ve süre dolunca bir sonraki duruma geçilir. millis() mantığını daha önce görmediysen önce Arduino millis() ve delay() yazısına göz atabilirsin. Kodu yazmadan önce durumları bir tabloya dökmek işini çok kolaylaştırır:

DurumAraç lambasıYaya lambasıSüreSonraki durum
ARAC_YESILYeşilKırmızıEn az 8 sn, talep gelene kadarARAC_SARI
ARAC_SARISarıKırmızı3 snYAYA_BEKLE
YAYA_BEKLEKırmızıKırmızı1 snYAYA_GECER
YAYA_GECERKırmızıYeşil6 snYAYA_FLAS
YAYA_FLASKırmızıYeşil yanıp söner3 snYAYA_BITTI
YAYA_BITTIKırmızıKırmızı1 snKIRMIZI_SARI
KIRMIZI_SARIKırmızı + sarıKırmızı1 snARAC_YESIL
Arduino (C++)
1// Yaya butonlu trafik lambası: millis() + durum makinesi / pedestrian light: millis() + state machine
2const int KIRMIZI = 10, SARI = 9, YESIL = 8;  // araç lambası / vehicle light
3const int YAYA_KIRMIZI = 7, YAYA_YESIL = 6;   // yaya lambası / pedestrian light
4const int BUTON = 2;                          // diğer ucu GND / other leg to GND
5
6enum Durum { ARAC_YESIL, ARAC_SARI, YAYA_BEKLE, YAYA_GECER, YAYA_FLAS, YAYA_BITTI, KIRMIZI_SARI };
7const char* const ISIM[] = { "ARAC_YESIL", "ARAC_SARI", "YAYA_BEKLE", "YAYA_GECER",
8                             "YAYA_FLAS", "YAYA_BITTI", "KIRMIZI_SARI" };
9
10// Süreler (ms) / durations (ms)
11const unsigned long MIN_YESIL = 8000;   // araçlara en az yeşil / minimum green for cars
12const unsigned long SARI_SURE = 3000;   // sarı / yellow
13const unsigned long GUVENLIK = 1000;    // tüm kırmızı / all red
14const unsigned long YAYA_SURE = 6000;   // yaya yeşili / pedestrian green
15const unsigned long FLAS_SURE = 3000;   // yanıp sönen yaya yeşili / flashing green man
16const unsigned long KS_SURE = 1000;     // kırmızı + sarı / red + yellow
17
18Durum durum = ARAC_YESIL;
19unsigned long durumBaslangic = 0;       // durumun başladığı an / when the state started
20bool yayaTalebi = false;                // butona basıldı mı? / was the button pressed?
21
22void aracLamba(bool k, bool s, bool y) {
23  digitalWrite(KIRMIZI, k);
24  digitalWrite(SARI, s);
25  digitalWrite(YESIL, y);
26}
27
28void yayaLamba(bool k, bool y) {
29  digitalWrite(YAYA_KIRMIZI, k);
30  digitalWrite(YAYA_YESIL, y);
31}
32
33// Yeni duruma geç: lambaları ayarla, saati başlat / enter a state: set the lamps, restart the clock
34void gec(Durum yeni) {
35  durum = yeni;
36  durumBaslangic = millis();
37  switch (yeni) {
38    case ARAC_YESIL:   aracLamba(0, 0, 1); yayaLamba(1, 0); break;
39    case ARAC_SARI:    aracLamba(0, 1, 0); yayaLamba(1, 0); break;
40    case YAYA_BEKLE:   aracLamba(1, 0, 0); yayaLamba(1, 0); break;
41    case YAYA_GECER:   aracLamba(1, 0, 0); yayaLamba(0, 1); yayaTalebi = false; break;
42    case YAYA_FLAS:    aracLamba(1, 0, 0); yayaLamba(0, 1); break;
43    case YAYA_BITTI:   aracLamba(1, 0, 0); yayaLamba(1, 0); break;
44    case KIRMIZI_SARI: aracLamba(1, 1, 0); yayaLamba(1, 0); break;
45  }
46  Serial.println(ISIM[yeni]);           // durum günlüğü / state log
47}
48
49void setup() {
50  Serial.begin(9600);
51  pinMode(KIRMIZI, OUTPUT);
52  pinMode(SARI, OUTPUT);
53  pinMode(YESIL, OUTPUT);
54  pinMode(YAYA_KIRMIZI, OUTPUT);
55  pinMode(YAYA_YESIL, OUTPUT);
56  pinMode(BUTON, INPUT_PULLUP);
57  gec(ARAC_YESIL);
58}
59
60void loop() {
61  unsigned long gecen = millis() - durumBaslangic;  // bu durumda geçen süre / time spent in this state
62
63  // yaya yeşili yanarken basış yok sayılır / presses during the green man are ignored
64  if (digitalRead(BUTON) == LOW && durum != YAYA_GECER) {
65    yayaTalebi = true;                  // talebi hatırla / remember the request
66  }
67
68  switch (durum) {
69    case ARAC_YESIL:
70      if (yayaTalebi && gecen >= MIN_YESIL) gec(ARAC_SARI);
71      break;
72    case ARAC_SARI:
73      if (gecen >= SARI_SURE) gec(YAYA_BEKLE);
74      break;
75    case YAYA_BEKLE:
76      if (gecen >= GUVENLIK) gec(YAYA_GECER);
77      break;
78    case YAYA_GECER:
79      if (gecen >= YAYA_SURE) gec(YAYA_FLAS);
80      break;
81    case YAYA_FLAS:
82      digitalWrite(YAYA_YESIL, (gecen / 250) % 2 == 0);  // 250 ms'de bir yanıp söner / toggles every 250 ms
83      if (gecen >= FLAS_SURE) gec(YAYA_BITTI);
84      break;
85    case YAYA_BITTI:
86      if (gecen >= GUVENLIK) gec(KIRMIZI_SARI);
87      break;
88    case KIRMIZI_SARI:
89      if (gecen >= KS_SURE) gec(ARAC_YESIL);
90      break;
91  }
92}

Kodun kalbi gec() fonksiyonudur: yeni durumu kaydeder, başlangıç zamanını sıfırlar, lambaları o duruma göre ayarlar ve durumun adını Seri Monitör'e yazar. loop() içindeki switch ise yalnızca "süre doldu mu?" diye bakar. Butona basıldığında hemen bir şey yapılmaz; yayaTalebi bayrağı kaldırılır ve araçlar en az 8 saniyelik yeşillerini tamamlayınca döngü başlar. Basışlar artık hiçbir zaman kaybolmaz ve buton yalnızca bir bayrağı değiştirdiği için mekanik titreşim (bounce) de sorun yaratmaz. Yaya yeşili zaten yanarken gelen basışlar ise yok sayılır; yoksa yayalar geçtikten hemen sonra gereksiz yere yeni bir döngü başlardı. Seri Monitör'ü 9600 baud hızında açarsan her geçişin adını görürsün; bu çıktı hem hata ayıklamada hem de ödev raporunda işine yarar.

Yeni bir özellik eklemek istediğinde önce tabloya bir satır ekle, sonra koda bir case yaz. Örneğin sarının yanıp söndüğü bir gece durumu ekleyebilir ve bir LDR ışık sensörü karanlığı algılayınca bu duruma geçebilirsin. Durum makinelerini daha ayrıntılı öğrenmek için Arduino durum makinesi yazısına bakabilirsin.

4. adım: iki yönlü kavşak

Gerçek bir kavşakta iki yolun ışıkları birbirine bağlıdır: A yolu yeşilken B yolu mutlaka kırmızıdır. Bu sürümde altı LED kullanıyoruz: A yönü için D10, D9 ve D8, B yönü için D5, D4 ve D3. Önemli bir ayrıntı, tüm yönlerin kırmızı olduğu kısa güvenlik aralığıdır: bir yön kırmızıya döndükten sonra diğer yön hemen yeşil olmaz, kavşağın içinde kalan araçların çıkabilmesi için 1 saniye beklenir. Bu kez durumları switch yerine bir tabloya (diziye) yazıyoruz; her adımda hangi lambaların yanacağı tek bir bayttaki bitlerle, ne kadar süreceği ise milisaniye olarak tutulur.

Arduino (C++)
1// İki yönlü kavşak: tablo tabanlı durum makinesi / two-way junction: table-driven state machine
2const byte A_KIRMIZI = 10, A_SARI = 9, A_YESIL = 8;  // A yönü (ana yol) / direction A (main road)
3const byte B_KIRMIZI = 5,  B_SARI = 4, B_YESIL = 3;  // B yönü (ara yol) / direction B (side road)
4const byte PINLER[6] = { A_KIRMIZI, A_SARI, A_YESIL, B_KIRMIZI, B_SARI, B_YESIL };
5
6struct Adim {
7  byte maske;            // bit sırası: A_K A_S A_Y B_K B_S B_Y / bit order: A_R A_Y A_G B_R B_Y B_G
8  unsigned long sure;    // ms
9};
10
11const Adim PLAN[] = {
12  { 0b001100, 8000 },   // A yeşil, B kırmızı / A green, B red
13  { 0b010100, 3000 },   // A sarı, B kırmızı / A yellow, B red
14  { 0b100100, 1000 },   // tüm yönler kırmızı / all red
15  { 0b100110, 1000 },   // A kırmızı, B kırmızı + sarı / A red, B red + yellow
16  { 0b100001, 5000 },   // A kırmızı, B yeşil / A red, B green
17  { 0b100010, 3000 },   // A kırmızı, B sarı / A red, B yellow
18  { 0b100100, 1000 },   // tüm yönler kırmızı / all red
19  { 0b110100, 1000 },   // A kırmızı + sarı, B kırmızı / A red + yellow, B red
20};
21const byte ADIM_SAYISI = sizeof(PLAN) / sizeof(PLAN[0]);
22
23byte adim = 0;
24unsigned long adimBaslangic = 0;
25
26void uygula(byte maske) {
27  if ((maske & 0b001000) && (maske & 0b000001)) {  // iki yeşil aynı anda yanamaz / never two greens
28    maske = 0b100100;                              // güvenli durum: tüm kırmızı / fail-safe: all red
29  }
30  for (byte i = 0; i < 6; i++) {
31    digitalWrite(PINLER[i], (maske >> (5 - i)) & 1);  // soldaki bit A_KIRMIZI / leftmost bit is A_RED
32  }
33}
34
35void setup() {
36  for (byte i = 0; i < 6; i++) pinMode(PINLER[i], OUTPUT);
37  uygula(PLAN[0].maske);
38  adimBaslangic = millis();
39}
40
41void loop() {
42  if (millis() - adimBaslangic >= PLAN[adim].sure) {
43    adimBaslangic += PLAN[adim].sure;   // kaymasız zamanlama / drift-free timing
44    adim = (adim + 1) % ADIM_SAYISI;
45    uygula(PLAN[adim].maske);
46  }
47  // Burada buton, sensör ya da ekran işleri çalışabilir / buttons, sensors or a display can run here
48}

PLAN dizisindeki her satır bir adımdır. 0b001100 gibi ikili sayılarda soldan sağa bitler A kırmızı, A sarı, A yeşil, B kırmızı, B sarı ve B yeşil lambalarını temsil eder; 1 yanık, 0 sönük demektir. uygula() fonksiyonu bu altı biti sırayla pinlere yazar. Fonksiyonun başındaki kontrol, bir yazım hatası yüzünden iki yönün aynı anda yeşil yanmasını engeller ve sistemi tüm kırmızıya alır. Gerçek kavşak denetleyicilerinde de çakışan yeşilleri yakalayan ayrı bir güvenlik devresi bulunur. Süreleri değiştirmek ya da adım eklemek için yalnızca tabloyu düzenlemen yeter; kodun geri kalanına dokunmazsın. LED sayısı artınca pin yetmezse 74HC595 shift register ile üç pinden sekiz çıkış elde edebilirsin.

Tinkercad'de deneme: malzeme olmadan simülasyon

  1. tinkercad.com adresinde ücretsiz bir hesap aç ve Circuits (Devreler) bölümünden yeni bir devre oluştur.
  2. Sağdaki bileşen listesinden bir Arduino Uno R3, bir breadboard, üç LED ve üç direnç sürükle. LED'e tıklayıp rengini seç, dirence tıklayıp değerini 220 Ω yap.
  3. Kabloları çiz: LED anotları dirençler üzerinden D10, D9 ve D8'e, katotlar GND'ye. Kablo renklerini ayarlamak (örneğin GND için siyah) şemayı okunur kılar.
  4. Code düğmesine bas, açılır menüden Text seç, hazır örnek kodu sil ve bu yazıdaki kodu yapıştır.
  5. Start Simulation (Simülasyonu Başlat) düğmesine bas; LED'ler sırayla yanmaya başlar. Seri çıktıyı kod panelinin altındaki Serial Monitor bölümünde görebilirsin.

Tinkercad, devreyi gerçek parçalarla kurmadan önce bağlantıları ve kodu denemenin en kolay yoludur; direnci unutursan simülasyon LED'in üzerinde akımın çok yüksek olduğunu belirten bir uyarı gösterir. Butonlu sürümü denemek için simülasyon sırasında butona fareyle tıklaman yeterli. Bilgisayarın yavaşsa simülasyon gerçek zamandan yavaş akabilir, bu yüzden süreler biraz uzun görünebilir. Ödev teslim ederken Tinkercad'in paylaşım bağlantısını ya da devrenin ekran görüntüsünü ekleyebilirsin. Ayrıntılı kullanım için Tinkercad Arduino simülasyonu, ESP32 ve Pico'yu da destekleyen bir alternatif için Wokwi yazılarına bakabilirsin.

ESP32 ile trafik lambası ve gece modu

Bu yazıdaki Arduino kodları ESP32'de de çalışır; Arduino IDE'de kart olarak ESP32 Dev Module seçip pin sabitlerini tablodaki gibi değiştirmen yeterlidir. Aşağıdaki sürüm buna bir özellik ekliyor: gece modu. Trafiğin az olduğu saatlerde birçok kavşakta lambalar yanıp sönen sarıya geçer. ESP32 DevKit kartlarında GPIO 0'a bağlı bir BOOT düğmesi bulunduğu için ek bir butona bile gerek yoktur: kart açıldıktan sonra bu düğme normal bir giriş gibi okunabilir. Her basış normal mod ile gece modu arasında geçiş yapar.

Arduino (C++)
1// ESP32 trafik lambası + gece modu (BOOT düğmesi) / ESP32 traffic light + night mode (BOOT button)
2const int KIRMIZI = 5;      // GPIO 5
3const int SARI = 18;        // GPIO 18
4const int YESIL = 19;       // GPIO 19
5const int BOOT_BUTON = 0;   // kart üzerindeki BOOT düğmesi / on-board BOOT button
6
7struct Adim { bool k, s, y; unsigned long sure; };
8const Adim PLAN[] = {
9  { true,  false, false, 5000 },   // kırmızı / red
10  { true,  true,  false, 1000 },   // kırmızı + sarı / red + yellow
11  { false, false, true,  5000 },   // yeşil / green
12  { false, true,  false, 3000 },   // sarı / yellow
13};
14const int ADIM_SAYISI = sizeof(PLAN) / sizeof(PLAN[0]);
15
16int adim = 0;
17unsigned long adimBaslangic = 0;
18bool geceModu = false;
19int sonOkuma = HIGH, kararli = HIGH;     // debounce için / for debouncing
20unsigned long degisimZamani = 0;
21
22void lambalar(bool k, bool s, bool y) {
23  digitalWrite(KIRMIZI, k);
24  digitalWrite(SARI, s);
25  digitalWrite(YESIL, y);
26}
27
28void setup() {
29  Serial.begin(115200);
30  pinMode(KIRMIZI, OUTPUT);
31  pinMode(SARI, OUTPUT);
32  pinMode(YESIL, OUTPUT);
33  pinMode(BOOT_BUTON, INPUT_PULLUP);
34  lambalar(PLAN[0].k, PLAN[0].s, PLAN[0].y);
35  adimBaslangic = millis();
36}
37
38void loop() {
39  unsigned long simdi = millis();
40
41  // 50 ms kararlı kalan basışı kabul et / accept a press that stays stable for 50 ms
42  int okuma = digitalRead(BOOT_BUTON);
43  if (okuma != sonOkuma) {
44    sonOkuma = okuma;
45    degisimZamani = simdi;
46  }
47  if (simdi - degisimZamani >= 50 && okuma != kararli) {
48    kararli = okuma;
49    if (kararli == LOW) {                  // yeni basış / new press
50      geceModu = !geceModu;
51      Serial.println(geceModu ? "Gece modu / Night mode" : "Normal mod / Normal mode");
52      adim = 0;                            // normal mod kırmızıdan başlar / normal mode restarts on red
53      adimBaslangic = simdi;
54      lambalar(PLAN[0].k, PLAN[0].s, PLAN[0].y);
55    }
56  }
57
58  if (geceModu) {
59    lambalar(false, (simdi / 500) % 2 == 0, false);  // yanıp sönen sarı / flashing yellow
60    return;
61  }
62
63  if (simdi - adimBaslangic >= PLAN[adim].sure) {
64    adimBaslangic = simdi;
65    adim = (adim + 1) % ADIM_SAYISI;
66    lambalar(PLAN[adim].k, PLAN[adim].s, PLAN[adim].y);
67  }
68}

Buton okumasında basit bir debounce var: okunan değer 50 ms boyunca değişmeden kalırsa gerçek bir basış sayılır. Lambaları tutan PLAN tablosu, kavşak kodundaki fikrin daha okunur bir hâlidir. ESP32'nin pinleri 3,3 V verdiği için LED'ler Arduino'dakinden daha az akım çeker ve biraz daha soluk yanar. Kırmızı ve sarı LED'ler 220 Ω ile yeterince parlaktır; ileri gerilimi 3 V civarında olan parlak yeşil LED ise çok soluk kalabilir, bu durumda yeşil için direnci 100 Ω'a düşür. ESP32'ye yeni başlıyorsan kurulum ve güvenli pinler için ESP32 Nedir? yazısı iyi bir başlangıç noktasıdır.

Raspberry Pi Pico (MicroPython) ile yaya butonlu trafik lambası

Raspberry Pi Pico'da aynı durum makinesini MicroPython ile yazıyoruz. Bu kez durumlar bir listede duruyor: her satırda durumun adı, beş lambanın değeri ve süre var. Buton için kesme (interrupt) kullanıyoruz: GP4'teki pin LOW'a düştüğünde buton_kesmesi() fonksiyonu çalışır ve yalnızca talep bayrağını kaldırır. Kodu Thonny ile Pico'ya main.py adıyla kaydedersen kart her açıldığında otomatik olarak başlar.

MicroPython
1# Raspberry Pi Pico (MicroPython): yaya butonlu trafik lambası / pedestrian traffic light
2from machine import Pin
3import time
4
5yesil = Pin(11, Pin.OUT)              # araç yeşil / vehicle green
6sari = Pin(12, Pin.OUT)               # araç sarı / vehicle yellow
7kirmizi = Pin(13, Pin.OUT)            # araç kırmızı / vehicle red
8yaya_yesil = Pin(14, Pin.OUT)         # yaya yeşil / pedestrian green
9yaya_kirmizi = Pin(15, Pin.OUT)       # yaya kırmızı / pedestrian red
10buton = Pin(4, Pin.IN, Pin.PULL_UP)   # GP4, diğer ucu GND / other leg to GND
11
12# (durum, kırmızı, sarı, yeşil, yaya kırmızı, yaya yeşil, süre ms)
13# (state, red, yellow, green, ped red, ped green, duration ms)
14DURUMLAR = [
15    ("ARAC_YESIL",   0, 0, 1, 1, 0, 8000),   # en az yeşil süresi / minimum green
16    ("ARAC_SARI",    0, 1, 0, 1, 0, 3000),
17    ("YAYA_BEKLE",   1, 0, 0, 1, 0, 1000),   # tüm kırmızı / all red
18    ("YAYA_GECER",   1, 0, 0, 0, 1, 6000),
19    ("YAYA_FLAS",    1, 0, 0, 0, 1, 3000),
20    ("YAYA_BITTI",   1, 0, 0, 1, 0, 1000),   # tüm kırmızı / all red
21    ("KIRMIZI_SARI", 1, 1, 0, 1, 0, 1000),
22]
23
24talep = False
25adim = 0                              # şu anki durumun sırası / index of the current state
26
27def buton_kesmesi(pin):               # kesmede yalnız bayrak kaldır / only set a flag in the IRQ
28    global talep
29    if DURUMLAR[adim][0] != "YAYA_GECER":   # yaya yeşilinde basış yok sayılır / ignored during the green man
30        talep = True
31
32buton.irq(trigger=Pin.IRQ_FALLING, handler=buton_kesmesi)
33
34def uygula(i):
35    global talep
36    ad, k, s, y, yk, yy, _ = DURUMLAR[i]
37    kirmizi.value(k)
38    sari.value(s)
39    yesil.value(y)
40    yaya_kirmizi.value(yk)
41    yaya_yesil.value(yy)
42    if ad == "YAYA_GECER":
43        talep = False                 # talep karşılandı / request served
44    print("Durum / state:", ad)
45
46uygula(adim)
47baslangic = time.ticks_ms()
48
49while True:
50    gecen = time.ticks_diff(time.ticks_ms(), baslangic)   # taşmaya karşı güvenli / wrap-safe
51    ad = DURUMLAR[adim][0]
52
53    if ad == "YAYA_FLAS":
54        yaya_yesil.value((gecen // 250) % 2 == 0)          # yanıp sönen yaya yeşili / flashing green man
55
56    if gecen >= DURUMLAR[adim][6] and (ad != "ARAC_YESIL" or talep):
57        adim = (adim + 1) % len(DURUMLAR)                   # yeşil, talep gelene kadar sürer / green lasts until a request
58        uygula(adim)
59        baslangic = time.ticks_ms()
60
61    time.sleep_ms(10)

MicroPython'da millis() yerine time.ticks_ms(), düz çıkarma yerine de time.ticks_diff() kullanılır; ticks sayacı belirli bir değerden sonra başa döndüğü için iki değeri doğrudan çıkarmak yanlış sonuç verebilir. Kesme fonksiyonunu kısa tutmak önemlidir: içinde print, uyku ya da uzun hesaplar yapma, yalnızca bir bayrak değiştir. Pico'nun pinleri de 3,3 V ile çalışır, bu yüzden ESP32 bölümündeki yeşil LED notu burada da geçerlidir. Pico'ya MicroPython yüklemeyi ve pin numaralarını Raspberry Pi Pico Nedir? yazısında, dilin temellerini MicroPython temelleri yazısında bulabilirsin.

Sık yapılan hatalar ve çözümleri

BelirtiOlası nedenÇözüm
Hiçbir LED yanmıyorGND bağlı değil ya da breadboard'un güç hattı ortadan bölünmüşArduino GND'sini mavi hatta bağla, uzun breadboard'da iki yarıyı köprüle
Tek bir LED yanmıyorLED ters takılmış ya da yanlış pine bağlıUzun bacak (anot) direnç üzerinden pine, kısa bacak GND'ye; pin numarasını kodla karşılaştır
Yanlış renk yanıyorKodda kırmızı ve yeşil pinleri yer değiştirmişKabloları ya da sabitleri pin tablosuna göre düzelt
LED çok parlak ve ısınıyorDirenç takılmamışHer LED'e 220 Ω seri direnç ekle
Buton tepki vermiyor ya da hep basılı görünüyorButon içeriden zaten bağlı iki bacağa takılmış ya da INPUT_PULLUP unutulmuşÇapraz bacakları kullan, pinMode(BUTON, INPUT_PULLUP) yaz
Butona basınca hemen tepki yokAraçların en kısa yeşil süresi henüz dolmadıBu bir hata değil; MIN_YESIL değerini küçült
Program bir süre sonra saçmalıyorZaman değişkeni int olarak tanımlanmışmillis() ile kullanılan değişkenleri unsigned long yap

Ödev ve proje raporu için ipuçları

Trafik lambası; fen, bilişim teknolojileri ve robotik derslerinde en sık verilen Arduino ödevlerinden biridir. Öğretmenin genellikle yalnızca çalışan bir devre değil, onu anladığını ve anlatabildiğini görmek ister. Raporunu şu bölümlerle hazırlarsan hem düzenli görünür hem de önemli bir noktayı atlamamış olursun:

  • Amaç: Projenin neyi gösterdiğini bir iki cümleyle yaz; örneğin "dijital çıkışlarla bir trafik ışığını zamanlamak".
  • Malzeme listesi: Yukarıdaki tablodaki gibi her parçanın adını, adedini ve değerini (220 Ω) yaz.
  • Devre şeması: Tinkercad ya da Fritzing ekran görüntüsü ekle; elle çiziyorsan sembolleri doğru kullan. Devre şeması okuma yazısı bu konuda yardımcı olur.
  • Direnç hesabı: R = (5 − 2) / 0,015 ≈ 200 Ω hesabını göster ve neden 220 Ω seçtiğini açıkla.
  • Durum tablosu ya da akış şeması: Durumları, süreleri ve geçişleri gösteren tabloyu rapora koy; kodu anladığını gösteren en güçlü kanıt budur.
  • Kod ve açıklaması: Kodun tamamını ekle ve önemli satırları kendi cümlelerinle anlat.
  • Test ve gözlemler: Süreleri kronometreyle ölç, multimetreyle bir LED'in akımını ölçüp hesapladığın değerle karşılaştır.
  • Sonuç ve geliştirme: Karşılaştığın sorunları, nasıl çözdüğünü ve ekleyebileceğin özellikleri (yaya butonu, sesli uyarı, gece modu) yaz.
Fark yaratmak istiyorsan delay() ile yazdığın ilk sürümle millis() ile yazdığın sürümü karşılaştır: ilkinde buton basışlarının neden kaçırıldığını, ikincisinde neden hiç kaçırılmadığını açıkla. Devrenin çalıştığı 20–30 saniyelik bir video ve Seri Monitör çıktısının ekran görüntüsü de raporu güçlendirir.

Trafik lambasını geliştirme fikirleri

  • Geri sayım göstergesi: Yeşilin bitmesine kalan saniyeleri bir 7 segment display üzerinde göster.
  • Görme engelliler için sesli uyarı: Yaya yeşili yanarken bir buzzer ile hızlı, kırmızıda yavaş bip sesi çal.
  • Akıllı kavşak: Ara yolda bekleyen aracı bir HC-SR04 mesafe sensörü ile algıla ve B yönüne yalnızca araç varsa yeşil ver.
  • Otomatik gece modu: Hava kararınca yanıp sönen sarıya geçmek için bir LDR kullan.
  • Uzaktan kontrol: ESP32 ile bir web sayfası hazırla, modu ve süreleri telefondan değiştir.
  • Hemzemin geçit: Aynı mantıkla iki kırmızı LED'in sırayla yanıp söndüğü tren geçidi uyarı ışığı yap; karaşimşek ve yürüyen LED projeleri de benzer zamanlama fikirlerini kullanır.

Trafik lambası küçük bir proje gibi görünür, ama içindeki fikirler, yani durum makinesi, bekletmeden zamanlama ve güvenli geçişler, ileride yapacağın robotların, alarm sistemlerinin ve akıllı ev cihazlarının da temelidir. Sırada ne yapacağını seçmek için Arduino proje fikirleri listesine göz atabilirsin. BlueGrays uygulamasında Arduino, ESP32 ve Raspberry Pi Pico için trafik lambası projelerini, Pico'nun dört modlu trafik lambası ve LED hemzemin geçit uyarı ışıkları gibi devamlarını adım adım devre şeması ve hazır koduyla bulup kendi kartında deneyebilirsin.

Sıkça sorulan sorular

Arduino trafik lambası nasıl yapılır?

Kırmızı, sarı ve yeşil LED'in uzun bacaklarını 220 Ω dirençler üzerinden Arduino'nun dijital pinlerine (örneğin D10, D9 ve D8), kısa bacaklarını GND'ye bağlarsın. Kodda bu pinleri OUTPUT yapar, loop() içinde LED'leri sırayla yakıp söndürürsün. İlk sürüm için delay() yeterlidir; buton ve ek özellikler için millis() kullanman gerekir.

Trafik lambası için kaç ohm direnç kullanılır?

5 V ile çalışan Arduino Uno'da her LED için 220 Ω yaygın ve güvenli bir seçimdir; kırmızı LED'den yaklaşık 13–14 mA akım geçer. 330 Ω ile LED daha az parlak ama yine net görünür. 3,3 V ile çalışan ESP32 ve Pico'da da 220 Ω kullanılabilir; yalnızca parlak yeşil LED soluk kalırsa 100 Ω'a düşebilirsin.

Trafik lambasında kırmızı ve sarı neden birlikte yanar?

Türkiye, Almanya ve İngiltere gibi ülkelerde kırmızı ile sarının birlikte yanması yeşilin yanmak üzere olduğunu bildirir; sürücü hazırlanır ama yeşil yanmadan hareket etmez. ABD, Fransa, İtalya ve İspanya gibi ülkelerde bu faz yoktur ve kırmızıdan doğrudan yeşile geçilir. Arduino projende bu fazı istersen ekler, istersen çıkarırsın.

Arduino trafik lambasında delay mi millis mi kullanmalıyım?

Yalnızca LED'leri sırayla yakacaksan delay() en kolay ve yeterli yoldur. Buton, sensör, ekran ya da ses eklediğin anda delay() programı beklettiği için bu girişler kaçırılır. millis() ve durum makinesiyle yazılan kod hiç beklemez ve her şeyi aynı anda takip edebilir.

Trafik lambası projesi Tinkercad'de yapılabilir mi?

Evet. Tinkercad Circuits'te Arduino Uno, LED, direnç ve buton hazır bileşen olarak bulunur; devreyi çizip kodu Text bölümüne yapıştırarak simülasyonu başlatabilirsin. Gerçek malzeme olmadan devreyi ve kodu denemek, ödev için ekran görüntüsü ya da paylaşım bağlantısı almak için idealdir.

Yaya butonlu trafik lambası nasıl çalışır?

Araçlar normalde yeşil yanar ve yaya butona bastığında bir talep kaydedilir. Araçların en kısa yeşil süresi dolunca ışık sarıya, sonra kırmızıya döner ve kısa bir güvenlik aralığından sonra yaya yeşili yanar. Bu yazıdaki kodda yaya yeşili bitmeden yanıp söner, ardından döngü yeniden araçlara geçer.

Tüm projeleri gör
Trafik Lambası
#3Başlangıç

Trafik Lambası

Kırmızı, sarı ve yeşil LED'lerle gerçek bir trafik lambası simülasyonu yapan proje. Zamanlama ve sıralı kontrol mantığını öğretir.

4.8(126)
337
ESP32 Trafik Lambası
#107Başlangıç

ESP32 Trafik Lambası

Kırmızı-sarı-yeşil 3 LED'le gerçek trafik lambası simülasyonu. Süreler gerçek trafik mantığına yakın: kırmızı uzun, sarı kısa, yeşil uzun. Sıralı GPIO kontrolünün klasik örneği.

4.4(21)
44
Raspberry Pi Pico Trafik Lambası (3 LED Sıralı, GP11/GP12/GP13)
#150Başlangıç

Raspberry Pi Pico Trafik Lambası (3 LED Sıralı, GP11/GP12/GP13)

Kırmızı, sarı ve yeşil LED'lerle gerçek trafik ışığı mantığını simüle ediyoruz — Pico serisinin 3 LED'e geçen ilk projesi! Yeşil 4 saniye "geç", sarı 1.5 saniye "hazırlan", kırmızı 3 saniye "dur" ve kırmızı+sarı 1 saniye "kalkış" (Avrupa trafik lambası pattern'i). Bu proje ile **fonksiyon tanımlama** (`def all_off()`), **durum makinesi mantığı** (sequence) ve **coklu GPIO senkronizasyonu** kavramlarını öğreneceksin. Devre çok basit: GP11 (Yeşil), GP12 (Sarı), GP13 (Kırmızı) pinlerine 3 LE

4.0(1)
12
2 Adet Led Yakma
#2Başlangıç

2 Adet Led Yakma

İki LED'i sırayla yakıp söndürerek flip-flop efekti oluşturan proje. Birden fazla çıkış pinini kontrol etmeyi öğretir.

4.6(103)
317
Buton İle Led Yakma (Push)
#9Başlangıç

Buton İle Led Yakma (Push)

Butona basıldığında LED'i yakan, bırakıldığında söndüren anlık (push) kontrol projesi. digitalRead ile buton okuma ve koşullu ifade kullanımını öğretir.

4.9(112)
190
Polis Çakar
#4Başlangıç

Polis Çakar

İki LED'i hızlı aralıklarla yanıp söndürerek polis çakar lambası efekti oluşturan proje. for döngüsü ve zamanlama kontrolünü öğretir.

4.7(97)
227
Raspberry Pi Pico Trafik Lambası 4 Mod Buton Seçici (State Machine, GP11-13 + GP4)
#158Orta
Premium

Raspberry Pi Pico Trafik Lambası 4 Mod Buton Seçici (State Machine, GP11-13 + GP4)

1 buton ile trafik lambasının 4 farklı modu! Butona her basışta mod değişir: **Normal döngü** (kırmızı → sarı → yeşil sırayla), **Sadece Kırmızı** (dur emri), **Sarı Flash** (dikkat/gece modu), **Kapalı** (bakım). Öğrenme: **state machine** (durum makinesi — `mode` değişkeni hangi davranışın çalışacağını belirler), **modulo mod cycling** (`(mode + 1) % 4` → 0→1→2→3→0 sonsuz döngü), **non-blocking timing** (LED fazları `time.ticks_ms()` + `time.ticks_diff()` ile zamanlanır; ancak kod mod değişimi

5.0(1)
14
LED Hemzemin Geçit Uyarı Işıkları
#146İleri
Premium

LED Hemzemin Geçit Uyarı Işıkları

Hemzemin geçitlerdeki gibi dönüşümlü yanıp sönen LED uyarı ışıkları.

5.0(2)
14