Introduction: POV Clock & Message Display W/RTC and PS/2 Keyboard

About: Retired Math teacher who needs a hobby! So I tinker with stuff: Arduino, welding, my 1958 TR-3 , my tennis serve
Having built a couple of POV devices before, I wanted a way to change the message in real time . So this project uses a PS/2 keyboard attached to the Arduino Uno which allows for changing the message by simply typing in the new message and hitting the enter key.

Below is a short video of it in action


Also to give it something else to do, it defaults to the time when not being used to display messages. I use a Real Time CLock module attached to the Arduino to keep the time current. Without it, the time would need to be set each time the power was turned off.


I used code from various sources and modified as I needed.


The actual build took many turns and went down several dead ends and as such I do not have pictures of the individual steps but I will try to detail as best I can.

Step 1: Supplies I Used

1. piece of wood from basement
2. aluminum pieces from basement
3. misc wires , switches, pcb boards

4. Arduino uno
5. PS/2 connector
6. RTC module from Sparkfun
7. reed switch from Sparkfun
8. 6 wire slip ring from Adafruit

Step 2: The Motor

I used a computer fan which I found at a flea market to be the motor for this project. It is powered by an AC adapter that I had. It outputs 9 volts and 450 mA. This adapter powers both the fan and the Arduino. It is wired thru a switch(see photo). It is secured to the board with screws in each corner.

Step 3: Attachments to the Fan

First I glued a small piece of wood to the fan using an epoxy mix. This would be the base upon which everything else is attached. A second somewhat longer piece of wood is glued to the first. This second piece will be used to attach the aluminum rail which has the 5 LEDs on it.


The five LEDs are inserted into drilled out holes in the aluminum rail and help in place with a small amount of hot glue. The aluminum rail is screwed to the second piece of wood.


Also attached to the rail is a 6 wire slip ring . It is centered over the center of the fan . Then each Led is soldered to one of the six wires. The ground side of the LEDs are all soldered to a common wire ( I used the black one).


The wires that come out of the other side of the slip ring are soldered to lengths of wire and wire tied to the aluminum arm the hangs above the slip ring. The bundle of 6 wires is then routed thru the board to the backside to be attached to the Arduino.

Step 4: The Reed Switch

Also on the front of the board is a Reed switch which is used to trigger the LEDs. It is sitting on a small board which is glued to the board. The wires are routed thru the board to the back to be attached to the arduino.

in order for the switch to operate, I have a small magnet on the end of the screw shown in the photo. Each time it passes the reed switch it will close the reed switch contacts.

Step 5: The Connections on the Back

PS/2 - RTC module - Reed Switch connections


For each of these there are many examples here on Instructables and other sites.

If anyone wants more detail about this particular build, I would be glad to forward what I did.

In the next step I will include my code which is commented freely and should be instructional to some extent.

Step 6: The Code I Used

