Single Hand POV Clock

Single Hand POV Clock

A single handed clock that tells hours, minutes, and seconds! How can this be done? Through persistence of vision, of course.

The POV clock uses RGB LEDs to flash red, green, and blue, at the precise moment the hand is in the position for the hours, minutes, and seconds, respectively.

CORR2539

Above you can see the 4 RGB LEDs that are aimed up at the hands of the clock. I should have included 2 LEDs on the bottom of the clock, and 2 at the top, as the hands are faded at the upper portion of the clock due to the LEDs being so far away.

2

Above is the schematic to the device. At the heart is an ATtiny85, programmed via Arduino IDE using an Arduino and a breadboard (with necessary jumpers to program). There are dozens of tutorials online to program these things, so I won’t go into too much detail here, although I will provide you with my code a little later.

Still on the schematic, the SV8 on the left is just a jumper that leads to the board that holds the LEDs via ribbon cable. The SL1 and SL1 are just jumpers for the voltage in, and the motor, respectively.

The motor runs off of 9-12 volts, so the voltage for the ATtiny is regulated via LM7805. The two caps on each side of the LM7805 are part of the standard setup as shown on the LM7805 data sheet.

SV5 is a jumper that leads to a slot interrupter as seen below. The slot interrupter has an IR LED on one side, and a phototransistor on the other. The motor has a wheel on the back side of it with slots in it. When the slot passes in-between the IR LED and phototransistor, the phototransistor conducts, and drops the high signal biased by the +5v and 22k resistor to ground. That is, the signal goes low when the slot passes once every revolution. I should also note that the ground pin on this jumper leads to both IR LED and phototransistor (they share a ground).

CORR2541

The only other component worth noting here is the PC817 photocoupler, which bridges the gap between the higher voltage motor and the ATtiny. My intent, originally, was to be able to control the motor speed with PWM, but I never programmed it that way. I knew the switching on and off of the motor would cause nasty voltage spikes, so I decided to go with optocoupler instead of just transistor; however, that never happened.

Originally, I didn’t have the darlington transistor, but the optocoupler alone was not enough to get the motor started, so I had to add it in. A regular small signal transistor probably would have worked well here too.

1

Above, I’ve included the schematic for the RGB LED board. There are 4 pins on each LED, all .1 inches apart, so I just used the part for a jumper since it’s the same pins and spacing (this is for PCB creation). Notice the current limiting resistors on the anode (common) of each LED.

CORR2536

Above, you can see the completed clock, using number stickers, SRM Stickers We’ve Got Your Number, X-Large, Black, that I ordered on Amazon.

The code, as posted below, uses an interrupt to detect the falling edge of the slot interrupter, indicating that the hand has made a full revolution. Every time this happens, the time, in microseconds, is recorded. This is subtracted from the previous time to get the total amount of microseconds it took for the last revolution of the hand.

The variable, tick, is then recorded by taking the total revolution time and dividing it by 60. So, for example, if it takes 1200 microseconds for one revolution, then a tick (or one second on the clock) occurs every (1200/60 = 20) twenty microseconds. This is how the program keeps track of the position of the hand.

The position of the hand is estimated by taking the current time in microseconds, subtracting it from the last moment when it passed the slot, and dividing it by the tick time. So, for example, if the hand passed the 12 at 546000 microseconds, and the current time is 546600 microseconds, then the position is at 546600 – 546000 = 600, then 600 / 20(the example tick time)  = 30 seconds past the moment when the slot interrupt occurred. Easy!

For speed, I opted out of the digitalwrite function and went for port control. The port is written for every cycle of the main loop, and is updated every time as well based on the current position of the hands.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/*
LED POV REFLECTION CLOCK
 by George Gardner
 
 PB0 - red - physical pin 5 to UNO D8
 PB1 - blue - physical pin 6 to UNO D9
 PB3 - green - physical pin 2 to UNO D11
 PB4 - motor - physical pin 3 to UNO D12
 Slot interrupt is connected to physical pin 7, D2, INT0
 */


volatile boolean revolution = 0;
long lastmillis = 0;

unsigned long lastmicros = 0;
unsigned int tick = 0;

byte hour = 3;
byte minute = 30;
byte second = 0;

byte portBbits = B00011011;

void setup(){
  DDRB &= B00011011;
  attachInterrupt(0, cycle, FALLING);
}

void loop(){

  //IF THE HAND HAD REVOLVED, CALCULATE THE CYCLE TIME
  if(revolution) cyclecalc();

  //FIND THE LOCATION OF THE HAND BASED ON TIME PAST CYCLETIME
  int handposition = (micros() - lastmicros) / tick;
  handposition--;//subtract to adjust hand positioning (used if slot isn't lined up exactly with zero point)
  if(handposition < 0) handposition += 60;

  //IF HAND LOCATION MATCHES DESIERED LOCATION, SET THE BIT
  if (handposition == second){
    portBbits &= ~(1 << 1);
  }
  else {
    portBbits |= (1 << 1);
  }
  if (handposition == ((hour * 5) + (minute / 12))){ //this allows for a 12 minute resolution on the hour hand.
    portBbits &= ~1;
  }
  else {
    portBbits |= 1;
  }
  if (handposition == minute){
    portBbits &= ~(1 << 3);
  }
  else {
    portBbits |= (1 << 3);
  } //WRITE THE BITS, TURN THE APPROPRIATE LEDs ON PORTB = portBbits; //CHECK TO SEE IF TIME HAS ADVANCED if(millis() - lastmillis >= 1000){
  lastmillis += 1000;
  second++;
  if(second > 59){
    second = 0;
    minute++;
    checkintegrity();
  }
}


void cyclecalc(){
  unsigned long cycletime = micros() - lastmicros;
  lastmicros = micros();
  tick = cycletime / 60;
  revolution = 0;
}

void cycle(){
  if((micros() - lastmicros) > 30000) revolution = 1;
}

void checkintegrity(){
  if(minute > 59){
    minute = 0;
    hour++ ;
    if(hour > 11){
      hour = 0;
    }
  }
}

Please feel free to comment or suggest a better way! Enjoy!