Now that we have the OLED screen working (see previous video) we can do something fun with it for an HO model railway: a station platform train departure display, with an analog clock that really works.
The 128×32 pixel display is just a little bit on the tall side for HO scale, but it doesn’t look too bad. It is fun to have one or more of these signs on your station platforms.
The real sign has dark text on a white background. This can be chosen by wiring pin 8, called COLOR_PIN in the code, to GND.
To display the texts is quite straightforward with the oled.setCursor(x,y) and the oled.print(“text”) commands.
The clock also is not too difficult to make … it’s a series of draw line commands:
oled.drawLine(x1, y1, x2, y2, color);
To draw the clock’s 5 minute ticks we use a for() loop where we run the pointer from 0 – 360, with increments of 30 degrees. Some math with cos(angle) and sin(angle) that you might remember from high school renders us the start- and end points of the 5 minute tick lines:
for(int z=0; z<360; z=z+30){ angle = (float)z / 57.3; // Convert degrees to radians int x1=(16+(sin(angle)*15)); // 16,15 is mid point of clock circle int y1=(15-(cos(angle)*15)); int x2=(16+(sin(angle)*(12))); // 15 - 12 specifies line length int y2=(15-(cos(angle)*(12))); oled.drawLine(x2,y2,x3,y3,fg); }
The seconds, minutes and hour hands are drawn in a similar manner.
angle=((float)second * 6 / 57.3); // Convert degrees to radians int x2=(16+(sin(angle)*(14))); int y2=(15-(cos(angle)*(14))); oled.drawLine(16,15,x3,y3,fg);
To update the departure times and texts, 6 inputs are used. These can be connected to manual operated push buttons, or they can be connected to a DCC decoder, to accomplish that every time a train leaves the station a new departure time and text is shown. A random time between Tmin and Tmax is generated for the next departure time.
In the Netherlands the platform signs have a white background with dark blue text. This white background mode can be selected by making the COLOR_PIN low. Personally I found the readability of white text on a black background much better, this is the default with the COLOR_PIN not connected.
Read on below the video …
The complete code:
// OLED Model railway Station Platform Display - Rudy B, August 2019 // 6 different messages can be shown, based on 6 inputs // OLED SSD1306 - I2C wires: SDA or A4, SCL or A5 #define MSG1_PIN 2 #define MSG2_PIN 3 #define MSG3_PIN 4 #define MSG4_PIN 5 #define MSG5_PIN 6 #define MSG6_PIN 7 #define COLOR_PIN 8 #define TMIN 5 // departure time of next train, minimum of random time #define TMAX 13 // departure time of next train, maximum of random time #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> // setup oled Adafruit_SSD1306 oled(4); #if (SSD1306_LCDHEIGHT != 32) #error("Height incorrect, please fix Adafruit_SSD1306.h!"); #endif byte hour, minute, second; byte bg, fg, msgnr, msgnr_old, msgflag, msghour, msgminute; unsigned long time_to_update; float angle; void calc_msg_time() { msgminute = minute + random(TMIN, TMAX); msghour = hour; if (msgminute > 59) { msghour = (msghour + 1) % 24; msgminute = msgminute - 60; } } void print_msg_time() { oled.setTextColor(fg); oled.setTextSize(1); oled.setCursor(40,2); if(msghour < 10) oled.print(" "); oled.print(msghour); oled.print(":"); if(msgminute < 10) oled.print("0"); oled.print(msgminute); } void setup() { pinMode(MSG1_PIN, INPUT_PULLUP); pinMode(MSG2_PIN, INPUT_PULLUP); pinMode(MSG3_PIN, INPUT_PULLUP); pinMode(MSG4_PIN, INPUT_PULLUP); pinMode(MSG5_PIN, INPUT_PULLUP); pinMode(MSG6_PIN, INPUT_PULLUP); pinMode(COLOR_PIN, INPUT_PULLUP); Serial.begin(9600); Wire.begin(); oled.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C // generate random startup time randomSeed(analogRead(A0)); hour = random(7, 20); minute = random(0, 60); } void loop() { // determine background- and foreground color based on COLOR_PIN if(digitalRead(COLOR_PIN) == LOW) {bg = WHITE; fg = BLACK;} else {bg = BLACK; fg = WHITE;} // update time if(millis() > time_to_update) { time_to_update = millis() + 1000UL; second = second + 1; if(second == 60) { second = 0; minute = minute + 1; } if (minute == 60) { minute = 0; hour = (hour + 1) % 24; } } // clear oled oled.clearDisplay(); oled.fillRect(0,0,127,31,bg); // draw clock ticks for(int z=0; z<360;z=z+30){ angle = (float)z / 57.3; int x1=(16+(sin(angle)*15)); int y1=(15-(cos(angle)*15)); int x2=(16+(sin(angle)*(12))); int y2=(15-(cos(angle)*(12))); oled.drawLine(x1,y1,x2,y2,fg); } // draw clock second angle=((float)second * 6 / 57.3) ; //Convert degrees to radians int x2=(16+(sin(angle)*(14))); int y2=(15-(cos(angle)*(14))); oled.drawLine(16,15,x2,y2,fg); // draw clock minute angle=((float)minute * 6 / 57.3) ; //Convert degrees to radians x2=(16+(sin(angle)*(12))); y2=(15-(cos(angle)*(12))); oled.drawLine(16,15,x2,y2,fg); // draw clock hour angle=((float)hour * 30 + (float)minute / 2) / 57.3 ; //Convert degrees to radians x2=(16+(sin(angle)*(10))); y2=(15-(cos(angle)*(10))); oled.drawLine(16,15,x2,y2,fg); /* // platform number oled.fillRect(107, 5, 19, 22, fg); oled.setTextSize(2); oled.setTextColor(bg); oled.setCursor(111,9); oled.print("3"); */ // display time and messages if(!digitalRead(MSG1_PIN)) msgnr = 1; if(!digitalRead(MSG2_PIN)) msgnr = 2; if(!digitalRead(MSG3_PIN)) msgnr = 3; if(!digitalRead(MSG4_PIN)) msgnr = 4; if(!digitalRead(MSG5_PIN)) msgnr = 5; if(!digitalRead(MSG6_PIN)) msgnr = 6; if(msgnr != msgnr_old) { calc_msg_time(); msgnr_old = msgnr; } print_msg_time(); switch (msgnr) { case 1: oled.setCursor(72,2); oled.print("Intercity"); // max 10 characters oled.setCursor(40,12); oled.print("Eindhoven"); // max 14 characters oled.setCursor(40,22); oled.print("via Strijp-S"); // max 14 characters break; case 2: oled.setCursor(72,2); oled.print("Sprinter"); oled.setCursor(40,12); oled.print("sHertogenbosch"); oled.setCursor(40,22); oled.print("via Boxtel"); break; case 3: oled.setCursor(72,2); oled.print("Stoptrein"); oled.setCursor(40,12); oled.print("Maliebaan"); oled.setCursor(40,22); oled.print("via Eindhoven"); break; case 4: oled.setCursor(72,2); oled.print("Intercity"); oled.setCursor(40,12); oled.print("Venlo"); oled.setCursor(40,22); oled.print("via Deurne"); break; case 5: oled.setCursor(72,2); oled.print("Intercity"); oled.setCursor(40,12); oled.print("Maastricht"); oled.setCursor(40,22); oled.print("via Sittard"); break; case 6: oled.setCursor(72,2); oled.print("Intercity"); oled.setCursor(40,12); oled.print("Heerlen"); oled.setCursor(40,22); oled.print("via Weert"); break; } // refresh screen oled.display(); }
In this code a random time is generated for the clock at startup. It is possible however to display the real time via the use of a so called Real Time Clock module. This is the subject of the next video.
— 0 —
Brilliant, just finished building, works great, can’t wait for the rtc version !
LikeLike
You have 6 inputs here, with 6 messages. Can you combine the inputs, to allow you 2^6=64 messages?
LikeLike
Of course you can. Simplest way would be to place the texts to display in a switch – case and generate the number of the case via the 6 input pins with n = pin6 * 32 + pin5 * 16 + … etc.
LikeLike
Hello, I like your project very much. If you are willing, I would like to ask you to create some arduino and display module development for me. Looking forward to your reply
LikeLike
amy, I do not do projects, also not if they may be payed ones.
LikeLike
Hey, thank you so much for posting these guides! I have a decoder set up with a servo and lights. I am trying to integrate the oled platform sign and accessory decoder onto one arduino mega, but the dcc stops working when I add the oled code in. Do I need to run these on separate arduinos? Or is it possible to combine them? Thank you
LikeLike
Yes, the OLED driver and DCC interfere, they need to run on different Arduinos.
LikeLike
awesome, thank you 🙂
LikeLike
Ziet er geweldig uit. Is er ergens een lijstje beschikbaar waar instaat welke componenten ik bij ome Ali kan bestellen om zo’n infobord te maken ? ( en dan bestel ik alles in 2-voud, want ik wil 2 infoborden maken voor mijn toekomstige treinbaan. Moet wel eerlijk zeggen dat ik weinig tot geen verstand heb van programmeren en elektronica, maar gelukkig is er op Internet voldoende hulp en kennis aanwezig. Bedankt alvast…. groet, Erik, Vlissingen
LikeLike
Behalve een Arduino en de 128×32 SSD1306display (let op, 4 pins) heb je niets nodig.
LikeLike
Loopt het op een Arduino Uno???
LikeLike
Ja, een Uno is prima, maar elk ander type zou ook moeten werken.
LikeLike
Huum,ik krijg de volgende error messages, kan jij mij verder helpen:
Arduino:1.8.13 (Windows 10), Kort”Arduino Uno”
Info_display:1:1: error: ‘OLED’ does not name a type
OLED Model railway Station Platform Display – Rudy B, August 2019
^~~~
In file included from C:\Users\apadt\OneDrive\Documents\Arduino\Info_display\Info_display.ino:15:0:
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src/Wire.h:86:8: error: ‘TwoWire’ does not name a type; did you mean ‘TwoWire_h’?
extern TwoWire Wire;
^~~~~~~
TwoWire_h
In file included from C:\Users\apadt\OneDrive\Documents\Arduino\Info_display\Info_display.ino:17:0:
C:\Users\apadt\OneDrive\Documents\Arduino\libraries\Adafruit_SSD1306/Adafruit_SSD1306.h:129:42: error: ‘TwoWire’ has not been declared
Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi = &Wire,
^~~~~~~
C:\Users\apadt\OneDrive\Documents\Arduino\libraries\Adafruit_SSD1306/Adafruit_SSD1306.h:171:3: error: ‘TwoWire’ does not name a type; did you mean ‘TwoWire_h’?
TwoWire *wire;
^~~~~~~
TwoWire_h
C:\Users\apadt\OneDrive\Documents\Arduino\libraries\Adafruit_SSD1306/Adafruit_SSD1306.h:129:58: error: ‘Wire’ was not declared in this scope
Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi = &Wire,
^~~~
C:\Users\apadt\OneDrive\Documents\Arduino\Info_display\Info_display.ino: In function ‘void setup()’:
Info_display:58:3: error: ‘Wire’ was not declared in this scope
Wire.begin();
^~~~
C:\Users\apadt\OneDrive\Documents\Arduino\Info_display\Info_display.ino: In function ‘void setup()’:
Info_display:181:7: error: redefinition of ‘void setup()’
}void setup() {
^~~~~
C:\Users\apadt\OneDrive\Documents\Arduino\Info_display\Info_display.ino:49:6: note: ‘void setup()’ previously defined here
void setup() {
^~~~~
C:\Users\apadt\OneDrive\Documents\Arduino\Info_display\Info_display.ino: In function ‘void loop()’:
Info_display:186:6: error: redefinition of ‘void loop()’
void loop() {
^~~~
C:\Users\apadt\OneDrive\Documents\Arduino\Info_display\Info_display.ino:67:6: note: ‘void loop()’ previously defined here
void loop() {
^~~~
Mange bibliotek ble funnet for “Adafruit_SSD1306.h”
I bruk: C:\Users\apadt\OneDrive\Documents\Arduino\libraries\Adafruit_SSD1306
Ikke i bruk: C:\Users\apadt\OneDrive\Documents\Arduino\libraries\Adafruit_SSD1306_Wemos_Mini_OLED
exit status 1
‘OLED’ does not name a type
Denne rapporten ville hatt mer informasjon med
“Vis detaljert informasjon under kompilering”
alternativet aktivert i Fil -> Innstillinger.
LikeLike
Geen idee wat er aan de hand is. Verrassend dat er allerlei verchillende talen gebruikt worden in de foutboodschap. Weet je zeker dat er niets mis is gegaan bij copy/paste van de code van de blog post naar je .ino file? Waar komt bijvoorbeeld de genoemde ‘TwoWire’ vandaan?
LikeLike
Puuf, sorry nieuw copy fixed it 🙂 Je praat om input pins, hoe/met wat verbind ik deze??
Hartetijk dank! AJ
LikeLike
Inputs die een schakelfunctie hebben kunnen verbonden worden met drukknoppen waarvan de andere kant met GND verbonden is. Niet ingedrukt ‘zweeft’ de input, de interne pull up weerstand houdt hem ‘HIGH’. Een druk op de knop verbindt de input met GND, dan is ie ‘LOW’.
LikeLike
Hartelijk dank voor je antwoord! HG AJ
LikeLike
Nu werk alles perfekt. Hartelijk van uit Oslo! HG AJ
LikeLike
hallo Rudy, kan je dit ook aansturen via DCC, en wat heb je dan nodig?
LikeLike
Ingangen 2-7 worden gebruikt om een van de 6 verschillende boodschappen te tonen. Je kunt deze ingangen aansluiten op de uitgangen van een DCC accessory decoder. Dat kan een Arduino zijn of een willekeurige andere accessory decoder.
LikeLike
hartelijk dank voor je antwoord
LikeLike
Hallo Rudy,
Is het ook mogelijk om meerdere OLED schermen met 1 arduino aan te sturen?
LikeLike
Indien je in staat bent ze beide een eigen adres te geven zou het wellicht kunnen, ik heb er geen ervaring mee.
LikeLike