Arduino Ampel selber bauen: LED-Verkehrsampel mit Fußgängerampel & Taster

Die LED-Ampel ist der Klassiker unter den Arduino-Einsteigerprojekten – und aus gutem Grund: Mit ein paar LEDs, Widerständen und einem Arduino UNO lernst du die wichtigsten Grundlagen – digitale Ausgänge, Timing und Programm-Zustände. In diesem Tutorial bleibt es aber nicht beim simplen Blinken: Wir bauen eine echte Verkehrsampel mit korrekter Rot-Gelb-Grün-Phasenfolge, erweitern sie um eine Fußgängerampel mit Anforderungstaster und machen alles am Ende mit millis() fließend – ohne dass der Taster je „hängt“.

Warum die Ampel das perfekte erste Projekt ist

Eine Ampel ist ein Zustandsautomat in seiner reinsten Form: Sie kennt eine feste Abfolge von Phasen (Rot → Rot-Gelb → Grün → Gelb → Rot), jede mit eigener Dauer. Genau dieses Denken in Zuständen brauchst du später in fast jedem größeren Projekt – vom Automaten über Menue-Steuerungen bis zur Maschinensteuerung. Die Ampel bringt es dir spielerisch bei, weil du das Ergebnis sofort blinken siehst. Und weil nur LEDs, Widerstände und ein Taster nötig sind, ist sie in einer halben Stunde aufgebaut.

ℹ️ Rot-Gelb-Grün – die deutsche Reihenfolge: Anders als in vielen Ländern gibt es in Deutschland vor Grün eine kurze Rot-Gelb-Phase („Gleich geht’s los“). Genau diese Phase bauen wir mit ein – so wird deine Ampel richtig authentisch.

Das brauchst du

UNO R3 Entwicklungsboard (USB-C) – steuert alle LEDs (ein Nano V3 tut es genauso)
3mm LED Sortiment – rot, gelb und grün für Auto- und Fußgängerampel
Widerstands-Set (220–330 Ω) – ein Vorwiderstand pro LED
Breadboard 400 Kontakte – für den steckbaren Aufbau
Dupont-Jumperkabel – die Verbindungen

Für den Anforderungstaster reicht ein einfacher Taster – wir nutzen den internen Pull-up-Widerstand des Arduino, sodass kein zusätzliches Bauteil nötig ist.

1Verkabelung der Auto-Ampel

Jede LED bekommt einen eigenen Digitalpin und einen Vorwiderstand in der Plus-Leitung. Das lange Beinchen (Anode) zeigt zum Widerstand/Pin, das kurze (Kathode) an GND.

LED / Bauteil Arduino UNO Hinweis
Auto Rot D2 (über 220 Ω) Halt
Auto Gelb D3 (über 220 Ω) Übergang
Auto Grün D4 (über 220 Ω) Fahrt frei
Fuß Rot D5 (über 220 Ω) Warten
Fuß Grün D6 (über 220 Ω) Gehen
Taster D7 ↔ GND interner Pull-up
alle Kathoden GND-Schiene gemeinsame Masse

ℹ️ Welcher Widerstand passt? Für 3mm-LEDs an 5 V sind 220–330 Ω ideal. Die genaue Rechnung mit dem Ohmschen Gesetz zeigt der Guide LED-Vorwiderstand berechnen.

2Die einfache Auto-Ampel

Fangen wir mit dem Kern an: die reine Autoampel mit korrekter Phasenfolge. Dieser erste Sketch nutzt bewusst noch delay(), weil er so am leichtesten zu verstehen ist:

const int AUTO_ROT = 2, AUTO_GELB = 3, AUTO_GRUEN = 4;

void setup() {
  pinMode(AUTO_ROT, OUTPUT);
  pinMode(AUTO_GELB, OUTPUT);
  pinMode(AUTO_GRUEN, OUTPUT);
}

void loop() {
  // Rot
  digitalWrite(AUTO_ROT, HIGH);
  delay(4000);

  // Rot-Gelb (gleich geht es los)
  digitalWrite(AUTO_GELB, HIGH);
  delay(1500);

  // Gruen
  digitalWrite(AUTO_ROT, LOW);
  digitalWrite(AUTO_GELB, LOW);
  digitalWrite(AUTO_GRUEN, HIGH);
  delay(4000);

  // Gelb
  digitalWrite(AUTO_GRUEN, LOW);
  digitalWrite(AUTO_GELB, HIGH);
  delay(1500);
  digitalWrite(AUTO_GELB, LOW);
}

