Project – Scrolling text clock

The purpose of this project is to build a scrolling text clock that displays the time as it is spoken (for example, “it’s midnight”).

scrolling-clock-pmdway

This is a quick project – we give you enough to get going with the hardware and sketch, and then you can take it further to suit your needs.

Hardware

You’ll need three major items –

You might want an external power supply, but we’ll get to that later on. The first stage is to fit your real-time clock. Click here for the tutorial if you need help with that. By now I hope you’re thinking “how do you set the time?”.

There’s two answers to that question. If you’re using the DS3231 just set it in the sketch (see below) as the accuracy is very good, you only need to upload the sketch with the new time twice a year to cover daylight savings.

Otherwise add a simple user-interface – a couple of buttons could do it. Finally you just need to put the hardware on the back of the DMD. There’s plenty of scope to meet your own needs, a simple solution might be to align the control board so you can access the USB socket with ease – and then stick it down with some Sugru.

With regards to powering the clock – you can run ONE LED display from the Arduino, and it runs at a good brightness for indoor use. If you want the DMD to run at full, retina-burning brightness you need to use a separate 5V 4A DC power supply. If you’re using two DMDs – that goes to 8A, and so on. Simply connect the external power to one DMD’s terminals (connect the second or more DMDs to these terminals):

highpowerss

If you don’t fancy chopping the end of your power supply cable, use a DC socket breakout.

The Arduino Sketch

You will need to install the following two Arduino libraries – TimerOne and DMD. Then upload the sketch:

// for RTC
#include "Wire.h"
#define DS1307_I2C_ADDRESS 0x68 // the DS1307 RTC is 0x68

// for LED display
#include "SPI.h"
#include "DMD.h"
#include "TimerOne.h"
#include "SystemFont5x7.h"
#include "Arial_black_16.h"
#define DISPLAYS_ACROSS 1 // you could have more than one DMD in a row
#define DISPLAYS_DOWN 1
DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);

String finalString; // used to hold final sentence to display on DMD

void ScanDMD() // required for DMD
{
dmd.scanDisplayBySPI();
}

void setup()
{
// for DMD
Timer1.initialize( 5000 );
Timer1.attachInterrupt( ScanDMD );
dmd.clearScreen(true);

// for RTC
Wire.begin(); // fire up I2C bus
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
// change the variables and uncomment the setDateDs1307 to set the time
// then re-comment out the function and upload the sketch again
second = 0;
minute = 13;
hour = 23;
dayOfWeek = 4;
dayOfMonth = 19;
month = 5;
year = 13;
// setDateDs1307(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
}

// usual RTC functions
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
return ( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
return ( (val/16*10) + (val%16) );
}

void setDateDs1307(byte second, // 0-59
byte minute, // 0-59
byte hour, // 1-23
byte dayOfWeek, // 1-7
byte dayOfMonth, // 1-28/29/30/31
byte month, // 1-12
byte year) // 0-99
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0);
Wire.write(decToBcd(second)); // 0 to bit 7 starts the clock
Wire.write(decToBcd(minute));
Wire.write(decToBcd(hour));
Wire.write(decToBcd(dayOfWeek));
Wire.write(decToBcd(dayOfMonth));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.write(00010000); // sends 0x10 (hex) 00010000 (binary) to control register - turns on square wave
Wire.endTransmission();
}

// Gets the date and time from the ds1307
void getDateDs1307(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
// Reset the register pointer
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0);
Wire.endTransmission();

Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

// A few of these need masks because certain bits are control bits
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f); // Need to change this if 12 hour am/pm
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}

void drawText(String oldString)
{
dmd.clearScreen(true);
dmd.selectFont(Arial_Black_16);
char newString[256];
int sLength = oldString.length();
oldString.toCharArray(newString, sLength+1);
dmd.drawMarquee(newString,sLength,(32*DISPLAYS_ACROSS)-1,0);
long start=millis();
long timer=start;
long timer2=start;
boolean ret=false;
while(!ret){
if ((timer+20) < millis()) {
ret=dmd.stepMarquee(-1,0);
timer=millis();
}
}
}

