/* Schrankensteuerung mit Servo V0.2 15.9.2015
* Die Schrankensteuerung benötigt die 'MobaTools' - library
*
* Version mit erweiterter Ablaufsteuerung: mit Vorlauf für Glocke und
* Wechselblinker.
*
* Für eine bessere Übersicht und Erweiterbarkeit ist das Programm logisch in einzelen Blöcke aufgeteilt.
* Diese Blöcke sind im loop hintereinander angeordnet, arbeiten aber weitgehend unabhängig. Damit dies
* möglich ist, dürfen innerhalb der Blöcke keine Warteschleifen/Delays werwendet werden, die den
* Programmablauf temporär anhalten.
*
* 1.Block: Einschaltlogik.
* Hier wird bestimmt, ob die Schranke geschlossen oder geöffnet werden soll. Derzeit ist dies einfach ein
* Schaltereingang, der abgefragt wird. Soll die Schranke später automatisch durch die Züge gesteuert werden
* muss dies in diesem Block eingerichtet werden. Ergebnis der Einschaltlogik ist ein Flag 'schrankeSchliessen'
*
* 2. Block Ablaufsteuerung Schrankenlogik
* zentraler Block, der den Ablauf des Schrankenschliessens bzw -öffnens steuert. Der Block agiert abhängig
* von dem Flag 'schrankeSchliessen' und dem momentanen Zustand der Schrankenlogik
* Hier werden auch die Flags gesetzt, mit denen die Glocke (glAktiv) und der Wechselblinker (wbAktiv)
* ein- bzw ausgeschaltet werden.
*
* 3. Block Ansteuerung der Glocke
* abhängig vom Flag 'glAktiv' wird der Impulsausgang für die Glocke ein- bzw ausgeschaltet. Je nach ange-
* schlossenem Audio-Modul muss gegebenenfalls auch darauf geachtet werden, dass der letzte Glockenschlag
* nicht abgeschnitten wird.
*
* 4. Block Wechselblinker
* abhängig vom Flag 'wbAktiv' wird der Wechselblinker ein- bw ausgeschaltet. Beim Einschalten sind kurz beide
* Blinker gleichzeitig an, bevor sie dann abwechselnd blinken.
*
* 5.Block Endlagenjustierung
* noch nicht enthalten ;-)
*
*
*
*/
#include <Eggtimer.h> // Timer nach dem Prinzip der Eieruhr (Aufziehen und läuft dann ab)
#include <Mobatools.h> // Lib für Hardwareerzeugte Servo-Pulse an Port 9,10
#define SCHRANKENZAHL 2 // Zahl der Schrankenbäume ( derzeit nur 2 erlaubt)
//////////////// Portzuordnungen und Konstante /////////////////////////
// 1. Einschaltlogik --------------------------------------------------
const byte schrankeZuP = 7; // Pin HIGH bedeutet Schranke schliessen
// 2. Ablaufsteuerung --------------------------------------------------
const byte ServoPort[SCHRANKENZAHL] = {9,10}; // Nur Port 9 / 10 erlaubt
const byte schrTempo[SCHRANKENZAHL] = { 5,4 };
const int vorlaufZu = 6000; // Vorlaufzeit: Glocke und Wechselblinker aktiv, Schranke noch ruhend
const int nachlaufZu = 2000; // Nachlaufzeit: Schranke zu, Glocke nochaktiv
// 3. Glocke -------------------------------------------------------------
const byte glockeP = 4; // Impulsausgang zur Ansteuerung einer Glocke
const int glZykl = 2000; // Glockenrythmus
const int glImp = 50; // Impulslänge am Glockenausgang
// 4. Wechselblinker ----------------------------------------------------
const byte led1P = 6; // Ausgänge für den Wechselblinker ( Ports müssen PWM-fähig sein )
const byte led2P = 5;
const int wbZykl = 1100; // Zykluszeit des Wechselblinkers
const int wbSoft = 300; // Auf/Abblendzeit der Lampen
// sonst. ----------------------------------------------------------------
////////////////////// globale Variable //////////////////////////////////////
// 1. Einschaltlogik ----------------------------------------------------
bool schrankeSchliessen; // Wird derzeit nur durch einen einfachen Schalter gesteuert
// 2. Ablaufsteuerung ---------------------------------------------------
int positionZu[2] = {1040,1550}; // Servopostionen, über Justiervorgang einstellbar
int positionAuf[2] = {1900, 1450}; // Servopostionen, über Justiervorgang einstellbar
Servo2 Schranke[2]; // Für die Schrankenservos
EggTimer VorlaufT;
// Zustand, in dem sich die Ablaufsteuerung gerade befindet
byte bueZustand; // Aktueller Zustand
byte bueVorZustand; // vorheriger Zustand des Bue ( noch nicht verwendet)
#define OFFEN 0
#define VORLAUF_ZU 1 // Wechselblinker und Glocke, aber noch keine Bewegung
#define SCHRANKE_SCHLIESST 2 // Bewegung Schrankenbaum zu
#define NACHLAUF_ZU 3 // Beide Schrankenbäume in Endpos, Glocke läutet noch.
#define GESCHLOSSEN 4 // Schranke geschlossen
#define SCHRANKE_OEFFNET 6 // Bewegung Schrankenbaum auf
// 3. Glocke -------------------------------------------------------------
EggTimer glockeT;
byte glAktiv = false; // Flag ob Glocke aktiv ist
// 4. Wechselblinker ------------------------------------------------------
SoftLed Wblinker[2]; // 2 Leds für den Wechselblinker
EggTimer BlinkerT;
byte wbAktiv = false; // Flag ob Wechselblinker aktiv ist
byte ledState = LOW; // Status Wechselblinker
// Zustand Wechselblinker
byte wblZustand = 0;
#define WBL_AUS 0
#define WBL_START 1 // Beim Start sind kurz beide Lampen an
#define WBL_BLINKT 2
// sonst. -----------------------------------------------------------------
// für debugging
byte debug;
char buf[40];
//-------------- Ende der Definitionen -------------------------------------
void setup()
{
// 1. Einschaltlogik ----------------------------------------------------
pinMode(schrankeZuP, INPUT_PULLUP);
// 2. Ablaufsteuerung ---------------------------------------------------
/////// Servo-Initiierung //////////////////////
Schranke[0].attach(ServoPort[0]); //Servo an Pin 9
Schranke[1].attach(ServoPort[1]); //Servo an Pin 10
Schranke[0].setSpeed( schrTempo[0] );
Schranke[1].setSpeed( schrTempo[1] );
// 3. Glocke -------------------------------------------------------------
pinMode( glockeP, OUTPUT );
// 4. Wechselblinker ------------------------------------------------------
Wblinker[0].attach(led1P); // Portzuordnung für den WEchselblinker
Wblinker[1].attach(led2P);
Wblinker[0].riseTime(wbSoft); // Weiches Auf/Abblenden der Lampen
Wblinker[1].riseTime(wbSoft);
// sonst. -----------------------------------------------------------------
//Serial.begin(19200); //Debugging
} // End Setup
void loop()
{
// 1. Einschaltlogik ----------------------------------------------------
////////////// Eingang zur Steuerung des Bahnübergangs /////////////////
schrankeSchliessen = ( digitalRead( schrankeZuP) == HIGH );
// 2. Ablaufsteuerung ---------------------------------------------------
//////////// Ablaufsteuerung des Bue - Haupt-Zustandsautomat ///////////////////
switch ( bueZustand ) {
case OFFEN:
// Schranke ist geöffnet, warten auf Eingang
if ( schrankeSchliessen ) {
// Schranke soll sich schliessen, Glocke und Wechselblinker startet.
wbAktiv = true; // Wechselblinker einschalten
glAktiv = true; // Glocke einschalten.
VorlaufT.setTime( vorlaufZu );
bueZustand = VORLAUF_ZU;
}
break; //----------------------------------------------------------
case VORLAUF_ZU:
// Warten bis die Vorlaufzeit abgelaufen ist, dann die Schrankenbewegung starten
if ( !VorlaufT.running() ) {
// Vorlaufzeit abgelaufen, Schrankenbewegung starten.
// spätestens hier muss auch die Glocke aktiviert werden
glAktiv = true; // wurde sie schon aktivert, machts auch nichts ;-)
Schranke[0].write( positionZu[0]);
Schranke[1].write( positionZu[1] );
bueZustand = SCHRANKE_SCHLIESST;
}
break; //----------------------------------------------------------
case SCHRANKE_SCHLIESST:
// Schrankenbaum schliesst sich.
if ( Schranke[0].moving() == 0 &&
Schranke[1].moving() == 0 ) {
// beide Schrankenbäume haben ihre Endposition erreicht
VorlaufT.setTime( nachlaufZu );
bueZustand = NACHLAUF_ZU;
}
break; //----------------------------------------------------------
case NACHLAUF_ZU:
// Schrankenbaum geschlossen, kurzer Nachlauf für Glocke.
if ( !VorlaufT.running() ) {
glAktiv = false;
bueZustand = GESCHLOSSEN;
}
break; //----------------------------------------------------------
case GESCHLOSSEN:
// Schranke ist zu, warten auf Eingang
if ( schrankeSchliessen == false ) {
// Schranke soll sich öffnen, Bewegung einleiten
Schranke[0].setSpeed( schrTempo[0]);
Schranke[1].setSpeed( schrTempo[1]);
Schranke[0].write( positionAuf[0]);
Schranke[1].write( positionAuf[1]);
wbAktiv = false; // Wechselblinker ausschalten
bueZustand = SCHRANKE_OEFFNET;
}
break; //----------------------------------------------------------
case SCHRANKE_OEFFNET:
// Schrankenbaum öffnet sich, warten bis offen
if ( Schranke[0].moving() == 0 &&
Schranke[1].moving() == 0 ) {
// beide Schrankenbäume haben ihre Endposition erreicht
bueZustand = OFFEN;
}
break; //----------------------------------------------------------
} ////////////// Ende Zustandsautomat Bahnübergang /////////////////////
// 3. Glocke -------------------------------------------------------------
////////////////// Glockenimpuls erzeugen ////////////////////////////////
if ( glAktiv ) {
if ( !glockeT.running() ) {
// Glockentimer abgelaufen, Impuls erzeugen
if ( digitalRead( glockeP ) == HIGH ) {
// Port ist gesetzt, abschalten
digitalWrite( glockeP, LOW );
glockeT.setTime( glZykl - glImp );
} else {
// Port ist aus, einschalten
digitalWrite( glockeP, HIGH );
glockeT.setTime( glImp );
}
}
} else {
// Glocke inaktiv, Ausgang abschalten wenn Timer nicht mehr läuft
if ( !glockeT.running() ) {
// Die Timerabfrage stellt sicher, dass auch der letzte Impuls immer in
// voller Länge ausgegeben wird
digitalWrite( glockeP, LOW );
}
}
// 4. Wechselblinker ------------------------------------------------------
/////////////// Wechselblinker (Zustandsautomat ) //////////////////
switch (wblZustand) {
case WBL_AUS:
// Beide Lampen sind aus, warten auf einschalten
if ( wbAktiv ) {
// Beide Leds einschalten, Timer für gemeinsames Startleuchten
Wblinker[0].on();
Wblinker[1].on();
BlinkerT.setTime( wbSoft/2 );
wblZustand = WBL_START;
}
break;
case WBL_START:
// Startphase: Nach Zeitablauf erste Led wieder aus
if ( !BlinkerT.running() ) {
// Übergang zur normalen Blinkphase
ledState = HIGH;
Wblinker[1].off();
BlinkerT.setTime(wbSoft);
wblZustand = WBL_BLINKT;
}
break;
case WBL_BLINKT:
if ( !BlinkerT.running() ) {
BlinkerT.setTime(wbZykl/2);
if ( ledState == LOW ) {
Wblinker[0].on();
Wblinker[1].off();
ledState = HIGH;
} else {
ledState = LOW;
Wblinker[1].on();
Wblinker[0].off();
}
}
if ( !wbAktiv ) {
// Wechselblinker abschalten
Wblinker[0].off();
Wblinker[1].off();
wblZustand = WBL_AUS;
}
break;
} /////////// Ende switch Wechselblinker ////////////////////////
} // End Loop