Fun with Arduino 40 Station Platform Departure Display with Analog Clock

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.

40020

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.

stationsignoverview

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.

40040

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 —

23 thoughts on “Fun with Arduino 40 Station Platform Departure Display with Analog Clock

    • 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.

      Like

  1. 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

    Like

  2. 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

    Like

  3. 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

    Like

  4. 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.

    Like

  5. 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?

    Like

    • 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’.

      Like

    • 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.

      Like

Leave a comment