/*
##########################
# Arduino POV for 5 LEDs #
##########################
Circuit: 5 LEDs
Each character is given a 5x5 grid to represent it's self
For example "H" is
O O
O O
OOOOO
O O
O O
each of these vertical lines is made from a single number
for the "H" example the numbers are 31,4,4,4,31
16 O O
8 O O
4 OOOOO
2 O O
1 O O
31 is 16 + 8 + 4 + 2 + 1 (all leds are lit)
4 is 0 + 0 + 4 + 0 + 0 (only the middle led is lit)
// 7/13 - started with ill fated project
// 11/13 V4 - neede to change fron byte to char for the keyboard stroke.
// 11/29 V5 remove insert blanks and wired switch correctly
// works pretty good for any message less than 11 characters
// 12/2 v6 - start to add time function
// 12/5 - v7 changed showing the time as a default
*/
#define VERSION "1.00a0"
#include
// to use the RTC time
#include "Wire.h"
#define DS1307_ADDRESS 0x68
String stringOne= "";
const int DataPin = 4;
const int IRQpin = 3;
PS2Keyboard keyboard;
const short int debug =0;
const short int debug2 = 0;
const short int debug3 = 0;
const short int debug4 = 0;
int charBreak =1750; //char delay time
int betweenColumns = 685;
int LED1 = 6;
int LED2 = 2;
int LED3 = 5;
int LED4 = 8;
int LED5 = 7;
int switchPina = 10; // reed switch to center the display
int ps2Power = 11; // power for keyboard
// for the time
int second=0, minute=0, hour=0; //start the time on 00:00:00
//needed to recv data
const int totChar = 13;
char textArray[totChar] ;
int textArrayindx = 0;
char textDsply[totChar] ;
// -------------------------- setup ---------------------
void setup()
{
keyboard.begin(DataPin, IRQpin); // keyboard
Wire.begin(); // time
// Serial.begin(9600);
if(debug){
delay(1000);
Serial.println("Keyboard Test:");
}
stringOne = String(" ");
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(LED3, OUTPUT);
pinMode(LED4, OUTPUT);
pinMode(LED5, OUTPUT);
pinMode(switchPina, INPUT); // reed switch
pinMode(ps2Power, OUTPUT); // ps2 keyboard
digitalWrite(switchPina, HIGH);
digitalWrite(ps2Power, HIGH);
// test to see all leds light
digitalWrite(LED1 , HIGH); // set the LED on
delay(500);
digitalWrite(LED2 , HIGH); // set the LED on
delay(500);
digitalWrite(LED3 , HIGH); // set the LED on
delay(500);
digitalWrite(LED4 , HIGH); // set the LED on
delay(500);
digitalWrite(LED5 , HIGH); // set the LED on
delay(500);
}
// --------------------------------------------------------------
// ------------------------------- loop -----------------------
void loop() {
// idea is :
// default will be the time.
// if key stroke entered, then disrupt time and show message.
if (keyboard.available()) {
// read the incoming byte
char c = keyboard.read();
if(debug){
// check for some of the special keys
if (c == PS2_ENTER) {
Serial.print("^");
} else if (c == PS2_TAB) {
Serial.print("[Tab]");
} else if (c == PS2_ESC) {
Serial.print("[ESC]");
} else if (c == PS2_PAGEDOWN) {
Serial.print("[PgDn]");
} else if (c == PS2_PAGEUP) {
Serial.print("[PgUp]");
} else if (c == PS2_LEFTARROW) {
Serial.print("[Left]");
} else if (c == PS2_RIGHTARROW) {
Serial.print("[Right]");
} else if (c == PS2_UPARROW) {
Serial.print("[Up]");
} else if (c == PS2_DOWNARROW) {
Serial.print("[Down]");
} else if( c == PS2_DELETE) {
Serial.print("[Del]");
} else {
// otherwise, just print all normal characters
Serial.print(c);
}
}
delay(10); // prevents overwhelming the serial port
// if hit the enter key and textarray is not empty
if ( c == PS2_ENTER)
{
if(textArrayindx >= 1) {
showIt();
}
else
{
showTime();
}
}
// get here means just another part of the message so add it to the array
// add incoming byte to the message array
textArray[textArrayindx] = c;
textArrayindx ++;
}
else
{
showTime();
}
}
// ----------- functions for the time only ------------------------------
// ----------------------------------------------------------------------------
void showTime()
{
while (keyboard.available() == 0)
{
// now need to create the string to display instead of just printing to the
// serial stream
printDate();
// delay(1000);
}
}
// ----------------------------------------------------------------
byte bcdToDec(byte val) {
// Convert binary coded decimal to normal decimal numbers
return ( (val/16*10) + (val%16) );
}
// ---------------------------------------------------------------
void printDate(){
// Reset the register pointer
Wire.beginTransmission(DS1307_ADDRESS);
byte zero = 0x00;
Wire.write(zero);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 7);
int second = bcdToDec(Wire.read());
int minute = bcdToDec(Wire.read());
int hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
int weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
int monthDay = bcdToDec(Wire.read());
int month = bcdToDec(Wire.read());
int year = bcdToDec(Wire.read());
if(debug){
//print the date EG 3/1/11 23:59:59
Serial.print(month);
Serial.print("/");
Serial.print(monthDay);
Serial.print("/");
Serial.print(year);
Serial.print(" ");
Serial.print(hour);
Serial.print(":");
Serial.print(minute);
Serial.print(":");
Serial.println(second);
}
// create the string to be displayerd
// if hour = 0 then change to 12
// if hr greter than 12 then subtract 12
if(hour >= 13){
hour = (hour - 12);
}
stringOne += hour;
stringOne += ":";
stringOne += minute;
stringOne += ":";
stringOne += second;
int val2 = digitalRead(switchPina);
if (val2 == LOW) {
delay (26);
// displayString("12:34:00");
// displayString(stringOne);
for (int i = 0; i<=(stringOne.length()); i++)
{
displayChar(stringOne[i]);
}
}
stringOne = "" ;
}
// ----------- functions for the message only ------------------------------
// ----------------------------------------------------------------------------
void showIt()
{
while (keyboard.available() == 0)
{
// test to see what the strings look like
if(debug3){
Serial.println(textArray);
Serial.println(textDsply);
delay(3000);
}
sensorTest();
}
// get here means something pressed
// if just enter key then show time
// if anything else then a new message is beginning
char c = keyboard.read();
if ( c == PS2_ENTER) {
clearIt();
showTime();
}
else{
clearIt();
textArray[textArrayindx] = c;
textArrayindx ++;
}
}
// --------------------------------------------------------
void sensorTest()
{
int val = digitalRead(switchPina);
if (val == LOW) {
// need to delay based on size of message
int numberBlanks = (totChar - textArrayindx) / 2 ;
if(debug){
delay(1000);
Serial.println(numberBlanks);
}
if (numberBlanks <= 1){
delay (12);
}else
if (numberBlanks == 2){
delay (26);
} else
if (numberBlanks == 3){
delay (30);
}else
if (numberBlanks == 4){
delay (35);
}else
if (numberBlanks == 5){
delay (40);
}else
if (numberBlanks == 6){
delay (45);
}
for (int k=0; k
displayChar(textArray[k]);
}
}
}
// ------------------------------------------------------------------
void clearIt()
{
// get here means another stream has started
// so need to empty textArray and start over
// loop thru the array and replace the characters
// and reset the index number
for (int z=0; z
(textDsply[z] = ' ');
(textArray[z] = ' ');
textArrayindx = 0;
}
}
int a[] = {1, 6, 26, 6, 1};
int b[] = {31, 21, 21, 10, 0};
int c2[] = {14, 17, 17, 10, 0};
int d[] = {31, 17, 17, 14, 0};
int e[] = {31, 21, 21, 17, 0};
int f[] = {31, 20, 20, 16, 0};
int g[] = {14, 17, 19, 10, 0};
int h[] = {31, 4, 4, 4, 31};
int i[] = {0, 17, 31, 17, 0};
int j[] = {0, 17, 30, 16, 0};
int k[] = {31, 4, 10, 17, 0};
int l[] = {31, 1, 1, 1, 0};
int m[] = {31, 12, 3, 12, 31};
int n[] = {31, 12, 3, 31, 0};
int o[] = {14, 17, 17, 14, 0};
int p[] = {31, 20, 20, 8, 0};
int q[] = {14, 17, 19, 14, 2};
int r[] = {31, 20, 22, 9, 0};
int s[] = {8, 21, 21, 2, 0};
int t[] = {16, 16, 31, 16, 16};
int u[] = {30, 1, 1, 30, 0};
int v[] = {24, 6, 1, 6, 24};
int w[] = {28, 3, 12, 3, 28};
int x[] = {17, 10, 4, 10, 17};
int y[] = {17, 10, 4, 8, 16};
int z[] = {19, 21, 21, 25, 0};
// numbers
int n1[] = {0, 0, 31, 0, 0};
int n2[] = {17, 19, 21, 25, 0};
int n3[] = {0, 17, 21, 21, 31};
int n4[] = {28, 4, 4, 31, 0};
int n5[] = {0, 29, 21, 21, 23};
int n6[] = {31, 5, 5, 7, 0};
int n7[] = {0, 16,16, 16,31};
int n8[] = {0, 31, 21, 21, 31};
int n9[] = {0, 28,20, 20,31};
int n0[] = {0,31,17, 17,31};
int eos[] = {0, 1, 0, 0, 0};
int excl[] = {0, 29, 0, 0, 0};
int ques[] = {8, 19, 20, 8, 0};
int equ[] = {0, 10,10,10,0};
int colon[] = { 0,10,0 };
void displayLine(int line)
{
int myline;
myline = line;
if (myline>=16) {digitalWrite(LED1, HIGH); myline-=16;} else {digitalWrite(LED1, LOW);}
if (myline>=8) {digitalWrite(LED2, HIGH); myline-=8;} else {digitalWrite(LED2, LOW);}
if (myline>=4) {digitalWrite(LED3, HIGH); myline-=4;} else {digitalWrite(LED3, LOW);}
if (myline>=2) {digitalWrite(LED4, HIGH); myline-=2;} else {digitalWrite(LED4, LOW);}
if (myline>=1) {digitalWrite(LED5, HIGH); myline-=1;} else {digitalWrite(LED5, LOW);}
}
void displayChar(char c)
{
if (c == 'a'){for (int i = 0; i <5; i++){displayLine(a[i]);delayMicroseconds( betweenColumns) ;}displayLine(0);}
if (c == 'b'){for (int i = 0; i <5; i++){displayLine(b[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'c'){for (int i = 0; i <5; i++){displayLine(c2[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'd'){for (int i = 0; i <5; i++){displayLine(d[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'e'){for (int i = 0; i <5; i++){displayLine(e[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'f'){for (int i = 0; i <5; i++){displayLine(f[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'g'){for (int i = 0; i <5; i++){displayLine(g[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'h'){for (int i = 0; i <5; i++){displayLine(h[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'i'){for (int it = 0; it <5; it++){displayLine(i[it]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'j'){for (int i = 0; i <5; i++){displayLine(j[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'k'){for (int i = 0; i <5; i++){displayLine(k[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'l'){for (int i = 0; i <5; i++){displayLine(l[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'm'){for (int i = 0; i <5; i++){displayLine(m[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'n'){for (int i = 0; i <5; i++){displayLine(n[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'o'){for (int i = 0; i <5; i++){displayLine(o[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'p'){for (int i = 0; i <5; i++){displayLine(p[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'q'){for (int i = 0; i <5; i++){displayLine(q[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'r'){for (int i = 0; i <5; i++){displayLine(r[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 's'){for (int i = 0; i <5; i++){displayLine(s[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 't'){for (int i = 0; i <5; i++){displayLine(t[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'u'){for (int i = 0; i <5; i++){displayLine(u[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'v'){for (int i = 0; i <5; i++){displayLine(v[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'w'){for (int i = 0; i <5; i++){displayLine(w[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'x'){for (int i = 0; i <5; i++){displayLine(x[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'y'){for (int i = 0; i <5; i++){displayLine(y[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == 'z'){for (int i = 0; i <5; i++){displayLine(z[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '!'){for (int i = 0; i <5; i++){displayLine(excl[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '?'){for (int i = 0; i <5; i++){displayLine(ques[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '.'){for (int i = 0; i <5; i++){displayLine(eos[i]);delayMicroseconds( betweenColumns) ;}displayLine(0);}
if (c == '='){for (int i = 0; i <5; i++){displayLine(equ[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == ':'){for (int i = 0; i <3; i++){displayLine(colon[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
// numbers
if (c == '1'){for (int i = 0; i <5; i++){displayLine(n1[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '2'){for (int i = 0; i <5; i++){displayLine(n2[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '3'){for (int i = 0; i <5; i++){displayLine(n3[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '4'){for (int i = 0; i <5; i++){displayLine(n4[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '5'){for (int i = 0; i <5; i++){displayLine(n5[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '6'){for (int i = 0; i <5; i++){displayLine(n6[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '7'){for (int i = 0; i <5; i++){displayLine(n7[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '8'){for (int i = 0; i <5; i++){displayLine(n8[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '9'){for (int i = 0; i <5; i++){displayLine(n9[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
if (c == '0'){for (int i = 0; i <5; i++){displayLine(n0[i]);delayMicroseconds( betweenColumns);}displayLine(0);}
delayMicroseconds(charBreak);
}

Step 7: Summary and Comments

This is just a short and not too detailed instructable but I will try to answer questions if they come up.

The 5 LEDs are kind of inadequate  for making the letters well defined.  Seven or 8 LEDs would be better.


In person the effect of the LEDs is better than the video shows.





Hardware Hacking

Participated in the
Hardware Hacking

Make It Glow Contest

Participated in the
Make It Glow Contest