Lego Eisenbahn Schranken mit Blinklicht und Weichen steuern

Erstmal Sorry,
Ich wurde angewiesen einen neuen Thread zu erstellen.
... fand den anderen halt passend ...

Ich bin neu hier und auch im Arduino Bereich.
Ich bin an etwas ähnlichem dran...
Bitte seid gnädig, ich schaffe damit erst seit 2 Wochen... und möchte eine Weichensteuerung (Wegesteuerung an Kreuzweiche mit 3 Taster) "läuft", sowie eine 1 Taster Schrankensteuerung (Servo hoch_runter) mit Blinkleds (Andreaskreuz) "hier hackts".
Im Start -> Schranke unten blinkt... "OK"...Taste 1x Schranke hoch blinkt aber noch... "n. OK" ... nach mehrmaligen drücken geht auch mal die LED aus nicht nachvollziehbar... dann teilweise Taste 1x Schranke runter, blinkt nicht -> noch mal Taste 1x Schranke bleibt unten aber blinkt nun auch...

#include <Servo.h>

Servo Weiche1;

Servo Weiche2;

Servo Weiche3;

Servo Weiche4;

Servo Schranke;

int Grad;

const int T_W = 53;

const int T_O = 52;

const int T_G1 = 51;

const byte T_S= 50;                 // Taster an Pin 1 und GND

const long interval = 500;               // Blinkintervall

const byte entprellzeit = 20;     // Zeitintervall der Tasterabfrage

boolean taster_gedrueckt = 0;     // Speichervariable für den Tasterzustand

boolean last_tasterzustand = 0;   // zur Erkennung der Flanke

// Leds

const int Pin_LED2_Rot = A0;         // LED Gerade an PIN A0 high

const int Pin_LED3_Gruen = A0;         // LED Gerade an PIN A0 low

const int Pin_LED6_Gruen = A1;         // LED Gerade an PIN A1 high

const int Pin_LED7_Rot = A1;         // LED Gerade an PIN A1 low

const int Pin_LED4_Rot = A2;         // LED Gerade an PIN A0 high

const int Pin_LED5_Gruen = A2;         // LED Gerade an PIN A0 low

const int Pin_LED8_Gruen = A3;         // LED Gerade an PIN A1 high

const int Pin_LED9_Rot = A3;         // LED Gerade an PIN A1 low

// Blinkleds

int ledState = LOW;                       // Led-Status

uint8_t TasterValue = 0;                  // Taster-Status

uint8_t BlinkValue = 0;                   // Blink-Status    

uint8_t status = 0;

unsigned long previousMillis = 0;        

//___________________VOID SETUP______________

void setup()

{

  pinMode(T_W, INPUT_PULLUP);

  pinMode(T_O, INPUT_PULLUP);

  pinMode(T_G1, INPUT_PULLUP);

  pinMode(T_S, INPUT_PULLUP);

 

// BlinkLed

 

  pinMode(LED_BUILTIN, OUTPUT);           // Led Pin13

// Leds definieren

  pinMode(Pin_LED3_Gruen, OUTPUT);  

  digitalWrite(Pin_LED3_Gruen, HIGH);    // Initialisieren der LED auf Grundstellung, LED Gerade leuchtet

  pinMode(Pin_LED2_Rot, OUTPUT);

  digitalWrite(Pin_LED2_Rot, LOW);     // Initialisieren der LED auf Grundstellung, LED Abzweig ist aus

  pinMode(Pin_LED5_Gruen, OUTPUT);  

  digitalWrite(Pin_LED5_Gruen, LOW);    // Initialisieren der LED auf Grundstellung, LED Gerade leuchtet

  pinMode(Pin_LED4_Rot, OUTPUT);

  digitalWrite(Pin_LED4_Rot, HIGH);     // Initialisieren der LED auf Grundstellung, LED Abzweig ist aus

  pinMode(Pin_LED6_Gruen, OUTPUT);  

  digitalWrite(Pin_LED6_Gruen, HIGH);    // Initialisieren der LED auf Grundstellung, LED Gerade leuchtet

  pinMode(Pin_LED7_Rot, OUTPUT);

  digitalWrite(Pin_LED7_Rot, LOW);     // Initialisieren der LED auf Grundstellung, LED Abzweig ist aus

  pinMode(Pin_LED8_Gruen, OUTPUT);  

  digitalWrite(Pin_LED8_Gruen, LOW);    // Initialisieren der LED auf Grundstellung, LED Gerade leuchtet

  pinMode(Pin_LED9_Rot, OUTPUT);

  digitalWrite(Pin_LED9_Rot, HIGH);     // Initialisieren der LED auf Grundstellung, LED Abzweig ist aus

}

//___________FUNKTION "schalten"________________

void schalten (Servo Weiche, int PIN, int Winkel)

{

  Weiche.attach(PIN);

  Weiche.write(Winkel);

  delay(200);

  Weiche.detach();

}

//___________FUNKTION "W1"___________________

