Blockstellensteuerung mit Arduino

Daß wir analog fahren und schalten ist dem aufmerksamen Leser bestimmt bisher nicht entgangen. Und das wir trotzdem einen zum Teil recht automatisch ablaufenden Fahrbetrieb haben möchten auch nicht. Und dazu gehört natürlich eine Blockstellensteuerung. Irgendwie bietet sich da ein Arduino an ;)

Kurze Einführung in Blockstellen allgemein

Ein “Block” ist ein Streckenabschnitt, der sich selbst nach hinten sichert, in dem er, sobald in ihm ein Zug ist, die Einfahrt in ihn für nachfolgende Züge verbietet. Ist kein Zug im Block lässt er den nachfolgenden Zug einfahren und - schaltet dann wieder um und macht “dicht”, denn nun ist ja ein Zug drin.

Auf der Modellbahn heisst das, da wir ja “im Kreis” fahren, dass irgendwo der letzte Block wieder an den ersten stösst. Wenn man also z.B. drei Züge automatisch fahren lassen möchte, braucht man vier Blöcke - immer einen mehr als maximal gewünschte Züge auf einem Gleis, denn ein Block muss ja immer frei sein damit die Einfahrt erlaubt wird:

Block

In diesem Beispiel habe ich den ersten Block auch Block 1 genannt, den nachfolgenden Block 2 usw. - manchmal sieht man die Bezeichnung andersherum, was ja auch völlig egal ist - zum Verstehen der Schaltung finde ich es aber so einfacher. Block 1 ist “FREI”, demnach ist das Einfahrsignal für diesen Block 1 (Signal 1) auch auf “FREI” gestellt. Der Zug mit dem grünen Richtungspfeil, der in Block 2 rumsteht, darf nun in Block 1 einfahren. Sobald der in Block 1 ist, schaltet nun das Signal den Block 1 auf “BESETZT” und das zugehörige Signal auf “HALT”:

Block2

Wie schon erwähnt besteht ein Block aus einer längeren Blockstrecke und einer “Stopstrecke” vor dem Signal des nächsten Blockes. Wenn also eine Lok anhalten soll, muss die Stopstrecke entsprechend lang sein - das kennt man ja von bisherigen Streckensignalen auch, sofern man mit dem Signal den Zug beeinflusst. Für N sind das - wenn man nicht abrupt stehen bleiben möchte sondern vielleicht noch etwas geregelt bremsen möchte (das kommt später noch unter Feinheiten ;) ) so um die 30 cm - je nachdem, welche Loks man hat. Achtung: Ist es z.B. eine S-Bahnstrecke, auf der die alten ARNOLD S-Bahn-Züge fahren sollen, muss die Strecke länger sein: Der Motorwagen sitzt in der Mitte des Treibzuges. Die Länge der Blockstrecke bestimmt sich natürlich aus der Länge des längsten Zuges, den ich dort fahren lassen will: Sie muss schonmal länger als dieser Zug sein, damit der bei “Halt” nicht hinten über den Block übersteht. Damit es auch ein bisschen Spass macht, empfehle ich hier mindestens die doppelte Zuglänge, besser die dreifache - ansonsten stehen die Züge fast nur. Für N sind das bei mir mindestens 4,50 m pro Blockstrecke - jaja, der GATX-Kesselwagenzug ist halt lang...

Und damit die Züge auch fahren - man braucht die Blockstellensteuerung ja nur als Sicherung nach hinten - empfehle ich in 4 Blockstellen nur 2 Züge fahren zu lassen. Dann steht nicht immer alles, und es sieht nicht so nervig aus wenn alle paar Sekunden ein Zug anhält und losfährt und anhält... ...aber das ist Geschmackssache. Meine Empfehlung: Anzahl Züge +2 = Anzahl Blockstellen. Technisch geht natürlich

Anzahl Züge + 1 = Anzahl Blockstellen.

Blockstellensteuerung auf der Modellbahn

