Arduino Ampel bauen: Schaltung, Code, Fußgängerampel, Kreuzung
Arduino Ampel bauen: Schaltung mit 3 LEDs und 220 Ω, Code mit delay und millis, Fußgängerampel mit Taster, Kreuzung, Tinkercad sowie ESP32 und Pico.
Eine Arduino Ampel ist das klassische erste Elektronikprojekt: Drei LEDs in Rot, Gelb und Grün leuchten nacheinander auf, genau wie an einer echten Ampel. Du brauchst nur einen Arduino Uno, drei LEDs, drei 220-Ω-Widerstände und ein paar Jumper-Kabel, und die erste funktionierende Version steht in einer halben Stunde. Trotzdem steckt in diesem einfachen Projekt viel: digitale Ausgänge, die Berechnung des Vorwiderstands, Zeitsteuerung, das Einlesen eines Tasters und der Zustandsautomat, mit dem du deinen Code am saubersten strukturierst.
In dieser Anleitung siehst du zuerst, in welcher Reihenfolge und wie lange eine echte Ampel schaltet. Dann baust du die Schaltung auf und schreibst den einfachsten Code mit delay(). Danach kommt eine Fußgängerampel mit Taster, du schreibst dieselbe Logik mit millis() und einem Zustandsautomaten blockierfrei um und steuerst eine Kreuzung mit zwei Richtungen. In den letzten Abschnitten findest du, wie du alles ohne Installation in Tinkercad testest, die Versionen für ESP32 und Raspberry Pi Pico (MicroPython) sowie Tipps für einen Projektbericht in der Schule.

