Modellbahnsteuerung

Hallo zusammen,

mein Name ist Sascha und ich bin Neuling im Bereich Arduino Programmierung. Ich habe als erstes Projekt eine Modellbahn, sowie ein bisschen Drumherum auf dem Zettel.
Aktuell kann ich noch nicht testen, da die Hardware erst noch aufgebaut und verdrahtet wird. Am Sonntag soll das erste Mal getestet werden und es wäre schön, wenn sich dann zumindest schon mal ein bisschen was tut. Ein Foto vom derzeitigen Stand mit einigen Beschriftungen ist einmal angehängt um besser zu erklären, worum es geht. Die blauen Punkte markieren Hall Sensoren zur Zugerfassung. Die Weiche wird nicht verstellt, und sorgt immer für die eingezeichnete Fahrweise.

Grundlegend wird das Projekt mit einem ESP8266 umgesetzt. Die Strecke ist in vier Abschnitte unterteilt (Außenkurve, Innenkurve, Bahnhof, Reststrecke), welche jeweils mit ihrer eigenen Geschwindigkeit durchfahren werden sollen. Auf einen Tastendruck hin, soll der Zug starten. Die Geschwindigkeit soll von einem PWM Ausgang kommen, der einen MOSFET mit der Sollvorgabe füttert. Nach einer vorgegebenen Zeit, soll der Zug wieder anhalten.

Damit das Ganze ein bisschen lebhafter wird, soll auch ein Karussell installiert sein. Auf Tastendruck hin, sollen drei Ausgänge geschaltet werden. Zum einen ein PWM Ausgang für die Beleuchtung, dann ein "normaler" digitaler Ausgang für die Drehung, sowie ein nur kurzzeitig geschalteter Impuls, der auf einem MP3 Player ein Soundfile abspielt. Nach einer festgelegten Zeit soll das Karussell dann wieder aufhören zu drehen und die Beleuchtung wird abgeschaltet.

Durch diverse Forenbeiträge und Mustercodes habe ich versucht, den Delay Befehl zu vermeiden und mir mal etwas zusammengebaut. Ich habe hier mal meinen Code eingefügt und würde gerne wissen, ob ich damit schon auf der richtigen Fährte bin oder ob das eher die falsche Richtung ist:

int inZuege             = 30;     //Züge  Taster ein
int inKarussell         = 32;     //Karussell Taster Ein
int inHallAKE           = 35;     //Hall Sensor Einfahrt Außenkurve Einfahrt
int inHallAKA           = 36;     //Hall Sensor Ausfahrt Außenkurve Ausfahrt (Signalgeber)
int inHallIKE           = 37;     //Hall Sensor Einfahrt Innenkurve Einfahrt
int inHallIKA           = 38;     //Hall Sensor Ausfahrt Innenkurve Ausfahrt (Signalgeber)
int inHallBahnhofE      = 39;     //Hall Sensor Bahnhof Einfahrt
int inHallBahnhofA      = 40;     //Hall Sensor Bahnhof Ausfahrt (Signalgeber)
int inHallTunnel        = 41;     //Hall Sensor Tunnel

int outKarussellPWM     = 2;      //Karussell PWM Ausgang Beleuchtung
int outStrecke1PWM      = 6;      //Streckenabschnitt 1 Außenkurve PWM Ausgang
int outStrecke2PWM      = 7;      //Streckenabschnitt 2 Innenkurve PWM Ausgang
int outStrecke3PWM      = 8;      //Streckenabschnitt 3 Bahnhof PWM Ausgang
int outStrecke4PWM      = 9;      //Streckenabschnitt 4 Reststrecke PWM Ausgang

int outKarussellD       = 14;     //Karussell Dauerausgang Karussell
int outKarussellImp     = 15;     //Karussell Impulsausgang Ton
int outAKSig            = 16;     //Außenkurve Signalgeber
int outIKSig            = 17;     //Innenkurve Signalgeber
int outBahnhofSig       = 18;     //Bahnhof Signalgeber

int helligkeitKarussell = 160;    //Helligkeit Beleuchtung Karussell
int geschwStrecke1      = 120;    //Geschwindigkeit Außenkurve rechts
int geschwStrecke2      = 140;    //Geschwindigkeit Innenkurve rechts
int geschwStrecke3      = 80;     //Geschwindigkeit Bahnhof
int geschwStrecke4      = 160;    //Geschwindigkeit Reststrecke