void createTextTime(int hh, int mm)
// this mashes up all the time data into text as one sentence
{
finalString=" "; // wipe the sentence out for special cases (below)
finalString=finalString+"It's ";

// now add the hour
if (hh==1 || hh==13) {
finalString=finalString+"one ";
}
if (hh==2 || hh==14) {
finalString=finalString+"two ";
}
if (hh==3 || hh==15) {
finalString=finalString+"three ";
}
if (hh==4 || hh==16) {
finalString=finalString+"four ";
}
if (hh==5 || hh==17) {
finalString=finalString+"five ";
}
if (hh==6 || hh==18) {
finalString=finalString+"six ";
}
if (hh==7 || hh==19) {
finalString=finalString+"seven ";
}
if (hh==8 || hh==20) {
finalString=finalString+"eight ";
}
if (hh==9 || hh==21) {
finalString=finalString+"nine ";
}
if (hh==10 || hh==22) {
finalString=finalString+"ten ";
}
if (hh==11 || hh==23) {
finalString=finalString+"eleven ";
}

// now add the minutes
switch(mm){
case 1:
finalString=finalString+"oh one ";
break;
case 2:
finalString=finalString+"oh two ";
break;
case 3:
finalString=finalString+"oh three ";
break;
case 4:
finalString=finalString+"oh four ";
break;
case 5:
finalString=finalString+"oh five ";
break;
case 6:
finalString=finalString+"oh six ";
break;
case 7:
finalString=finalString+"oh seven ";
break;
case 8:
finalString=finalString+"oh eight ";
break;
case 9:
finalString=finalString+"oh nine ";
break;
case 10:
finalString=finalString+"ten ";
break;
case 11:
finalString=finalString+"eleven ";
break;
case 12:
finalString=finalString+"twelve ";
break;
case 13:
finalString=finalString+"thirteen ";
break;
case 14:
finalString=finalString+"fourteen ";
break;
case 15:
finalString=finalString+"fifteen ";
break;
case 16:
finalString=finalString+"sixteen ";
break;
case 17:
finalString=finalString+"seventeen ";
break;
case 18:
finalString=finalString+"eighteen ";
break;
case 19:
finalString=finalString+"nineteen ";
break;
case 20:
finalString=finalString+"twenty ";
break;
case 21:
finalString=finalString+"twenty one ";
break;
case 22:
finalString=finalString+"twenty two ";
break;
case 23:
finalString=finalString+"twenty three ";
break;
case 24:
finalString=finalString+"twenty four ";
break;
case 25:
finalString=finalString+"twenty five";
break;
case 26:
finalString=finalString+"twenty six";
break;
case 27:
finalString=finalString+"twenty seven";
break;
case 28:
finalString=finalString+"twenty eight ";
break;
case 29:
finalString=finalString+"twenty nine ";
break;
case 30:
finalString=finalString+"thirty ";
break;
case 31:
finalString=finalString+"thirty one ";
break;
case 32:
finalString=finalString+"thirty two";
break;
case 33:
finalString=finalString+"thirty three ";
break;
case 34:
finalString=finalString+"thirty four";
break;
case 35:
finalString=finalString+"thirty five ";
break;
case 36:
finalString=finalString+"thirty six";
break;
case 37:
finalString=finalString+"thirty seven";
break;
case 38:
finalString=finalString+"thirty eight ";
break;
case 39:
finalString=finalString+"thirty nine ";
break;
case 40:
finalString=finalString+"forty ";
break;
case 41:
finalString=finalString+"forty one ";
break;
case 42:
finalString=finalString+"forty two ";
break;
case 43:
finalString=finalString+"forty three ";
break;
case 44:
finalString=finalString+"forty four ";
break;
case 45:
finalString=finalString+"forty five ";
break;
case 46:
finalString=finalString+"forty six ";
break;
case 47:
finalString=finalString+"forty seven ";
break;
case 48:
finalString=finalString+"forty eight ";
break;
case 49:
finalString=finalString+"forty nine ";
break;
case 50:
finalString=finalString+"fifty ";
break;
case 51:
finalString=finalString+"fifty one ";
break;
case 52:
finalString=finalString+"fifty two ";
break;
case 53:
finalString=finalString+"fifty three ";
break;
case 54:
finalString=finalString+"fifty four ";
break;
case 55:
finalString=finalString+"fifty five ";
break;
case 56:
finalString=finalString+"fifty six ";
break;
case 57:
finalString=finalString+"fifty seven ";
break;
case 58:
finalString=finalString+"fifty eight ";
break;
case 59: finalString=finalString+"fifty nine "; break;
}

// midday?
if (hh==12 && mm==0) {
finalString=finalString+"midday ";
}
// midnight?
if (hh==00 && mm==0) {
finalString=finalString+"midnight ";
}

}

void loop()
{
// get the time from the RTC
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

// convert the time into a sentence string
createTextTime(hour,minute);

// now send the text to the DMD
drawText(finalString);
}

The sketch has the usual functions to set and retrieve the time from DS1307/3232 real-time clock ICs, and as usual with all our clocks you can enter the time information into the variables in void setup(), then uncomment setDateDs1307(), upload the sketch, re-comment setDateDs1307, then upload the sketch once more. Repeat that process to re-set the time if you didn’t add any hardware-based user interface.

Once the time is retrieved in void loop(), it is passed to the function createTextTime(). This function creates the text string to display by starting with “It’s “, and then determines which words to follow depending on the current time. Finally the function drawText() converts the string holding the text to display into a character variable which can be passed to the DMD.

And here it is in action:

Conclusion

This was a quick project, however we hope you found it either entertaining or useful – and another random type of clock that’s easy to reproduce or modify yourself.

This post brought to you by pmdway.com – offering everything for makers and electronics enthusiasts, with free delivery worldwide.

To keep up to date with new posts at tronixstuff.com, please subscribe to the mailing list in the box on the right, or follow us on twitter @tronixstuff.