Wie funktioniert eine echte Ampel?
Eine Ampel ist ein Zeitgeber, der den Verkehrsströmen nacheinander freie Fahrt gibt. In Deutschland, Großbritannien und der Türkei läuft der Zyklus so: Rot → Rot-Gelb → Grün → Gelb → Rot. Rot und Gelb zusammen bedeuten "gleich kommt Grün, mach dich bereit, aber fahr noch nicht los"; Gelb allein heißt "die Ampel schaltet auf Rot, halte an, wenn es sicher möglich ist". In den USA, Frankreich, Italien und Spanien gibt es keine Rot-Gelb-Phase, dort springt die Ampel direkt von Rot auf Grün. Auch die Gelbzeit ist kein Zufall: Bei erlaubten 50 km/h sind in Deutschland 3 Sekunden Gelb vorgesehen, bei 60 km/h 4 Sekunden und bei 70 km/h 5 Sekunden. Wer beim Umschalten auf Gelb zu nah zum Bremsen ist, muss die Kreuzung noch sicher räumen können.
| Phase | Leuchtet | Bedeutung | Zeit in dieser Anleitung |
|---|---|---|---|
| 1 | Rot | Halt | 5 s |
| 2 | Rot + Gelb | Gleich kommt Grün, bereit machen | 1 s |
| 3 | Grün | Fahren | 5 s |
| 4 | Gelb | Gleich kommt Rot, anhalten | 3 s |
| An der Kreuzung | Alle Richtungen Rot | Räumzeit, damit die Kreuzung frei wird | 1 s |
Bauteile und Berechnung des 220-Ω-Vorwiderstands
| Bauteil | Anzahl | Hinweis |
|---|---|---|
| Arduino Uno (oder Nano) | 1 | Ein ESP32 oder Raspberry Pi Pico geht auch |
| 5-mm-LEDs: rot, gelb, grün | je 1 | +1 rot und +1 grün für die Fußgängerampel, +1 Satz für die Kreuzung |
| Widerstand 220 Ω | 3 | Einer pro LED; 8 für alle Versionen |
| Taster (6×6 mm) | 1 | Nur für die Fußgängerampel |
| Steckbrett (Breadboard) | 1 | Halbe Größe (400 Kontakte) reicht |
| Jumper-Kabel | 10–20 | Stecker-Stecker |
| USB-Kabel | 1 | Zum Hochladen und zur Stromversorgung |
LEDs begrenzen ihren Strom nicht selbst; eine LED direkt an 5 V zieht mehr Strom, als sie und der Pin vertragen. Deshalb bekommt jede LED einen Vorwiderstand in Reihe. Seinen Wert liefert das ohmsche Gesetz: R = (Versorgungsspannung − Flussspannung der LED) / Strom. Für eine rote LED ergibt (5 V − 2,0 V) / 0,015 A ≈ 200 Ω; mit dem nächsten Normwert 220 Ω fließen etwa 13,6 mA. Damit leuchtet die LED hell genug, und der für einen Arduino-Pin empfohlene Strom von 20 mA wird nicht überschritten. Ein 220-Ω-Widerstand trägt bei vier Ringen die Farben Rot-Rot-Braun-Gold, bei fünf Ringen Rot-Rot-Schwarz-Schwarz-Braun. Zur Sicherheit hilft das Werkzeug Widerstands-Farbcode, für andere Farben und Spannungen der LED-Vorwiderstandsrechner.
| LED-Farbe | Flussspannung (ca.) | Strom bei 5 V (220 Ω) | Strom bei 3,3 V (220 Ω) |
|---|---|---|---|
| Rot | 1,8–2,2 V | ≈ 13,6 mA | ≈ 5,9 mA |
| Gelb | 1,9–2,2 V | ≈ 13,2 mA | ≈ 5,5 mA |
| Grün (gelbgrün) | 2,0–2,4 V | ≈ 13,2 mA | ≈ 5,5 mA |
| Grün (hell, reines Grün) | 2,8–3,4 V | ≈ 9,1 mA | ≈ 1,4 mA (sehr schwach) |
Schaltung aufbauen und Pin-Tabelle
Das lange Bein jeder LED (Anode, +) geht über einen Widerstand an einen digitalen Pin des Arduino, das kurze Bein (Kathode, −) an GND. Ob der Widerstand auf der Anoden- oder Kathodenseite sitzt, ist egal, solange er in Reihe liegt. Auf dem Steckbrett ist es am ordentlichsten, den GND-Pin des Arduino mit der blauen (−) Schiene zu verbinden und die kurzen Beine aller drei LEDs dort zu sammeln. Die Tabelle zeigt die Pins aller Sketche in dieser Anleitung; der erste Sketch nutzt nur die ersten drei Zeilen. Die Pinbelegung deines Boards findest du im Werkzeug Pinout.
| Funktion | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| Fahrzeug Rot (Richtung A) | D10 | GPIO 5 | GP13 |
| Fahrzeug Gelb (Richtung A) | D9 | GPIO 18 | GP12 |
| Fahrzeug Grün (Richtung A) | D8 | GPIO 19 | GP11 |
| Fußgänger Rot | D7 | GPIO 21 | GP15 |
| Fußgänger Grün | D6 | GPIO 22 | GP14 |
| Fußgängertaster (anderes Bein an GND) | D2 | GPIO 23 | GP4 |
| Richtung B Rot / Gelb / Grün | D5 / D4 / D3 | GPIO 25 / 26 / 27 | GP8 / GP7 / GP6 |
| LED-Kathoden und Taster | GND | GND | GND |
Schritt 1: der einfachste Ampel-Code mit delay()
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}Der Sketch schaltet die drei Pins in setup() als Ausgänge und führt in loop() die vier Phasen nacheinander aus. Statt immer wieder drei digitalWrite-Zeilen zu schreiben, nutzen wir eine kleine Funktion namens lambalar() ("Lampen"); so passt jede Phase in eine Zeile und Fehler fallen sofort auf. Wähle zum Hochladen im Menü Werkzeuge den Arduino Uno als Board und den richtigen Port und klicke auf Hochladen. Klappt das nicht, hilft die Checkliste in Arduino Sketch lässt sich nicht hochladen. Um die Zeiten zu ändern, passt du einfach die Millisekunden in delay() an: 5000 bedeutet 5 Sekunden.
Schritt 2: Fußgängerampel mit Taster
Bedarfsampeln, wie du sie aus der Stadt kennst, zeigen den Autos dauerhaft Grün und geben Fußgängern erst frei, wenn jemand den Taster drückt. In dieser Version ergänzen wir die Fahrzeugampel um eine Fußgängerampel mit zwei LEDs (Rot und Grün) und einen Taster an D2. Ein Bein des Tasters kommt an D2, das diagonal gegenüberliegende an GND; weil der Code INPUT_PULLUP verwendet, brauchst du keinen externen Widerstand, und der Pin liest beim Drücken LOW. Mehr zu den inneren Verbindungen eines Tasters mit vier Beinen und zum Entprellen findest du in Taster am Arduino anschließen.

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}Dieser Sketch funktioniert, hat aber eine große Schwäche: Nach einem Tastendruck hängt der Arduino rund 25 Sekunden in delay()-Zeilen fest. Ein weiterer Druck in dieser Zeit wird nicht bemerkt, über die serielle Schnittstelle kann nichts gelesen und kein Display aktualisiert werden. Drücke während der 8 Sekunden Wartezeit am Ende gehen komplett verloren: Der Fußgänger drückt, lässt los, nichts passiert, und er muss erneut drücken. Willst du Ton oder einen Countdown ergänzen, wird es mit dieser Struktur noch schwieriger. Die Lösung: nicht mehr warten, sondern Zeit messen.
Schritt 3: blockierfrei mit millis() und Zustandsautomat
Ein Zustandsautomat (State Machine) ist ein Entwurfsmuster, bei dem sich das System immer in genau einem Zustand befindet und beim Eintreten bestimmter Bedingungen in den nächsten wechselt. Eine Ampel ist das perfekte Beispiel: "Grün für Autos", "Gelb für Autos" und "Fußgänger gehen" sind jeweils ein Zustand. Der Code wartet nicht mehr mit delay(); loop() stellt bei jedem Durchlauf nur zwei Fragen: In welchem Zustand bin ich, und wie lange schon? Die vergangene Zeit misst millis() - durumBaslangic (Startzeitpunkt des Zustands), und ist sie abgelaufen, geht es weiter. Ist millis() neu für dich, lies zuerst Arduino millis() statt delay(). Schreib die Zustände vor dem Programmieren in eine Tabelle, das macht die Arbeit viel leichter:
| Zustand | Fahrzeugampel | Fußgängerampel | Dauer | Nächster Zustand |
|---|---|---|---|---|
| ARAC_YESIL | Grün | Rot | Mindestens 8 s, bis eine Anforderung kommt | ARAC_SARI |
| ARAC_SARI | Gelb | Rot | 3 s | YAYA_BEKLE |
| YAYA_BEKLE | Rot | Rot | 1 s | YAYA_GECER |
| YAYA_GECER | Rot | Grün | 6 s | YAYA_FLAS |
| YAYA_FLAS | Rot | Grün blinkt | 3 s | YAYA_BITTI |
| YAYA_BITTI | Rot | Rot | 1 s | KIRMIZI_SARI |
| KIRMIZI_SARI | Rot + Gelb | Rot | 1 s | 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}Herzstück des Sketches ist die Funktion gec() ("wechseln"): Sie speichert den neuen Zustand, startet die Zeitmessung neu, setzt die Lampen für diesen Zustand und schreibt seinen Namen in den seriellen Monitor. Das switch in loop() prüft nur noch, ob die Zeit abgelaufen ist. Ein Tastendruck löst nicht sofort etwas aus, sondern setzt das Flag yayaTalebi (Fußgängeranforderung); der Ablauf startet, sobald die Autos mindestens 8 Sekunden Grün hatten. So geht kein Tastendruck mehr verloren, und weil der Taster nur ein Flag ändert, stört auch das Prellen nicht. Drücke, während das Fußgänger-Grün schon leuchtet, werden ignoriert; sonst würde direkt nach dem Überqueren unnötig ein neuer Zyklus starten. Öffne den seriellen Monitor mit 9600 Baud, dann siehst du jeden Zustandswechsel; diese Ausgabe hilft bei der Fehlersuche und im Projektbericht.
Schritt 4: Kreuzung mit zwei Richtungen
An einer echten Kreuzung hängen die Ampeln beider Straßen zusammen: Ist Straße A grün, muss Straße B rot sein. Diese Version nutzt sechs LEDs: D10, D9 und D8 für Richtung A sowie D5, D4 und D3 für Richtung B. Wichtig ist die kurze Alles-Rot-Phase (Räumzeit): Schaltet eine Richtung auf Rot, wird die andere nicht sofort grün, sondern die Steuerung wartet 1 Sekunde, damit Fahrzeuge in der Kreuzung sie verlassen können. Die Zustände stehen diesmal nicht in einem switch, sondern in einer Tabelle (einem Array): Für jeden Schritt werden die leuchtenden Lampen als Bits in einem einzigen Byte und die Dauer in Millisekunden gespeichert.
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}Jede Zeile im Array PLAN ist ein Schritt. In Binärzahlen wie 0b001100 stehen die Bits von links nach rechts für A-Rot, A-Gelb, A-Grün, B-Rot, B-Gelb und B-Grün; 1 heißt an, 0 heißt aus. Die Funktion uygula() ("anwenden") schreibt diese sechs Bits der Reihe nach auf die Pins. Die Prüfung am Anfang der Funktion verhindert, dass wegen eines Tippfehlers beide Richtungen gleichzeitig Grün zeigen, und schaltet stattdessen alles auf Rot. Auch echte Ampelsteuerungen haben eine eigene Sicherheitsschaltung, die gleichzeitiges Grün für kreuzende Richtungen erkennt. Um Zeiten zu ändern oder Schritte hinzuzufügen, bearbeitest du nur die Tabelle, der restliche Code bleibt unverändert. Reichen die Pins bei mehr LEDs nicht aus, liefert ein 74HC595-Schieberegister acht Ausgänge über drei Pins.
In Tinkercad testen: Simulation ganz ohne Bauteile
- Lege auf tinkercad.com ein kostenloses Konto an und erstelle im Bereich Circuits (Schaltkreise) eine neue Schaltung.
- Zieh aus der Bauteilliste rechts einen Arduino Uno R3, ein Steckbrett, drei LEDs und drei Widerstände herein. Klick auf eine LED, um die Farbe zu wählen, und auf einen Widerstand, um 220 Ω einzustellen.
- Zeichne die Drähte: LED-Anoden über die Widerstände an D10, D9 und D8, Kathoden an GND. Drahtfarben (zum Beispiel Schwarz für GND) machen den Plan übersichtlich.
- Klick auf Code, wähle im Aufklappmenü Text, lösch den Beispielcode und füge den Sketch aus dieser Anleitung ein.
- Klick auf Start Simulation (Simulation starten); die LEDs schalten nacheinander. Serielle Ausgaben siehst du im Serial Monitor unten im Code-Bereich.
Tinkercad ist der einfachste Weg, Verdrahtung und Code zu testen, bevor du mit echten Bauteilen arbeitest; fehlt ein Widerstand, zeigt die Simulation an der LED eine Warnung, dass der Strom zu hoch ist. Für die Fußgängerversion klickst du während der Simulation einfach mit der Maus auf den Taster. Auf einem langsamen Rechner kann die Simulation langsamer als in Echtzeit laufen, dann wirken die Zeiten etwas länger. Für eine Schulaufgabe kannst du den Freigabelink von Tinkercad oder einen Screenshot der Schaltung abgeben. Eine ausführliche Anleitung findest du in Arduino-Simulation mit Tinkercad, eine Alternative mit ESP32- und Pico-Unterstützung in Wokwi.
ESP32-Ampel mit Nachtmodus
Die Arduino-Sketche dieser Anleitung laufen auch auf dem ESP32: Wähle in der Arduino IDE das Board ESP32 Dev Module und passe die Pin-Konstanten wie in der Tabelle an. Die folgende Version bringt eine Zusatzfunktion mit: den Nachtmodus. In verkehrsarmen Zeiten schalten viele Kreuzungen auf gelbes Blinklicht. ESP32-DevKit-Boards haben einen BOOT-Taster an GPIO 0, du brauchst also nicht einmal einen zusätzlichen Taster: Nach dem Start lässt er sich wie ein normaler Eingang lesen. Jeder Druck wechselt zwischen Normal- und Nachtmodus.
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}Das Einlesen des Tasters enthält eine einfache Entprellung: Ein Druck zählt nur, wenn der gelesene Wert 50 ms lang unverändert bleibt. Die Tabelle PLAN mit den Lampenzuständen ist eine besser lesbare Form der Idee aus dem Kreuzungs-Sketch. Da die ESP32-Pins nur 3,3 V liefern, fließt weniger Strom als am Arduino und die LEDs leuchten etwas schwächer. Rote und gelbe LEDs sind mit 220 Ω hell genug, eine helle grüne LED mit rund 3 V Flussspannung bleibt aber sehr dunkel; nimm für Grün dann 100 Ω. Bist du neu beim ESP32, ist Was ist der ESP32? ein guter Einstieg für Installation und sichere Pins.
Fußgängerampel mit Raspberry Pi Pico (MicroPython)
Auf dem Raspberry Pi Pico schreiben wir denselben Zustandsautomaten in MicroPython. Diesmal stehen die Zustände in einer Liste: Jede Zeile enthält den Namen, die Werte der fünf Lampen und die Dauer. Der Taster nutzt einen Interrupt: Fällt GP4 auf LOW, läuft die Funktion buton_kesmesi() ("Taster-Interrupt") und setzt nur das Anforderungs-Flag. Speicherst du den Code mit Thonny als main.py auf dem Pico, startet er bei jedem Einschalten automatisch.
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)In MicroPython nimmst du time.ticks_ms() statt millis() und time.ticks_diff() statt einer einfachen Subtraktion; der Ticks-Zähler läuft nach einem bestimmten Wert über, deshalb kann direktes Subtrahieren falsche Ergebnisse liefern. Halte die Interrupt-Funktion kurz: kein print, kein sleep, keine langen Rechnungen, nur ein Flag ändern. Auch die Pins des Pico arbeiten mit 3,3 V, der Hinweis zur grünen LED aus dem ESP32-Abschnitt gilt also auch hier. Wie du MicroPython installierst und wie die Pins nummeriert sind, steht in Was ist der Raspberry Pi Pico?, die Grundlagen der Sprache in MicroPython-Grundlagen.
Häufige Fehler und Lösungen
| Symptom | Mögliche Ursache | Lösung |
|---|---|---|
| Keine LED leuchtet | GND nicht verbunden oder die Versorgungsschiene des Steckbretts ist in der Mitte unterbrochen | Arduino-GND mit der blauen Schiene verbinden, bei langen Steckbrettern beide Hälften überbrücken |
| Eine LED bleibt dunkel | LED verpolt oder am falschen Pin | Langes Bein (Anode) über den Widerstand an den Pin, kurzes Bein an GND; Pinnummer mit dem Code vergleichen |
| Falsche Farbe leuchtet | Rote und grüne Pins im Code vertauscht | Kabel oder Konstanten nach der Pin-Tabelle korrigieren |
| LED sehr hell und wird warm | Kein Vorwiderstand | Jede LED bekommt einen 220-Ω-Widerstand in Reihe |
| Taster reagiert nicht oder scheint immer gedrückt | Taster an zwei intern verbundenen Beinen angeschlossen oder INPUT_PULLUP vergessen | Diagonale Beine nutzen und pinMode(BUTON, INPUT_PULLUP) schreiben |
| Keine sofortige Reaktion auf den Taster | Die Mindestgrünzeit der Autos ist noch nicht abgelaufen | Kein Fehler; MIN_YESIL verkleinern |
| Programm spielt nach einer Weile verrückt | Zeitvariable als int deklariert | Alle Variablen für millis() als unsigned long deklarieren |
Tipps für Hausaufgabe und Projektbericht
Die Ampel ist eine der häufigsten Arduino-Aufgaben im Technik-, Informatik- und Robotikunterricht. Deine Lehrkraft will meist nicht nur eine funktionierende Schaltung sehen, sondern dass du sie verstehst und erklären kannst. Gliederst du deinen Bericht in die folgenden Abschnitte, wirkt er aufgeräumt und du vergisst nichts Wichtiges:
- Ziel: Beschreibe in ein, zwei Sätzen, was das Projekt zeigt, zum Beispiel "eine Ampel mit digitalen Ausgängen zeitlich steuern".
- Bauteilliste: Nenne wie in der Tabelle oben Name, Anzahl und Wert (220 Ω) jedes Bauteils.
- Schaltplan: Füge einen Screenshot aus Tinkercad oder Fritzing ein; zeichnest du von Hand, nutze die richtigen Symbole. Schaltpläne lesen hilft dir dabei.
- Widerstandsberechnung: Zeig die Rechnung R = (5 − 2) / 0,015 ≈ 200 Ω und begründe, warum du 220 Ω gewählt hast.
- Zustandstabelle oder Ablaufdiagramm: Nimm die Tabelle mit Zuständen, Zeiten und Übergängen auf; sie ist der stärkste Beweis, dass du den Code verstanden hast.
- Code und Erklärung: Füge den ganzen Code ein und erkläre die wichtigen Zeilen in eigenen Worten.
- Tests und Beobachtungen: Miss die Phasen mit einer Stoppuhr, miss den Strom einer LED mit dem Multimeter und vergleiche ihn mit deiner Rechnung.
- Fazit und Erweiterungen: Beschreibe Probleme und Lösungen sowie Funktionen, die du ergänzen könntest (Fußgängertaster, Ton, Nachtmodus).
Ideen zum Erweitern deiner Ampel
- Countdown-Anzeige: Zeig die verbleibenden Sekunden bis zum Ende der Grünphase auf einer 7-Segment-Anzeige.
- Akustisches Signal für Sehbehinderte: Lass einen Buzzer bei Fußgänger-Grün schnell und bei Rot langsam ticken.
- Intelligente Kreuzung: Erkenne ein wartendes Auto in der Seitenstraße mit einem HC-SR04-Ultraschallsensor und gib Richtung B nur dann Grün.
- Automatischer Nachtmodus: Schalte mit einem LDR bei Dunkelheit auf gelbes Blinklicht um.
- Fernsteuerung: Bau mit dem ESP32 eine Webseite und ändere Modus und Zeiten vom Handy aus.
- Bahnübergang: Mit derselben Logik baust du ein Warnlicht mit zwei abwechselnd blinkenden roten LEDs; die Projekte Lauflicht und Knight Rider nutzen ähnliche Zeitsteuerungen.
Eine Ampel wirkt wie ein kleines Projekt, doch die Ideen darin, Zustandsautomat, blockierfreie Zeitsteuerung und sichere Übergänge, sind auch die Grundlage für Roboter, Alarmanlagen und Smart-Home-Geräte, die du später baust. Was du als Nächstes bauen kannst, zeigen dir die Arduino-Projektideen. In der BlueGrays-App findest du Ampelprojekte für Arduino, ESP32 und Raspberry Pi Pico sowie Fortsetzungen wie die Pico-Ampel mit vier Modi und LED-Warnlichter für einen Bahnübergang, jeweils mit Schritt-für-Schritt-Schaltplan und fertigem Code zum Ausprobieren auf deinem eigenen Board.
Häufige Fragen
Wie baut man eine Ampel mit Arduino?
Verbinde die langen Beine einer roten, gelben und grünen LED über 220-Ω-Widerstände mit digitalen Pins des Arduino (zum Beispiel D10, D9 und D8) und die kurzen Beine mit GND. Im Code setzt du diese Pins auf OUTPUT und schaltest die LEDs in loop() der Reihe nach. Für die erste Version reicht delay(); für Taster und weitere Funktionen brauchst du millis().
Welchen Vorwiderstand brauche ich für die Ampel-LEDs?
Am Arduino Uno mit 5 V sind 220 Ω pro LED eine gängige und sichere Wahl; eine rote LED zieht dann etwa 13–14 mA. Mit 330 Ω leuchtet sie schwächer, aber immer noch gut sichtbar. Auch am ESP32 und Pico mit 3,3 V funktionieren 220 Ω; nur wenn eine helle grüne LED zu dunkel bleibt, gehst du auf 100 Ω.
Warum leuchten an der Ampel Rot und Gelb gleichzeitig?
In Ländern wie Deutschland, Großbritannien und der Türkei kündigt Rot-Gelb an, dass gleich Grün kommt: Man macht sich bereit, fährt aber erst bei Grün los. In den USA, Frankreich, Italien und Spanien gibt es diese Phase nicht, dort folgt auf Rot direkt Grün. In deinem Arduino-Projekt kannst du die Phase nach Belieben einbauen oder weglassen.
Delay oder millis für die Arduino Ampel?
Schaltest du nur die LEDs nacheinander, ist delay() der einfachste Weg und völlig ausreichend. Sobald ein Taster, Sensor, Display oder Ton dazukommt, hält delay() das Programm an und diese Eingaben gehen verloren. Code mit millis() und einem Zustandsautomaten wartet nie und behält alles gleichzeitig im Blick.
Kann ich die Ampel in Tinkercad simulieren?
Ja. Tinkercad Circuits enthält Arduino Uno, LEDs, Widerstände und Taster als fertige Bauteile; du zeichnest die Schaltung, fügst den Code im Text-Editor ein und startest die Simulation. Ideal, um Schaltung und Code ohne echte Bauteile zu testen und einen Screenshot oder Freigabelink für die Hausaufgabe zu bekommen.
Wie funktioniert eine Fußgängerampel mit Taster?
Normalerweise haben die Autos Grün, und ein Tastendruck meldet eine Anforderung an. Ist die Mindestgrünzeit der Autos abgelaufen, schaltet die Ampel auf Gelb, dann auf Rot, und nach einer kurzen Räumzeit bekommen die Fußgänger Grün. Im Code dieser Anleitung blinkt das Fußgänger-Grün kurz vor dem Ende, danach sind wieder die Autos dran.
Projekte, die du danach bauen kannst
Alle Projekte ansehen
Ampel
Projekt, das eine echte Ampel mit roten, gelben und grünen LEDs simuliert. Lehrt das Timing und die sequentielle Steuerlogik.