Lädst du das hoch, läuft die Ampel schon komplett durch. Schön – aber solange delay() läuft, ist der Arduino „taub“ für Tastendrücke. Genau das lösen wir gleich.

3Fußgängerampel mit Anforderungstaster

Jetzt kommt der spannende Teil: eine Fußgängerampel, die erst auf Knopfdruck reagiert. Damit der Taster jederzeit erkannt wird, ersetzen wir die delay()-Blöcke durch einen Zustandsautomaten mit millis(). Jede Phase merkt sich, wann sie begonnen hat, und wechselt erst, wenn ihre Dauer um ist:

const int AUTO_ROT = 2, AUTO_GELB = 3, AUTO_GRUEN = 4;
const int FUSS_ROT = 5, FUSS_GRUEN = 6;
const int TASTER = 7;

enum Phase { AUTO_FREI, AUTO_GELBPHASE, FUSS_FREI, FUSS_RAEUMEN };
Phase phase = AUTO_FREI;
unsigned long phasenStart = 0;
bool anforderung = false;

void setzePhase(Phase p) {
  phase = p;
  phasenStart = millis();
}

void setup() {
  for (int p = 2; p <= 6; p++) pinMode(p, OUTPUT);
  pinMode(TASTER, INPUT_PULLUP);
  setzePhase(AUTO_FREI);
}

void loop() {
  unsigned long jetzt = millis();

  // Taster jederzeit abfragen (LOW = gedrueckt)
  if (digitalRead(TASTER) == LOW) anforderung = true;

  switch (phase) {
    case AUTO_FREI:
      digitalWrite(AUTO_GRUEN, HIGH); digitalWrite(AUTO_GELB, LOW);
      digitalWrite(AUTO_ROT, LOW);
      digitalWrite(FUSS_ROT, HIGH);   digitalWrite(FUSS_GRUEN, LOW);
      // Nach Anforderung mindestens 3 s Auto-Gruen lassen
      if (anforderung && jetzt - phasenStart > 3000) setzePhase(AUTO_GELBPHASE);
      break;

    case AUTO_GELBPHASE:
      digitalWrite(AUTO_GRUEN, LOW); digitalWrite(AUTO_GELB, HIGH);
      if (jetzt - phasenStart > 1500) setzePhase(FUSS_FREI);
      break;

    case FUSS_FREI:
      digitalWrite(AUTO_GELB, LOW); digitalWrite(AUTO_ROT, HIGH);
      digitalWrite(FUSS_ROT, LOW);  digitalWrite(FUSS_GRUEN, HIGH);
      if (jetzt - phasenStart > 5000) setzePhase(FUSS_RAEUMEN);
      break;

    case FUSS_RAEUMEN:  // Fuss-Gruen blinkt, dann zurueck
      digitalWrite(FUSS_GRUEN, (jetzt / 300) % 2);  // blinken
      if (jetzt - phasenStart > 3000) {
        digitalWrite(FUSS_GRUEN, LOW); digitalWrite(FUSS_ROT, HIGH);
        digitalWrite(AUTO_ROT, LOW);
        anforderung = false;
        setzePhase(AUTO_FREI);
      }
      break;
  }
}

💡 Das ist ein Zustandsautomat: Statt eines starren Ablaufs fragt der Arduino in jeder Runde „in welcher Phase bin ich, und ist ihre Zeit um?“. Weil loop() nie blockiert, wird der Taster in jeder Millisekunde geprüft. Das gleiche Muster steckt hinter unzähligen Projekten – mehr dazu im Guide millis() statt delay().

4Taster sauber entprellen

Mechanische Taster „prellen“: Beim Drücken schließt der Kontakt nicht sauber, sondern flackert millisekundenlang. In unserem Ampel-Code ist das egal, weil wir nur ein Flag setzen. Willst du aber zählen, wie oft gedrückt wurde, brauchst du Entprellung. So sieht sie kompakt aus:

const int TASTER = 7;
bool letzterStand = HIGH;
unsigned long letzteAenderung = 0;
int zaehler = 0;

void setup() {
  pinMode(TASTER, INPUT_PULLUP);
  Serial.begin(115200);
}

void loop() {
  bool stand = digitalRead(TASTER);
  if (stand != letzterStand && millis() - letzteAenderung > 50) {
    letzteAenderung = millis();
    letzterStand = stand;
    if (stand == LOW) {          // sauberer Druck erkannt
      zaehler++;
      Serial.print("Anforderung Nr. ");
      Serial.println(zaehler);
    }
  }
}

Die 50 Millisekunden sind das übliche Entprell-Fenster. Warum das so funktioniert und wie du Taster per Interrupt abfragst, vertieft der Guide Arduino Interrupts & Entprellen.

