Digital table clock

Among the various electronic components I bought more than a year ago, which I hold scattered in a box, there was a 4 digit 7-segment kelly-green display gathering dust. As the display has four digits and a colon it was just perfect for showing the current time.

I can think of different ways of getting current time, e.g., from one of the subframes sent by GPS satellites or from an oscillator, the former requiring just reading the frame and the latter setting-up the clock each time the microcontroller runs out of power. In this case I just implemented a Python script that reads the time from the computer and then sends it via serial communication to Arduino.


#!/usr/bin/env python

def main():
import serial
from time import strftime
from sys import platform as _platform

# Open serial port
if _platform == 'linux' or _platform == 'linux2':
ser = serial.Serial(port='/dev/ttyUSB0',baudrate=9600)
elif _platform == 'win32':
ser = serial.Serial(port='COM4',baudrate=9600)

# Loop
time0 = 0
while ser.isOpen():
try:
time1 = strftime("%H%M%Sn")
if time1 != time0:
ser.write(time1)
time0 = time1
except (KeyboardInterrupt, SystemExit):
ser.close()
break

if __name__ == "__main__":
from sys import exit
exit(main())

On the Arduino side, the microcontroller reads the current time string “%H%M%S”, displays hour and minutes and uses the seconds as a trigger to turn on and off the colon.


#include <SPI.h>

String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
int csPin = 10; //You can use any IO pin but for this example we use 10
long value = 0;

void setup()
{
pinMode(csPin, OUTPUT);
digitalWrite(csPin, HIGH); //By default, don't be selecting OpenSegment

Serial.begin(9600); //Start serial communication at 9600 for debug statements
inputString.reserve(20);

SPI.begin(); //Start the SPI hardware
SPI.setClockDivider(SPI_CLOCK_DIV64); //Slow down the master a bit

//Send the reset command to the display - this forces the cursor to return to the beginning of the display
digitalWrite(csPin, LOW); //Drive the CS pin low to select OpenSegment
SPI.transfer('v'); //Reset command
}

void loop()
{
if (stringComplete) {
value = inputString.toInt();
spiSendTime(value); //Send the four characters to the display
delay(1); //If we remove the slow debug statements, we need a very small delay to prevent flickering
// clear the string:
inputString = "";
stringComplete = false;
}
}

//Given a number, spiSendValue chops up an integer into four values and sends them out over spi
void spiSendTime(long time) {
digitalWrite(csPin, LOW); //Drive the CS pin low to select OpenSegment

SPI.transfer(time / 100000); //Send the left most digit
time %= 100000; //Now remove the left most digit from the number we want to display
SPI.transfer(time / 10000);
time %= 10000;
SPI.transfer(time / 1000);
time %= 1000;
SPI.transfer(time / 100); //Send the right most digit

if (time%2 == 0) {
SPI.transfer(0x77);
SPI.transfer(0x10);
} else {
SPI.transfer(0x77);
SPI.transfer(0x00);
}
digitalWrite(csPin, HIGH); //Release the CS pin to de-select OpenSegment
}

void serialEvent() {
while (Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:
inputString += inChar;
// it the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == 'n') {
stringComplete = true;
}
}
}

I could later reuse this display to show laptimes and/or as a countdown for a 24h race; or even as a speedometer, odometer, etc.