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


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

  1. 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

    2. 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.