Arduino Ampel mit Lego

Ampelanlage mit Arduino

Ob für die Modellbahn, LEGO oder den Schulunterricht, eine funktionierende Ampelanlage ist einfach super! Sie kann mit unter sehr komplex werden. Hier zeige ich dir, wie du die Ampelanlage einer einfachen Kreuzung mit Fußgängerampel mit Arduino aufbauen kannst.

Bauteile

  • LEDs (Dieses 500 LED-Set hab ich benutzt).
  • 10 Widerstände 220 Ohm

Schaltplan

Ampel Schaltplan

Natürlich können auch die anderen Ampeln der Anlage einfach parallel betrieben werden. Dazu bitte auch den Hinweis weiter unten beachten.

Ampel Schematische Darstellung, Schematic
Hier noch einmal als schematische Darstellung.

Hintergrund

Welche Ampelphasen gibt es denn bei so einer Ampel? Sehen wir uns die Kreuzung mal genauer an. 


Sieh dir jetzt meinen neuen Arduino-Videokurs an: Jetzt ansehen!


Genau wie die Animation könnte man auch die Ampelanlage als Bild betrachten. Die einzelnen Lampen sind die Pixel und die sind entweder an oder aus (Bitmap). Und wie bei einer Animation ist jeder Zustand ein Frame, sozusagen ein Standbild. Welche Bilder sind das denn genau?

Links sind also die Bilder, rechts ist bereits ein Array-Code. Es ist ein zweidimensionales Array. Wir können es genau so verwenden und müssen nur bestimmen, welcher »State« (oder Frame) gezeigt werden soll. 

Ampelphasen Kreuzung

Um nicht so viel Code schreiben zu müssen, legen wir eine extra Methode an:

void zeigeAmpelphase() {
  for (int i = 0; i < 10; i++) {
    digitalWrite(ledPins[i], ampelphasen[myState][i]);
  }
  delay(wartezeiten[myState]);
}

Die For-Schleife sorgt dafür, dass jede LED des Arrays ausgewertet wird. 

Da nicht jede Ampelphase gleich lang angezeigt wird, gibt es ein weiteres Array mit den Wartezeiten der jeweiligen Ampelphase. Diese wird ebenfalls in der zeigeAmpelphase()-Methode ausgewertet. 

Anzeige

Code

Der gesamte Code sieht so aus:

int ledPins[10] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