void W1(int Stellung)                //0=gerade - 1=abbiegen

{  if(Stellung == 0)                  //Stellung gerade aus

  {

    Grad = 0;                         //Position in Grad      

  }

  if(Stellung == 1)                  //Stellung abbiegend

  {

    Grad = 90;                        //Position in Grad

  }

  schalten(Weiche1, 2, Grad);         //(Servo, Anschlusspin, Position)

}

//___________FUNKTION "W2"___________________

void W2(int Stellung)               //0=gerade - 1=abbiegen

{  if(Stellung == 0)                  //Stellung gerade aus

  {

    Grad = 0;                         //Position in Grad      

  }

    if(Stellung == 1)                  //Stellung abbiegend

  {

    Grad = 90;                        //Position in Grad

  }

  schalten(Weiche2, 3, Grad);         //(Servo, Anschlusspin, Position)

}

//___________FUNKTION "W3"___________________

void W3(int Stellung)                //0=gerade - 1=abbiegen

{  if(Stellung == 0)                  //Stellung gerade aus

  {

    Grad = 0;                         //Position in Grad      

  }

  if(Stellung == 1)                  //Stellung abbiegend

  {

    Grad = 90;                        //Position in Grad

  }

  schalten(Weiche3, 2, Grad);         //(Servo, Anschlusspin, Position)

}

//___________FUNKTION "W4"___________________

void W4(int Stellung)                //0=gerade - 1=abbiegen

{  if(Stellung == 0)                  //Stellung gerade aus

  {  

    Grad = 0;                         //Position in Grad      

  }

  if(Stellung == 1)                  //Stellung abbiegend

  {

    Grad = 90;                        //Position in Grad

  }

  schalten(Weiche4, 3, Grad);         //(Servo, Anschlusspin, Position)

}

  //___________FUNKTION "Schranke"___________________

void S(int Stellung)   //void S(int Stellung) //0=offen - 1=geschlossen

{              

  if(Stellung == 0)                  //Stellung gerade aus

  {

    Grad = 0;                         //Position in Grad      

  }

  if(Stellung == 1)                  //Stellung abbiegend

  {

    Grad = 90;                        //Position in Grad

  }

  schalten(Schranke, 4, Grad);        //(Servo, Anschlusspin, Position)

}

 

//_____________VOID LOOP______________________

void loop()

{

  while(digitalRead(T_W) == LOW)        //Start Weiche 1 zu 3

    {

      W1(1);

      digitalWrite(Pin_LED3_Gruen, HIGH);

      digitalWrite(Pin_LED8_Gruen, LOW);

      W2(0);

      digitalWrite(Pin_LED4_Rot, HIGH);

      digitalWrite(Pin_LED7_Rot, LOW);

    }

 

  while(digitalRead(T_O) == LOW)        //Start Weiche 1/2 und 3/4

    {

      W1(0);

      digitalWrite(Pin_LED3_Gruen, HIGH);

      digitalWrite(Pin_LED6_Gruen, HIGH);

      W2(0);

      digitalWrite(Pin_LED5_Gruen, LOW);

      digitalWrite(Pin_LED8_Gruen, LOW);

    }

 

  while(digitalRead(T_G1) == LOW)       //Start Weiche 3 zu 4

    {

      W2(1);

      digitalWrite(Pin_LED2_Rot, LOW);

      digitalWrite(Pin_LED9_Rot, HIGH);

      W1(0);

      digitalWrite(Pin_LED5_Gruen, LOW);

      digitalWrite(Pin_LED6_Gruen, HIGH);

    }

  while(digitalRead(T_S) == LOW)      //Start Schranke schließen

    {

      S(0);

     

    }

  while(digitalRead(T_S) == LOW)       //Start Schranke öffnen

    {

      S(1);

   

    }

 

    // Blinkled

 

  // BlinkWithoutDelay....

  unsigned long currentMillis = millis();

  if (BlinkValue == HIGH)

  {                  

    if (currentMillis - previousMillis >= interval)

    {

      previousMillis = currentMillis;

      if (ledState == LOW)

        ledState = HIGH;

      else

        ledState = LOW;

      digitalWrite(LED_BUILTIN, ledState);

    }

  }

  else

    digitalWrite(LED_BUILTIN, LOW);            

  // Ende BlinkWithoutDelay....

   

  TasterValue = digitalRead (T_S);        // Tasterstatus einlesen

  if (status != TasterValue)                 // Aenderung des Taster ?

  {

    if (TasterValue)                     // geschaltet

    {                          

      BlinkValue = !BlinkValue;                 // ...und wird gewechselt.

    }

    status = TasterValue;                       // Status merken

  }

}


ich hoffe es ist schick so... ich teste mit Wokwi

