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

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>

// setup oled
#if (SSD1306_LCDHEIGHT != 32)
#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
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(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 —

## 8 thoughts on “Fun with Arduino 40 Station Platform Departure Display with Analog Clock”

1. Brian says:

Brilliant, just finished building, works great, can’t wait for the rtc version !

Like

2. FoxUnpopuli says:

You have 6 inputs here, with 6 messages. Can you combine the inputs, to allow you 2^6=64 messages?

Like

• 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

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

• amy, I do not do projects, also not if they may be payed ones.

Like

4. Joshua Pye says:

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

• Yes, the OLED driver and DCC interfere, they need to run on different Arduinos.

Like

5. Joshua Pye says:

awesome, thank you 🙂

Like