/* Schrankensteuerung mit Servo und DCC-Ansteuerung V0.6 30.11.2015
* Die Schrankensteuerung benötigt die 'MobaTools' - library ab der Version 0.6
* und die NmraDcc Lib (Standard-Version ohne Timer 0 Modifikation)
*
* V0.6: Kleine Korrekturen und Verbesserungen:
* - Beim Beschreiben der CVs mit den Default-Werten werden nun für jeden der beiden Servos die passenden Werte geschrieben
* - bei der Justage den Befehl readMicroseconds() durch read() ersetzt
* - bei notifyDccAccState einen DB_PRINT Befehl eingefügt, der den Inhalt der vier CVs im Debug-Mode ausgibt
*
* V0.5: Speicherung der Position ZU und Position AUF in CVs
* Der Zustand (offen/geschlossen) in CV 46
* Die Position ZU in CV 47 + 48
* Die Position AUF in CV 49 + 50
* Lok-Adresse für POM-Programmierung eines Zubehördecoders in CV 44 und 45
*
* V0.4: Umstellung von Ansteuerung auf Tastendruck auf DCC-Ansteuerung. Der Schranke wird eine DCC-Zubehör-
* Adresse (Weichendecoder) zuordnet, 'gerade' oder 'grün' schließen die Schranke, 'abzweig' oder 'rot' öffnen sie
* Die Justierung erfolgt zunächst weiterhin über die beiden separaten Taster
*
* V0.3: Mit Justierung der Endlagen und Speichern der Werte im EEPROM, vorbereitet für 4 Schrankenbäume.
* Die gesamte Bewegungskontrolle der Schrankenbäume ist in ein eigenes Unterprogramm ausgelagert.
* Dies erleichtert die Umstellung auf einen anderen Antrieb, z.b. mit einem Schrittmotor.
* Die Endlagenjustierung ist ebenfalls in diesem UP enthalten und kann jederzeit während des Betriebes vorgenommen
* werden. Pro Schranke wird ein Taster benötigt. Wird während der Schrankenbewegung der Taster gedrückt und
* gehalten, so läuft die Schranke verlangsamt weiter, bis die Taste losgelassen wird. Das ist dann die neue
* Endlage. Dies funktioniert sowohl beim Öffnen, als auch beim Schliessen der Schranke.
*
* V0.2: 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, über eine DCC-Zubehör-Adresse
* 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.
*
* Verwendete Anschlüsse:
* Pin2: DCC-Eingang
* Pin4: ACK-Ausgang
* Pin3 + Pin11: LED-Ausgänge
* Pin5 + Pin6: Servo-Ausgänge
* Pin9 + Pin10: Tastereingänge zum Justieren der Servos
* Pin7: Ausgang für die Ansteuerung der Glocke
*
*
*/
#include <MobaTools.h>
#include <NmraDcc.h>
#define DCC_DECODER_VERSION_ID 5 // Versions-ID zur Speicherung in CV 7
#define DCC_ADDR 50 // Zubehör-Adresse, Weichendecoder
#define CV_DCC_POM_PROG 44 // CV 44 beinhaltet LSB der Digitaladresse, unter der der Decoder auf dem Hauptgleis programmiert werden kann (POM)
#define DEBUG ; // Wenn dieser Wert gesetzt ist, werden Debug ausgaben auf dem ser. Monitor ausgegeben
#define SCHRANKENZAHL 2 // Zahl der Schrankenbäume
// gegebenenfalls anzupassende Werte (weitere Werte können im Abschnitt 'Portzuordnungen und Konstante' angepasst werden)
#define PULS_ZU1 50 // Initiale Winkel für die Endpositionen. Die Werte können über die Justierung
#define PULS_AUF1 100 // angepasst werden. Die Drehrichung kann durch Vertauschen der Werte für ZU / AUF
#define PULS_ZU2 50 // umgedreht werden. Dies ist über die Justierung NICHT anpassbar.
#define PULS_AUF2 100
#define GLOCKE_ZYK 1000 // Glockenzyklus und Impulslänge muss an das angeschlossene Soundmodul
#define GLOCKE_PULS 200 // angepasst werden.
////////////////////////////////////////////////////////////////////////
//////////////// Portzuordnungen und Konstante /////////////////////////
// 1. Einschaltlogik --------------------------------------------------
const byte DccInputP = 2; // IRQ-Pin 2 des Arduino
const byte DccAckPin = 4; // Ausgang für das ACK
// 2. Ablaufsteuerung --------------------------------------------------
const byte ServoPort[SCHRANKENZAHL] = { 5, 6 };
const byte schrTempo[SCHRANKENZAHL] = { 5, 4 };
const int schrVerzZU[SCHRANKENZAHL] = { 10, 500 }; // Verzögerung für die Schrankenbewegung (ms)
// damit laufen die Schranken nicht exakt gleichzeitig los.
// bei 4 Schrankenbäumen kann dies genutzt werden, um die in Auto-Fahrtrichtung
// hinteren Schranken später schliessen zu lassen
const int vorlaufZu = 6000; // Vorlaufzeit: Glocke und Wechselblinker aktiv, Schranke noch ruhend
const int nachlaufZu = 2000; // Nachlaufzeit: Schranke zu, Glocke nochaktiv
const byte Justage1P = 9; // Taster zum Justieren von Servo1
const byte Justage2P = 10; // Taster zum Justieren von Servo2
// 3. Glocke -------------------------------------------------------------
const byte glockeP = 7; // Impulsausgang zur Ansteuerung einer Glocke
const int glZykl = GLOCKE_ZYK; // Glockenrythmus
const int glImp = GLOCKE_PULS; // Impulslänge am Glockenausgang
// 4. Wechselblinker ----------------------------------------------------
const byte led1P = 3; // Ausgänge für den Wechselblinker ( Ports müssen PWM-fähig sein )
const byte led2P = 11;
const int wbZykl = 1100; // Zykluszeit des Wechselblinkers
const int wbSoft = 300; // Auf/Abblendzeit der Lampen
// sonst. ----------------------------------------------------------------
////////////////////// globale Variable //////////////////////////////////////
// 1. Einschaltlogik ----------------------------------------------------
bool schrankeSchliessen; // Wird über DCC Zubehördecoder angesteuert
NmraDcc DCC;
DCC_MSG Packet ;
// 2. Ablaufsteuerung ---------------------------------------------------
// Aufteilung der CV-Variablen (Standardadressen werden in der NmraDcc.h definiert)
// CV-Adresse für die Speicherung des BÜ-Zustandes
const word CvBueZustand = 46; // CV 46, Werte: 50 = offen, 54 = geschlossen
// CV-Adressen für die Speicherung der Endlagen (Winkel des Servos)
const word CvPosZu = 47; // CV 47, Werte zwischen 0 und 180, 255 gilt als ungültig (leeres EEPROM)
const word CvPosAuf = CvPosZu + (SCHRANKENZAHL);
static uint8_t FactoryDefaultCVIndex;
// Variable im RAM
int positionZu[SCHRANKENZAHL] = {PULS_ZU1, PULS_ZU2}; // Servopostionen, über Justiervorgang einstellbar
int positionAuf[SCHRANKENZAHL] = {PULS_AUF1, PULS_AUF2}; // Servopostionen, über Justiervorgang einstellbar
byte justageAktiv[SCHRANKENZAHL];
Servo8 Schranke[SCHRANKENZAHL]; // Für die Schrankenservos
EggTimer SchrankeT[SCHRANKENZAHL]; // Schrankenspezifische Zeiten
EggTimer VorlaufT;
// Aufrufparameter für das Unterprogramm 'Schrankenbewegung':
#define SB_INIT 0 // Servoansteuerung initiieren
#define SB_START_AUF 1 // Schranke öffnen starten
#define SB_START_ZU 2 // Schranke schliessen starten
#define SB_ENDE 3 // Bewegung überwachen und Ende erkennen. Ggfs. Endlagen justieren
// Zustand, in dem sich die Ablaufsteuerung gerade befindet
byte bueZustand; // Aktueller Zustand
byte bueVorZustand; // vorheriger Zustand des Bue ( noch nicht verwendet)
#define OFFEN 50
#define VORLAUF_ZU 51 // Wechselblinker und Glocke, aber noch keine Bewegung
#define SCHRANKE_SCHLIESST 52 // Bewegung Schrankenbaum zu
#define NACHLAUF_ZU 53 // Beide Schrankenbäume in Endpos, Glocke läutet noch.
#define GESCHLOSSEN 54 // Schranke geschlossen
#define SCHRANKE_OEFFNET 56 // Bewegung Schrankenbaum auf
// CV Speicher Struktur
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
// CV Default-Werte
CVPair FactoryDefaultCVs [] =
{
{CV_ACCESSORY_DECODER_ADDRESS_LSB, DCC_ADDR},
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
{CV_VERSION_ID, DCC_DECODER_VERSION_ID},
{CV_MANUFACTURER_ID, MAN_ID_DIY},
{CV_29_CONFIG, 192},
{CV_DCC_POM_PROG, DCC_ADDR},
{CV_DCC_POM_PROG + 1, 0 },
{CvBueZustand, OFFEN},
{CvPosZu, PULS_ZU1},
{CvPosZu + 1, PULS_ZU2},
{CvPosAuf, PULS_AUF1},
{CvPosAuf + 1, PULS_AUF2},
};
// 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
#ifdef DEBUG
#define DB_PRINT( ... ) sprintf( dbgbuf,"Dbg: " __VA_ARGS__ ) ; Serial.println( dbgbuf )
byte debug;
char dbgbuf[80];
#else
#define DB_PRINT ;
#endif
//###################### Ende der Definitionen ##############################
//###########################################################################
void setup()
{
byte i;
#ifdef DEBUG
Serial.begin(38400); //Debugging
Serial.println( "start of Program" );
#endif
// 1. Einschaltlogik ----------------------------------------------------
DCC.pin(digitalPinToInterrupt(DccInputP), DccInputP, 1); // Dcc-Signal mit Pullup
DCC.init( MAN_ID_DIY, DCC_DECODER_VERSION_ID,
FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_DCC_ACCESSORY_DECODER,
CV_DCC_POM_PROG ); // Zubehördecoder, 11bit-Adresse
/////////////////////////////////////
// DCC-Adresse setzen
if ( DCC.getCV( 1 ) == 255 ) { // wenn keine gültige Adresse gespeichert ist
DCC.setCV( 1, DCC_ADDR % 256 );
DCC.setCV( 9, DCC_ADDR / 256 );
}
if ( DCC.getCV( CV_DCC_POM_PROG ) == 255 ) { // wenn keine gültige Adresse gespeichert ist
DCC.setCV( CV_DCC_POM_PROG, DCC_ADDR );
DCC.setCV( CV_DCC_POM_PROG + 1, 0 );
}
#ifdef DEBUG
// CV-Werte ausgeben
DB_PRINT( "CV1:%d, CV9:%d, CV29:%d, CV7=%d, CV8=%d", DCC.getCV(1), DCC.getCV(9), DCC.getCV(29), DCC.getCV(7), DCC.getCV(8) );
#endif
pinMode(DccAckPin, OUTPUT);
digitalWrite( DccAckPin, LOW ); // Der ACK Pin wird auf LOW gesetzt,damit ein definierter Zustand vorhanden ist
// 2. Ablaufsteuerung ---------------------------------------------------
pinMode(Justage1P, INPUT_PULLUP); // Zur Justage der Endlage Schranke 1
pinMode(Justage2P, INPUT_PULLUP); // dto, Schranke 2
////////////////////// Grundinitiierung ////////////////////////////
// Testen ob gültige Werte in den CVs abgelegt sind. Wenn nicht, Grundinitiierung mit default-Werten
// Initiiert wird auch, wenn während des Programmstarts einer der Justageschalter betätigt ist
if ( DCC.getCV( CvPosZu ) == 255 ||
digitalRead( Justage1P) == LOW ||
digitalRead( Justage2P) == LOW ) {
// Grundinitiierung
DB_PRINT( "CV Initiierung" );
// Endlagen auf Default-Werte setzen
DCC.setCV( CvPosZu, PULS_ZU1 );
DCC.setCV( CvPosAuf, PULS_AUF1 );
DCC.setCV( CvPosZu + 1, PULS_ZU2 );
DCC.setCV( CvPosAuf + 1, PULS_AUF2 );
DCC.setCV(CvBueZustand, OFFEN);
}
/////////////////////////////////////
// Positionswerte aus CV lesen
for ( i = 0; i < SCHRANKENZAHL; i++ ) {
positionZu[i] = DCC.getCV( CvPosZu + i );
positionAuf[i] = DCC.getCV( CvPosAuf + i );
DB_PRINT( "Setup // Schr.%d - PosZu=%d PosAuf=%d", i, positionZu[i], positionAuf[i] );
DB_PRINT( "Zustand: %d", DCC.getCV( CvBueZustand ));
}
#ifdef DEBUG
// CV-Werte ausgeben
DB_PRINT( "CV1:%d, CV29:%d, CV7:%d, CV8:%d", DCC.getCV(1), DCC.getCV(29), DCC.getCV(7), DCC.getCV(8) );
#endif
/////////////////////////////////////////
/////// Antriebs-Initiierung ////////////
Schrankenbewegung( SB_INIT );
// Anfangsstellung der Schranke setzen
bueZustand = DCC.getCV( CvBueZustand );
if ( bueZustand == GESCHLOSSEN ) {
Schrankenbewegung( SB_START_ZU );
while ( !Schrankenbewegung( SB_ENDE ) );
wbAktiv = true;
} else {
Schrankenbewegung( SB_START_AUF );
while ( !Schrankenbewegung( SB_ENDE ) );
}
// 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. -----------------------------------------------------------------
} // End Setup
//############################### ENDE SETUP ##################################
//#############################################################################
void loop()
{
// 1. Einschaltlogik ----------------------------------------------------
////////////// Eingang zur Steuerung des Bahnübergangs /////////////////
DCC.process();
// schrankeSchliessen wird in notifyDccFunc gesetzt
// 2. Ablaufsteuerung ---------------------------------------------------
justageAktiv[0] = ( digitalRead( Justage1P ) == LOW ) ;
justageAktiv[1] = ( digitalRead( Justage2P ) == LOW ) ;
//////////// 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. Diesen Befehl auskommentieren wenn die Glocke erst
// mit der Schrankenbewegung starten soll
VorlaufT.setTime( vorlaufZu );
bueZustand = VORLAUF_ZU;
DB_PRINT("Zustandswechsel: %d", bueZustand );
}
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 ;-)
wbAktiv = true;
Schrankenbewegung(SB_START_ZU); // Überwachung initiieren
bueZustand = SCHRANKE_SCHLIESST;
}
break; //----------------------------------------------------------
case SCHRANKE_SCHLIESST:
// Schrankenbaum schliesst sich.
if ( ( Schrankenbewegung( SB_ENDE ) ) ) {
// 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;
DCC.setCV( CvBueZustand, bueZustand );
}
break; //----------------------------------------------------------
case GESCHLOSSEN:
// Schranke ist zu, warten auf Eingang
if ( schrankeSchliessen == false ) {
// Schranke soll sich öffnen, Bewegung einleiten
Schrankenbewegung(SB_START_AUF); // Überwachung initiieren
wbAktiv = false; // Wechselblinker ausschalten
bueZustand = SCHRANKE_OEFFNET;
}
break; //----------------------------------------------------------
case SCHRANKE_OEFFNET:
// Schrankenbaum öffnet sich, warten bis offen
if ( schrankeSchliessen == true ) {
// Notfall: beim Öffnen der Schranke kommt wieder der Befehl Schranke schliessen
bueZustand = VORLAUF_ZU; // Da der Vorlauftimer nicht läuft, schliesst die Schranke sofort
}
if ( Schrankenbewegung( SB_ENDE ) ) {
// beide Schrankenbäume haben ihre Endposition erreicht
bueZustand = OFFEN;
DCC.setCV( CvBueZustand, bueZustand );
}
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 ////////////////////////
/* Prüfen, ob die default CV-Werte benötigt werden */
if ( FactoryDefaultCVIndex && DCC.isSetCVReady())
{
FactoryDefaultCVIndex--; // Zunächst verringern, da es zu Beginn die Größe des Arrays ist
DCC.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV,
FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
}
} // End Loop
//#################### ENDE LOOP ##################################################
//#################################################################################
byte Schrankenbewegung( byte mode ) {
// Bewegungsvorgang der Schranken überwachen, gegebenenfalls auch die Endlage
// justieren. Das Unterprogramm wird im Loop während der Bewegung zyklisch aufgerufen
// Der Funktionswert ist 'true', wenn die Bewegung aller Schranken abgeschlossen ist,
// sonst immer 'false'
// mode:SB_INIT Grundinitiierung
// SB_START_ZU Schliessen der Schranken einleiten
// SB_START_AUF Öffnen der Schranken einleiten
// SB_ENDE Bewegung überwachen, meldet 'true' wenn alle Bewegungen abgeschlossen
// sind
// -------------------------------------------------------------------------
//
static enum { WAIT, NORMAL, JUSTAGE_AKTIV, JUSTAGE_ENDE, WIPPEN } ssZustand[SCHRANKENZAHL] ;
static int startPos[SCHRANKENZAHL] ; // Position der Schranke zu Bewegungsbeginn
static enum { AUF, ZU } richtung;
byte bewegung = 0, sn;
int tmp;
for ( sn = 0; sn < SCHRANKENZAHL; sn++ ) {
// für alle Schranken durchlaufen
switch ( mode ) {
case SB_INIT: // Initiierung der Schranken
Schranke[sn].attach(ServoPort[sn], 1); //Servos an Pin 5 / 6 mit AutoOff
Schranke[sn].setSpeed( schrTempo[sn] );
bewegung = 1;
break; //---------------------------------------------
case SB_START_ZU: // Schliessen der Schranken einleiten
richtung = ZU;
Schranke[sn].setSpeed( schrTempo[sn]);
SchrankeT[sn].setTime( schrVerzZU[sn] ); //Wartezeit bis Schrankenbewegung
DB_PRINT( "Start Wartezeit %d mit %d ms", sn, schrVerzZU[sn] );
ssZustand[sn] = WAIT;
bewegung = 1;
break; //---------------------------------------------
case SB_START_AUF: // Öffnen der Schranken einleiten
richtung = AUF;
Schranke[sn].setSpeed( schrTempo[sn]);
Schranke[sn].write( DCC.getCV( CvPosAuf + sn ));
ssZustand[sn] = NORMAL;
startPos[sn] = Schranke[sn].read();
DB_PRINT( "Schranke %d, Position: %d, Richtung= %d", sn, startPos[sn], richtung );
bewegung = 1;
break; //---------------------------------------------
case SB_ENDE: // Bewegung überwachen, auf Ende prüfen
// Schrankenbewegung
switch ( ssZustand[sn] ) {
case WAIT: // Verzögerungszeit bis zum Schrankenstart abwarten
// Wird nur beim Schliessen der Schranke durchlaufen
if ( SchrankeT[sn].running() == false ) {
// Zeit abgelaufen, Bewegung starten
Schranke[sn].write( DCC.getCV( CvPosZu + sn ));
ssZustand[sn] = NORMAL;
startPos[sn] = Schranke[sn].read();
DB_PRINT( "Schranke %d, Position: %d, Richtung= %d", sn, startPos[sn], richtung );
ssZustand[sn] = NORMAL;
}
bewegung = 1;
break; //......................................
case NORMAL:
bewegung += Schranke[sn].moving();
if ( justageAktiv[sn] ) {
// Justageschalter betätigt, Speed auf 1, Servobewegung um 10% weitersetzen,
// sodass die neue Endposition auch hinter der alten liegen kann.
Schranke[sn].setSpeed( 1 );
ssZustand[sn] = JUSTAGE_AKTIV;
if ( richtung == ZU ) tmp = (positionZu[sn] * 3 - positionAuf[sn]) / 2;
else tmp = (positionAuf[sn] * 3 - positionZu[sn]) / 2;
DB_PRINT( "Schranke %d, Justage Endpso=%d", sn, tmp );
Schranke[sn].write( tmp );
}
// if ( bewegung < 5 ) ssZustand[SchrNr] = WIPPEN;
break; // .....................................
case WIPPEN:
// to be defined ;-)
break; //......................................
case JUSTAGE_AKTIV:
bewegung += 1; // keine Endemeldung während der Justage
if ( !justageAktiv[sn] ) {
// Justageschalter wurde wieder losgelassen. Momentane Servo-Position
// als neuen Endpunkt speichern und Servo anhalten
tmp = Schranke[sn].read();
DB_PRINT( "Schranke %d, Justage ende, Pos = %d", sn, tmp );
Schranke[sn].write( tmp );
if ( richtung == ZU ) {
positionZu[sn] = tmp;
DCC.setCV( CvPosZu + sn, tmp );
} else {
positionAuf[sn] = tmp;
DCC.setCV( CvPosAuf + sn, tmp );
}
ssZustand[sn] = JUSTAGE_ENDE;
}
break; //......................................
case JUSTAGE_ENDE:
bewegung += Schranke[sn].moving();
break; //......................................
} // ..... Ende switch 'Schrankenzustand' ......
break; //---------------------------------------------
default:
// falscher Programmaufruf, keine Reaktion
;
} // --- Ende Switch 'mode' --------
} // ........Ende forschleife der Schranken........
if ( bewegung == 0 ) DB_PRINT( "Endpositionen erreicht");
return ( bewegung == 0 );
}
//###################################################################################
//###################### DCC - Funktionen ###########################################
// Auswertung des Zubehördecoders
void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State)
{
if (Addr == DCC.getAddr()) {
schrankeSchliessen = ((OutputAddr & 0x1) != 0);
DB_PRINT( " Adresse: %u, Status %x", Addr, schrankeSchliessen );
DB_PRINT( " CV 47: %d, CV 48: %d, CV 49: %d, CV 50: %d", DCC.getCV(47),DCC.getCV(48),DCC.getCV(49),DCC.getCV(50) );
}
}
// Diese Funktion wird von der Nmra-Lib aufgerufen, wenn ein DCC ACK gesendet werden muss
// z.B. beim Programmieren des Decoders über die Digitalzentrale
void notifyCVAck(void)
{
digitalWrite( DccAckPin, HIGH );
delay( 6 );
digitalWrite( DccAckPin, LOW );
}
// Ein Decoder Reset wird angefordert
void notifyCVResetFactoryDefault()
{
// Setze FactoryDefaultCVIndex ungleich Null und auf die Anzahl der CVs, die zurückgesetzt werden müssen
// damit die Funktion im loop erkennt, dass ein Reset durchzuführen ist
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs) / sizeof(CVPair);
}