int ampelphasen[10][10] = {
  {1, 0, 0, 0, 1, 0, 0, 1, 1, 0},  // state 0
  {1, 0, 0, 1, 0, 0, 0, 1, 1, 0},  // state 1
  {1, 0, 0, 1, 0, 0, 1, 0, 1, 0},  // state 2
  {1, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 3
  {1, 1, 0, 1, 0, 1, 0, 0, 1, 0},  // state 4
  {0, 0, 1, 1, 0, 1, 0, 0, 0, 1},  // state 5
  {0, 0, 1, 1, 0, 1, 0, 0, 1, 0},  // state 6
  {0, 1, 0, 1, 0, 1, 0, 0, 1, 0},  // state 7
  {1, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 8
  {1, 0, 0, 1, 0, 1, 1, 0, 1, 0}   // state 9
};

int wartezeiten[10] = {5000, 2000, 500, 2000, 500, 5000, 2000, 500, 2000, 500};

int myState = 0;

void setup() {
  for (int i = 0; i < 10; i++) {     
    pinMode(ledPins[i], OUTPUT);
  }
}

void zeigeAmpelphase() {
  for (int i = 0; i < 10; i++) {
    digitalWrite(ledPins[i], ampelphasen[myState][i]);
  }
  delay(wartezeiten[myState]);
}

void loop() {
  zeigeAmpelphase();
  myState++;
  if (myState >= 10) myState = 0;
}

Hinweis: Ein digitaler Kanal des Arduino-Boards kann bis zu 60 mA liefern. Alle Pins zusammen sind für maximal 200 mA ausgelegt. Bei 5mm Signal-LEDs beträgt der Strom bei einem Vorwiderstand von 220 Ohm 12 – 15 mA. Es können also pro digitalen Pin 4 LEDs gleichzeitig betrieben werden und nicht mehr als 13 LEDs gleichzeitig leuchten. Ansonsten musst du eine Verstärkerschaltung einbauen.


Ampelanlage mit Anforderungstaster

Da in den Kommentaren danach gefragt wurde (danke an Ben und Mani), habe ich noch eine Version mit Anforderungstastern für die Fußgängerampeln erstellt.

Ampelanlage mit Arduino und Taster, Schalter, Anforderungstaster

Code

Um die Anforderungstaster nutzen zu können, muss der Code umgebaut werden. Der Befehl

delay();

muss durch Timer-Funktionen ersetzt werden. Das habe ich hier mal im Detail erklärt: Timer mit Arduino – Die Alternative zu Delays.

Wird nun einer der Taster gedrückt und befindet sich gerade die kreuzende Fahrspur im Grün-Modus, wird die vorgesehene Fahrzeit auf 0 gesetzt und somit wird die nächste Ampelphase ausgeführt.

int ledPins[10] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

int button1Pin = 12;  // Fussgaengerschalter 1
int button2Pin = 13;  // Fussgaengerschalter 2

int ampelphasen[10][10] = {
  {1, 0, 0, 0, 1, 0, 0, 1, 1, 0},  // state 0
  {1, 0, 0, 1, 0, 0, 0, 1, 1, 0},  // state 1
  {1, 0, 0, 1, 0, 0, 1, 0, 1, 0},  // state 2
  {1, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 3
  {1, 1, 0, 1, 0, 1, 0, 0, 1, 0},  // state 4
  {0, 0, 1, 1, 0, 1, 0, 0, 0, 1},  // state 5
  {0, 0, 1, 1, 0, 1, 0, 0, 1, 0},  // state 6
  {0, 1, 0, 1, 0, 1, 0, 0, 1, 0},  // state 7
  {1, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 8
  {1, 0, 0, 1, 0, 1, 1, 0, 1, 0}   // state 9
};

long wartezeiten[10] = {5000, 2000, 500, 2000, 500, 5000, 2000, 500, 2000, 500};
int myState = 0;

long myTimer = 0;
long myTimeout = 0;

void setup() {
  for (int i = 0; i < 10; i++) {     
    pinMode(ledPins[i], OUTPUT);
  }
  pinMode(button1Pin, INPUT_PULLUP);
  pinMode(button2Pin, INPUT_PULLUP);
}

void zeigeAmpelphase() {
  for (int i = 0; i < 10; i++) {
    digitalWrite(ledPins[i], ampelphasen[myState][i]);
  }
}

void loop() {
  if (myTimer+myTimeout<millis()){
    myTimer=millis();
    myState++;
    if (myState >= 10) myState = 0;
    myTimeout=wartezeiten[myState];
    zeigeAmpelphase();
  }

  if ((digitalRead(button1Pin)==LOW)&&(myState==0)) myTimeout=0;
  if ((digitalRead(button2Pin)==LOW)&&(myState==5)) myTimeout=0;
  
  delay(10);
}

Hier noch eine alternative Version, in der die Fußgänger nach dem Betätigen des Tasters noch etwas warten müssen:

int ledPins[10] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

int button1Pin = 12;  // Fussgaengerschalter 1
int button2Pin = 13;  // Fussgaengerschalter 2

int ampelphasen[10][10] = {
  {1, 0, 0, 0, 1, 0, 0, 1, 1, 0},  // state 0
  {1, 0, 0, 1, 0, 0, 0, 1, 1, 0},  // state 1
  {1, 0, 0, 1, 0, 0, 1, 0, 1, 0},  // state 2
  {1, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 3
  {1, 1, 0, 1, 0, 1, 0, 0, 1, 0},  // state 4
  {0, 0, 1, 1, 0, 1, 0, 0, 0, 1},  // state 5
  {0, 0, 1, 1, 0, 1, 0, 0, 1, 0},  // state 6
  {0, 1, 0, 1, 0, 1, 0, 0, 1, 0},  // state 7
  {1, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 8
  {1, 0, 0, 1, 0, 1, 1, 0, 1, 0}   // state 9
};

long wartezeiten[10] = {5000, 2000, 500, 2000, 500, 5000, 2000, 500, 2000, 500};
int myState = 0;

long myTimer = 0;
long myTimeout = 0;

void setup() {
  for (int i = 0; i < 10; i++) {
    pinMode(ledPins[i], OUTPUT);
  }
  pinMode(button1Pin, INPUT_PULLUP);
  pinMode(button2Pin, INPUT_PULLUP);
}

void zeigeAmpelphase() {
  for (int i = 0; i < 10; i++) {
    digitalWrite(ledPins[i], ampelphasen[myState][i]);
  }
}

void loop() {
  if (myTimer + myTimeout < millis()) {
    myTimer = millis();
    myState++;
    if (myState >= 10) myState = 0;
    myTimeout = wartezeiten[myState];
    zeigeAmpelphase();
  }

  if ((digitalRead(button1Pin) == LOW) && (myState == 0)) {
    myTimer = millis();
    myTimeout = 2000;
  }
  if ((digitalRead(button2Pin) == LOW) && (myState == 5)) {
    myTimer = millis();
    myTimeout = 2000;
  }

  delay(10);
}

Und hier noch eine (ungetestete) Variante für Ampelanlagen aus Österreich auf Anfrage von Harald, bei der die Auto-Ampeln viermal blinken, bevor sie auf Gelb schalten.

int ledPins[10] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

int button1Pin = 12;  // Fussgaengerschalter 1
int button2Pin = 13;  // Fussgaengerschalter 2

int ampelphasen[26][10] = {
  {1, 0, 0, 0, 1, 0, 0, 1, 1, 0},  // state 0
  {1, 0, 0, 1, 0, 0, 0, 1, 1, 0},  // state 1

  {1, 0, 0, 1, 0, 0, 0, 0, 1, 0},  // state 2 B
  {1, 0, 0, 1, 0, 0, 0, 1, 1, 0},  // state 3 B
  {1, 0, 0, 1, 0, 0, 0, 0, 1, 0},  // state 4 B
  {1, 0, 0, 1, 0, 0, 0, 1, 1, 0},  // state 5 B
  {1, 0, 0, 1, 0, 0, 0, 0, 1, 0},  // state 6 B
  {1, 0, 0, 1, 0, 0, 0, 1, 1, 0},  // state 7 B
  {1, 0, 0, 1, 0, 0, 0, 0, 1, 0},  // state 8 B
  {1, 0, 0, 1, 0, 0, 0, 1, 1, 0},  // state 9 B
  
  {1, 0, 0, 1, 0, 0, 1, 0, 1, 0},  // state 10
  {1, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 11
  {1, 1, 0, 1, 0, 1, 0, 0, 1, 0},  // state 12
  {0, 0, 1, 1, 0, 1, 0, 0, 0, 1},  // state 13
  {0, 0, 1, 1, 0, 1, 0, 0, 1, 0},  // state 14
  
  {0, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 15 B
  {0, 0, 1, 1, 0, 1, 0, 0, 1, 0},  // state 16 B
  {0, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 17 B
  {0, 0, 1, 1, 0, 1, 0, 0, 1, 0},  // state 18 B
  {0, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 19 B
  {0, 0, 1, 1, 0, 1, 0, 0, 1, 0},  // state 20 B
  {0, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 21 B
  {0, 0, 1, 1, 0, 1, 0, 0, 1, 0},  // state 22 B
  
  {0, 1, 0, 1, 0, 1, 0, 0, 1, 0},  // state 23
  {1, 0, 0, 1, 0, 1, 0, 0, 1, 0},  // state 24
  {1, 0, 0, 1, 0, 1, 1, 0, 1, 0}   // state 25
};

long wartezeiten[26] = {5000, 2000, 200, 200, 200, 200, 200, 200, 200, 200, 500, 2000, 500, 5000, 2000,200, 200, 200, 200, 200, 200, 200, 200, 500, 2000, 500};
int myState = 0;

long myTimer = 0;
long myTimeout = 0;

void setup() {
  for (int i = 0; i < 10; i++) {
    pinMode(ledPins[i], OUTPUT);
  }
  pinMode(button1Pin, INPUT_PULLUP);
  pinMode(button2Pin, INPUT_PULLUP);
}

void zeigeAmpelphase() {
  for (int i = 0; i < 10; i++) {
    digitalWrite(ledPins[i], ampelphasen[myState][i]);
  }
}

void loop() {
  if (myTimer + myTimeout < millis()) {
    myTimer = millis();
    myState++;
    if (myState >= 26) myState = 0;
    myTimeout = wartezeiten[myState];
    zeigeAmpelphase();
  }

  if ((digitalRead(button1Pin) == LOW) && (myState == 0)) {
    myTimer = millis();
    myTimeout = 2000;
  }
  if ((digitalRead(button2Pin) == LOW) && (myState == 13)) {
    myTimer = millis();
    myTimeout = 2000;
  }

  delay(10);
}

Wenn dir das Projekt gefallen hat und du von weiteren interessanten Projekten inspiriert werden willst, sieh dir doch mal mein neues E-Book »Arduino Projekte Volume 1« an!

  • Die beliebtesten Arduino-Projekte von StartHardware
  • Inklusive Schaltplan, Beschreibung und Code
  • Arduino-Schnellstart-Kapitel
  • Kompakter Programmierkurs


26 Gedanken zu „Ampelanlage mit Arduino“

  1. Hallo Stefan

    Habe zwei Fragen:
    1. Wenn ich meinen Code so anpasse wie du ihn gemacht hast für die Steuerung mit Anforderungstaster dann überspringt er natürlich den State0 da ja myState++; vor zeigeAmpelphase(); kommt.
    Das möchte ich aber nicht so haben. Wie könnte ich also den Loop anpassen, dass er den Ablauf wie ohne Anforderungstaster macht.

    2. Irgendwie bekomme ich es einfach nicht hin, dass der Loop unterbricht wenn ich mein Taster drücke. Der Taster soll den State0 wieder einberufen und somit diese Fahrspur grün schalten.
    Testhalber habe ich es so gemacht, dass dies passiert wenn die kreuzende Fahrspur grün hat (State6) und ich den Taster drücke. Aber es passiert einfach nix… Habe das Gefühl mein Code stimmt nicht. Kannst du ihn mal ansehen?

    Mein Code sieht folgendermassen aus:

    // C++ code
    //
    int ledPins[9] = {9, 8, 7, 6, 5, 4, 3, 2, 1};

    int buttonNSPin = 12;
    int buttonEWPin = 11;
    int buttonPPin= 10;

    int test[2][11] = {
    {1, 1, 1, 1, 1, 1, 1, 1, 1}, // testate 0
    {0, 0, 0, 0, 0, 0, 0, 0, 0}, // testate 1
    };

    int testpause[2] = {3000, 3000};

    int myTestate = 0;

    int phase[11][9] = {
    {1, 0, 0, 1, 0, 0, 1, 0, 0}, // state 0
    {1, 1, 0, 1, 0, 0, 1, 0, 0}, // state 1
    {0, 0, 1, 1, 0, 0, 1, 0, 0}, // state 2
    {0, 1, 0, 1, 0, 0, 1, 0, 0}, // state 3
    {1, 0, 0, 1, 0, 0, 1, 0, 0}, // state 4
    {1, 0, 0, 1, 1, 0, 1, 0, 0}, // state 5
    {1, 0, 0, 0, 0, 1, 1, 0, 0}, // state 6
    {1, 0, 0, 0, 1, 0, 1, 0, 0}, // state 7
    {1, 0, 0, 1, 0, 0, 1, 0, 0}, // state 8
    {1, 0, 0, 1, 0, 0, 0, 0, 1}, // state 9
    {1, 0, 0, 1, 0, 0, 0, 1, 0}, // state 10
    };

    long pause[11] = {3000, 1000, 10000, 4000, 3000, 1000, 10000, 4000, 3000, 10000, 5000};

    int myState = 0;

    long myTimer = 0;
    long myTimeout = 0;

    void setup() {
    for (int i = 0; i < 9; i++) {
    pinMode(ledPins[i], OUTPUT);
    }
    pinMode(buttonNSPin, INPUT_PULLUP);
    pinMode(buttonEWPin, INPUT_PULLUP);
    pinMode(buttonPPin, INPUT_PULLUP);
    // Test-LED
    for (int i = 0; i < 9; i++) {
    digitalWrite(ledPins[i], test[myTestate][i]);
    }
    delay(testpause[myTestate]);
    myTestate++;
    for (int i = 0; i < 9; i++) {
    digitalWrite(ledPins[i], test[myTestate][i]);
    }
    delay(testpause[myTestate]);
    }

    void showphase() {
    for (int i = 0; i < 9; i++) {
    digitalWrite(ledPins[i], phase[myState][i]);
    }
    delay(pause[myState]);
    }

    void loop() {
    if (myTimer + myTimeout = 11) myState = 0;
    myTimeout = pause[myState]
    showphase();
    }

    if ((digitalRead(buttonNSPin) == LOW)&&(myState == 6)) myTimeout=0;
    delay(10);
    }
    }

    Herzlichen Dank im Voraus !!

    1. Hi Sam, also der State 0 wird ja nur beim ersten Durchlauf übersprungen, danach wird der ja so wie die anderen States ausgeführt. Ich habe das so gemacht, um den Code möglichst kurzzuhalten. Und der Button in deinem Beispiel wird ja nur angewendet, wenn der State 6 ist. Wenn du den immer auslösen willst, guck dir mal die Verwendung von Interrupts an. Liebe Grüße Stefan

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

 

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.