Hallo furiossteve
Poste auch noch ein Gleisbild mit Signalen und dem Bahnübergang.
Schaue dir einmal die Deklaration der Led´s an. Ist das beim Editieren ein Fehler unterlaufen?
Gehören zur jeder Weiche ein Led-Paar?
Wenn ja, dann macht es Sinn den Led einen passenden Namen zugeben.
Hast du bereits Erfahrung mit array´s und struct´s gemacht?
Wenn ja, dann kann man den Sketch in C++ geschmeidig programmieren.
Ich wünsche einen geschmeidigen Tag und viel Spass beim Programmieren in C++.

In dem anderen Thread haben mir schon einige geholfen... Danke erst mal dafür

Das war dort der letzte Stand von ec2021
[sketch.ino - Wokwi Arduino and ESP32 Simulator ]

leider blinkt hier die Led nur kurz unabhängig von der Schranke...

Ich hatte gestern Abend noch etwas gespielt und dabei kam das heraus...

hier funktioniert erst einmal soweit alles wie gewünscht...
ich habe etwas aufgeräumt und Leerzeichen gelöscht.
Es gab ein Tipp mit Taster entprellen und alles "zusammenzuschieben", dass es mehr parallel arbeitet anstatt hintereinander weg...
Daran hapert es noch...

Ich kann mit Beispielen oder Programmänderungen bessere Ergebnisse und Rückschlüsse erkennen.
Habe aktuell 3 Wokwi offen zum vergleichen...
Ich weiß bin noch Anfänger...

a) entferne die sinnlosen Leerzeilen aus deinem Sketch und poste den Sketch neu.

b) sorry, das ist geschaffel:

Schreibe exakt:
Was machst du/bzw welchen Taster betätigst du.
Was möchtest du dass passiert. --> der Sollzustand
Was passiert statt dessen --> der konkrete Fehler.

Ein Fehler nach dem anderen.
Am besten nur mal einen Fehler damit wir hier zusammenwachsen.

Sorry,

darf Wokwi hier nicht erwähnt werden, oder warum wurde es versteckt?

Hallo furiossteve
Wir erwarten den Sketch in Textform gut formatiert - Leerzeilen löschen - und mit sinnvollen Kommentaren.

So jetzt ist er wieder freigeschaltet worden. Danke dafür
@noiasca:
Das Wokwi furiossteve.ino ist derzeit mein Stand, Leerzeichen sind raus und alles etwas zusammengerutscht.
Das Geschwafel habe ich nur aus dem anderen Thread kopiert, zwecks der Zuweisung bzw. um die Anderen, welche schon hilfreich waren nicht vor den Kopf zu stoßen.
Nicht das es heißt "...das hatten wir doch erst".

Schranke mit Blinklicht ist eigentlich selbsterklärend und die anderen 4 Weichen gehören zu einer Kreuzweiche welche über Fahrstrecken gesteuert wird.
Also:
von oben nach unten - die Strecke Grün
Alle geradeaus - alle 4 Grün
von unten nach oben - Grün
die jeweils andere Strecke ist Rot.

Beim Start geht erst mal alles an als Test...

noch mal von vorn.
du drückst auf den linken grünen Taster.
Der Servo oben in der Mitte Bewegt sich
Die rote LED blink wenn der oberste Servo nach unten zeigt.

Was soll anders sein?

(ich sehe keine Kreuzungsweiche, ich sehe keine Strecke, ich sehe keinen Bahnschranken. Ich sehe Buttons, Leds und Servos)

@furiossteve : Für dein Vorhaben wäre es erstmal wichtig, dass Du lernst, wie man ohne delay programmiert, und mehrere DInge 'quasi gleichzeitig' mit dem Arduino erledigen kann. Für Modelleisenbahner habe ich dafür mal ein Tutorial geschrieben, dass @Tommy56 freundlicherweise auf seiner Webseite gehostet hat. Auch die Beispielsketche gibt es da, damit man sie nicht abtippen muss.
Ein Wechselblinker für einen Bahnübergang kommt im Titorial auch vor, das könntest Du sogar übernehmen. Wenn Du dieses Prinzip verstanden hast, sollte eine Erweiterung auf die Schranken kein Problem sein ( wenn Du magst, hab' ich da auch eine Vorlage :wink: ).

@noiasca:
Habe den Sketch noch mal geändert, und mit Hilfe einiger geht jetzt eigentlich alles...
Habe den Taster für die Schranke jetzt Rot gemacht und noch mal ein paar Erklärungen hinzugefügt.
Schranke ( Einzelservo) oben , keine Led blinkt...
den roten Taster 1x drücken Schranke (Einzelservo) geht runter und Leds blinken...
wieder den roten Taster 1x drücken Schranke (Einzelservo) geht hoch und Leds aus...
Nun bin ich an den Aufgaben der Anderen dabei...also "Ohne Delay" damit Ardu artig Multitasken kann und das Entprellen der Taster...
@MicroBahner:
Hoffe habe das richtig verstanden mit dem Multitasking.
Danke für den Tipp und ich werde mir die Seite bei Gelegenheit mal anschauen,
mal sehen ob ich da was zusammen bekomme.
Ich sag mal frech "ne Vorlage" wäre schön, da kann ich vergleichen und mir den Rest zusammen texten... Somit vorab schon mal Danke falls Du es machst.

So ist der Sketch aktuell, ich hoffe das Aufräumen und Beschriften passt so...und er funktioniert erst mal so wie ich mir das vorgestellt habe... (Taster entprellen und Multitasking fehlt noch)

Also danke erst mal an alle Mitwirkenden.

Ist für ein einfaches Schaltpult für meinen Kleinen.

Ich war davon ausgegangen, dass die Led genau während des Senkens und Hebens der Schranke blinken soll ... :wink: Das Blinken während sie geschlossen ist, hätte man in meinem Sketch nur mit dem Zustand SchrankeOben verknüpfen müssen ... Zum Beispiel

if(!SchrankeOben) {BlinkValue = HIGH;} else { BlinkValue = LOW;} // Um Deine Variablen zu nutzen

oder einfacher:

boolean Blinken;
...
Blinken = !SchrankeOben;

Ich hatte gestern Abend leider keine Zeit, sonst hätte ich Dir genau das vorgeschlagen, was @MicroBahner geschrieben hat: Für Anwendungen dieser Art sind "Zustandsmaschinen" die geeignete Art der Umsetzung. Alles andere ergibt schwer überschaubares Flickwerk und macht jede Erweiterung bzw. Anpassung zur Höllenaufgabe ...

Wie das besser geht, hat @MicroBahner sehr gut beschrieben (siehe Link im Post # 9)!

Vielen Dank für die Rückmeldung... den Link von MicroBahner werde ich mir am Wochenende mal zu Gemüte führen mal sehen.
Ich hatte auch mal was von einer MoBaTools gelesen und Teile gesehen, das ist wiederum anders aufgebaut.
Und was ich schon mitbekommen habe, man kann diese Ansätze nicht so einfach mischen...
Also bin ich noch am schauen und rätseln...
Am Ende sollen mehrere Einzelweichen mit einfachen Signalen, 2 Kreuzweichen, 2 Schranken mit Blinklicht, 2 Schranken nur mit Blinklicht (ohne Servo), vielleicht noch etwas Sound, ein kleines Display als Status und idealer Weise dann alles über BT mit Tablet/PC steuerbar, herauskommen.
Bis dahin dauert aber noch, erst mal "Analog" mit Tastern....

So sieht das mit dem Bü aus:

Den Sketch dazu kann ich noch rauskramen, wenn er dich interessiert.

Dann fang gleich gescheit mit Zustandsmaschinen an. Wie @ec2021 schon schrieb:

Bei dem Ziel macht es keinen Sinn 'erstmal' irgendwie anzufangen. Das musst Du gleich richtig aufsetzen. Der Lernaufwand am Anfang lohnt sich ...

Die MobaTools sind nur eine Library, die bei vielen 'Standardaufgaben' hilft. Der Aufbau deines Sketch ist davon unabhängig.

@MicroBahner
Das mit dem Sketch wäre nett...
Sieht gut aus, so mit Ping Ping würde der Stift sich sicherlich freuen. Wäre halt etwas größer, da in Lego...
Schau es mir am WE mal an...Zeit ist so ne Sache, aktuell bin Krank zu Hause und Schläfstörungen helfen, aber die Frau schimpft trotzdem... kennt ihr bestimmt... :wink:
Wenn ich in ein paar Jahren dann vielleicht die alte TTBahn raus hole, kann man es ja auch nutzen.
Danke vielmals

Um es mal so zu sagen "Mischen Impossible" :wink:

Wer bei so etwas mit einer sequentiellen und daher zwingend an bestimmten Stellen blockierend funktionierenden Struktur anfängt, arbeitet i.d.R. für die Tonne. Das gleich zielführend anzugehen, erspart Zeit und Frust! Man erlernt nebenbei gleich die "richtige Denke" für solche Aufgaben und nutzt die Fähigkeiten eines Microcontrollers viel besser aus. Der kann nämlich unglaublich viele Aufgaben pro Sekunde nacheinander bearbeiten, aber eben nicht parallel.

Daher muss man die Aufgaben quasi "in Scheiben schneiden" und den Controller reihum prüfen lassen, wo er wieder etwas tun kann/muss. Ansonsten braucht es entweder ein RTOS, das sich der Sache annimmt, oder ein multitaskingfähiges OS mit all seinen Stärken und Schwächen ...

P.S.: Wenn Du die Weichen nach den Servonummern benannt hast, ist m.E. die Pinzuordnung bei W3 und W4 noch vertauscht; die Servos 1 und 4 sowie 2 und 3 hängen am gleichen Pin (nicht wie im Sketch 1 und 3 bzw. 2 und 4.)

P.P.S.: In Deinem Sketch steuerst Du die Servos mit folgendem Code an:

void schalten (Servo Weiche, int PIN, int Winkel)
{
  Weiche.attach(PIN);
  Weiche.write(Winkel);
  delay(200);
  Weiche.detach();
}

Das sieht erstmal so aus, als würdest Du durch attach/detach wahlweise unterschiedliche Servos, die am selben Pin hängen, ansteuern wollen ... ?? Oder täusche ich mich da (hoffentlich geht es nur um das Verhindern des "Nachregelns")?

Hab den Sketch mal rausgesucht und an die aktuelle MobaTools Version angepasst ( der Sketch ist schon etwas älter :wink: ).
Der Ablauf ist nach dem Zustandsautomaten Prinzip ( wie im Tutorial ) aufgebaut und blockadefrei. Lässt sich daher auch als Basis für Erweiterungen hernehmen. Es laufen ja jetzt auch schon 2 Automaten gleichzeitig ( Blinker und Schranke ).

/* Schrankensteuerung mit Servo V0.2 15.9.2015
 *  4.2.22 - angepasst an aktuelle MobaTools Version
 *  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 <MobaTools.h>      // Lib für Servo-Pulse und Zeitfunktionen

#define SCHRANKENZAHL   2   // Zahl der Schrankenbäume ( derzeit nur 2 erlaubt)
//#define DEBUG 1             // Wenn dieser Wert gesetztist, werden Debug ausgaben auf dem ser. Monitor ausgegeben

//////////////// Portzuordnungen und Konstante /////////////////////////
// 1. Einschaltlogik --------------------------------------------------
const byte schrankeZuP  = 7;   // Pin HIGH bedeutet Schranke schliessen

// 2. Ablaufsteuerung --------------------------------------------------
const byte ServoPort[SCHRANKENZAHL] = {9,10}; 
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 
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, gegebenenfalls anpassen
int positionAuf[2] = {1900, 1450};  // Servopostionen, gegebenenfalls anpassen

MoToServo Schranke[2];     // Für die Schrankenservos
MoToTimer 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 -------------------------------------------------------------
MoToTimer glockeT;
byte glAktiv = false;   // Flag ob Glocke aktiv ist

// 4. Wechselblinker ------------------------------------------------------
MoToSoftLed Wblinker[2];    // 2 Leds für den Wechselblinker
MoToTimer 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. Diesen Befehl auskommentieren wenn die Glocke erst
                                // mit der Schrankenbewegung starten soll
            VorlaufT.setTime( vorlaufZu );
            bueZustand = VORLAUF_ZU;
            sprintf(buf,"Zustandswechsel: %d", bueZustand ); Serial.println( buf);
        }
        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 );
                //Serial.println( "500ms");
            }
        }
    } 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

Ich habe auch eine Version, bei der sich die Endlagen im Betrieb justieren und im EEPROM speichern lassen. Ist aber naturgemäß etwas komplexer :wink:

Macht dann halt mehr her - dem Sketch ist das ja egal :sunglasses:

@MicroBahner hat mit seiner "State Machine" sicher die zukunftsfähigste Lösung aufgezeigt; ich habe indes mal Deinen Sketch in eine "verkappte" State Machine verwandelt, bei der sicher noch einiges mehr zu optimieren wäre, aber die auf jeden Fall schon mal die bisherigen Funktionen ohne delays abbildet:

#include <Servo.h>
#include <MobaTools.h>

enum  {
      UNBEKANNT,
      NACHLINKS,
      LINKS,
      NACHRECHTS,
      RECHTS
} Weichenstatus;

enum {
    WEICHE1,
    WEICHE2,
    WEICHE3,
    WEICHE4
} WeichenNummer;

enum {
    GERADE,
    ABBIEGEN
} WeichenStatus;

enum {
    OBEN,
    UNTEN
} SchrankenStatus;


struct WeichenTyp {
       Servo   servo;
       int     Abbiegen    = 90;
       int     Gerade      =  0;
       int     servoPin;
       boolean isAttached = false;
       unsigned long SchaltDelay = 200;
       unsigned long lastSchaltTime ;
};

struct SchrankenTyp {
       Servo   servo;
       int     Unten    = 90;
       int     Oben     =  0;
       int     Zustand  = 90;
       int     servoPin;
       boolean isAttached = false;
       unsigned long SchaltDelay = 200;
       unsigned long lastSchaltTime ;
       boolean Schliessen = false;
       boolean Oeffnen = false;
};

SchrankenTyp Schranke;

const int WeichenZahl = 4;
WeichenTyp Weiche[WeichenZahl];

//Servo Schranke;               //Einzelservo Oben
int Grad;
int T_W = 53;                   //Taster weiß Frahrstrecke an Kreuzweiche von OL-> UR
int T_O = 52;                   //Taster orange Frahrstrecke an Kreuzweiche von OL-> OR & UL-> UR
int T_G1 = 51;                  //Taster grün Frahrstrecke an Kreuzweiche von UL-> OR
const byte T_S = 50;              // Taster rot Schranke an Pin 50 und GND
const long interval = 500;        // Blinkintervall
const byte entprellzeit = 20;     // Zeitintervall der Tasterabfrage
boolean taster_gedrueckt = 0;     // Speichervariable für den Tasterzustand
boolean last_tasterzustand = 0;   // zur Erkennung der Flanke
MoToTimer Entprellen;
bool SchrankeValue = true;
// Leds je 2 Leds an 1 Pin -> Sterung mit LOW/HIGH
int Pin_LED2_Rot = A0;            // LED Gerade an PIN A0 high
int Pin_LED3_Gruen = A0;          // LED Gerade an PIN A0 low
int Pin_LED6_Gruen = A1;          // LED Gerade an PIN A1 high
int Pin_LED7_Rot = A1;            // LED Gerade an PIN A1 low
int Pin_LED4_Rot = A2;            // LED Gerade an PIN A0 high
int Pin_LED5_Gruen = A2;          // LED Gerade an PIN A0 low
int Pin_LED8_Gruen = A3;          // LED Gerade an PIN A1 high
int Pin_LED9_Rot = A3;            // LED Gerade an PIN A1 low
// Blinkleds
int ledState = LOW;                       // Led-Status
uint8_t TasterValue = 0;                  // Taster-Status
boolean SchrankeIstUnten = true;
unsigned long previousMillis = 0;


enum  {
     WEISS,
     ORANGE,
     GRUEN,
     ROT
} Tastenfarbe;

const int TastenDelay = 50;
int TastenPin[4] = {53,52,51,50};

struct Tastentyp{
        boolean ausloesen = false;
        boolean gedrueckt = false;
        boolean zeitsetzen   = true;
        unsigned long lastTastenDruck;
};

Tastentyp Taste[4];

//___________FUNKTION "Taste Gedrueckt"________________
boolean TasteGedrueckt(int Farbe){
    boolean status = !digitalRead(TastenPin[Farbe]);
    if (status != Taste[Farbe].gedrueckt && Taste[Farbe].zeitsetzen){
       Taste[Farbe].zeitsetzen = false;
       Taste[Farbe].lastTastenDruck = millis();
    }  
    if(millis()-Taste[Farbe].lastTastenDruck > TastenDelay){
       Taste[Farbe].ausloesen = (!Taste[Farbe].gedrueckt && status);
       Taste[Farbe].gedrueckt = status;
       Taste[Farbe].zeitsetzen = true;                    
    }
    
  return Taste[Farbe].gedrueckt;  
};


//___________FUNKTION "schalten ohne delay"________________
void schaltenOhneDelay (int Nr, int Winkel)
{
  if (!Weiche[Nr].isAttached){   // Falls der Pin noch belegt ist ....
    Weiche[Nr].servo.attach(Weiche[Nr].servoPin);
    Weiche[Nr].isAttached = true;
    Weiche[Nr].lastSchaltTime = millis();
    Weiche[Nr].servo.write(Winkel);
  }  else {
    Serial.print(Nr);Serial.println("\t Weichennummer");
    Serial.print(Weiche[Nr].servoPin);Serial.println("\t ist noch belegt!");
    }
}
void SchrankeSchaltenOhneDelay (SchrankenTyp schranke)
{
  if (!Schranke.isAttached){   // Falls der Pin noch belegt ist ....
    Schranke.servo.attach(Schranke.servoPin);
    Schranke.isAttached = true;
    Schranke.lastSchaltTime = millis();
    Schranke.servo.write(schranke.Zustand);
    if (Schranke.Zustand == Schranke.Oben) digitalWrite(LED_BUILTIN, LOW);
  }  else {
    Serial.println("Schranke");
    Serial.print(Schranke.servoPin);Serial.println("\t ist noch belegt!");
  }
}

void SchrankeLangsam(){
  static unsigned long previousTime = 0;
   if (Schranke.Oeffnen && Schranke.Zustand > Schranke.Oben){
     if (millis() - previousTime > 250) {
          previousTime = millis();
          Schranke.Schliessen = false;
          Schranke.Zustand -= 10;
          if (Schranke.Zustand < Schranke.Oben) {
              Schranke.Zustand = Schranke.Oben;
              Schranke.Oeffnen = false;
              };
          SchrankeSchaltenOhneDelay(Schranke);
      }
   }
   if (Schranke.Schliessen && Schranke.Zustand < Schranke.Unten){
      if (millis() - previousTime > 250) {
           previousTime = millis();
          Schranke.Zustand += 10;
          Schranke.Oeffnen = false;
          if (Schranke.Zustand > Schranke.Unten) {
              Schranke.Zustand = Schranke.Unten;
              Schranke.Schliessen = false;
              };
          SchrankeSchaltenOhneDelay(Schranke);
      }
   }

}

//___________FUNKTION "Detach nach Intervall"________________
void DetachNachZeit() {
  for (int i = WEICHE1; i <= WEICHE4; i++) {
    if(Weiche[i].isAttached){
      if(millis()-Weiche[i].lastSchaltTime > Weiche[i].SchaltDelay){
         Weiche[i].servo.detach();
         Weiche[i].isAttached = false;
      }
    }
  }
  if (Schranke.isAttached){
      if(millis()-Schranke.lastSchaltTime > Schranke.SchaltDelay){
         Schranke.servo.detach();
         Schranke.isAttached = false;
      }
  } 
}

//___________FUNKTION "Bewege Weiche Nr. x in Richtung"___________________
void schalte(int Nr, int Richtung)                //GERADE oder ABBIEGEN
{ 
  int Grad;
  if (Richtung == GERADE) {Grad = Weiche[Nr].Gerade;
  }                 else  {Grad = Weiche[Nr].Abbiegen;};
  schaltenOhneDelay(Nr, Grad);
}


//___________FUNKTION "Weichen und Schranke initialisieren"________________
void WeichenUndSchrankeInit(){
   Weiche[WEICHE1].servoPin = 2;
   Weiche[WEICHE2].servoPin = 3;
   Weiche[WEICHE3].servoPin = 3;
   Weiche[WEICHE4].servoPin = 2;
   Schranke.servoPin        = 4;
}

//___________________VOID SETUP______________
void setup()
{
  Serial.begin(115200);
  pinMode(T_W, INPUT_PULLUP);
  pinMode(T_O, INPUT_PULLUP);
  pinMode(T_G1, INPUT_PULLUP);
  pinMode(T_S, INPUT_PULLUP);
  // BlinkLed
  pinMode(LED_BUILTIN, OUTPUT);           // Led Pin13
  // Leds definieren
  pinMode(Pin_LED2_Rot, OUTPUT);
  digitalWrite(Pin_LED2_Rot, LOW);     // Initialisieren der LED auf Grundstellung, LED Abzweig ist aus

  pinMode(Pin_LED3_Gruen, OUTPUT);
  digitalWrite(Pin_LED3_Gruen, HIGH);    // Initialisieren der LED auf Grundstellung, LED Gerade leuchtet

  pinMode(Pin_LED4_Rot, OUTPUT);
  digitalWrite(Pin_LED4_Rot, HIGH);     // Initialisieren der LED auf Grundstellung, LED Abzweig ist aus

  pinMode(Pin_LED5_Gruen, OUTPUT);
  digitalWrite(Pin_LED5_Gruen, LOW);    // Initialisieren der LED auf Grundstellung, LED Gerade leuchtet

  pinMode(Pin_LED6_Gruen, OUTPUT);
  digitalWrite(Pin_LED6_Gruen, HIGH);    // Initialisieren der LED auf Grundstellung, LED Gerade leuchtet

  pinMode(Pin_LED7_Rot, OUTPUT);
  digitalWrite(Pin_LED7_Rot, LOW);     // Initialisieren der LED auf Grundstellung, LED Abzweig ist aus

  pinMode(Pin_LED8_Gruen, OUTPUT);
  digitalWrite(Pin_LED8_Gruen, LOW);    // Initialisieren der LED auf Grundstellung, LED Gerade leuchtet

  pinMode(Pin_LED9_Rot, OUTPUT);
  digitalWrite(Pin_LED9_Rot, HIGH);     // Initialisieren der LED auf Grundstellung, LED Abzweig ist aus

  WeichenUndSchrankeInit();
}

//___________FUNKTION "Taste WEISS"___________________
void TasteWeiss(){
      if (!Taste[WEISS].ausloesen) return;
      Taste[WEISS].ausloesen = false;
      schalte(WEICHE1,ABBIEGEN);
      schalte(WEICHE2,GERADE);
      digitalWrite(Pin_LED3_Gruen, HIGH);
      digitalWrite(Pin_LED4_Rot, HIGH);
      digitalWrite(Pin_LED7_Rot, LOW);
      digitalWrite(Pin_LED8_Gruen, LOW);
};
//___________FUNKTION "Taste ORANGE"___________________
void TasteOrange(){
      if (!Taste[ORANGE].ausloesen) return;
      Taste[ORANGE].ausloesen = false;
      schalte(WEICHE1,GERADE);
      schalte(WEICHE2,GERADE);
      digitalWrite(Pin_LED3_Gruen, HIGH);
      digitalWrite(Pin_LED5_Gruen, LOW);
      digitalWrite(Pin_LED6_Gruen, HIGH);
      digitalWrite(Pin_LED8_Gruen, LOW);
};
//___________FUNKTION "Taste GRUEN"___________________
void TasteGruen(){
      if (!Taste[GRUEN].ausloesen) return;
      Taste[GRUEN].ausloesen = false;
      schalte(WEICHE2,ABBIEGEN);
      schalte(WEICHE1,GERADE);
      digitalWrite(Pin_LED2_Rot, LOW);
      digitalWrite(Pin_LED5_Gruen, LOW);
      digitalWrite(Pin_LED6_Gruen, HIGH);
      digitalWrite(Pin_LED9_Rot, HIGH);
};

//___________FUNKTION "Taste ROT"___________________
void TasteRot(){
    {     
      if (!Taste[ROT].ausloesen) return;
      Taste[ROT].ausloesen = false;
      Schranke.Schliessen = false;
      Schranke.Oeffnen    = false;
      if (Schranke.Zustand == Schranke.Oben) {Schranke.Zustand = Schranke.Unten;
                                      } else {Schranke.Zustand = Schranke.Oben;}
      SchrankeSchaltenOhneDelay(Schranke);
    }
};
//___________FUNKTION "Blinken"___________________
void Blinken(){
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval)
      {
        previousMillis = currentMillis;
        ledState = !ledState; 
        digitalWrite(LED_BUILTIN, ledState);
      };
  }

void SerialIn(){
   if (Serial.available()){
  char c = Serial.read();
     if (c == 'w') {Taste[WEISS].ausloesen = true;TasteWeiss();};
     if (c == 'o') {Taste[ORANGE].ausloesen = true;TasteOrange();};
     if (c == 'g') {Taste[GRUEN].ausloesen = true;TasteGruen();};
     if (c == 'r') {Taste[ROT].ausloesen = true;TasteRot();};
     if (c == 'O') {Schranke.Oeffnen = true;};
     if (c == 'S') {Schranke.Schliessen = true;};

   }
}


//_____________VOID LOOP______________________
void loop()
{
  SerialIn();
  if (TasteGedrueckt(WEISS))  TasteWeiss();  //Start Weiche 1 zu 3
  if (TasteGedrueckt(ORANGE)) TasteOrange(); //Start Weiche 1/2 und 3/4
  if (TasteGedrueckt(GRUEN))  TasteGruen(); //Start Weiche 3 zu 4
  if (TasteGedrueckt(ROT))    TasteRot(); // Schranke bewegen
  if (Schranke.Zustand != Schranke.Oben) Blinken();
  DetachNachZeit();                      // Falls Servos attached sind ..
  SchrankeLangsam();
}



Als kleines Schmankerl kann man die Tastendrücke auch per Serial eingeben

w für Weiss, o für Orange, g für Grün und ... r für Rot

Bei Wokwi zu finden unter

https://wokwi.com/arduino/projects/322669877828518484

Wenn man dort sehr schnell nacheinander auf die gleiche Taste klickt, wird im seriellen Monitor ausgeworfen, bei welchen Servo-Pins die Wartezeiten zum Detachen noch nicht abgelaufen sind ...

Die Tasten sind gegen Wiederholung in der loop() verriegelt, so dass eine weitere Auslösung der Funktion erst möglich wird, wenn man sie wieder losgelassen hat.

Um Beispiele zu liefern, habe ich die Schranke als "Einzelobjekt" und die Weichen als Array ausgelegt. Das Prinzip bei den Weichen lässt sich so auf die Schranke(n) übertragen, dass auch mehrere möglich werden.

Die Tastenpins werden separat in einem Array gehalten; diese könnte man auch in der Struktur TastenTyp unterbringen.

Die Behandlung der Led habe ich 1:1 übernommen, dort wäre sicher auch noch die Möglichkeit, die Nachvollziehbarkeit zu verbessern.

Es ist also noch einiges an Optimierungsmöglichkeiten vorhanden ...

P.S.: Noch eine weitere Ergänzung: Mit den Tasten O für Öffnen und S für Schließen wird jetzt [23:35 Uhr] die Schranke mit Verzögerung in Schritten geöffnet bzw. geschlossen. Während des Vorgangs blinkt die Led ebenfalls schon.

Also erst mal an alle... Danke für Eure Hilfe...
@ec2021 :
Attach soll den Servo starten und detach ausschalten, soll das Zittern in Endlage beheben... habe es so übernommen...
Aus Deinem Sketch konnte ich erst mal was rauslesen und stand nicht ganz so auf dem Schlauch... :wink:
@MicroBahner:
Danke auch für deinen Sketch, aber hier muss ich zugeben, das es etwas schwieriger ist...
Jetzt nerven erst mal die Kids... dazu muss ich Ruhe haben...

Ich melde mich sobald ich durch bin...

Das ist so auch ok. Hast Du Dir den Sketch von Post # 18 in Wokwi angeschaut? Dort kannst Du im Eingabefeld des seriellen Monitors (kleines einzeiliges Fenster rechts ganz unten) die Text Kommandos eingeben und mit Return bestätigen.

O und S als Grossbuchstaben zeigen "langsames" schrittweises Öffnen/Schliessen der Schranke, das ohne das Delay-lose Vorgehen den gesamten Sketch aufhalten würden. Schön zu testen ist dort auch, eine Taste mal gedrückt zu halten. Im "alten" Sketch bleibt das Blinken stehen, hier nicht mehr. Allerdings kann ich nicht ausschliessen, das "wildes Herumtasten" noch zu Problemen führt; dann muss man eventuell kollidierende Tasten kurzfristig "sperren", aber nur dann.