Zum Inhalt springen
RailFX Waldarbeiten Arduino Modellbahn Servo Fallender Baum

RailFX-Waldarbeiten Modul

    Dieses Modul des RailFX-Systems ist vom Fallender-Baum-Set von NOCH inspiriert, zu dem ich eine Anfrage bekommen hatte. Hier nun also gleich eine ganze Einsatzstelle. Es hat folgende Funktionen:

    • Fallender Baum
    • Ladekran (Links, Rechts, Aufnehmen, Ablassen)
    • drei Paar blinkende Warnleuchten
    • Fahrzeugbeleuchtung
    • Einsatzstellen-Beleuchtung

    Da es nicht möglich ist, das Soundmodul DFPlayer gleichzeitig mit Servomotoren zu verwenden, gibt es auf diesem Modul kein leider keine Soundausgabe. Allerdings kann dafür das unabhängige RailFX-Soundmodul (gerade in Arbeit) nutzen. 

    Schaltplan

    RailFX Arduino Fallender Baum Servo Waldarbeiten Modul
    • D10: Servo »Fallender Baum«
    • D11: Kranservo Drehung
    • D12: Kranservo Winde
    • D2 – D7: LEDs für Blinklichter
    • D8, D9: Fahrzeugbeleuchtung
    • A1 – A3: Arbeitslicht (die analogen Inputs werden als digitale Outputs verwendet) 

    Achtung: Am Pin A4 wird das Control-Signal des RailFX Control-Moduls angelegt. Es steuert die Tageszeit und ist zwingend erforderlich.


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


    Bauteile

    Einstellungen im Code

    Auch dieses Modul bietet eine Reihe an Einstellmöglichkeiten. Zum einen sollten alle Zielzustände der Servos eingestellt werden. Es gibt für jeden der drei Servos jeweils zwei Zustände, z.B. baumLiegt und baumSteht. Dabei handelt es sich um die Gradzahl, auf die sich der Servo einstellen soll. 

    Dabei ist zu beachten, dass der Bereich zwischen 0 und 180 liegen sollte, obwohl Servos oft nicht diese Extremwerte erreichen können. Deshalb müssen diese Grenzen ausprobiert werden. Ich schlage vor, sich vorsichtig an diese Grenzen heranzutasten, z.B. 30 – 150, 20 – 160 …

    Des Weiteren muss baumLiegt größer sein als baumStehtkranRechts größer als kranLinks und windeOben größer als windeUnten.

    Der fallende Baum wird per Zufall ausgelöst. Je kleiner die Variable fallenderBaumZufall ist, desto öfter fällt der Baum um.

    Der Beladekran vollzieht eine Animation, die zeitgesteuert abläuft. Die Dauer der einzelnen Phasen lässt ich über das Array kranTimeouts bestimmen. Hier werden die Zeiten in Millisekunden angegeben (1000ms=1s). Die Animation hat folgende Phasen:

    • Warten (Wahrscheinlichkeit)
    • Aufnehmen
    • Warten
    • Drehen
    • Warten
    • Ablassen
    • Warten (Wahrscheinlichkeit)
    • Aufnehmen
    • Warten
    • Drehen
    • Warten
    • Ablassen

    Position 0 und 6 (Die erste Stelle im Array ist immer 0) legen eine Wahrscheinlichkeit fest, mit der der Kran weiter arbeitet: je höher, desto seltener wird die nächste Animationsphase ausgeführt. Dadurch entsteht ein zufälligeres Arbeitsbild. 

    Die Drehgeschwindigkeit der beiden Kranservos (Winde und Drehung) lassen sich über die Variablen kranWindeSpeedund kranDrehungSpeed einstellen. Je größer der Wert, desto langsamer bewegen sich die Servos.

    Es gibt drei Warnlicht-Paare, die unterschiedlich schnell blinken. Die Frequenz kann man über das Array warnlichtTimer einstellen. Eine leichte Varianz in der Frequenz erzeugt ein realistischeres Blinkbild. 

    /* ***** ***** Einstellungen ***** ***** ***** ***** */
    int baumLiegt = 120;         // Servoposition des liegenden Baumes
    int baumSteht = 30;          // Servoposition des stehenden Baumes
    long fallenderBaumZufall = 200000;  // je kleiner der Wert, desto öfter fällt der Baum
    /* Legt die Wartezeiten der Kranabfolge fest:
       warten (Wahrscheinlichkeit), aufnehmen, warten, drehen, warten, ablassen, warten (Wahrscheinlichkeit), aufnehmen, warten, drehen, warten, ablassen
       -> Position 0 und 6 legen eine Wahrscheinlichkeit fest, mit der der Kran weiter arbeitet: je höher, desto seltener
    */
    int kranTimeouts[] = {1000, 3000, 1000, 3000, 1000, 3000, 1000, 3000, 1000, 3000, 1000, 3000};
    int kranWindeSpeed = 10;     // Geschwindigkeit der Kranwinde (größer = langsamer)
    int kranDrehungSpeed = 10;   // Geschwindigkeit der Krandrehung (größer = langsamer)
    int windeOben = 120;         // Servoposition der eingezogenen Kranwinde
    int windeUnten = 60;         // Servoposition der herabgelassenen Kranwinde
    int kranRechts = 160;        // Servoposition des Kranturms Rechts
    int kranLinks = 30;          // Servoposition des Kranturms Links
    int warnlichtTimer[3] = {800, 700, 650}; // Warnlicht Blitzfrequenz

    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 das Modul: RailFX-Waldarbeiten

    /*
        Rail-FX Waldarbeiten-Modul
        StartHardware.org
    
        Permalink: https://starthardware.org/railfx-waldarbeiten-modul/
    */
    
    #include <Servo.h>
    
    /* ***** ***** Einstellungen ***** ***** ***** *****  ***** ***** ***** *****  ***** ***** ***** ***** */
    
    int baumLiegt = 120;         // Servoposition des liegenden Baumes
    int baumSteht = 30;          // Servoposition des stehenden Baumes
    
    long fallenderBaumZufall = 200000;  // je kleiner der Wert, desto öfter fällt der Baum
    
    /* Legt die Wartezeiten der Kranabfolge fest:
       warten (Wahrscheinlichkeit), aufnehmen, warten, drehen, warten, ablassen, warten (Wahrscheinlichkeit), aufnehmen, warten, drehen, warten, ablassen
       -> Position 0 und 6 legen eine Wahrscheinlichkeit fest, mit der der Kran weiter arbeitet: je höher, desto seltener
    */
    int kranTimeouts[] = {1000, 3000, 1000, 3000, 1000, 3000, 1000, 3000, 1000, 3000, 1000, 3000};
    
    int kranWindeSpeed = 10;     // Geschwindigkeit der Kranwinde (größer = langsamer)
    int kranDrehungSpeed = 10;   // Geschwindigkeit der Krandrehung (größer = langsamer)
    int windeOben = 120;         // Servoposition der eingezogenen Kranwinde
    int windeUnten = 60;         // Servoposition der herabgelassenen Kranwinde
    int kranRechts = 160;        // Servoposition des Kranturms Rechts
    int kranLinks = 30;          // Servoposition des Kranturms Links
    
    int warnlichtTimer[3] = {800, 700, 650}; // Warnlicht Blitzfrequenz
    
    /* ***** ***** Ab hier beginnt der Programmcode, der nicht angepasst werden muss ***** ***** ***** ***** */
    
    int warnlichtPins[6] = {2, 3, 4, 5, 6, 7}; // Warnlicht Pins
    int fahrzeugbeleuchtungPins[2] = {8, 9};   // Pins der Fahrzeugbeleuchtung
    int beleuchtungsPins[3] = {15, 16, 17};    // an diesem Pin ist die Einsatzstellen-Beleuchtung angebracht
    int baumServoPin = 10;
    int kranServoPin = 11;
    int windeServoPin = 12;
    
    
    /* State Variablen */
    int baumStatus = 0;                         // 0 = nichts, 1=fällt, 2=liegt, 3=aufrichten
    int kranStatus = 0;
    
    /* Speicher-Variablen */
    int baumPosition;
    int windePosition;
    int kranPosition;
    
    /* Timer Variablen */
    long kranTimer;
    long baumTimer;
    long kranDrehungTimer;
    long kranWindeTimer;
    
    
    int baumTimeout = 100;
    int baumTimeoutMax = 100;
    
    /* Objekte anlegen */
    Servo baumServo;
    Servo kranServo;
    Servo windeServo;
    
    /* 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 = 0;
    
    #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
    
      for (int i = 0; i < 3; i++) {           // Für die Anzahl der Beleuchtung-Pins
        pinMode(beleuchtungsPins[i], OUTPUT);    // Deklariere den Pin als Output
        digitalWrite(beleuchtungsPins[i], HIGH); // Schalte die LED an diesem Pin ab
      }
    
      for (int i = 0; i < 2; i++) {           // Für die Anzahl der Fahrzeugbeleuchtung-Pins
        pinMode(fahrzeugbeleuchtungPins[i], OUTPUT);    // Deklariere den Pin als Output
        digitalWrite(fahrzeugbeleuchtungPins[i], HIGH); // Schalte die LED an diesem Pin ab
      }
    
      for (int i = 0; i < 6; i++) {           // Für die Anzahl der Warnlicht-Pins
        pinMode(warnlichtPins[i], OUTPUT);    // Deklariere den Pin als Output
        digitalWrite(warnlichtPins[i], HIGH); // Schalte die LED an diesem Pin ab
      }
    
      baumServo.attach(baumServoPin);
      kranServo.attach(kranServoPin);
      windeServo.attach(windeServoPin);
    }
    
    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 *****
          warnlichtAn();                // Warnlichter einschalten
          beleuchtungAn();              // Beleuchtung einschalten
          fahrzeugbeleuchtungAn();      // Fahrzeugbeleuchtung einschalten
          baumAn();                     // Fallenden Baum einschalten
          kranAn();                     // Kranbewegungen einschalten
        } else if (myTime > 18) {       // ***** Abend *****
          warnlichtAn();                // Warnlichter einschalten
          beleuchtungAn();              // Beleuchtung einschalten
          fahrzeugbeleuchtungAn();      // Fahrzeugbeleuchtung einschalten
          baumAn();                     // Fallenden Baum einschalten
          kranAn();                     // Kranbewegungen einschalten
        } else if (myTime > 12) {       // ***** Nachmittag *****
          warnlichtAn();                // Warnlichter einschalten
          beleuchtungAus();             // Beleuchtung ausschalten
          fahrzeugbeleuchtungAus();     // Fahrzeugbeleuchtung ausschalten
          baumAn();                     // Fallenden Baum einschalten
          kranAn();                     // Kranbewegungen einschalten
        } else if (myTime > 9) {        // ***** Vormittag *****
          warnlichtAn();                // Warnlichter einschalten
          beleuchtungAus();             // Beleuchtung ausschalten
          fahrzeugbeleuchtungAus();     // Fahrzeugbeleuchtung ausschalten
          baumAn();                     // Fallenden Baum einschalten
          kranAn();                     // Kranbewegungen einschalten
        } else if (myTime > 7) {        // ***** Morgen *****
          warnlichtAn();                // Warnlichter einschalten
          beleuchtungAus();             // Beleuchtung ausschalten
          fahrzeugbeleuchtungAus();     // Fahrzeugbeleuchtung ausschalten
          baumAn();                     // Fallenden Baum einschalten
          kranAn();                     // Kranbewegungen einschalten
        } else {                        // ***** Nacht *****
          warnlichtAus();               // Warnlichter einschalten
          beleuchtungAus();             // Beleuchtung einschalten
          fahrzeugbeleuchtungAn();      // Fahrzeugbeleuchtung einschalten
          baumAus();                    // Fallenden Baum ausschalten
        }
      }
    }
    
    void fahrzeugbeleuchtungAn() {
      digitalWrite(fahrzeugbeleuchtungPins[0], LOW);
      digitalWrite(fahrzeugbeleuchtungPins[1], LOW);
    }
    
    void fahrzeugbeleuchtungAus() {
      digitalWrite(fahrzeugbeleuchtungPins[0], HIGH);
      digitalWrite(fahrzeugbeleuchtungPins[1], HIGH);
    }
    
    void beleuchtungAn() {
      if (random(2000) <= 1) digitalWrite(beleuchtungsPins[0], LOW);
      if (random(2000) <= 1) digitalWrite(beleuchtungsPins[1], LOW);
      if (random(2000) <= 1) digitalWrite(beleuchtungsPins[2], LOW);
    }
    
    void beleuchtungAus() {
      for (int i = 0; i < 3; i++) {
        digitalWrite(beleuchtungsPins[i], HIGH);
      }
    }
    
    void warnlichtAn() {
      for (int i = 0; i < 4; i++) {
        if (millis() % warnlichtTimer[i] < warnlichtTimer[i] / 2) {
          digitalWrite(warnlichtPins[i * 2], HIGH);
          digitalWrite(warnlichtPins[i * 2 + 1], LOW);
        } else {
          digitalWrite(warnlichtPins[i * 2], LOW);
          digitalWrite(warnlichtPins[i * 2 + 1], HIGH);
        }
      }
    }
    
    void warnlichtAus() {
      for (int i = 0; i < 8; i++) {
        digitalWrite(warnlichtPins[i], HIGH);
      }
    }
    
    void kranAn() {
      if ((kranStatus != 0) || (kranStatus != 6)) {
        if (kranTimer + kranTimeouts[kranStatus] < millis()) {
          kranStatus++;
          if (kranStatus > 11) kranStatus = 0;
          kranTimer = millis();
          Serial.print("Kranstatus="); Serial.println(kranStatus);
        }
      }
    
      switch (kranStatus) {
        case 0: // warten
          if (random(kranTimeouts[0]) < 1) kranStatus = 1;
          break;
        case 1:  // aufnehmen
          if (kranWindeTimer + kranWindeSpeed < millis()) {
            if (windePosition < windeOben) {
              windePosition++;
              windeServo.write(windePosition);
            }
            kranWindeTimer = millis();
          }
          break;
        case 2:  // pause
          break;
        case 3:  // drehen
          if (kranDrehungTimer + kranDrehungSpeed < millis()) {
            if (kranPosition < kranRechts) {
              kranPosition++;
              kranServo.write(kranPosition);
            }
            kranDrehungTimer = millis();
          }
          break;
        case 4:  // pause
          break;
        case 5:  // ablassen
          if (kranWindeTimer + kranWindeSpeed < millis()) {
            if (windePosition > windeUnten) {
              windePosition--;
              windeServo.write(windePosition);
            }
            kranWindeTimer = millis();
          }
          break;
        case 6:  // warten
          if (random(kranTimeouts[6]) < 1) kranStatus = 7;
          break;
        case 7:  // aufnehmsen
          if (kranWindeTimer + kranWindeSpeed < millis()) {
            if (windePosition < windeOben) {
              windePosition++;
              windeServo.write(windePosition);
            }
            kranWindeTimer = millis();
          }
    
          break;
        case 8:  // pause
          break;
        case 9:  // drehen
          if (kranDrehungTimer + kranDrehungSpeed < millis()) {
            if (kranPosition > kranLinks) {
              kranPosition--;
              kranServo.write(kranPosition);
            }
            kranDrehungTimer = millis();
          }
          break;
        case 10: // pause
          break;
        case 11: // ablassen
          if (kranWindeTimer + kranWindeSpeed < millis()) {
            if (windePosition > windeUnten) {
              windePosition--;
              windeServo.write(windePosition);
            }
            kranWindeTimer = millis();
          }
          break;
      }
    
    }
    
    void baumAn() {
      switch (baumStatus) {
        case 0:
          if (baumTimer + baumTimeout < millis()) {
            if (random(fallenderBaumZufall) < 1) {
              baumStatus = 1;
              baumTimeout = baumTimeoutMax;
            }
          }
          break;
        case 1:
          if (baumTimer + baumTimeout < millis()) {
            if (baumPosition < baumLiegt) {
              baumPosition++;
              baumTimeout -= 2;
              baumServo.write(baumPosition);
            } else {
              baumTimeout = 10000;
              baumStatus = 2;
            }
            delay(1);
            baumTimer = millis();
          }
    
          break;
        case 2:
          if (baumTimer + baumTimeout < millis()) {
            baumStatus = 3;
            baumTimeout = baumTimeoutMax;
          }
          break;
        case 3:
          if (baumTimer + baumTimeout < millis()) {
            if (baumPosition > baumSteht) {
              baumPosition--;
              baumServo.write(baumPosition);
            } else {
              baumTimeout = 1000;
              baumStatus = 0;
            }
            delay(1);
            baumTimer = millis();
          }
          break;
      }
      /**/
    }
    
    void baumAus() {
      if (baumPosition > baumSteht) {
        if (baumTimer + baumTimeout < millis()) {
          baumPosition--;
          baumServo.write(baumPosition);
        }
      }
    }
    
    /* ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** */
    
    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
    }

    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


    Ein Gedanke zu „RailFX-Waldarbeiten Modul“

    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.