ESP32 Ampel
Simulation einer echten Ampel mit 3 rot-gelb-grünen LEDs. Die Zeiten sind nahe an der realen Verkehrslogik: rot lang, gelb kurz, grün lang. Klassisches Beispiel für sequenzielle GPIO-Steuerung.

Raspberry Pi Pico Ampel-Simulator (3 LEDs sequenziell, GP11/GP12/GP13)
Wir simulieren eine echte Ampel mit roten, gelben und grünen LEDs. Grün 4 Sek "gehen", gelb 1,5 Sek "bereit", rot 3 Sek "stopp" und rot+gelb 1 Sek "starten" (europäisches Ampelmuster). Lerne Funktionsdefinition, Zustandsmaschinen-Logik und Multi-GPIO-Synchronisation. Sehr einfache Schaltung: 3 LEDs an GP11/GP12/GP13, jeweils mit 220Ω Widerstand.

Zwei LEDs abwechselnd blinken
Ein Projekt, das einen Flip-Flop-Effekt erzeugt, indem es zwei LEDs nacheinander ein- und ausschaltet. Lehrt die Steuerung von mehr als einem Ausgangspin.

LED mit Taster schalten (Push)
Sofortiges (Druck-)Steuerprojekt, bei dem die LED aufleuchtet, wenn die Taste gedrückt wird, und erlischt, wenn sie losgelassen wird. Lehrt die Verwendung von Tastenlesung und bedingtem Ausdruck mit d

LED-Polizeilicht
Projekt zur Erzeugung eines Polizeistrobe-Lichteffekts durch Blinken zweier LEDs in schnellen Intervallen. Lehrt für Schleifen- und Zeitsteuerung.

Raspberry Pi Pico-Ampel mit 4-Modus-Tastenwahlschalter (Zustandsmaschine, GP11-13 + GP4)
4 verschiedene Modi für die Ampel mit nur einer Taste! Mit jedem Tastendruck wechselt der Modus: **Normaler Zyklus** (nacheinander rot → gelb → grün), **Nur Rot** (Haltebefehl), **Gelb blinkend** (Achtung/Nachtmodus), **Aus** (Wartung). Lerninhalte: **Zustandsmaschine** (die Variable `mode` bestimmt, welches Verhalten ausgeführt wird), **Modulo-Modus-Zyklus** (`(mode + 1) % 4` → 0→1→2→3→0 – endloser Zyklus), **non-blocking timing** (die LED-Phasen werden mit `time.ticks_ms()` + `time.ticks_diff(

LED-Signalanlagen für Bahnübergänge
LED-Warnblinklichter für Bahnübergänge.