5Kreuzung aus zwei Ampeln

Eine echte Kreuzung hat zwei Autoampeln, die gegenläufig schalten: Ist die eine grün, ist die andere rot. Mit einem zweiten LED-Satz und einer kleinen Erweiterung wird daraus eine komplette Kreuzung. Der Trick: eine gemeinsame Sicherheitsphase, in der beide Richtungen rot sind, bevor die andere Seite grün wird:

// Richtung A: D2/D3/D4  |  Richtung B: D8/D9/D10
void alleRot() {
  digitalWrite(2, HIGH); digitalWrite(3, LOW); digitalWrite(4, LOW);
  digitalWrite(8, HIGH); digitalWrite(9, LOW); digitalWrite(10, LOW);
}

void richtungFrei(int rot, int gelb, int gruen) {
  digitalWrite(rot, LOW); digitalWrite(gelb, LOW); digitalWrite(gruen, HIGH);
}

void loop() {
  alleRot();            delay(2000);   // Sicherheitsphase
  richtungFrei(2,3,4);  delay(5000);   // A faehrt
  digitalWrite(4, LOW); digitalWrite(3, HIGH); delay(1500);
  alleRot();            delay(2000);   // Sicherheitsphase
  richtungFrei(8,9,10); delay(5000);   // B faehrt
  digitalWrite(10, LOW); digitalWrite(9, HIGH); delay(1500);
}

ℹ️ Warum die Sicherheitsphase? In echt sorgt die kurze Allrot-Phase dafür, dass die Kreuzung leer ist, bevor Querverkehr Grün bekommt. Baust du das nach, ist deine Kreuzung nicht nur hübsch, sondern auch „verkehrssicher“.

Praxistipps & Erweiterungen

  • Nachtmodus: Lässt du nur die gelbe LED im Sekundentakt blinken, hast du den typischen „Ampel-aus“-Zustand nachgebaut.
  • Countdown: Kombiniere die Fußgängerampel mit einem LCD1602, das die Restsekunden bis Grün anzeigt.
  • Summer für Barrierefreiheit: Ein Piepton bei Fuß-Grün macht die Ampel wie im Original für Sehbehinderte nutzbar.
  • Wenig Pins? Multiplexen: Mit einem Schieberegister steuerst du viele LEDs über nur drei Pins.
  • Dauerhaft aufbauen: Läuft alles, löte die Ampel auf eine Lochrasterplatine.

Troubleshooting

⚠️ Eine LED bleibt dunkel: Meist ist sie verpolt. Das lange Beinchen (Anode) muss zum Widerstand/Pin, das kurze an GND. Dreh die LED einfach um.

⚠️ Taster wird nicht erkannt: Ohne INPUT_PULLUP „schwebt“ der Pin. Entweder den internen Pull-up nutzen (Taster gegen GND) oder einen 10-kΩ-Widerstand nach 5 V setzen.

⚠️ Ampel hängt in einer Phase: Wahrscheinlich eine Zeitbedingung mit falschem Vergleich (> vs. <) oder phasenStart wird nicht neu gesetzt. Prüfe, dass jeder Phasenwechsel über setzePhase() läuft.

⚠️ LEDs leuchten alle schwach: Fehlende gemeinsame Masse zwischen Breadboard und Arduino. Eine durchgehende GND-Leitung löst das.

⚠️ Fuß-Grün blinkt nicht: Der Ausdruck (jetzt / 300) % 2 braucht einen unsigned long – stelle sicher, dass jetzt aus millis() stammt.

🚀 Dein Ampel-Starterpaket

Alle Bauteile für dieses Projekt im Shop:

Fazit

Aus fünf LEDs, ein paar Widerständen und einem Taster ist eine echte Ampelanlage geworden – mit deutscher Rot-Gelb-Grün-Phasenfolge, Fußgängerampel auf Anforderung und einer verkehrssicheren Kreuzung. Vor allem aber hast du das wichtigste Konzept der Mikrocontroller-Programmierung gelernt: den Zustandsautomaten mit millis(), der mehrere Dinge scheinbar gleichzeitig erledigt, ohne je zu blockieren.

Von hier ist der Weg zu größeren Projekten kurz: Der gleiche Aufbau steckt hinter Automaten, Waschmaschinen-Steuerungen und Menue-Systemen. Bau die Ampel um einen Countdown, einen Summer oder eine zweite Kreuzung herum – und du hast das Denken in Zuständen für immer verinnerlicht. Viel Spaß beim Blinken!

Zurück zum Blog