Analoge Blockstellensteuerbausteine gibt’s auf dem Markt genug, von Fleischmann bis hin zu Conrad-Bausätzen. Jeder dieser Steuerbausteine steuert einen (!) Block und wird mit den anderen Blöcken verbunden, um die Signale weitergeben zu können. Wenn ich also eine Fahrtstrecke mit 4 Blöcken pro Richtung habe sind das schon 4 Steuerbausteine - pro Richtung. Das geht ein bisschen in’s Geld...

Zusätzlich muss man jedem einzelnen Steuerbaustein ja auch den Zustand des Blockabschnittes mitteilen, damit das funktioniert: Besetzt oder Frei. Das wird klassisch mit entweder Kontaktgleisen oder Magnetschaltern, sogenannten “Reed-Kontakten” erledigt. Was ich davon halte? Zuverlässig, ja. Schön? nee... ...und bei Reed-Kontakten muss ich auch noch unter den letzten Wagen eines Zuges einen Magneten kleben, der den Reed-Kontakt auslöst. Geht’s noch? Unter manche Wagen will ich nix kleben... der arme Wagen... echt jetzt...

Warum also nicht die Logik der Schaltung in einem Arduino-Board abarbeiten und eine moderne Gleisbesetztmeldung benutzen? So schwer ist die Logik doch nicht:

    Wenn Blockstrecke 1 belegt ODER Haltestrecke 1 belegt DANN schalte Signal 1 auf rot UND schalte der Haltestrecke 2 den Strom ab.
    Wenn Blockstrecke 1 frei UND Haltestrecke 1 frei DANN schalte Signal 1 auf grün UND gib Strom auf die Haltestrecke 1.

Das ist’s schon im Grunde!

Jeder, der ein bisschen mehr mit z.B. Excel vertraut ist, kennt solche WENN-DANN-Anweisungen. Und in der Arduino-Programmiersprache ist das auch nicht allzu schwer!

Blockstellensteuerung mit Arduino Mega 2560

Das Arduino Mega 2560 hat eine Menge Ein- und Ausgänge, die man ja für die ganzen Steuerungen auch benötigt:

ArduinoMega2560

Und wenn schon so viele Pins (So werden die einzelnen Ein- und Ausgänge genannt) da sind - da machen wir doch direkt eine Steuerung mit je vier Blockstellen in beide Richtungen einer Hauptstrecke, oder? Klar!

Wer hier die Seite Ampelsteuerung mit Arduino gelesen hat, erinnert sich vielleicht noch an den Aufbau eines Arduino-Programmes (genannt ‘Sketch’):

    Variablen definieren
    void setup ()
    void loop ()

Zu Beginn definieren wir also die Variablen, die wir brauchen. Dazu müssen wir wissen, welcher PIN des Arduino Mega 2560 welche Adresse hat. Diese Info bekommt man auf der Arduino webseite (arduino.cc). Also auf geht’s:

Zuerst die Variablen für die Gleisbesetztmelder:

    B1M - Block 1 Melder
    H1M - Haltestrecke 1 Melder

usw. - das sind die Eingänge, wo der Gleisbesetztmelder de Arduino sagt, ob der entsprechende Abschnitt besetzt ist. Für die eine Richtung habe ich Block 1 bis Block 4 verwendet, für die Gegenrichtung Block 5 bis Block 8. Wer’s also noch übersichtlicher haben möchte, ergänzt seine Variablen eventuell noch um “A” für Strecke A oder sowas... ...es sind halt nur Variablennamen, und wie man sich die abkürzt ist dem Arduino egal. Weiter im Text:

Da das Arduino (siehe Ampelsteuerung) durchaus LED-Signale direkt ansteuern kann, brauchen wir Variablen für den Zustand des roten bzw. grünen Lichtes am Blocksignal:

    S1R - Signal 1 rot
    S1G - Signal 1 grün.

usw.

Da wir den Fahrstrom auch noch an- bzw. abschalten müssen (in den Haltestrecken), brauchen wir dafür auch Variablen:

    H1F - Haltestelle 1 Fahrstrom

usw.

An dieser Stelle habe ich gemerkt, dass das Arduino Mega 2560 noch einige Anschlüsse frei hat - also entweder für noch ein paar Blöcke oder für eine LED-Rückmeldung zum Stellpult:

    RB1B - Rückmeldung Block 1 besetzt
    RB1F - Rückmeldung Block 1 frei

wieder usw. für alle Blöcke.

Pro Block brauche ich also 2 Meldeeingänge (Block und Haltestrecke), 2 Signalausgänge (Rot und Grün), 1 Fahrstromsignal für die Haltestrecke und ggf. 2 Ausgänge für die Gleispultrückmeldung (die mit einer zweifarbigen, grün-roten Duo-LED bestimmt cool aussieht).

 

Nach der Variablendefinition kommt der Abschnitt ‘void setup’, der beim Start des Arduino-Boards einmal durchlaufen wird. Zu Beginn wird dort definiert, ob ein PIN ein Eingang oder ob er ein Ausgang ist, also ob dort das Board informationen bekommt oder Zustände mitteilt. Dort wird auch der “Anfangszustand” aller Pins festgelegt - also z.B. erstmal alle Haltestrecken stromlos und alle Signale auf “rot”.

 

Im Anschluss daran folgt der ‘void loop’, der im Betrieb permanent durchlaufen wird und immer wieder oben anfängt. Dort erfolgen dann die eigentlichen Steueraufgaben. Schaut Euch das Sketch einfach mal an, allzu schwer ist er nicht:

// *********************************************
  //                                           *
  // Blocksteuerung für 4 Bloecke 2 Richtungen *
  //   - mit Besetztmeldung für Stellpult -    *
  //                                           *
  //       Blöcke 1 bis 4 = Richtung 1         *
  //       Blöcke 5 bis 8 = Richtung 2         *
  //                                           *
  // *******************************************
 
// Anschlüsse:

  // a: Melder
 
int B1M = 97;    // Block 1 Melder - PIN A0
int H1M = 96;    // Halte 1 Melder - PIN A1
int B2M = 95;    // Block 2 Melder - PIN A2
int H2M = 94;    // Halte 2 Melder - PIN A3
int B3M = 93;    // Block 3 Melder - PIN A4
int H3M = 92;    // Halte 3 Melder - PIN A5
int B4M = 91;    // Block 4 Melder - PIN A6
int H4M = 90;    // Halte 4 Melder - PIN A7
int B5M = 89;    // Block 5 Melder - PIN A8
int H5M = 88;    // Halte 5 Melder - PIN A9
int B6M = 87;    // Block 6 Melder - PIN A10
int H6M = 86;    // Halte 6 Melder - PIN A11
int B7M = 85;    // Block 7 Melder - PIN A12
int H7M = 84;    // Halte 7 Melder - PIN A13
int B8M = 83;    // Block 8 Melder - PIN A14
int H8M = 82;    // Halte 8 Melder - PIN A15

  // b: Signale
 
int S1R = 78;    // Signal 1 Rot  - PIN 22
int S1G = 77;    // Signal 1 Grün - PIN 23
int S2R = 76;    // Signal 2 Rot  - PIN 24
int S2G = 75;    // Signal 2 Grün - PIN 25
int S3R = 74;    // Signal 3 Rot  - PIN 26
int S3G = 73;    // Signal 3 Grün - PIN 27
int S4R = 72;    // Signal 4 Rot  - PIN 28
int S4G = 71;    // Signal 4 Grün - PIN 29
int S5R = 42;    // Signal 5 Rot  - PIN 42
int S5G = 41;    // Signal 5 Grün - PIN 43
int S6R = 40;    // Signal 6 Rot  - PIN 44
int S6G = 39;    // Signal 6 Grün - PIN 45
int S7R = 38;    // Signal 7 Rot  - PIN 46
int S7G = 37;    // Signal 7 Grün - PIN 47
int S8R = 36;    // Signal 8 Rot  - PIN 48
int S8G = 35;    // Signal 8 Grün - PIN 49

  // c: Fahrstromschalter

int H1F = 59;    // Fahrstromsignal Halt 1 - PIN 31
int H2F = 58;    // Fahrstromsignal Halt 2 - PIN 32
int H3F = 57;    // Fahrstromsignal Halt 3 - PIN 33
int H4F = 56;    // Fahrstromsignal Halt 4 - PIN 34
int H5F = 59;    // Fahrstromsignal Halt 5 - PIN 35
int H6F = 58;    // Fahrstromsignal Halt 6 - PIN 36
int H7F = 57;    // Fahrstromsignal Halt 7 - PIN 37
int H8F = 56;    // Fahrstromsignal Halt 8 - PIN 38

  // d: Rückmeldung für Stellpult

int RB1B = 6;    // Rueckmeldung Block 1 besetzt - PIN 2
int RB1F = 7;    // Rueckmeldung Block 1 frei   - PIN 3
int RB2B = 1;    // Rueckmeldung Block 2 besetzt - PIN 4
int RB2F = 5;    // Rueckmeldung Block 2 frei   - PIN 5
int RB3B = 15;   // Rueckmeldung Block 3 besetzt - PIN 6
int RB3F = 16;   // Rueckmeldung Block 3 frei   - PIN 7
int RB4B = 17;   // Rueckmeldung Block 4 besetzt - PIN 8
int RB4F = 18;   // Rueckmeldung Block 4 frei   - PIN 9
int RB5B = 23;   // Rueckmeldung Block 5 besetzt - PIN 10
int RB5F = 24;   // Rueckmeldung Block 5 frei   - PIN 11
int RB6B = 25;   // Rueckmeldung Block 6 besetzt - PIN 12
int RB6F = 26;   // Rueckmeldung Block 6 frei   - PIN 13
int RB7B = 64;   // Rueckmeldung Block 7 besetzt - PIN 14
int RB7F = 63;   // Rueckmeldung Block 7 frei   - PIN 15
int RB8B = 13;   // Rueckmeldung Block 8 besetzt - PIN 16
int RB8F = 12;   // Rueckmeldung Block 8 frei   - PIN 17


void setup() {             
  // Pins als OUTPUT oder INPUT definieren
  pinMode(B1M, INPUT); pinMode(H1M, INPUT);
  pinMode(B2M, INPUT); pinMode(H2M, INPUT);
  pinMode(B3M, INPUT); pinMode(H3M, INPUT);
  pinMode(B4M, INPUT); pinMode(H4M, INPUT);
  pinMode(S1R, OUTPUT); pinMode(S1G, OUTPUT); pinMode(H1F, OUTPUT);
  pinMode(S2R, OUTPUT); pinMode(S2G, OUTPUT); pinMode(H2F, OUTPUT);
  pinMode(S3R, OUTPUT); pinMode(S3G, OUTPUT); pinMode(H3F, OUTPUT);
  pinMode(S4R, OUTPUT); pinMode(S4G, OUTPUT); pinMode(H4F, OUTPUT);
  pinMode(B5M, INPUT); pinMode(H5M, INPUT);
  pinMode(B6M, INPUT); pinMode(H6M, INPUT);
  pinMode(B7M, INPUT); pinMode(H7M, INPUT);
  pinMode(B8M, INPUT); pinMode(H8M, INPUT);
  pinMode(S5R, OUTPUT); pinMode(S5G, OUTPUT); pinMode(H5F, OUTPUT);
  pinMode(S6R, OUTPUT); pinMode(S6G, OUTPUT); pinMode(H6F, OUTPUT);
  pinMode(S7R, OUTPUT); pinMode(S7G, OUTPUT); pinMode(H7F, OUTPUT);
  pinMode(S8R, OUTPUT); pinMode(S8G, OUTPUT); pinMode(H8F, OUTPUT);
  pinMode(RB1B, OUTPUT); pinMode(RB1F, OUTPUT);
  pinMode(RB2B, OUTPUT); pinMode(RB2F, OUTPUT);
  pinMode(RB3B, OUTPUT); pinMode(RB3F, OUTPUT);
  pinMode(RB4B, OUTPUT); pinMode(RB4F, OUTPUT);
  pinMode(RB5B, OUTPUT); pinMode(RB5F, OUTPUT);
  pinMode(RB6B, OUTPUT); pinMode(RB6F, OUTPUT);
  pinMode(RB7B, OUTPUT); pinMode(RB7F, OUTPUT);
  pinMode(RB8B, OUTPUT); pinMode(RB8F, OUTPUT);
 
  // Alle Blöcke Rot als Anfangszustand
  digitalWrite(S1R, LOW); digitalWrite(S1G, LOW); digitalWrite(H1F, LOW);
  digitalWrite(S2R, LOW); digitalWrite(S2G, LOW); digitalWrite(H2F, LOW);
  digitalWrite(S3R, LOW); digitalWrite(S3G, LOW); digitalWrite(H3F, LOW);
  digitalWrite(S4R, LOW); digitalWrite(S4G, LOW); digitalWrite(H4F, LOW);
  digitalWrite(S5R, LOW); digitalWrite(S5G, LOW); digitalWrite(H5F, LOW);
  digitalWrite(S6R, LOW); digitalWrite(S6G, LOW); digitalWrite(H6F, LOW);
  digitalWrite(S7R, LOW); digitalWrite(S7G, LOW); digitalWrite(H7F, LOW);
  digitalWrite(S8R, LOW); digitalWrite(S8G, LOW); digitalWrite(H8F, LOW);
 
  // Alle Rueckmeldungen Besetzt als Anfangszustand
  digitalWrite(RB1B, HIGH); digitalWrite(RB1F, LOW);
  digitalWrite(RB2B, HIGH); digitalWrite(RB2F, LOW);
  digitalWrite(RB3B, HIGH); digitalWrite(RB3F, LOW);
  digitalWrite(RB4B, HIGH); digitalWrite(RB4F, LOW);
  digitalWrite(RB5B, HIGH); digitalWrite(RB5F, LOW);
  digitalWrite(RB6B, HIGH); digitalWrite(RB6F, LOW);
  digitalWrite(RB7B, HIGH); digitalWrite(RB7F, LOW);
  digitalWrite(RB8B, HIGH); digitalWrite(RB8F, LOW);
}

// Blöcke steuern
void loop() {
  // Blöcke 1-4 = 1. Fahrtrichtung
  if (B1M == HIGH || H1M == HIGH) {
   digitalWrite(S2R, HIGH);digitalWrite(S2G, LOW); digitalWrite(H2F, LOW); digitalWrite(RB1B, HIGH);
  }
  if (B1M == LOW && H1M == LOW) {
   digitalWrite(S2R, LOW);digitalWrite(S2G, HIGH); digitalWrite(H2F, HIGH); digitalWrite(RB1F, LOW);
  }
 
  if (B2M == HIGH || H2M == HIGH) {
   digitalWrite(S3R, HIGH);digitalWrite(S3G, LOW); digitalWrite(H3F, LOW); digitalWrite(RB2B, HIGH);
  }
  if (B2M == LOW && H2M == LOW) {
   digitalWrite(S3R, LOW);digitalWrite(S3G, HIGH); digitalWrite(H3F, HIGH); digitalWrite(RB2F, LOW);
  }
 
  if (B3M == HIGH || H3M == HIGH) {
   digitalWrite(S4R, HIGH);digitalWrite(S4G, LOW); digitalWrite(H4F, LOW); digitalWrite(RB3B, HIGH);
  }
  if (B3M == LOW && H3M == LOW) {
   digitalWrite(S4R, LOW);digitalWrite(S4G, HIGH); digitalWrite(H4F, HIGH); digitalWrite(RB3F, LOW);
  }
 
  if (B4M == HIGH || H4M == HIGH) {
   digitalWrite(S1R, HIGH);digitalWrite(S1G, LOW); digitalWrite(H1F, LOW); digitalWrite(RB4B, HIGH);
  }
  if (B4M == LOW && H4M == LOW) {
   digitalWrite(S1R, LOW);digitalWrite(S1G, HIGH); digitalWrite(H1F, HIGH); digitalWrite(RB4F, LOW);
  }
 
  // Blöcke 5-8 = 2. Fahrtrichtung
  if (B5M == HIGH || H5M == HIGH) {
   digitalWrite(S6R, HIGH);digitalWrite(S6G, LOW); digitalWrite(H6F, LOW); digitalWrite(RB5B, HIGH);
  }
  if (B5M == LOW && H5M == LOW) {
   digitalWrite(S6R, LOW);digitalWrite(S6G, HIGH); digitalWrite(H6F, HIGH); digitalWrite(RB5F, LOW);
  }
 
  if (B6M == HIGH || H6M == HIGH) {
   digitalWrite(S7R, HIGH);digitalWrite(S7G, LOW); digitalWrite(H7F, LOW); digitalWrite(RB6B, HIGH);
  }
  if (B6M == LOW && H6M == LOW) {
   digitalWrite(S7R, LOW);digitalWrite(S7G, HIGH); digitalWrite(H7F, HIGH); digitalWrite(RB6F, LOW);
  }
 
  if (B7M == HIGH || H7M == HIGH) {
   digitalWrite(S8R, HIGH);digitalWrite(S8G, LOW); digitalWrite(H8F, LOW); digitalWrite(RB7B, HIGH);
  }
  if (B7M == LOW && H7M == LOW) {
   digitalWrite(S8R, LOW);digitalWrite(S8G, HIGH); digitalWrite(H8F, HIGH); digitalWrite(RB7F, LOW);
  }
 
  if (B8M == HIGH || H8M == HIGH) {
   digitalWrite(S5R, HIGH);digitalWrite(S5G, LOW); digitalWrite(H5F, LOW); digitalWrite(RB8B, HIGH);
  }
  if (B8M == LOW && H8M == LOW) {
   digitalWrite(S5R, LOW);digitalWrite(S5G, HIGH); digitalWrite(H5F, HIGH); digitalWrite(RB8F, LOW);
  }
 
}

Jetzt wird’s bei mir langsam Zeit für den Versuchsaufbau - da das Sketch noch nicht getestet ist: Abtippen auf eigene Gefahr (und download kommt erst wenn es getestet wurde). Bald mehr!

Tja, “bald mehr” ist leider ein Versprechen gewesen, dass ich so nicht habe halten können. Beruflich und privat bedingt hat es eine Auszeit von der Modellbahn gegeben - und ein bischen wird es auch noch dauern (wir haben jetzt Juli 2019), bis ich wieder dazu komme. Falls jemand das hier getestet hat, würde ich mich freuen, wenn Ihr mir darüber berichten könntet! :)

...dabei ist mir noch eine Idee gekommen... was wäre, wenn...
...ja, wenn man anstatt der Rückmeldung (die man ja eigentlich auch beim Signal abnehmen kann) eine Steuerung für HP2 (halbe Fahrt) einbaut: Ist der Block vor dem Zug frei, gibt’s HP2 - und ist auch davor der Block frei gibt’s HP1 (volle Fahrt). Also nur dann volle Geschwindigkeit, wenn vor dem Zug 2 Blöcke frei sind... ...für lange Strecken mit vielen Blöcken und wenig Zügen kann das sinnvoll sein, damit ein schnellerer Zug nicht dauernd auf den Bummelzug davor “aufläuft”...