unsigned long zeitKarussell     = 0;      //Betätigungszeitpunkt Karussell
unsigned long zeitZuege         = 0;      //Betätigungszeitpunkt Züge
unsigned long laufzeitKarussell = 5000;   //Laufzeit Karussell
unsigned long laufzeitZuege     = 60000;  //Laufzeit Züge
unsigned long pauseBahnhof      = 4000;   //Anhaltezeit im Bahnhof
unsigned long impulszeit        = 250;    //Impulsausgang
unsigned long entprellzeit      = 50;     //Entprellzeit Tasterdruck

void setup() {
  pinMode(inZuege, INPUT);
  pinMode(inKarussell, INPUT);
  pinMode(inHallAKE, INPUT);
  pinMode(inHallAKA, INPUT);
  pinMode(inHallIKE, INPUT);
  pinMode(inHallIKA, INPUT);
  pinMode(inHallBahnhofE, INPUT);
  pinMode(inHallBahnhofA, INPUT);
  pinMode(inHallTunnel, INPUT);

  pinMode(outKarussellD, OUTPUT);
  pinMode(outKarussellImp, OUTPUT);
  pinMode(outKarussellPWM, OUTPUT);
  pinMode(outAKSig, OUTPUT);
  pinMode(outIKSig, OUTPUT);
  pinMode(outBahnhofSig, OUTPUT);
  pinMode(outStrecke1PWM, OUTPUT);
  pinMode(outStrecke2PWM, OUTPUT);
  pinMode(outStrecke3PWM, OUTPUT);
  pinMode(outStrecke4PWM, OUTPUT);
}

void loop() {

  //Karussell

  if (digitalRead(inKarussell) == LOW) {                //Prüfen ob Taster Karussell betätigt ist
    analogWrite(outKarussellPWM, helligkeitKarussell);  //PWM Ausgang Beleuchtung setzen
    digitalWrite(outKarussellD, HIGH);                  //Ausgang Drehen setzen
    digitalWrite(outKarussellImp, HIGH);                //Ausgang Impuls setzen
    zeitKarussell = millis();                           //Betätigungszeitpunkt Taster speichern
  }
  if (millis() - zeitKarussell > laufzeitKarussell && digitalRead(inKarussell) == HIGH)   //Prüfen ob Laufzeit abgelaufen und Taster nicht mehr betätigt ist
  {
    analogWrite(outKarussellPWM, 0);                    //PWM Ausgang Beleuchtung zurücksetzen
    digitalWrite(outKarussellD, LOW);                   //Ausgang Drehen zurücksetzen
  }
  if (millis() - zeitKarussell > impulszeit && digitalRead(inKarussell) == HIGH)          //Prüfen ob Impulszeit abgelaufen und Taster nicht mehr betätigt ist
  {
    digitalWrite(outKarussellImp, LOW);                 //Impulsausgang zurücksetzen
  }

  //Züge

  if (digitalRead(inZuege) == LOW) {                    //Prüfen ob Taster Züge betätigt ist
    analogWrite(outStrecke1PWM, geschwStrecke1);        //PWM Ausgang Außenkurve setzen
    analogWrite(outStrecke2PWM, geschwStrecke2);        //PWM Ausgang Innenkurve setzen
    analogWrite(outStrecke3PWM, geschwStrecke3);        //PWM Ausgang Bahnhof setzen
    analogWrite(outStrecke4PWM, geschwStrecke4);        //PWM Ausgang Reststrecke setzen
    zeitZuege = millis();                               //Betätigungszeit Taster speichern
  }
  if (millis() - zeitZuege > laufzeitZuege && digitalRead(inZuege) == HIGH)    //Prüfen ob Laufzeit abgelaufen und Taster nicht mehr betätigt ist
  {
    analogWrite(outStrecke1PWM, 0);                     //PWM Ausgang Außenkurve setzen
    analogWrite(outStrecke2PWM, 0);                     //PWM Ausgang Innenkurve setzen
    analogWrite(outStrecke3PWM, 0);                     //PWM Ausgang Bahnhof setzen
    analogWrite(outStrecke4PWM, 0);                     //PWM Ausgang Reststrecke setzen
  }
}

Für die Zukunft stehen noch einige Erweiterungen an, wo ich zum Großteil leider noch keine richtige Idee zur Umsetzung habe:

