Zum Inhalt springen
Arduino Bahnhof RailFX OLED

RailFX: Arduino Bahnhof

    Dieses Modul des RailFX-Systems steuert einen Arduino Bahnhof für die Modellbahn. Zu den Funktionen zählten:

    • OLED-Abfahrtsanzeiger mit wechselnden Inhalten
    • Zufalls- oder sensorgesteuerte Umschaltung des Displays
    • Sound-Modul mit Ansagen der »Blechelse«
    • Bahnhofsbeleuchtung mit Leuchtstoffröhren-Einschalteffekt

    Schaltplan

    RailFX, Arduino Bahnhof Modul mit Sound, MP3 und OLED-Display
    • D2, D3: DFPlayer MP3 Modul
    • D4 – D12: LEDs als Leuchtstoffröhren
    • A0 (D14) und A1 (D15) Lichtschranken für Zugdetektion
    • A3: Anschluss des Zeitsignals des Conrol-Moduls
    • A4, A5: I2C Verbindung zum OLED-Display

    Achtung: Am Pin A3 (normalerweise A4, aber hier A3) wird das Control-Signal des RailFX Control-Moduls angelegt. Es steuert die Tageszeit und ist zwingend erforderlich.

    Bauteile Arduino Bahnhof

    Einstellungen im Code

    Auch dieses Modul bietet eine Reihe von Einstellmöglichkeiten. Die Beleuchtung des Bahnhofes kann beim Einschalten wie Leuchtstoffröhren flackern. Die Zeit, die alle Lampen zum Einschalten kann man mit der Variable flackerzeitLeuchten einstellen. Je höher der Wert ist, desto länger flackern die Leuchten. Wie heftig sie flackern lässt sich mit der Variable flackerGeschwindigkeit einstellen.


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


    Die Anzeigen auf dem OLED-Display wechseln sich entweder per Zufall oder per Lichtschranken, je nachdem, ob man die Methode anzeigeTafelAutomatisch() oder anzeigeTafelSensor() im Loop anspricht. Im Lichtschranken-Modus wechselt sich die Anzeigt, sobald beide Sensoren freigegeben werden, also, wenn sich kein Zug mehr auf dem Gleis befindet.

    Im automatischen Modus kann man die folgenden Einstellungen vornehmen: Die minimale Dauer pro Anzeige lässt sich mit der Variable anzeigenWechselMin einstellen. Zu ihr wird per Zufall eine Zeit zwischen 0 und dem in der Variable anzeigenwechselVariableZeit festgelegten Wert addiert.

    Die Texte auf dem Display werden im Array textSegments festgelegt. Es lässt sich durch weitere Texte erweitern. Dafür müssen die Dimensionen im Array und die Variable anzeigeAnzahl angepasst werden.

    Das Display zeigt automatisch beim Umschalten die aktuelle Modellbahnzeit an. Die Folgezüge werden im 30-Minuten-Takt angezeigt. Ist die aktuelle Zeit also 17:21, werden die Abfahrtszeiten der Folgezüge 17:51 und 18:21 angezeigt.

    Per Zufall zeigt das Display auch einen Hinweis an. Den Text kann man in der Variable hinweis anpassen.

    Für die Tonausgabe wird der DFPlayer Mini verwendet. Er spielt MP3- und WAV-Dateien von einer SD-Karte ab. Für die Nutzung muss die DFPlayer Mini Bibliothek eingebunden werden. Klicke dafür im Arduino-Menü auf Sketch>Bibliotheken einbinden>Bibliotheken verwalten und gib im Suchfeld »dfplayer mini« ein. Installiere die Bibliothek DFPlayer Mini MP3 by Makuna in der aktuellen Version.

    Die SD-Karte muss im FAT16 oder FAT32-Dateisystem formatiert sein. Darüber hinaus müssen sich die Dateien im Ordner »01« befinden und die Dateinamen müssen aufeinander folgend 001.mp3, 002.mp3, 003.mp3 usw. benannt sein. Wie erwähnt, dürfen auch WAV-Dateien verwendet werden: 001.mp3, 002.wav, 003.mp3 …

    Beispiel-Dateien können hier heruntergeladen werden:

    /* ***** ***** Einstellungen ***** ***** */
    int flackerzeitLeuchten = 1000;              // Je höher der Wert, desto mehr flackern die Leuchtstoffröhren beim Einschalten
    int flackerGeschwindigkeit = 100;            // Flackergeschwindigkeit der Leuchtstoffröhren
    int anzeigenWechselMin = 2000;               // Anzeigewechsel alle x Millisekunden
    int anzeigenwechselVariabeleZeit = 3000;     // Um diesen Wert (Millisekunden) variiert der Anzeigewechsel
    int mp3Anzahl = 10;                          // Menge der MP3 Dateien auf der SD-Karte
    int mp3Dauer[] = {16,16,15,17,17,18,19,18,19,9}; // Dauer der MP3 Dateien in Sekunden
    int mp3Wahrscheinlichkeit = 20;              // Wahrscheinlichkeit, mit der MP3s abgespielt werden, 10 oft, 100 selten
    int lautstaerke = 10;                        // Lautstärke des DFPlayers (0 – 30);
    /* Anzeigen */
    int anzeigeAnzahl = 6;                       // Muss so groß sein, wie die Anzahl der angezeigten Texte aus dem Array textSegments
    const char* textSegments[6][9] = {
      {"12", "", "Zugdurchfahrt", "17:08", "17:44", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"},
      {"12", "Leipzig Hbf - Halle (Saale)", "Hannover Hbf", "17:08", "17:44", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"},
      {"12", "Leipzig Hbf - Halle (Saale)", "Berlin Hbf", "17:08", "17:44", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"},
      {"12", "Leipzig Hbf - Halle (Saale)", "Dresden Hbf", "17:08", "17:44", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"},
      {"12", "Leipzig Hbf - Halle (Saale)", "Leipzig Hbf", "17:08", "17:44", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"},
      {"12", "Leipzig Hbf - Halle (Saale)", "Hamburg Hbf", "17:08", "17:44", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"}
    };
    const char* hinweis[] = {
      "Aufgrund einer Signalstorung ",
      "hat der ICE100 heute leider ca. 25 Minuten Verspatung. ",
      "Wir bitten um Ihr Verstandnis."
    };

    Code für das Modul: RailFX-Bahnhof

    Libraries

    Für dieses Projekt wird dieSSD1306Ascii Bibliothek von Bill Greiman benötigt. Man kann sie hinzufügen, indem man im Arduino Menü > Sketch > Bilbiothek hinzufügen > Bibliotheken verwalten nach »SSD1306Ascii« sucht und die aktuelle Version installiert.

    Darüber hinaus wird die Bibliothek DFRobotDFPlayerMini von DFRobot verwendet. Auch diese installiert man über Bibliotheken verwalten. Hier kann man einfach nach dfplayer suchen.

    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. Ansonsten kann man den folgenden Programmcode mit den oben erwähnten Änderungen einfach kopiert und auf das Arduino-Nano laden.

    #include <Wire.h>
    #include "SSD1306Ascii.h"
    #include "SSD1306AsciiWire.h"
    #include "SoftwareSerial.h"              // Wird für den DFPlayer benötigt
    #include "DFRobotDFPlayerMini.h"         // Wird für den DFPlayer benötigt
    /*
         Rail-FX Bahnhof
         StartHardware.org
    */
    
    /* ***** ***** Einstellungen ***** ***** ***** *****  ***** ***** ***** *****  ***** ***** ***** ***** */
    int flackerzeitLeuchten = 1000;              // Je höher der Wert, desto mehr flackern die Leuchtstoffröhren beim Einschalten
    int flackerGeschwindigkeit = 100;            // Flackergeschwindigkeit der Leuchtstoffröhren
    int anzeigenWechselMin = 2000;               // Anzeigewechsel alle x Millisekunden
    int anzeigenwechselVariabeleZeit = 3000;     // Um diesen Wert (Millisekunden) variiert der Anzeigewechsel
    int mp3Anzahl = 6;                           // Menge der MP3 Dateien auf der SD-Karte
    int mp3Dauer[] = {13, 16, 16, 10, 9, 9};     // Dauer der MP3 Dateien in Sekunden
    int mp3Wahrscheinlichkeit = 10;              // Wahrscheinlichkeit, mit der MP3s abgespielt werden, 10 oft, 100 selten
    int lautstaerke = 10;                        // Lautstärke des DFPlayers (0 – 30);
    int empfangsPuffer = 55;                     // Ändern der Anzeige kann das Empfangen des Zeitsignals verhindern. Deshalb werden ab dieser Modellbahn-Minute keine Displayänderungen ausgeführt (muss kleiner als 59 sein)
    
    /* Anzeigen */
    int anzeigeAnzahl = 6;                       // Muss so groß sein, wie die Anzahl der angezeigten Texte aus dem Array textSegments
    const char* textSegments[6][7] = {
      {"12", "", "Zugdurchfahrt", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"},
      {"12", "Leipzig Hbf - Halle (Saale)", "Hannover Hbf", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"},
      {"12", "Leipzig Hbf - Halle (Saale)", "Berlin Hbf", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"},
      {"12", "Leipzig Hbf - Halle (Saale)", "Dresden Hbf", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"},
      {"12", "Leipzig Hbf - Halle (Saale)", "Leipzig Hbf", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"},
      {"12", "Leipzig Hbf - Halle (Saale)", "Hamburg Hbf", "RB31", "RB31", "Elsterw-Biehal", "von Coswig (b. Dresde"}
    };
    const char* hinweis[] = {
      "Auf Grund einer Signalstorung ",
      "hat der ICE100 heute leider ca. 25 Minuten Verspatung. ",
      "Wir bitten um Ihr Verstandnis."
    };
    
    /* ***** ***** Ab hier beginnt der Programmcode, der nicht angepasst werden muss ***** ***** ***** ***** */
    // 0X3C+SA0 - 0x3C or 0x3D
    #define I2C_ADDRESS 0x3C
    uint32_t tickTime = 0;
    int tickerPosition = 0;
    #define RTN_CHECK 1
    int leuchtstoffroehrenPins[9] = {4, 5, 6, 7, 8, 9, 10, 11, 12};
    int sensorPin1 = 15;                      // an diesem Pin ist ein Abstandssensor angeschlossen (15 = A1)
    int sensorPin2 = 16;                      // an diesem Pin ist ein Abstandssensor angeschlossen (16 = A2)
    int sensor1Zustand = 0;                   // speichert den aktuellen Zustand des Sensors
    int sensor1ZustandAlt = 0;                // macht den Zustand des Sensors nach dem Neueinlesen verfügbar
    int sensor2Zustand = 0;                   // speichert den aktuellen Zustand des Sensors
    int sensor2ZustandAlt = 0;                // macht den Zustand des Sensors nach dem Neueinlesen verfügbar
    
    /* Speicher Variablen */
    int leuchtstoffroehrenZustand[9] = {1, 1, 1, 1, 1, 1, 1, 1, 1};
    TickerState state;        // Speichert den aktuellen Text-Pointer des OLED-Tickers
    int verspaetung = 0;      // wird per Zufall ermittelt; 1=Verspätung, 0=keine Verspätung
    int anzeigenwechselVariabeleZeitHilfsvariable = 0;
    
    /* Timer Variablen */
    long leuchtstoffroehrenTimer[9];
    long anzeigeTimer;
    
    /* Variablen für das OLED */
    SSD1306AsciiWire oled;
    
    /* Variablen für den MP3 Player*/
    long soundTimer = 0;                     // Timer des DFPlayers
    long soundTimeout = 0;                   // Speichert die Abspieldauer des aktuellen MP3
    int soundState = 0;                      // Status des DFPlayers
    int soundRandom;
    int theSound;
    int soundPlaying = false;
    SoftwareSerial mySoftwareSerial(3, 2);   // RX, TX für den DFPlayer
    DFRobotDFPlayerMini myDFPlayer;          // DFPlayer Objekt
    
    /* 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 = 17;
    int myTime = 8;
    int myMinutes = 0;
    long lastTimeSignal;
    int timeToHour;
    #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
      Wire.begin();                              // I2C Verbindung zum OLED
      Wire.setClock(400000L);                    // I2C Verbindung zum OLED
      mySoftwareSerial.begin(9600);              // started die serielle Kommunikation für den DFPlayer
      oled.begin(&Adafruit128x64, I2C_ADDRESS);  // Start des OLEDs
      for (int i = 0; i < 9; i++) {
        pinMode(leuchtstoffroehrenPins[i], OUTPUT);
        digitalWrite(leuchtstoffroehrenPins[i], HIGH);
      }
      randomSeed(A2);
      oled.tickerInit(&state, Adafruit5x7, 0, false, 27, 100);    // Ticker für die Anzeigetafel
      drawInfo(1);
      /* DFPlayer Setup */
      Serial.println(F("Initializing DFPlayer ... "));
      if (!myDFPlayer.begin(mySoftwareSerial, false)) { // nutze softwareSerial um mit dem DFPlayer zu sprechen
        Serial.println(F("Fehler: Prüfe Verbindung zum DFPlayer und SD-Karte"));
        /*while (true) {
          delay(0); // Code to compatible with ESP8266 watch dog.
          }*/
      }
      Serial.println(F("DFPlayer Mini online."));
      myDFPlayer.volume(lautstaerke);       // Lautstärke wird zugewiesen
      delay(1000);
      pinMode(sensorPin1, INPUT_PULLUP);
      pinMode(sensorPin2, INPUT_PULLUP);
    }
    
    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 *****
          leuchtstoffroehrenEin();      // Leuchtstoffröhren einschalten
          soundAn();                    // Sound An
        } else if (myTime > 18) {       // ***** Abend *****
          leuchtstoffroehrenEin();      // Leuchtstoffröhren einschalten
          soundAn();                    // Sound An
        } else if (myTime > 12) {       // ***** Nachmittag *****
          leuchtstoffroehrenAus();      // Leuchtstoffröhren ausschalten
          soundAn();                    // Sound An
        } else if (myTime > 9) {        // ***** Vormittag *****
          leuchtstoffroehrenAus();      // Leuchtstoffröhren ausschalten
          soundAn();                    // Sound An
        } else if (myTime > 7) {        // ***** Morgen *****
          leuchtstoffroehrenAus();      // Leuchtstoffröhren einschalten
          soundAn();                    // Sound An
        } else if (myTime > 0) {                     // ***** Nacht *****
          leuchtstoffroehrenEin();      // Leuchtstoffröhren einschalten
          soundAus();                    // Sound Aus
        }
        if (myMinutes < empfangsPuffer) {
          anzeigeTafelSensor(); // Anzeigetafel schaltet per Sensor um
          // anzeigeTafelAutomatisch(); // Anzeigetafel schaltet per Zufall um
        }
      }
    }
    
    void anzeigeTafelSensor() {
      sensor1Zustand = digitalRead(sensorPin1);
      sensor2Zustand = digitalRead(sensorPin2);
      //Serial.print("Sensor 1: ");Serial.print(sensor1Zustand);Serial.print("\t Sensor 2: ");Serial.println(sensor2Zustand);
      int i;
      if ((sensor1Zustand == 1) && (sensor2Zustand == 1)) { // Gleis leer
        if ((sensor1ZustandAlt == 0) || (sensor2ZustandAlt == 0)) { // Vorher Gleis besetzt
          i = random(anzeigeAnzahl);
          drawInfo(i);
          verspaetung = random(2);
        }
      }
      if ((verspaetung > 0) && (i != 0)) {
        if (tickTime <= millis()) {
          tickTime = millis() + 30;
          oled.setInvertMode(1);
          int8_t rtn = oled.tickerTick(&state);
          if (rtn <= RTN_CHECK) {
            oled.tickerText(&state, hinweis[(tickerPosition++) % 3]);
          }
        }
      }
      sensor1ZustandAlt = sensor1Zustand;
      sensor2ZustandAlt = sensor2Zustand;
    }
    
    void anzeigeTafelAutomatisch() {
      int i;
      if (anzeigeTimer + anzeigenWechselMin + anzeigenwechselVariabeleZeitHilfsvariable < millis()) { // der zweite Teil verhindert, dass ein Anzeigewechsel dem Empfang des Zeitsignals blockiert
        anzeigeTimer = millis();
        i = random(anzeigeAnzahl);
        drawInfo(i);
        verspaetung = random(2);
        anzeigenwechselVariabeleZeitHilfsvariable = random(anzeigenwechselVariabeleZeit);
      }
      if ((verspaetung > 0) && (i != 0)) {
        if (tickTime <= millis()) {
          tickTime = millis() + 30;
          oled.setInvertMode(1);
          int8_t rtn = oled.tickerTick(&state);
          if (rtn <= RTN_CHECK) {
            oled.tickerText(&state, hinweis[(tickerPosition++) % 3]);
          }
        }
      }
    }
    
    void drawInfo(int i) {      // OLED Ausgabe
      oled.clear();
      oled.setInvertMode(0);
      oled.setCursor(0 , 0);
      oled.setFont(Callibri15);
      oled.println(textSegments[i][0]);
      oled.clearToEOL();
      oled.setCursor(104 , 0);
      oled.setFont(Callibri10);
      oled.println(makeTime());
      oled.clearToEOL();
      oled.setFont(Callibri10);
      oled.setCursor(27 , 1);
      oled.println(textSegments[i][1]);
      oled.setFont(Callibri14);
      oled.setCursor(27 , 2);
      oled.println(textSegments[i][2]);
      oled.setFont(Callibri10);
      oled.setCursor(0 , 5);
      oled.println(makeTime1());
      oled.setCursor(0 , 6);
      oled.println(makeTime2());
      oled.setCursor(27 , 5);
      oled.println(textSegments[i][3]);
      oled.setCursor(27 , 6);
      oled.println(textSegments[i][4]);
      oled.setCursor(54 , 5);
      oled.println(textSegments[i][5]);
      oled.setCursor(54 , 6);
      oled.println(textSegments[i][6]);
    }
    
    String makeTime() {
      String myTimeString = "";
      int myHoursSub = myTime;
      int myMinutesSub = myMinutes;
      if (myHoursSub < 10) myTimeString = " " + String(myHoursSub);
      else myTimeString = String(myHoursSub);
      if (myMinutesSub < 10) myTimeString += ":0" + String(myMinutesSub);
      else myTimeString += ":" + String(myMinutesSub);
      return myTimeString;
    }
    
    String makeTime1() {
      // Nächster Zug
      String myTimeString = "";
      int myHoursSub = myTime;
      int myMinutesSub = myMinutes;
      //int myMinutesSub1=random(30);
      if (myMinutesSub > 29) {
        if (myHoursSub > 22) myHoursSub = 0;
        else myHoursSub++;
      } else {
        myMinutesSub += 30;
      }
      if (myHoursSub < 10) myTimeString = " " + String(myHoursSub);
      else myTimeString = String(myHoursSub);
      if (myMinutesSub < 10) myTimeString += ":0" + String(myMinutesSub);
      else myTimeString += ":" + String(myMinutesSub);
      return myTimeString;
    }
    
    String makeTime2() {
      // Übernächster Zug
      String myTimeString = "";
      int myHoursSub = myTime;
      int myMinutesSub = myMinutes;
      if (myHoursSub >= 23) myHoursSub = 0;
      else myHoursSub++;
      if (myHoursSub < 10) myTimeString = " " + String(myHoursSub);
      else myTimeString = String(myHoursSub);
      if (myMinutesSub < 10) myTimeString += ":0" + String(myMinutesSub);
      else myTimeString += ":" + String(myMinutesSub);
      return myTimeString;
    }
    
    void leuchtstoffroehrenEin() {
      for (int i = 0; i < 9; i++) {
        if (random(flackerzeitLeuchten) == 0) leuchtstoffroehrenZustand[i] = 0;
        if (leuchtstoffroehrenZustand[i] != 0) { // wenn die Röhre nicht eingeschaltet ist
          if (leuchtstoffroehrenZustand[i] + leuchtstoffroehrenTimer[i] < millis()) {
            leuchtstoffroehrenZustand[i] = random(flackerGeschwindigkeit) + 20; // erzeugt eine Timeout-Variable
            leuchtstoffroehrenTimer[i] = millis();
            int myOutput = random(2);
            digitalWrite(leuchtstoffroehrenPins[i], myOutput);
          }
        } else {
          digitalWrite(leuchtstoffroehrenPins[i], 0);
        }
      }
    }
    
    void leuchtstoffroehrenAus() {
      for (int i = 0; i < 9; i++) {
        leuchtstoffroehrenZustand[i] = 1;
        digitalWrite(leuchtstoffroehrenPins[i], HIGH);
      }
      /*if (random(ausschaltWahrscheinlichkeit) == 1) {   //  Einschalten der Leuchtstoffröhren per Zufall
        int thePin = random(10);
        digitalWrite(leuchtstoffroehrenPins[thePin], HIGH);
        leuchtstoffroehrenZustand[thePin] = 0;
        }*/
    }
    
    void soundAn() {
      switch (soundState) {
        case 0:
          Serial.println("soundTimer 0");
          soundRandom = random(mp3Wahrscheinlichkeit);
          if (soundRandom < 1) {
            soundState = 1;
            theSound = random(mp3Anzahl) + 1;
            soundTimer = millis();
            soundTimeout = mp3Dauer[theSound - 1] * 1000;
            Serial.print("Playsound: \t\t\t"); Serial.print(theSound); Serial.print("\t\t\t"); Serial.println(soundTimeout);
            myDFPlayer.playFolder(1, theSound); //play specific mp3 in SD:/15/004.mp3; Folder Name(1~99); File Name(1~255)
            soundPlaying = true;
          } else {
            soundTimer = millis();
            soundTimeout = 500;
            soundState = 1;
          }
          break;
        case 1:
          if (soundTimer + soundTimeout < millis()) {
            soundPlaying = false;
            soundState = 0;
          }
          break;
      }
    }
    
    void soundAus() {
      if (soundPlaying == true) {
        Serial.println(soundPlaying);
        myDFPlayer.pause();
        soundPlaying = false;
      }
    }
     
    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
        timeToHour = millis() - lastTimeSignal;
        lastTimeSignal = millis();
      }
      // 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
      if (receiveStarted == false) {
        if (millis() % 100 < 2) {
          int myNewMinutes = map(millis() - lastTimeSignal, 0, timeToHour, 0, 59);
          myNewMinutes = constrain(myNewMinutes, 0, 59);
          if (myNewMinutes != myMinutes) {
            Serial.print(myTime); Serial.print(":"); Serial.print(myMinutes);
            Serial.print("\t"); Serial.println(myTime);
          }
          myMinutes = myNewMinutes;
        }
      }
    }

    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


    33 Gedanken zu „RailFX: Arduino Bahnhof“

    1. Hallo Stefan,
      erst mal ein großes Lob und ein herzliches Dankeschön für Deine RailFX-Projekte.
      Ich habe schon einige nachgebaut und bin voll begeistert.
      Bin gerade dabei einen Bahnsteig mit Aufzug zu bauen, der mit einem als Motor umgebauten SG90 Servo angetrieben wird und über H-Brücke und 2 Micro-Endschalter jeweils in der oberen und unteren Position die Richtung wechselt. Leider bekomme ich es in meinem Sketch nicht hin, in den jeweiligen Endpostionen eine einstellbare Zeit zu warten. Kannst Du mir helfen wie der Sketch aussehen müsste, damit das funktioniert?

      #include

      #define DIRECTION_CCW -1
      #define DIRECTION_CW 1

      #define ENA_PIN 7 // The Arduino pin connected to the EN1 pin L298N
      #define IN1_PIN 6 // The Arduino pin connected to the IN1 pin L298N
      #define IN2_PIN 5 // The Arduino pin connected to the IN2 pin L298N

      ezButton limitSwitch_1(A0); // create ezButton object that attach to pin A0
      ezButton limitSwitch_2(A1); // create ezButton object that attach to pin A1

      int direction = DIRECTION_CW;
      int prev_direction = DIRECTION_CW;

      void setup() {
      Serial.begin(9600);

      limitSwitch_1.setDebounceTime(50); // set debounce time to 50 milliseconds
      limitSwitch_2.setDebounceTime(50); // set debounce time to 50 milliseconds

      // initialize digital pins as outputs.
      pinMode(ENA_PIN, OUTPUT);
      pinMode(IN1_PIN, OUTPUT);
      pinMode(IN2_PIN, OUTPUT);

      digitalWrite(ENA_PIN, HIGH); // max speed
      digitalWrite(IN1_PIN, HIGH); // control motor A spins clockwise
      digitalWrite(IN2_PIN, LOW); // control motor A spins clockwise
      }

      void loop() {

      limitSwitch_1.loop(); // MUST call the loop() function first
      limitSwitch_2.loop(); // MUST call the loop() function first

      if (limitSwitch_1.isPressed()) {
      direction *= -1; // change direction
      Serial.println(F(“The limit switch 1: TOUCHED”));

      }

      if (limitSwitch_2.isPressed()) {
      direction *= -1; // change direction
      Serial.println(F(“The limit switch 2: TOUCHED”));

      }

      if (prev_direction != direction) {
      Serial.print(F(“The direction -> “));
      if (direction == DIRECTION_CW) {
      Serial.println(F(“CLOCKWISE”));
      digitalWrite(IN1_PIN, HIGH); // control motor A spins clockwise
      digitalWrite(IN2_PIN, LOW); // control motor A spins clockwise

      } else {
      Serial.println(F(“ANTI-CLOCKWISE”));
      digitalWrite(IN1_PIN, LOW); // control motor A spins anti-clockwise
      digitalWrite(IN2_PIN, HIGH); // control motor A spins anti-clockwise

      }

      prev_direction = direction;

      }
      }

      Noch schöner wäre es, wenn man die Funktion in Dein Projekt “Bahnhof” integrieren könnte und der Aufzug zufällig ev. mit der Anzeige oder der Soundausgabe ablaufen würde.

      Viele Grüße und ein schönes Wochenende in Erwartung Deiner Antwort
      Manfred Knöfel

    2. Hallo zusammen

      Echt Klasse. Habe jetzt das Modul fertig nachgebaut und funktioniert auch. Nur ein Problem.

      Die Flackerleuchten gehen bei 20 Uhr aus, sollten aber angehen und gehen um 8 Uhr an und sollten aber ausgehen, Die Schaltbefehle sind gerade falsch herum. Was muss ich ändern?

    3. Hallo Stefan
      Wollte mich nur mal für das RailFX System bedanken,
      bis jetzt hat das meiste auf Anhieb Funktioniet, alles ist gut beschieben,
      das es auch ein anfänger wie ich es kapiert.
      LG Berthold

    4. Hallo zusammen

      Echt Klasse. Habe jetzt das Modul fertig nachgebaut und funktioniert auch. Nur ein Problem.

      Die Flackerleuchten gehen bei 20 Uhr aus, sollten aber angehen und gehen um 8 Uhr an und sollten aber ausgehen, Die Schaltbefehle sind gerade falsch herum. Was muss ich ändern?

    5. Hallo Stefan.
      Ich bin dabei das Modul zu bauen. Bin da allerdings auf ein Problem gestoßen. Ich will mit 12 Volt led schalten.
      Habe mir dazu Mosfet Module gekauft. Doch die wollen das Pwm Signal von vcc. Und bei dir wird ja mit GND geschaltet. Kann man das umstellen im Sketch geht sowas? Das ich das pwm + habe und nicht wie jetzt pwm-?

    6. Hallo,
      kurze Nachfrage, bin in der Welt von Adruino und Programmieren noch ein Anfänger, aber besteht die Möglichkeit den freien A ( 0 ) oder A ( 2 ) für einen Taster oder Schalter zu verwenden? Und mit dieser Option Lichtschranke und Taster oder Schalter einen bestimmten Sound abzuspielen?
      Mit freundlichen Grüßen
      Martin

      1. Hi Martin, ja klar, das geht schon. Die analogen Inputs kann man auch als digitale Pins verwenden. A0 wäre dann Pin 14, A1 wäre Pin 15 usw. Liebe Grüße Stefan

        1. Hallo Stefan,
          danke für die Info, dann wäre ja somit eine “kleine Personalisierung ” für eine Gleis 1 oder X möglich, mit einem Sound-File.
          Denn ich habe bei meinem Eigenbau Bremsmodulen noch einen Schaltkontakt (Schließer) frei, und wenn der aktive wäre könnte der Sound X abgespielt wären.
          Gruß
          Martin

          1. Hallo Stefan,
            kurzer Nachtrag, Ihre “Sound Machine mit Arduino” wäre eigentlich schon die Lösung, man müsste nur die Eingänge vom Taster gegen ein Schalter Signal ändern?
            Gruß
            Martin

    7. Moin Stefan, habe das Rail fx system schon auf der neuen spur z platte in Angriff genommen, meine Frage kann man die lichtschranken vom bahnhofsmodul an 2 arduino anschließen wollte noch eine Geschwindigkeitmessstrecke im Tunnel verbauen und etwas Material einsparen Grüße Michel

      1. Hi Michel, das weiß ich nicht genau. Solange du alle GNDs verbindest, könnte es klappen. Wäre es aber nicht einfacher, die Geschwindigkeitsmessung zum RailFX Modul hinzuzufügen? Liebe Grüße Stefan

    8. Hallo Hartmut,

      vielen Dank für die Nachricht! Zum Glück wissen wir jetzt, woran es lag. Die Module sehen ja fast gleich aus. Ich hab jetzt auch beide da und das MP3-TP-16P macht auch bei mir Probleme. Ich hab keien Ahnung, woran das liegt. Ich hab jetzt aber einen Hinweis zu allen RailFX-Modulen mit MP3-Player hinzugefügt. Dann viel Spaß mit dem System und ich will Fotos sehen ;-)

      Liebe Grüße

      Stefan

    9. Hallo Stefan,
      ich habe noch einmal ein DFPlayer Mini Modul bestellt und es ist heute angekommen.
      Alles funktioniert wunderbar. Warum das andere Modul nicht funktioniert würde mich aber doch interessieren.
      Ich danke dir für deine Mühe, du hast hier ein super Projekt begonnen.

      Gruß Hartmut

    10. Hallo Stefan,
      Bestellt war der dfp aber bekommen habe ich den MP3-TP-16P. Mir ist noch aufgefallen das er auf die Lautstärkeeinstellung nicht reagiert.
      Vielen Dank für deine Mühe.
      Gruß Hartmut

      1. Hi Hartmut, betreibst du das Bahnhofsmodul mit dem Control-Modul? Was steht im Seriellen Monitor der Arduino-Software? Liebe Grüße Stefan

        1. Ja das Control-Modul läuft einwandfrei und steuert die Funktionen im Bahnhofsmodul. Im seriellen Monitor steht:

          21:56:22.547 -> 6:55 6
          21:56:22.750 -> 6:56 6
          21:56:22.852 -> 6:57 6
          21:56:22.953 -> 7:58 7
          21:56:23.055 -> 7:0 7
          21:56:23.259 -> 7:1 7
          21:58:30.586 -> 23:45 23
          21:58:30.688 -> 23:46 23
          21:58:30.756 -> Playsound: 2 16000
          21:58:30.857 -> 23:47 23
          21:58:30.959 -> 23:48 23
          21:58:31.061 -> 23:49 23
          21:58:31.264 -> 23:50 23

          Gruß Hartmut

    11. Hallo Stefan,

      danke für Deine Antwort! Ich antworte mal hier, da es so alle sehen können.

      Hab die Speicherkarte gewechselt, einmal Fat16 einmal Fat32 formatiert. Das mit beiden Karten abwechselnd. Die Dateien und Ordner sind korrekt drauf. Ergebnis: Knacken bleibt, auch mit Labornetzteil 10A. Festgestellt habe ich, dass man die mp3 manuell beim Player starten kann, also die Karten funktionieren Beide. Die Ansagen sind echt cool!
      Vermutlich liegt es am DFP-Player, habe 2 Stück probiert, beide gehen nicht mit dem Code. Aber beide sind auch nicht von DFRobot sondern FN-M16P! Das wird das Problem sein. Habe direkt über die Links bei am*z*n bestellt, da bekommt man eben diese. Bei den Bewertungen hat “Gartenfreund” schön etwas beschrieben. Womöglich muss der Code auf diese Player angepasst werden. Hab aber noch keine Lösung gefunden…

      Viele Grüße!

      1. Hi Nochimmeranonym,

        da bin ich echt gespannt, ob es an den Playern liegt. Ich freue mich auf das Update :-) Und vielen Dank, dass du der Sache auf den Grund gehst!
        Ob das Tauschen der RX und TX-Verbindungen hilft, kann ich mir kaum vorstellen, ist den Versuch aber wert. Danke Marcus für die Idee.

        Liebe Grüße

        Stefan

    12. Hallo Stefan,
      habe mich Deinem Projekt gewidmet, da ich es einfach klasse finde!
      Darum großen Dank!

      Ich bekomme noch keinen Ton aus dem Lautsprecher. Er knackt beim Spannung einschalten und rauscht sehr leise. Die Ansagen werden mit kleinem Knack womöglich begonnen, aber nichts weiter. Was kann noch der Fehler sein?

      1. Hi bleibnochmalanonym,

        das könnte an der SD-Karte liegen. Kannst du mal prüfen, ob sie richtig formatiert ist (https://starthardware.org/dfplayer-mini-mp3-player-fuer-arduino/) und ob die Dateien im Richtigen Ordner abgelegt sind? Das Knacken beim Einschalten ist normal.
        Es könnte aber auch an einer zu klein dimensionierten Stromversorgung liegen. Probier das mal aus, ansonsten schreib mir noch mal eine Email: hello@starthardware.org

        Liebe Grüße

        Stefan

    13. Erst einmal vielen Dank für das tolle Projekt. Ich bin zwar kein Modelleisenbahnbauer, suchte aber ein Geburtstagsgeschenk für einen Freund und wurde hier fündig.
      Ich glaube, dass sich bei der Erweiterung für die Lichtschranken im Fritzing ein Fehler eingeschlichen hat.
      Für mich sieht es so aus, als ob die Lichtschranken an A2 und A1 angeschlossen sind.
      Im Code werden die Pins A1 und A0 verwendet.

      1. Hi Ralf, vielen Dank für den Hinweis! Da war tatsächlich noch ein Fehler drin. Ich hab es im Code geändert :-) Jetzt sollte es funktionieren.
        Liebe Grüße
        Stefan

    14. Halo Stefan, kannst Du mir eine Bezugsquelle für Modellbahnsounds in de Qualität wie Du sie hier zum Download bereitstellt nennen? Auch für die Deine Einsatzstelle finde ich nur Blechdosensounds. Bin auch gern bereits dafür zu bezahlen.

    15. Hi Volker, die Datei SSD1306AsciiWire.h ist eine Bibliothek, die eingebunden werden muss. Oben unter dem Punkt Libraries hab ich beschrieben, wie das geht. Hast du das schon probiert? Das mit dem Kontakt ist eine gute Idee. Ich würde das mit den Sensoren aus dem Bahnübergangsmodul machen: https://starthardware.org/railfx-bahnuebergang/ Ich werd die Tage mal eine Version damit entwickeln. Danke für die Anregung.

      Liebe Grüße Stefan

      1. Ich bin gespannt auf die Neuerung. Der Sensor ist perfekt!!
        Das habe ich gemacht, aber ich finde die Bibliothek nicht, alle anderen sind da. Es geht trotzdem, finde ich nur komisch.
        Gruß Volker

        1. Ich habe noch einen Nachtrag, vielleicht kann mit einem zweiten Sensor noch bei der Einfahrt ertönen lassen “Bitte zurücktreten von der Bahnsteigkante, Achtung bei Einfahrt des Zuges” oder so.
          Gruß Volker

    16. Hallo Stefan, ich bin jetzt bei dem Bahnhof bei und finde die Datei “SSD1306AsciiWire.h” nicht. Kannst Du mir sagen wo ich die finde?
      Eine Frage zur Schaltung, ist es nicht möglich die Anzeige über einen externen Kontakt weiter zu schalten? Immer wenn ein Zug den Bahnhof verlässt, schaltet er einen weiter? Kannst Du mir da einen Tipp geben?

    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.