// Ampelsteuerung
// Ampelsteuerung an einer einfachen Kreuzung mit Fussgängerampel, mit Tag/Nachtmodus, mit Wechselblinker
// Die Ampelphasen werden über eine Tabelle definiert
#include <MobaTools.h>
//vvvvvvvvvvvvvvvvvvvvvvvvv BEGIN WECHSELBLINKER vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Definitionen für den Wechselblinker
// Festlegen der Anschlüsse (Pinnummer) für Ein- und Ausgänge
const byte linksPin = 6; // linke Lampe
const byte rechtsPin = 7; // rechte Lampe
const byte belegtPin = 8; // Eingang 'Zug kommt'
// Benötigte MobaTools Objekte:
EggTimer StoppUhr; // Zum Messen von Zeiten
EggTimer StoppUhr2; // Zum Messen von Zeit Nacht
SoftLed LampeLinks;
SoftLed LampeRechts;
SoftLed LampeAmpelGelbNeben;
// Zeiten für den Blinkrhythmus ( in ms )
const int STARTZEIT = 400;
const int BLINKZEIT = 500;
const int FADEZEIT = 450;
EggTimer EntPrellung; // Zur Tasterentporellung
const int PRELLZEIT = 100;
// Variable
boolean bueBelegt; // = TRUE, wenn sich ein Zug im Bereich des Bue befindet
byte wBlinkerZustand; // aktueller Zustand unseres Blinkerautomaten
//^^^^^^^^^^^^^^^^^^^^^^^^ ENDE WECHSELBLINKER ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//vvvvvvvvvvvvvvvvvvvvvvvvv BEGIN AmpelNacht vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Definitionen für Ampelnacht, blinken nur Nebenstrasse
// Festlegen der Anschlüsse (Pinnummer) für Ein- und Ausgänge
const byte belegtPin2 = 9; // Eingang "es ist Nacht"
// Zeiten für den Blinkrhythmus Nacht ( in ms )
const int STARTZEIT2 = 400;
const int BLINKZEIT2 = 1200;
const int FADEZEIT2 = 1200;
const byte NebenGelbPin = A4; // Nebenstrasse Ampel Gelb
// Variable
boolean bueBelegt2; // = TRUE, wenn D9 geschlossen, dann Nacht
byte wBlinkerZustand2; // aktueller Zustand unseres Blinkerautomaten Nacht
//^^^^^^^^^^^^^^^^^^^^^^^^ ENDE AmpelNacht ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//vvvvvvvvvvvvvvvvvvvvvvvv BEGIN AMPELSTEUERUNG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Definition der Ausgangspins in Spalte 0 der Ampeltabelle
// Tabelle für die Ampelzustände ( Index 1 ... )
/*
const byte AmpelTabelle [][9] = {
// Ampelphase: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
// Tabellenindex: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
{ A0 ,OFF ,OFF , ON , ON , ON , ON , ON , ON }, // hauptRot
{ A1 ,OFF , ON ,OFF ,OFF ,OFF ,OFF ,OFF , ON }, // hauptGelb
{ A2 , ON ,OFF ,OFF ,OFF ,OFF ,OFF ,OFF ,OFF }, // hauptGruen
{ A3 , ON , ON , ON , ON ,OFF ,OFF , ON , ON }, // nebenRot
{ A4 ,OFF ,OFF ,OFF , ON ,OFF , ON ,OFF ,OFF }, // nebenGelb
{ A5 ,OFF ,OFF ,OFF ,OFF , ON ,OFF ,OFF ,OFF }, // nebenGruen
{ 0 , 5 , 2 , 2 , 2 , 4 , 2 , 2 , 2 } // zeitDauer
};
*/
const byte AmpelTabelle [][12] = {
// Ampelphase: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11
// Tabellenindex: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11
{ A0 ,OFF ,OFF ,OFF , ON , ON , ON , ON , ON , ON , ON , OFF}, // hauptRot
{ 3 , ON , ON , ON , ON , ON ,OFF , ON , ON , ON , ON , OFF}, // hauptRotFRot
{ A1 ,OFF , OFF, ON ,OFF ,OFF ,OFF ,OFF ,OFF ,OFF , ON , OFF}, // hauptGelb
{ A2 , ON ,ON ,OFF ,OFF ,OFF ,OFF ,OFF ,OFF ,OFF ,OFF , OFF}, // hauptGruen
{ 2 ,OFF ,OFF ,OFF ,OFF ,OFF , ON ,OFF ,OFF ,OFF ,OFF , OFF}, // hauptGruenFGruen
{ A3 , ON , ON , ON , ON , ON ,OFF ,OFF ,OFF , ON , ON , OFF}, // nebenRot
{ 5 ,OFF , ON , ON , ON , ON , ON , ON , ON , ON , ON , OFF}, // nebenRotFRot
{ A4 ,OFF ,OFF ,OFF ,OFF , ON ,OFF ,OFF , ON ,OFF ,OFF , OFF}, // nebenGelb
{ A5 ,OFF ,OFF ,OFF ,OFF ,OFF , ON , ON ,OFF ,OFF ,OFF , OFF}, // nebenGruen
{ 4 , ON ,OFF ,OFF ,OFF ,OFF ,OFF ,OFF ,OFF ,OFF ,OFF , OFF}, // nebenGruenFGruen
{ 0 ,20 , 2 , 2 , 3 , 2 , 10 , 2 , 2 , 3 , 2 , 60} // zeitDauer
};
// Die folgenden beiden Zeilen berechnen aus der Tabelle ( Spalten+Zeilenanzahl ) die
// Zahl der Ampelphasen ( Spaltenzahl-1) , und die Zahl der Leds ( Zeilenzahl - 1 )
const byte PhasenAnzahl = sizeof(AmpelTabelle[0])-2;
const byte PhasenAnzahl2 = sizeof(AmpelTabelle[0])-1;
const byte LedAnzahl = sizeof(AmpelTabelle)/sizeof(AmpelTabelle[0]) -1;
const byte zeitDauerIx = LedAnzahl; // Index für die Zeit ist direkt hinter den Leds
byte ampelPhase; // läuft von 1 .. PhasenAnzahl (Index 0 ist die Pinnr )
// Einrichten der notwendigen MoBaTools Objekte:
EggTimer AmpelTimer;
SoftLed AmpelLed[LedAnzahl];
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ENDE AMPELSTEUERUNG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
void setup() {
//vvvvvvvvvvvvvvvvvvvvvvvvv BEGIN WECHSELBLINKER vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Initiierungen Wechselblinker
// Pins einrichten
pinMode( belegtPin, INPUT_PULLUP ); // Mit einem Schalter nach GND wird Blinker eingeschaltet
LampeLinks.attach( linksPin ); // Zuordnung der Softleds zu den Ausgangspins
LampeRechts.attach( rechtsPin );
LampeLinks.riseTime( FADEZEIT ); // Überblendzeit einstellen
LampeRechts.riseTime( FADEZEIT );
wBlinkerZustand = 1; // Startzustand des Blinkautomaten.
//^^^^^^^^^^^^^^^^^^^^^^^^ ENDE WECHSELBLINKER ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//vvvvvvvvvvvvvvvvvvvvvvvvv BEGIN Nacht vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Initiierungen Wechselblinker
// Pins einrichten
pinMode( belegtPin2, INPUT_PULLUP ); // Mit einem Schalter nach GND wird Blinker eingeschaltet
LampeAmpelGelbNeben.attach( NebenGelbPin ); // Zuordnung der Softleds zu den Ausgangspins
// LampeRechts.attach( rechtsPin );
LampeAmpelGelbNeben.riseTime( FADEZEIT ); // Überblendzeit einstellen
// LampeRechts.riseTime( FADEZEIT );
wBlinkerZustand2 = 1; // Startzustand des Blinkautomaten.
//^^^^^^^^^^^^^^^^^^^^^^^^ ENDE WECHSELBLINKER ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//vvvvvvvvvvvvvvvvvvvvvvvv BEGIN AMPELSTEUERUNG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Softled Objekte initiieren
for ( byte ledIx = 0; ledIx < LedAnzahl; ledIx ++ ) {
AmpelLed[ledIx].attach( AmpelTabelle[ledIx][0] );
AmpelLed[ledIx].riseTime( 200 );
}
ampelPhase = 1; // Das System startet mit freier Fahrt auf der Hauptstraße
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ENDE AMPELSTEUERUNG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//----------------------------------------------------------------------
}
void loop() {
//vvvvvvvvvvvvvvvvvvvvvvvvv BEGIN WECHSELBLINKER vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Wechselblinkerautomat
// Belegtzusatnd abfragen. Das machen wir hier jetzterstmal ganz einfach: Letztendlich
// schalten wir mit dem Eingang einfach den Wechselblinker ein und aus.
// Im realen Praxisfall könnte hier gegebenenfalls auch ein komplexere Logik stehen, um zu
// erkennen, ob ein Zug sich nähert oder den Bü wieder verlassen hat.
//if ( not EntPrellung.running() ) {
// EntPrellung.setTime( PRELLZEIT );
bueBelegt = not digitalRead( belegtPin ); // ein LOW-Level am Eingangspin schaltet den Blinker ein
//}
switch ( wBlinkerZustand ) {
// je nach Wert der Variable wBlinkerZustand, wird die entsprechende 'case' Zeile angesprungen
case 1: // Grundzustand, abfragen, ob sich ein Zug nähert ( z.B. über einen Belegtmelder )
if ( bueBelegt ) {
// Der Zug ist in den Belegtabschnitt vor der Schranke eingefahren
// beide Lampen einschalten
LampeLinks.on();
LampeRechts.on();
// Stoppuhr mit der Zeit die beide Lampen an sein sollen vorbelegen
StoppUhr.setTime( STARTZEIT ); // StoppUhr 'aufziehen'
// in den 2. Zustand wechseln
wBlinkerZustand = 2; // Ab dem nächsten loop-Durchlauf werden nur die Anweisungen des case 2 ausgeführt
}
break; // Ende der Anweisungen für den 1. Zustand
//-------------------------------------------------------------------------
case 2: // beide Lampen ein, wir fragen ab, ob die Stoppuhr abgelaufen ist.
if ( not StoppUhr.running() ) {
// Die Stoppuhr läuft nicht mehr, die Zeit ist um
LampeRechts.off();
StoppUhr.setTime( BLINKZEIT ); // Stoppuhr 'aufziehen'
// in den 3. Zustand wechseln
wBlinkerZustand = 3; // Ab dem nächsten loop-Durchlauf werden nur die Anweisungen des case 3 ausgeführt
}
break; // Ende der Anweisungen für den 2. Zustand
//-------------------------------------------------------------------------
case 3: // linke Lampen ist an, rechte aus: wir fragen ab, ob die Stoppuhr abgelaufen ist.
if ( not StoppUhr.running() ) {
// Die Stoppuhr läuft nicht mehr, die Zeit ist um, Lampen umschalten
LampeLinks.off();
// Im 3. Zustand müssen wir auch noch abfragen, ob der Zug den Belegtabschnitt verlassen hat
if ( not bueBelegt ) {
// Der Zug ist komplett über den Bü gefahren, die Straße ist wieder frei
// Beide Lampen sind aus!
// wieder in den 1. Zustand wechseln. Eine Zeitüberwachung wird nicht gebraucht
wBlinkerZustand = 1; // Ab dem nächsten loop-Durchlauf werden nur die Anweisungen des case 1 ausgeführt
} else {
LampeRechts.on();
StoppUhr.setTime( BLINKZEIT ); // Stoppuhr 'aufziehen'
// wieder in den 4. Zustand wechseln
wBlinkerZustand = 4; // Ab dem nächsten loop-Durchlauf werden nur die Anweisungen des case 3 ausgeführt
}
}
break; // Ende der Anweisungen für den 3. Zustand
//-------------------------------------------------------------------------
case 4: // linke Lampen ist aus, rechte an: wir fragen ab, ob die Stoppuhr abgelaufen ist.
if ( not StoppUhr.running() ) {
// Die Stoppuhr läuft nicht mehr, die Zeit ist um, Lampen umschalten
LampeRechts.off();
// Im 4. Zustand müssen wir auch noch abfragen, ob der Zug den Belegtabschnitt verlassen hat
if ( not bueBelegt ) {
// Der Zug ist komplett über den Bü gefahren, die Straße ist wieder frei
// Beide Lampen sind aus!
// wieder in den 1. Zustand wechseln. Eine Zeitüberwachung wird nicht gebraucht
wBlinkerZustand = 1; // Ab dem nächsten loop-Durchlauf werden nur die Anweisungen des case 1 ausgeführt
} else {
LampeLinks.on();
StoppUhr.setTime( BLINKZEIT ); // Stoppuhr 'aufziehen'
// wieder in den 3. Zustand wechseln
wBlinkerZustand = 3; // Ab dem nächsten loop-Durchlauf werden nur die Anweisungen des case 3 ausgeführt
}
}
break; // Ende der Anweisungen für den 4. Zustand
} // Ende des switch-Blockes
//^^^^^^^^^^^^^^^^^^^^^^^^ ENDE WECHSELBLINKER ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//vvvvvvvvvvvvvvvvvvvvvvvv BEGIN AMPELSTEUERUNG Tag / Nacht vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
//if ( not EntPrellung.running() ) {
// EntPrellung.setTime( PRELLZEIT );
bueBelegt2 = not digitalRead( belegtPin2 ); // ein LOW-Level am Eingangspin schaltet den Nachtmodus ein
switch ( wBlinkerZustand2 ) {
case 1: // NachtModus
if ( bueBelegt2 ) {
ampelPhase = 12; // In Array SPalte alles OFF setzen
for ( byte ledIx = 0; ledIx < LedAnzahl; ledIx ++ ) {
AmpelLed[ledIx].write( AmpelTabelle[ledIx][ampelPhase] ); // Ampel-LEDs ausschalten
}
if ( not StoppUhr2.running() ) {
LampeAmpelGelbNeben.on();
StoppUhr2.setTime( BLINKZEIT2 ); // Stoppuhr 'aufziehen' für GelbLicht Nebenstrasse
LampeAmpelGelbNeben.off();
wBlinkerZustand2 = 1;
}
}
else
wBlinkerZustand2 = 2;
break;
case 2: // TagModus
// Tabellengesteuerte Ampel
if ( not bueBelegt2 ) {
LampeAmpelGelbNeben.off();
if( !AmpelTimer.running() ) {
// Zeit abgelaufen
// Ampeln schalten
for ( byte ledIx = 0; ledIx < LedAnzahl; ledIx ++ ) {
AmpelLed[ledIx].write( AmpelTabelle[ledIx][ampelPhase] );
}
// Zeit setzen ( da der Timer in ms rechnet, müssen die Tabellenwerte mit 1000
// multipliziert werden )
AmpelTimer.setTime( AmpelTabelle[zeitDauerIx][ampelPhase] * 1000 );
// Merker weiterrückem
if( ampelPhase >= PhasenAnzahl ) {
// Tabellenende erreicht
ampelPhase = 1;
} else {
ampelPhase++;
}
wBlinkerZustand2 = 2;
}
wBlinkerZustand2 = 1;
}
break;
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ENDE AMPELSTEUERUNG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
}