Wie kann ich den oberen Code so erweitern, dass eine Entprellzeit für die Taster berücksichtigt wird?

Der Zug soll nach einer zufälligen Anzahl an Runden soll der Zug im Bahnhof anhalten, kurz warten und dann wieder weiter fahren. Wie lässt sich dies umsetzen?

In einer weiteren Ausbaustufe sollen zwei Züge auf der Strecke fahren. Über die Hall Sensoren kann ich ja die die Position erfassen, aber wie lassen sich die diversen Streckenabschnitte gegeneinander verriegeln. Beipielsweise wenn einer der Züge im Bahnhof steht und der nächste in einer der Kurven befindet oder wenn beispielsweise beide in den Kurven sind und einer den Vorrang bekommen muss?

@ Entprellzeit: Frag' die Taster nach ~ 20ms noch mal ab. Wenn sich nichts ändert, dann ist der Wert stabil und du darfst was damit machen.

Esp8266: Du kannst den auch in die Lock packen und eine kleine H-Brücke für die Motorsteuerung verwenden :slight_smile:

Übrigens nette Winterlandschaft

imho solltest du zwei "Reststrecken" machen. Ansonsten steht ein Zug immer in einem deiner Abschnitte rum. mit zwei Reststrecken kannst du sie gleichzeitig durchfahren lassen. Auch brauchst du einen Abschnitt zwischen Bahnhof und Innekurve / Außenkurve, bzw. eine zusätzliche Trennstelle.
Oder muss der Kompromiss auf 4 Abschnitte beibehalten werden?

Der blaue Punkt vor dem Tunnelportal ist mir unklar was der ausdrücken soll.

Ob es den H-Brücken gefällt, wenn sie mit der Lok beim Übergang von einem Abschnitt in den anderen kurz verbunden werden, muss ein anderer Modellbauer beurteilen, bzw. würde mir das an deiner Stelle ganz genau ansehen ob das im Dauerbetrieb laufen kann.

Vieleicht doch eher behandeln als klassische Streckenabschnitte/schalten über Relais und den PWM als "Fahrtrafo" sehen und du schaltest den nur auf die einzelnen Streckenabschnitte auf.

Du hast dir da schon ein sehr interessantes Projekt vorgenommen. Allerdings bin ich der Meinung, dass der Happen für den Anfang etwas groß ist.
Du solltest dir auf jeden Fall mit ein paar kleineren Projekten/Übungssketchen noch ein paar Grundlagen erarbeiten. Arrays und gegebenenfalls Strukturen wären da ein lohnendes Lernziel. Außerdem solltest Du dich mit dem Thema 'Zustandsautomat' befassen. Diese Programmiertechnik ist ganz wichtig für solche Projekte.
Da Du ja Modellbahner bist - ich habe zum Thema blockadefreie Programmierung und Zustandsautomaten vor geraumer Zeit mal ein kleines Tutorial geschrieben. Vielleicht ist das ja auch was für dich (Arrays kommen da auch vor :wink: ).

nooka:
Grundlegend wird das Projekt mit einem ESP8266 umgesetzt.

Da passt dein Sketch aber überhaupt nicht dazu. Der ESP8266 hat nur sehr wenige I/O Pins. Dein Sketch ist offensichtlich für einen Mega geschrieben. Meiner Meinung nach ist der ESP8266 für dieses Projekt nicht geeignet.

noiasca:
Ob es den H-Brücken gefällt, wenn sie mit der Lok beim Übergang von einem Abschnitt in den anderen kurz verbunden werden, muss ein anderer Modellbauer beurteilen, bzw. würde mir das an deiner Stelle ganz genau ansehen ob das im Dauerbetrieb laufen kann.

Ist sicher nicht optimal - vor allem wenn sie asynchron angesteuert werden. Bekommen beide beim Überfahren der Abschnittgrenze das gleiche PWM-Signal könnte es eher funktionieren. Kurzschlußfest muss das Ganze aber sowieso werden, sonst wird er nicht lange Freude daran haben.

zwieblum:
@ Entprellzeit: Frag' die Taster nach ~ 20ms noch mal ab. Wenn sich nichts ändert, dann ist der Wert stabil und du darfst was damit machen.

Wenn man den Taster alle 20ms abfragt braucht man nichts vergleichen. Damit ist der bereits entprellt und der abgefragte Wert kann direkt verwendet werden.

so kann man's natürlich auch betrachen :slight_smile: