Zum Inhalt springen
RailFX Modellbahn, Modellbau Arduino Bahnübergang mit Schranke und Andreaskreuz

RailFX – Bahnübergang mit Arduino

    Dieses Modul des RailFX-Systems steuert einen Bahnübergang mit Arduino. Es bestehend aus zwei Schranken, zwei Andreaskreuzen und einer Beleuchtung für die Nacht. Ausgelöst wird die Schließung durch zwei TCRT5000 Lichtschranken.

    Für die korrekte Funktionsweise der Beleuchtung muss dieses Modul mit dem RailFX-Control-Modul verbunden werden.

    Bauteile

    Schaltplan: Bahnübergang mit Arduino

    RailFX Modellbahn, Modellbau Arduino Bahnübergang mit Schranke und Andreaskreuz Schaltplan Circuit

    Auch dieses Modul arbeitet mit einem Arduino Nano. Die rote LED im Schaltplan ist über einen 220 Ohm-Widerstand am digitalen Pin 5 am Arduino-Board angeschlossen. Man kann hier einfach mehr LEDs parallel zu der roten LED anschließen. Sie sollten sich aber zusammen in Reihe weithin einen Widerstand teilen. Am Pin 3 und 6 können LEDs als Straßenbeleuchtung angeschlossen werden, wobei die am Pin 6 per Zufall flackert.


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


    Die Schrankenservos sind an den Pins 8 und 9 angeschlossen. Die Pins 2 und 4 sind mit den digitalen Pins (D0) zweier TCRT5000 Lichtschranken verbunden. Ihre Empfindlichkeit lässt sich durch die integrierten Potentiometer einstellen. Sobald eine der Lichtschranken auslöst, blinken die Andreaskreuze und nach einstellbarer Zeit schließen sich die Schranken. Sind beide Lichtschranken wieder offen, öffnet sich die Schranke und die Andreaskreuze hören auf zu blinken.

    Achtung: Am Pin A4 ist wird das Control-Signal des RailFX Control-Moduls angelegt. Es steuert die Tageszeit und ist für die korrekte Funktionsweise der Straßenbeleuchtung erforderlich.

    Einstellungen im Code

    Im Code lassen folgende Einstellungen vornehmen:

    • Einschaltzeit der Straßenlampen (ms)
    • Flackern der Straßenlampe am Pin 6
    • Blinkgeschwindigkeit der LEDs des Andreaskreuzes
    • Minimum (geschlossen) und Maximum (geöffnet) der Servos
    • Schaltdauer der Schrankenabläufe (Blinken, Schließen, Öffnen, Blinken)
    • Geschwindigkeit der Schrankenbewegung

    Im folgenden Teil des Programmes lassen sie die genannten Parameter anpassen:

    int strassenlampenTimeout = 100;                     // Einschaltzeit der Straßenlampen (ms)
    int strassenlampeFlackern = 1000;                    // Je höher der Wert, desto langsamer flackert die LED an Pin 6
    int andreaskreuzBlinkgeschwindigkeit = 1000;         // Blinkgeschwindigkeit des Andreaskreuzes
    int servo1Min = 40;                                  // Servo 1 Minimum (geschlossen)
    int servo2Min = 40;                                  // Servo 2 Minimum (geschlossen)
    int servo1Max = 160;                                 // Servo 1 Maximum (geöffnet)
    int servo2Max = 160;                                 // Servo 2 Maximum (geöffnet)
    int schrankeTimeouts[4] = {1000, 2000, 2000, 1000};  // Schrankenzustände {Blinken, Schließen, Öffnen, Blinken}
    int schrankenGeschwindigkeit = 5;                   // 5 = schnell, 25 = mittel, 50 = langsam

    Beim Upload muss man darauf achten, dass das richtige Board im Arduino-Menü ausgewählt ist. Dazu muss ebenfalls im Werkzeuge-Menü im Unterpunkt Prozessor »ATmega328P (Old Bootlaoder)« ausgewählt sein.

    Der folgende Programmcode kann mit den oben erwähnten Änderungen einfach kopiert und auf das Arduino-Nano geladen werden.

    Code für RailFX: Bahnübergang mit Arduino

    #include <Servo.h>
    
    /*
         Rail-FX Bahnübergang
         StartHardware.org
    
         Permalink: https://starthardware.org/railfx-bahnuebergang/
    
    */
    
    /* ***** ***** Einstellungen ***** ***** ***** *****  ***** ***** ***** *****  ***** ***** ***** ***** */
    
    int strassenlampenTimeout = 100;                     // Einschaltzeit der Straßenlampen (ms)
    int strassenlampeFlackern = 1000;                    // Je höher der Wert, desto langsamer flackert die LED an Pin 6
    int andreaskreuzBlinkgeschwindigkeit = 1000;         // Blinkgeschwindigkeit des Andreaskreuzes
    int servo1Min = 40;                                  // Servo 1 Minimum (geschlossen)
    int servo2Min = 40;                                  // Servo 2 Minimum (geschlossen)
    int servo1Max = 160;                                 // Servo 1 Maximum (geöffnet)
    int servo2Max = 160;                                 // Servo 2 Maximum (geöffnet)
    int schrankeTimeouts[5] = {1000, 2000, 1000, 2000, 1000};  // Schrankenzustände {Blinken, Schließen, Warten vor Öffnung, Öffnen, Blinken}
    int schrankenGeschwindigkeit = 5;                   // 5 = schnell, 25 = mittel, 50 = langsam
    
    /* ***** ***** Ab hier beginnt der Programmcode, der nicht angepasst werden muss ***** ***** ***** ***** */
    
    int strassenlampenPin1 = 3;              // an diesem Pin sind Straßenlampen angeschlossen
    int strassenlampenPin2 = 6;              // an diesem Pin ist eine flackernde Straßenlampe angeschlossen
    int andreaskreuzBlinkPin1 = 5;           // an diesem Pin sind die LEDs des Andreaskreuzes angeschlossen
    int andreaskreuzBlinkPin2 = 7;           // an diesem Pin sind die LEDs des Andreaskreuzes angeschlossen
    int schrankenServoPin1 = 8;              // an diesem Pin ist ein Schrankenservo angeschlossen
    int schrankenServoPin2 = 9;              // an diesem Pin ist ein Schrankenservo angeschlossen
    int sensorPin1 = 2;                      // an diesem Pin ist ein Abstandssensor angeschlossen
    int sensorPin2 = 4;                      // an diesem Pin ist ein Abstandssensor angeschlossen
    
    /* Speicher-Variablen */
    int strassenlampenHelligkeit = 0;        // speichert, wie hell die Straßenlampen leuchten
    int myState = 0;                         // speichert den aktuellen Zustand des Bahnüberganges: 0 = frei, 1 = blinken, 2 = Schranke schließen, 3=Schranke geschlossen, 4 = Schranke öffnen
    boolean durchfahrt = false;              // speichert, ob gerade ein Zug durchfährt
    
    /* Timer Variablen */
    long strassenlampenTimer = 0;            // Timer der Straßenlampen
    long myTimer = 0;                        // Timer 
    long servoTimer = 0;                     // Timer der Servos
     
    /* Servo Objekte */
    Servo myServo1;                          // Servo Objekt 1
    Servo myServo2;                          // Servo Objekt 2
    int myServoPosition[2] = {80, 80};       // speichert die aktuellen Servo-Positionen
    
    /* Variablen vom Controlmodul um die Uhrzeit festzustellen*/
    boolean receive = false;
    boolean receiveStarted = false;
    int receiveTimeout = 10;
    long receiveTimer = 0;
    int receivedTime = 0;
    int receivePulse = 0;
    int lastReceivePulse = 0;
    int receivePin = 18;
    int myTime = 23;
    
    #define PAYLOAD_SIZE 2                   // nötig für die Kommunikation zum Master
    int uhrzeit = 0;                         // speichert die uhrzeit vom Master-Modul (0 und 255)
    byte nodePayload[PAYLOAD_SIZE];          // speichert die Daten vom Master-Modul zwischen
    
    void setup() {
      Serial.begin(115200);                  // started die serielle Kommunikation
      pinMode(receivePin, INPUT);            // Empfangspin vom Control-Modul
    
      /* Servo Variablen */
      myServo1.attach(schrankenServoPin1);
      myServo2.attach(schrankenServoPin2);
    
      myServo1.write(servo1Min);
      myServo2.write(servo2Min);
    
      myServoPosition[0] = servo1Min;
      myServoPosition[1] = servo2Min;
    
      delay(500);                             // Servos werden auf Ausgangsposition gefahren
    
      pinMode(andreaskreuzBlinkPin1, OUTPUT);
      pinMode(andreaskreuzBlinkPin2, OUTPUT);
      pinMode(sensorPin1, INPUT);
      pinMode(sensorPin2, INPUT);
    
    
      strassenlampenAus();
    }
    
    void loop() {
    
      receiveFunction();                // Führe Anweisungen für Empfang aus
    
      if (receiveStarted == false) {    // Falls gerade keine Daten empfangen werden:
        if (myTime > 22) {              // ***** Später Abend *****
          strassenlampenAn();           // Straßenlampen an
        } else if (myTime > 18) {       // ***** Abend *****
          strassenlampenAn();           // Straßenlampen an
        } else if (myTime > 12) {       // ***** Nachmittag *****
          strassenlampenAus();          // Straßenlampen aus
        } else if (myTime > 9) {        // ***** Vormittag *****
          strassenlampenAus();          // Straßenlampen aus
        } else if (myTime > 7) {        // ***** Morgen *****
          strassenlampenAn();           // Straßenlampen an
        } else {                        // ***** Nacht *****
          strassenlampenAn();           // Straßenlampen an
        }
    
    
        if ((digitalRead(sensorPin1) == 0) || (digitalRead(sensorPin2) == 0)) {
          if (myState == 0) {
            myState = 1;
            myTimer = millis();
          } else if ((myState == 5) || (myState == 6)) myState = 2;
        } else {
    
          if (myState == 3) {
            myState = 4;
            myTimer = millis();          // Timer resetten
          }
        }
    Serial.print("Sensor1: ");Serial.print(digitalRead(sensorPin1));Serial.print(" Sensor2: ");Serial.print(digitalRead(sensorPin2));
        Serial.print(" myState: ");Serial.println(myState);
    
        switch (myState) {
          case 0: // idle
            andreaskreuzAus();
            break;
          case 1: // blinken
            andreaskreuzAn();             // Andreaskreuz blinken
            if (myTimer + schrankeTimeouts[0] < millis()) { // wenn Zeit abgelaufen
              myTimer = millis();          // Timer resetten
              myState = 2;                // gehe zum nächsten Zustand
            }
            break;
          case 2: // Schranke schließen
            andreaskreuzAn();             // Andreaskreuz blinken
            schrankenSchliessen();
            if (myTimer + schrankeTimeouts[1] < millis()) { // wenn Zeit abgelaufen
              myTimer = millis();          // Timer resetten
              myState = 3;                // gehe zum nächsten Zustand
            }
            break;
          case 3: // Schranke geschloseen
            andreaskreuzAn();             // Andreaskreuz blinken
            break;
          case 4: // Schranke öffnen
            if (myTimer + schrankeTimeouts[2] < millis()) { // wenn Zeit abgelaufen
              myTimer = millis();          // Timer resetten
              myState = 5;                 // gehe zum nächsten Zustand
            }        
            break;
          case 5: // Schranke öffnen
            andreaskreuzAn();             // Andreaskreuz blinken
            schrankenOeffnen();
            if (myTimer + schrankeTimeouts[3] < millis()) { // wenn Zeit abgelaufen
              myTimer = millis();          // Timer resetten
              myState = 6;                // gehe zum nächsten Zustand
            }
            break;
          case 6: // blinken
            andreaskreuzAn();             // Andreaskreuz blinken
            if (myTimer + schrankeTimeouts[4] < millis()) { // wenn Zeit abgelaufen
              myTimer = millis();          // Timer resetten
              myState = 0;                // gehe zum nächsten Zustand
            }
            break;
        }
      }
    }
    
    void andreaskreuzAn() {
      if (millis() % andreaskreuzBlinkgeschwindigkeit * 2 < andreaskreuzBlinkgeschwindigkeit) {
        digitalWrite(andreaskreuzBlinkPin1, LOW);
        digitalWrite(andreaskreuzBlinkPin2, HIGH);
      } else {
        digitalWrite(andreaskreuzBlinkPin1, HIGH);
        digitalWrite(andreaskreuzBlinkPin2, LOW);
      }
    }
    
    void andreaskreuzAus() {
      digitalWrite(andreaskreuzBlinkPin1, HIGH);
      digitalWrite(andreaskreuzBlinkPin2, HIGH);
    }
    
    void schrankenSchliessen() {
      if (servoTimer + schrankenGeschwindigkeit < millis()) {
        if (myServoPosition[0] > servo1Min) myServoPosition[0]--;
        myServo1.write(myServoPosition[0]);
        if (myServoPosition[1] > servo2Min) myServoPosition[1]--;
        myServo2.write(myServoPosition[1]);
        servoTimer = millis();
      }
    
    }
    
    void schrankenOeffnen() {
      if (servoTimer + schrankenGeschwindigkeit < millis()) {
        if (myServoPosition[0] < servo1Max) myServoPosition[0]++;
        myServo1.write(myServoPosition[0]);
        if (myServoPosition[1] < servo2Max) myServoPosition[1]++;
        myServo2.write(myServoPosition[1]);
        servoTimer = millis();
      }
    
    }
    
    void strassenlampenAn() {
      if (strassenlampenTimer + strassenlampenTimeout < millis()) {
        strassenlampenHelligkeit++;
        if (strassenlampenHelligkeit > 255) strassenlampenHelligkeit = 255;
        strassenlampenTimer = millis();
        analogWrite(strassenlampenPin1, 255 - strassenlampenHelligkeit);
      }
    
      if (random(strassenlampeFlackern) == 1) analogWrite(strassenlampenPin2, random(255) - strassenlampenHelligkeit);
    
    }
    
    void strassenlampenAus() {
      strassenlampenHelligkeit = 0;
      analogWrite(strassenlampenPin1, 255);
      analogWrite(strassenlampenPin2, 255);
      strassenlampenTimer = millis();
    }
    
    void receiveFunction() {                      // Empfängt die Uhrzeit vom Control-Modul
      receivePulse = digitalRead(receivePin);     // Lies den Empfangspin aus
    
      if ((receiveTimer + receiveTimeout < millis()) && (receiveStarted == true)) {
        // Bei Timeout und aktivem Empfang
        receiveStarted = false;                   // beende aktiven Empfang
        myTime = receivedTime - 1;                // speichere die empfangene Zeit
        receivedTime = 0;                         // setze die Hilfsvariable zum Zeitempfang zurück
        Serial.println(myTime);                   // serielle Ausgabe
      }
      // falls ein Puls am Empfangspin erfasst wird, der vorher nicht da war
      if ((receivePulse == 0) && (lastReceivePulse == 1)) {
        receiveTimer = millis();                  // starte Timer neu
        if (receiveStarted == false) receiveStarted = true;  // starte aktiven Empfang, wenn noch nicht geschehen
        receivedTime++;                           // es gab einen Puls, also erhöhe die Hilfsvariable zum Zeitempfang
      }
      lastReceivePulse = receivePulse;            // aktuellen Zustand am Pin für nächsten Durchlauf merken
    }

    Hier findest du weitere RailFX-Module


    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


    19 Gedanken zu „RailFX – Bahnübergang mit Arduino“

    1. Betr.: RailFX “Bahnübergang”
      Hallo Stefan,
      ich habe festverbaute Servos, die Richtung des Servo-Lenkhebels müsste umgekehrt verlaufen, damit beim Blinken die Schranke schließt und umgekehrt. An welcher Stelle kann ich im Code etwas ändern, um diese Umkehr zu erreichen? Danke im Voraus für eine Antwort.
      Gruß
      Hans-Georg

      1. Hi Hans-Georg,

        kann es leider gerade nicht ausprobieren, aber ich glaube, du kannst einfach die Namen der beiden Funktionen tauschen, also von

        void schrankenSchliessen() {
        if (servoTimer + schrankenGeschwindigkeit < millis()) { if (myServoPosition[0] > servo1Min) myServoPosition[0]--;
        myServo1.write(myServoPosition[0]);
        if (myServoPosition[1] > servo2Min) myServoPosition[1]--;
        myServo2.write(myServoPosition[1]);
        servoTimer = millis();
        }
        }
        void schrankenOeffnen() {
        if (servoTimer + schrankenGeschwindigkeit < millis()) { if (myServoPosition[0] < servo1Max) myServoPosition[0]++; myServo1.write(myServoPosition[0]); if (myServoPosition[1] < servo2Max) myServoPosition[1]++; myServo2.write(myServoPosition[1]); servoTimer = millis(); } }

        zu dem hier:


        void schrankenOeffnen() {
        if (servoTimer + schrankenGeschwindigkeit < millis()) { if (myServoPosition[0] > servo1Min) myServoPosition[0]--;
        myServo1.write(myServoPosition[0]);
        if (myServoPosition[1] > servo2Min) myServoPosition[1]--;
        myServo2.write(myServoPosition[1]);
        servoTimer = millis();
        }
        }
        void schrankenSchliessen() {
        if (servoTimer + schrankenGeschwindigkeit < millis()) { if (myServoPosition[0] < servo1Max) myServoPosition[0]++; myServo1.write(myServoPosition[0]); if (myServoPosition[1] < servo2Max) myServoPosition[1]++; myServo2.write(myServoPosition[1]); servoTimer = millis(); } }

        Dann musst du mal gucken, ob die servo1Min und servo1Max-Werte (und natürlich die servo2Min und servo2Max) noch richtig rum sind, oder ob Min jetzt größer sein muss ... weiß ich leider gerade nicht.

        Liebe Grüße

        Stefan

    2. Ich habe probleme das control modul auf zu bauen .ich habe es genau so aufgebaut und den sketch geladen aber das display zeigt mir nichts an. es bleibt schwarz.

    3. Hallo, bei mir tritt vorgehender Fehler auf. Die LED blinkt dauernd und geht wenn der Servo arbeitet auf an. Nach auslösen des 2 Sensors geht die Schranke hoch und LED blinkt wieder. Habe nur die Ausgänge an eine vorhandene Schaltung angepasst.

      Danke schon mal für die Antworten

    4. Hallo hat wer den Sketch schon mal so angepasst das man die Geschwindigheit ändern kann ? Wenn ich die Bibliothek (moba.h) auswechsel kommt immer eine Fehlermeldung. trotz installierter Libery

      Schöne Grüsse Sven

        1. Hallo Stefan
          Ja genau wie langsam sich die Schranken bewegen.
          Ich habe die Servo.h durch die Moba.h ersetzt und vorab die MobaTools installiert. leider kommt dann einer Fehler “No such file or directory)

          Hast Du da eine Servo Empfehlung damit die sauber laufen ? Meine zucken immer so

          Schönen Gruss und Danke für Deine Geduld

    5. Hallo Sefan,
      habe ein Problem mit dem BÜ. Control funktioniert einwandfrei.
      Bei BÜ ist bei mir immer der Sensor an D4 aktiv, LED leuchtet. Habe den Sensor schon ausgetauscht gegen einen anderen. Auch getauscht mit dem Sensor von D2. Sensor an D2 funktioniert einwandfrei.
      Verdrahtung ist ok, habe ich mehrmals überprüft.
      Hast Du eine Idee?

      Gruss Mario

    6. Hallo zusammen,
      ein tolles Projekt das viel Spaß macht, für mich als Arduino Neuling schon eine Herausforderung die Programmierung zu verstehen.
      Deshalb eine Frage, die Schranke öffnet sich sofort nachdem die Lichtschranke nicht mehr bedeckt ist. Das ist nicht so glücklich, eine kurze Verzögerungszeit wäre hier hilfreich. Wenn zwischen den vorbeifahrenden Waggons eine Lücke ist gehen die Schranken kurz hoch und gleich wieder runter.
      Hat dazu jemand eine Idee das zu implementieren?
      Wie gesagt, ich als Neuling stehe da vor einer Herausforderung, beste Grüße Uwe

      1. Hi Uwe,

        danke für deine Anfrage. Ja, das ist tatsächlich ein Problem. Ich hab den Code verändert. Probier mal diese aktuelle Version. Es gibt nun einen fünften Zustand im schrankeTimeouts[5]-Array. Der dritte Eintrag ist für die Zeitverzögerung, bevor die Schranke wieder aufgeht. Und schick mir gerne Fotos von deiner Anlage, dann packe ich sie mit in den Beitrag :-)

        Liebe Grüße

        Stefan

        1. Moin Stefan,
          ich habe deinen geänderten Sketch ausprobiert, bei mir funktioniert es nur, wenn ich PWM Ausgänge (D5 und D6) für die Servos verwende. Die Zeitverzögerung klappt wunderbar, aufgefallen ist mir, dass, solange die Verzögerungszeit läuft, das Blinken des Andreaskreuzes eingefroren ist. Wenn du da noch eine Idee dazu hast, wäre super, ist aber eher eine Marginalie, damit kann ich leben. Ein Foto werde ich irgendwann mal beisteuern können, zur Zeit ist alles nur ein Versuchsaufbau!
          Danke
          Gruß Uwe

    7. Hallo Stefan
      Die Schaltung von BÜ funktioniert super.
      Ist es möglich eine zweite Rote Led zu integrieren mit mit der ersten
      Im Wechsel blinkt??
      Grüsse André

    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.