Hier ist eine simple Zug- Abfahrtsanzeige. Sie erhebt keinen Anspruch darauf, originalgetreu dem österreichischen Vorbild zu sein, es ist eine nette, kleine Spielerei um die Modelleisenbahn um ein paar kleine Details zu ergänzen.
Dazu braucht man nur einen Arduino(Nano) und ein OLED Display mit IIC oder I2C Anbindung.
Es ist absichtlich recht einfach gehalten, um auch Modellbahner mit wenig Programmier- und Interfaceerfahrung nicht abzuschrecken.
Die Abfahranzeige läuft in einer Schleife, mit einer vordefinierten Sequenz an Zugzielen, Bahnsteignummern und Wartezeiten. Diese Listen können vor dem Aufspielen auf den Arduino (fast) nach Belieben geändert werden, beim Programmablauf aber nicht beeinflußt oder geändert werden. Die Zeitabfolge läuft von unten nach oben, das heißt als Nächstes abfahrende Zug ist an 1. Stelle, der zuletzt abfahrende Zug an letzter Stelle. Je nachdem, wieviele Züge hier eingetragen wurden, ist die Liste insgesamt eher länger oder kürzer.
Beispiel: REX Zug nach Salzburg auf Gleis 3 erscheint in der letzten Zeile, Abfahrt in 9 Minuten. Wenn der nächste Zug abgefahren ist (er verschwindet aus der 1. Zeile) rückt der REX Zug nach Salzburg auf Gleis 3 in die vorletzte Zeile nach, Abfahrt in 6 Minuten. usw...
Die Zeit, nach wievielen Sekunden die Zeilen nachrücken sollen, kann natürlich geändert werden, Default sind hier 10 Sekunden.
Umlaute und Sonderzeichen können leider nicht dargestellt werden.
Wie eingangs schon erwähnt, die Schaltung ist simpel. In meinem Beispiel habe ich 2 Displays angeschlossen, die zeigen das exakt gleiche Bild. Wer mehr oder weniger Displays anhängen möchte, entfernt einfach die 2 Datenleitungen vom Arduino oder klemmt zusätzliche auf die beiden schon Vorhandenen. Es ist keine Umkonfiguration nötig.
Die Schaltung ist mehr als simpel, man benötigt nur:
Anschluß über IIC bzw. I2C (2 Pins + 2 Pins für Spannungsversorgung)
Sehr viel Info und libs für Monochromdisplays findet man bei olikraus.
Die Bibliothek von olikraus muß zuvor noch eingebunden werden, macht die Arduino IDE eh scho von selbst, einfach Unter Sketch / Bibliothek einbinden / Bibliotheken verwalten nach 8g2 suchen und installieren.
/* Initial idea from https://forum.arduino.cc/index.php?topic=512089.0https://phobien.ndesign.de Posting #13 Display Library & Doku https://github.com/olikraus/u8g2/wiki possibly helpful https://arduino-projekte.info/schriftarten-fuer-oled-display/ OLED Displays must use IIC bus but can be connected to an digital I/O Port of the Arduino. Please check that Voltage of displays and Arduino corresponds. Arduino Nano (china Clone, with 5V logic level Voltage) used OLED Display: 4pin 0.96" White/Blue/Yellow blue 0.96 inch OLED 128X64 OLED Display Module 0.96" IIC I2C Communicate for arduino Driver IC SSD1306, VCC 3-12V, Interface IIC/SPI OLED SSD1306 IIC Display #1 - SCK - D2 OLED SSD1306 IIC Display #1 - SDA - D3 There can be connected more Displays to those ports (of course they will show the same content) no further changes necessary OLED SSD1306 IIC Display #2 - SCK - D2 OLED SSD1306 IIC Display #2 - SDA - D2 FONTS used u8g2_font_crox3hb_tr bold for Station name/direction, 12 pixel Font u8g2_font_crox2h_tf common text 10 pixel Font Optional FONTS (not used here) u8g2_font_HelvetiPixelOutline_tr Ads?, 12 pixel Font u8g2_font_tenstamps_mr inverted (like website access counter) 12 pixel Font DISPLAY Options 5 lines of Info for train lists plus Header per line following information is shown: a. Train Type 3 characters (REX, R, ICE, S50, CJX, ...) b. destination City e.g. "Salzburg" max 14 characters, if more chars are specified they will be cut off c. Track number, one digit e.g. 5 d. Time [minutes] until train arrives, one digit e.g. 2 optional: Clear display = screen_OLED[X]("", "", "", 0, 0); Example CALL: OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline("R ", "Linz", 3, 0); screen_OLED1_2ndline("CJX", "Twinkydorf", 4, 2); screen_OLED1_3rdline("S45", "Zwettl", 5, 4); screen_OLED1_4thline("R ", "Pressbaum", 1, 5); screen_OLED1_5thline("REX", "Wien West BHF", 2, 6); } while ( OLED1.nextPage() ); delay(std_delay); */ #include <U8g2lib.h> #include <Wire.h> //======== INIT Section ============= int std_delay = 10000; //define standard delay between Screen steps //Define Display type and Pins used (any digital IO pin possible) for every Display U8G2_SSD1306_128X64_NONAME_1_SW_I2C OLED1(U8G2_R0, /* clock=*/ 2, /* data=*/ 3, /* reset=*/ U8X8_PIN_NONE); //======== INIT Section END ========= void setup() { Serial.begin(9600); Serial.println("Setup started..."); OLED1.begin(); //start Display1 Serial.println("std_delay set to " + String(std_delay)); Serial.println("Setup done..."); } //Define subroutine for Display-Header void screen_OLED1_header() { OLED1.setFont(u8g2_font_5x7_tr); OLED1.setCursor(0, 10); OLED1.println("ZUG ZIEL GLEIS MIN" ); } //Define subroutine for 1st Display-line void screen_OLED1_1stline(String Zug, String Ziel, byte Gleis, byte Min) { if (Ziel.length() < 15) { do { Ziel = Ziel + " "; } while (Ziel.length() < 14); } else { Ziel.remove(14); } OLED1.setFont(u8g2_font_5x7_tr); OLED1.setCursor(0, 20); OLED1.println(Zug + " " + Ziel + " " + String(Gleis) + " " + String(Min)); } //Define subroutine for 2nd Display-line void screen_OLED1_2ndline(String Zug, String Ziel, byte Gleis, byte Min) { if (Ziel.length() < 15) { do { Ziel = Ziel + " "; } while (Ziel.length() < 14); } else { Ziel.remove(14); } OLED1.setFont(u8g2_font_5x7_tr); OLED1.setCursor(0, 30); OLED1.println(Zug + " " + Ziel + " " + String(Gleis) + " " + String(Min)); } //Define subroutine for 3rd Display-line void screen_OLED1_3rdline(String Zug, String Ziel, byte Gleis, byte Min) { if (Ziel.length() < 15) { // fill Target name with blanks to 14 characters do { Ziel = Ziel + " "; } while (Ziel.length() < 14); } else { // shrink Target name to 14 or less characters Ziel.remove(14); } OLED1.setFont(u8g2_font_5x7_tr); OLED1.setCursor(0, 40); OLED1.println(Zug + " " + Ziel + " " + String(Gleis) + " " + String(Min)); } //Define subroutine for 4th Display-line void screen_OLED1_4thline(String Zug, String Ziel, byte Gleis, byte Min) { if (Ziel.length() < 15) { do { Ziel = Ziel + " "; } while (Ziel.length() < 14); } else { Ziel.remove(14); } OLED1.setFont(u8g2_font_5x7_tr); OLED1.setCursor(0, 50); OLED1.println(Zug + " " + Ziel + " " + String(Gleis) + " " + String(Min)); } //Define subroutine for 5th Display-line void screen_OLED1_5thline(String Zug, String Ziel, byte Gleis, byte Min) { if (Ziel.length() < 15) { do { Ziel = Ziel + " "; } while (Ziel.length() < 14); } else { Ziel.remove(14); } OLED1.setFont(u8g2_font_5x7_tr); OLED1.setCursor(0, 60); OLED1.println(Zug + " " + Ziel + " " + String(Gleis) + " " + String(Min)); } void loop(void) { Serial.println("loop started (again)"); // ======================== STEP =============================== OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline("REX", "Salzburg", 5, 2); screen_OLED1_2ndline("S50", "Tweetydorf", 2, 3); screen_OLED1_3rdline(" R ", "Linz", 3, 5); screen_OLED1_4thline("CJX", "Twinkydorf", 4, 6); screen_OLED1_5thline("S45", "Zwettl", 5, 9); } while ( OLED1.nextPage() ); delay(std_delay); //OLED2.firstPage(); // do { // screen_OLED2("SALZBURG", "RJ 65", "", 1, 6); // } while ( OLED2.nextPage() ); //delay(std_delay); OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline("REX", "Salzburg", 5, 0); screen_OLED1_2ndline("S50", "Tweetydorf", 2, 1); screen_OLED1_3rdline(" R ", "Linz", 3, 3); screen_OLED1_4thline("CJX", "Twinkydorf", 4, 4); screen_OLED1_5thline("S45", "Zwettl", 5, 7); } while ( OLED1.nextPage() ); delay(std_delay); OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline("S50", "Tweetydorf", 2, 0); screen_OLED1_2ndline(" R ", "Linz", 3, 1); screen_OLED1_3rdline("CJX", "Twinkydorf", 4, 2); screen_OLED1_4thline("S45", "Zwettl", 2, 6); screen_OLED1_5thline(" R ", "Pressbaum", 5, 8); } while ( OLED1.nextPage() ); delay(std_delay); OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline("R ", "Linz", 3, 0); screen_OLED1_2ndline("CJX", "Twinkydorf", 4, 2); screen_OLED1_3rdline("S45", "Zwettl", 2, 4); screen_OLED1_4thline("R ", "Pressbaum", 5, 6); screen_OLED1_5thline("REX", "Wien West BHF", 2, 6); } while ( OLED1.nextPage() ); delay(std_delay); OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline("CJX", "Twinkydorf", 4, 0); screen_OLED1_2ndline("S45", "Zwettl", 2, 2); screen_OLED1_3rdline(" R ", "Pressbaum", 5, 4); screen_OLED1_4thline("REX", "Wien West BHF", 2, 5); screen_OLED1_5thline("S50", "Loosdorf", 3, 9); } while ( OLED1.nextPage() ); delay(std_delay); OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline("S45", "Zwettl", 2, 0); screen_OLED1_2ndline(" R ", "Pressbaum", 5, 2); screen_OLED1_3rdline("REX", "Wien West BHF", 2, 3); screen_OLED1_4thline("S50", "Loosdorf", 3, 6); screen_OLED1_5thline("RJ ", "Wien HBF", 2, 7); } while ( OLED1.nextPage() ); delay(std_delay); OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline(" R ", "Pressbaum", 5, 0); screen_OLED1_2ndline("REX", "Wien West BHF", 2, 1); screen_OLED1_3rdline("S50", "Loosdorf", 3, 5); screen_OLED1_4thline("RJ ", "Wien HBF", 2, 6); screen_OLED1_5thline("REX", "Salzburg", 5, 9); } while ( OLED1.nextPage() ); delay(std_delay); OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline("REX", "Wien West BHF", 2, 0); screen_OLED1_2ndline("S50", "Loosdorf", 3, 2); screen_OLED1_3rdline("RJ ", "Wien HBF", 2, 2); screen_OLED1_4thline("REX", "Salzburg", 5, 7); screen_OLED1_5thline("S50", "Tweetydorf", 2, 9); } while ( OLED1.nextPage() ); delay(std_delay); OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline("S50", "Loosdorf", 3, 0); screen_OLED1_2ndline("RJ ", "Wien HBF", 2, 1); screen_OLED1_3rdline("REX", "Salzburg", 5, 4); screen_OLED1_4thline("S50", "Tweetydorf", 2, 7); screen_OLED1_5thline(" R ", "Linz", 3, 8); } while ( OLED1.nextPage() ); delay(std_delay); OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline("RJ ", "Wien HBF", 2, 0); screen_OLED1_2ndline("REX", "Salzburg", 5, 1); screen_OLED1_3rdline("S50", "Tweetydorf", 2, 5); screen_OLED1_4thline(" R ", "Linz", 3, 7); screen_OLED1_5thline("CJX", "Twinkydorf", 4, 9); } while ( OLED1.nextPage() ); delay(std_delay); }
Wie man sieht, ist das einfach eine Aneinandereihung von Display-Aufrufen:
OLED1.firstPage(); do { screen_OLED1_header(); screen_OLED1_1stline("RJ ", "Wien HBF", 2, 0); screen_OLED1_2ndline("REX", "Salzburg", 5, 1); screen_OLED1_3rdline("S50", "Tweetydorf", 2, 5); screen_OLED1_4thline(" R ", "Linz", 3, 7); screen_OLED1_5thline("CJX", "Twinkydorf", 4, 9); } while ( OLED1.nextPage() ); delay(std_delay); }
Jede Zeile in diesem Konstrukt ist inhaltlich folgendermaßen aufgebaut:
Ich habe hier absichtlich keine Automatismen eingebaut, könnte natürlich die Zugziele in ein Array reinschreiben und das dann Stück für Stück auslesen, aber das wäre für eine simple Lösung zu kompliziert.
Was ich eingebaut hab, ist eine Prüfung auf die Stellenanzahl beim Zielnamen auf 14, bei weniger werden Leerzeichen hinzugefügt, bei mehr wird der Name abgeschnitten. Zugname mit 3 Zeichen, Bahnsteignummer und Zeit mit je einer einstelligen Zahl angeben!