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.

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.
| Faz | Yanan lamba | Anlamı | Bu yazıdaki süre |
|---|---|---|---|
| 1 | Kırmızı | Dur | 5 sn |
| 2 | Kırmızı + sarı | Yeşil yanacak, hazırlan | 1 sn |
| 3 | Yeşil | Geç | 5 sn |
| 4 | Sarı | Kırmızı yanacak, dur | 3 sn |
| Kavşakta | Tüm yönler kırmızı | Kavşağın boşalması için güvenlik payı | 1 sn |
Malzemeler ve 220 Ω direnç hesabı
| Malzeme | Adet | Not |
|---|---|---|
| Arduino Uno (ya da Nano) | 1 | ESP32 ya da Raspberry Pi Pico da olur |
| 5 mm LED: kırmızı, sarı, yeşil | Her renkten 1 | Yaya ışığı için +1 kırmızı ve +1 yeşil, kavşak için +1 takım |
| 220 Ω direnç | 3 | Her LED için bir tane; tüm sürümler için 8 |
| Push buton (6×6 mm) | 1 | Yalnızca yaya butonlu sürümde |
| Breadboard | 1 | Yarım boy (400 delikli) yeterli |
| Jumper kablo | 10–20 | Erkek-erkek |
| USB kablo | 1 | Kodu 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örev | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| Araç kırmızı (A yönü) | D10 | GPIO 5 | GP13 |
| Araç sarı (A yönü) | D9 | GPIO 18 | GP12 |
| Araç yeşil (A yönü) | D8 | GPIO 19 | GP11 |
| Yaya kırmızı | D7 | GPIO 21 | GP15 |
| Yaya yeşil | D6 | GPIO 22 | GP14 |
| Yaya butonu (diğer ucu GND) | D2 | GPIO 23 | GP4 |
| B yönü kırmızı / sarı / yeşil | D5 / D4 / D3 | GPIO 25 / 26 / 27 | GP8 / GP7 / GP6 |
| LED katotları ve buton | GND | GND | GND |
1. adım: delay() ile en basit trafik lambası kodu
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.

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:
| Durum | Araç lambası | Yaya lambası | Süre | Sonraki durum |
|---|---|---|---|---|
| ARAC_YESIL | Yeşil | Kırmızı | En az 8 sn, talep gelene kadar | ARAC_SARI |
| ARAC_SARI | Sarı | Kırmızı | 3 sn | YAYA_BEKLE |
| YAYA_BEKLE | Kırmızı | Kırmızı | 1 sn | YAYA_GECER |
| YAYA_GECER | Kırmızı | Yeşil | 6 sn | YAYA_FLAS |
| YAYA_FLAS | Kırmızı | Yeşil yanıp söner | 3 sn | YAYA_BITTI |
| YAYA_BITTI | Kırmızı | Kırmızı | 1 sn | KIRMIZI_SARI |
| KIRMIZI_SARI | Kırmızı + sarı | Kırmızı | 1 sn | ARAC_YESIL |
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.
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.
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
- tinkercad.com adresinde ücretsiz bir hesap aç ve Circuits (Devreler) bölümünden yeni bir devre oluştur.
- 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.
- 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.
- Code düğmesine bas, açılır menüden Text seç, hazır örnek kodu sil ve bu yazıdaki kodu yapıştır.
- 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.
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.
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
| Belirti | Olası neden | Çözüm |
|---|---|---|
| Hiçbir LED yanmıyor | GND 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ıyor | LED 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ıyor | Kodda 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ıyor | Direnç takılmamış | Her LED'e 220 Ω seri direnç ekle |
| Buton tepki vermiyor ya da hep basılı görünüyor | Buton 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 yok | Araç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ıyor | Zaman 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.
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.
Bu yazıdan sonra yapabileceğin projeler
Tüm projeleri gör
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.

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.

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

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.

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.

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.

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

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

