Ratgeber · 12 Min. Lesezeit

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.

Rote, gelbe und grüne 5-mm-LEDs für eine Arduino Ampel
5-mm-LEDs: Für die Ampel reicht je eine rote, gelbe und grüne; leg dir für die Fußgängerampel und die Kreuzung ein paar mehr bereit.

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.

PhaseLeuchtetBedeutungZeit in dieser Anleitung
1RotHalt5 s
2Rot + GelbGleich kommt Grün, bereit machen1 s
3GrünFahren5 s
4GelbGleich kommt Rot, anhalten3 s
An der KreuzungAlle Richtungen RotRäumzeit, damit die Kreuzung frei wird1 s

Bauteile und Berechnung des 220-Ω-Vorwiderstands

BauteilAnzahlHinweis
Arduino Uno (oder Nano)1Ein ESP32 oder Raspberry Pi Pico geht auch
5-mm-LEDs: rot, gelb, grünje 1+1 rot und +1 grün für die Fußgängerampel, +1 Satz für die Kreuzung
Widerstand 220 Ω3Einer pro LED; 8 für alle Versionen
Taster (6×6 mm)1Nur für die Fußgängerampel
Steckbrett (Breadboard)1Halbe Größe (400 Kontakte) reicht
Jumper-Kabel10–20Stecker-Stecker
USB-Kabel1Zum 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-FarbeFlussspannung (ca.)Strom bei 5 V (220 Ω)Strom bei 3,3 V (220 Ω)
Rot1,8–2,2 V≈ 13,6 mA≈ 5,9 mA
Gelb1,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.

FunktionArduino UnoESP32Raspberry Pi Pico
Fahrzeug Rot (Richtung A)D10GPIO 5GP13
Fahrzeug Gelb (Richtung A)D9GPIO 18GP12
Fahrzeug Grün (Richtung A)D8GPIO 19GP11
Fußgänger RotD7GPIO 21GP15
Fußgänger GrünD6GPIO 22GP14
Fußgängertaster (anderes Bein an GND)D2GPIO 23GP4
Richtung B Rot / Gelb / GrünD5 / D4 / D3GPIO 25 / 26 / 27GP8 / GP7 / GP6
LED-Kathoden und TasterGNDGNDGND
Eine verpolte LED bleibt einfach dunkel und nimmt keinen Schaden; ohne Vorwiderstand werden LED und Pin jedoch überlastet, und die LED kann schnell an Helligkeit verlieren. Plane am Arduino Uno höchstens 20 mA pro Pin ein (absolutes Maximum 40 mA) und halte die Summe aller Pins unter 200 mA. Leuchtet gar keine LED, prüfe zuerst GND: Ist die blaue Schiene des Steckbretts nicht mit einem GND-Pin des Arduino verbunden, ist der Stromkreis nicht geschlossen.

Schritt 1: der einfachste Ampel-Code mit delay()

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}

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.

6×6-mm-Taster mit vier Beinen
6×6-mm-Taster: Die vier Beine sind intern paarweise verbunden; nimmst du zwei diagonal gegenüberliegende Beine, ist die Verdrahtung immer richtig.
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}

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:

ZustandFahrzeugampelFußgängerampelDauerNächster Zustand
ARAC_YESILGrünRotMindestens 8 s, bis eine Anforderung kommtARAC_SARI
ARAC_SARIGelbRot3 sYAYA_BEKLE
YAYA_BEKLERotRot1 sYAYA_GECER
YAYA_GECERRotGrün6 sYAYA_FLAS
YAYA_FLASRotGrün blinkt3 sYAYA_BITTI
YAYA_BITTIRotRot1 sKIRMIZI_SARI
KIRMIZI_SARIRot + GelbRot1 sARAC_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}

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.

Willst du eine Funktion ergänzen, füge zuerst eine Zeile in die Tabelle ein und schreib dann ein case in den Code. Du kannst zum Beispiel einen Nachtzustand mit blinkendem Gelb anlegen und dorthin wechseln, wenn ein LDR-Lichtsensor Dunkelheit erkennt. Mehr zu Zustandsautomaten findest du in Zustandsautomat mit Arduino.

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.

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}

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

  1. Lege auf tinkercad.com ein kostenloses Konto an und erstelle im Bereich Circuits (Schaltkreise) eine neue Schaltung.
  2. 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.
  3. 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.
  4. Klick auf Code, wähle im Aufklappmenü Text, lösch den Beispielcode und füge den Sketch aus dieser Anleitung ein.
  5. 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.

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}

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.

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)

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

SymptomMögliche UrsacheLösung
Keine LED leuchtetGND nicht verbunden oder die Versorgungsschiene des Steckbretts ist in der Mitte unterbrochenArduino-GND mit der blauen Schiene verbinden, bei langen Steckbrettern beide Hälften überbrücken
Eine LED bleibt dunkelLED verpolt oder am falschen PinLanges Bein (Anode) über den Widerstand an den Pin, kurzes Bein an GND; Pinnummer mit dem Code vergleichen
Falsche Farbe leuchtetRote und grüne Pins im Code vertauschtKabel oder Konstanten nach der Pin-Tabelle korrigieren
LED sehr hell und wird warmKein VorwiderstandJede LED bekommt einen 220-Ω-Widerstand in Reihe
Taster reagiert nicht oder scheint immer gedrücktTaster an zwei intern verbundenen Beinen angeschlossen oder INPUT_PULLUP vergessenDiagonale Beine nutzen und pinMode(BUTON, INPUT_PULLUP) schreiben
Keine sofortige Reaktion auf den TasterDie Mindestgrünzeit der Autos ist noch nicht abgelaufenKein Fehler; MIN_YESIL verkleinern
Programm spielt nach einer Weile verrücktZeitvariable als int deklariertAlle 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).
Willst du herausstechen, vergleiche deine erste Version mit delay() mit der millis()-Version: Erkläre, warum Tastendrücke in der ersten verloren gehen und in der zweiten nie. Ein 20 bis 30 Sekunden langes Video der laufenden Schaltung und ein Screenshot des seriellen Monitors machen den Bericht noch stärker.

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.

Alle Projekte ansehen
Ampel
#3Anfänger

Ampel

Projekt, das eine echte Ampel mit roten, gelben und grünen LEDs simuliert. Lehrt das Timing und die sequentielle Steuerlogik.

4.8(126)
337
ESP32 Ampel
#107Anfänger

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.

4.4(21)
44
Raspberry Pi Pico Ampel-Simulator (3 LEDs sequenziell, GP11/GP12/GP13)
#150Anfänger

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.

4.0(1)
12
Zwei LEDs abwechselnd blinken
#2Anfänger

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.

4.6(103)
317
LED mit Taster schalten (Push)
#9Anfänger

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

4.9(112)
190
LED-Polizeilicht
#4Anfänger

LED-Polizeilicht

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

4.7(97)
227
Raspberry Pi Pico-Ampel mit 4-Modus-Tastenwahlschalter (Zustandsmaschine, GP11-13 + GP4)
#158Mittel
Premium

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(

5.0(1)
14
LED-Signalanlagen für Bahnübergänge
#146Fortgeschritten
Premium

LED-Signalanlagen für Bahnübergänge

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

5.0(2)
14