There are several websites that explain how to use an analog clock mechanism with an Arduino to provide the control electronics. Custom clocks can be made this way that keep 12Hr, 24Hr, or Sidereal time as well as keeping track of ocean tides. Simon Monk describes a 24 hour clock project like this in his book, “The TAB Book of Arduino Projects: 36 Things to Make with Shields and Proto Shields.” For other examples see links at 1, 2, or 3.
Analog Clock Mechanism
These designs depend on an analog clock mechanism that uses a Lavet-type stepping motor. Although the Lavet motor is quite simple and efficient, it makes a ticking sound every second. This pulse is transmitted to the second hand that noticeably jerks as it goes around. The theory and operation of this motor are well documented so I won’t repeat that here. One of the most thorough explanations can be found in AN-CM-241 Analog Clock Motor Driver.
Today, the vast majority of the analog clock mechanisms you can buy on Amazon no longer use this motor. Variously described as silent, mute, quiet, non-ticking, or sweep motion, they employ a more continuous stepping motor. Rather than a ticking sound these clocks make a very faint growl and the second hand moves very smoothly. I suspect that even the clocks that don’t contain these keywords in the description still use this new motor. Unfortunately the two styles look identical and even internally it is hard to tell the difference. So if you want to build what I am describing here, look for ones that have the right keywords.
The back of the clock mechanism comes off by gently prying back some catches. Try not to let all the gears fly out before you take a photo of how they fit inside. You will have to disconnect the coil from its original electronics to do anything new. It isn't enough to just remove the 1.5V AA battery. Driving a signal into the coil provides enough power to wake up the original circuit and it will try to partially operate. Lets take a look at the original signal.
Inside Analog Clock
If you put an oscilloscope on the coil of the clock motor when it is operating normally, you get the waveform shown below. It has 4 steps, bipolar and is 8Hz. The steps are from the battery voltage of 1.5V to -1.5V with two 0V steps. So each step is 1/4 of 1/8 of a second or 1/32s which is 31.25ms. The coil has a DC resistance of 1.6kOhm and about 50mH of inductance. The best thing is to cut the circuit board traces to the coil. You could unsolder the coil, but it seems like the PCB provides part of the mounting structure and needs to stay in place. Then solder two thin stranded wires to the coil terminals and feed them outside the enclosure to connect to the Arduino. Reassemble the clock and you're ready to go.
Original Coil Waveform
One obvious way to generate the 4 step waveform is shown in the schematic below. The clock motor should see +1.5V when output D2 is High and D3 is Low. It would see -1.5V when D2 is Low and D3 is High. Naturally it sees 0V when both are off. That is if the clock motor was a simple 1.6kOhm resistor, but it isn't. The inductance of the coil causes large spikes as seen in the wavefrom below. Also, the series reistors greatly limit the current that can flow to the coil. With this simple two resistor circuit, the motor sometimes never starts, stops, and even skips beats.
The Wrong Drive Circuit
Bad Waveform
The schematic below is the right way to drive the coil. The 120 and 68 Ohm resistor dividers drop the voltage and ensure that there is enough current available to start the motor and keep it running. Below is the coil waveform with this circuit and it is nearly identical to the one from the original circuit shown above. It isn't the most efficient way since nearly all the energy from the Arduino is consumed by the resistors, but it doesn't consume much more power than a couple LEDs.
The Right Drive Circuit
Waveform from the Right Driver
The Arduino has a 16MHz clock and it can be used to make a fairly accurate clock. Its has two problems. First, it isn't exactly 16MHz and the other is that it drifts with temperature. We can tune the frequency error by simply calibrating, and for now we will just live with the drift.
I use Timer 1 to ultimately generate the signal to the motor. Below is the code that will go in the Arduino setup() routine. It configures the timer to free run with a divide by 8 prescale. So it will be counting at 2MHz and will just overflow and start over when it reaches 65532. However, whenever the count equals the compare register OCR1A, it will cause an interrupt. This IRQ can be made to occur every 1/32 of a second by updating OCR1A forward by about 62500.
cli(); // Config timer 1
TCCR1A = B00000000; // free running
TCCR1B = B00000010; // clk/8
TIMSK1 = B00000010; // A compare IRQ enable
sei();
Every 1/32s the ISR below is called. We need to keep track of which 32nd interval we are in and that is what timeA is doing. Next we update the compare register OCR1A by ocrAtick which is approximately 62500 depending on the actual 16MHz clock frequency. A 1Hz square wave is generated by changing output 13 first at 0 and then at 16. That means the built-in LED will be blinking on for half a second and off for the other half. The switch statement takes care of generating the bipolar drive signal. By ANDing (&)timeA with binary 11, the value cycles through 0 to 3 at the 8Hz rate the clock motor expects. Outputs 2 and 3 take turns going high and this generates the output discussed above.
ISR(TIMER1_COMPA_vect){ // Every 31.25ms
timeA = (timeA+1)%32; // Next 1/32nd s
OCR1A += ocrAtick; // Update compare A value
if (timeA==0) digitalWrite(13, 1); // Output 1 sec square wave
if (timeA==16) digitalWrite(13, 0);
switch(timeA&B00000011){ // Output for 12Hr clock
case 0: digitalWrite(3, 0); break;
case 1: digitalWrite(2, 1); break;
case 2: digitalWrite(2, 0); break;
case 3: digitalWrite(3, 1); break;
}
}
Below is the complete program that recreates the original signal generated by the analog clock. ocrAtick is adjusted to compensate for the 16MHz clock. If your clock runs fast, make it a little larger, or if too slow, smaller. I wouldn't think you would need to change it by more than 500 either way.
There is no loop() routine because the ISR does everything. If you want a clock where the hour hand only goes around once in 24 hours instead of 12 just use the code in the commented out switch statement. By right shifting >> timeA down 1 (dividing by 2), the frequency of the signal to the motor halves. Make sure you comment out the original switch line.
Const unsigned long ocrAtick=62531; // Coarse adj speed, 16MHz/8/32 = 62500 if exact
char timeA; // Keeps track of which 1/32nd of a sec
void setup(){ // Config I/O
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(13, OUTPUT);
cli(); // Config timer 1
TCCR1A = B00000000; // free running
TCCR1B = B00000010; // clk/8
TIMSK1 = B00000010; // A compare IRQ enable
sei();
}
void loop(){ // Everything is in the ISR
}
ISR(TIMER1_COMPA_vect){ // Every 31.25ms
timeA = (timeA+1)%32; // Next 1/32nd s
OCR1A += ocrAtick; // Update compare A value
if (timeA==0) digitalWrite(13, 1); // Output 1 sec square wave
if (timeA==16) digitalWrite(13, 0);
switch((timeA)&B00000011){ // Output for 12Hr clock
//switch((timeA>>1)&B00000011){ // Output for 24Hr clock
case 0: digitalWrite(3, 0); break;
case 1: digitalWrite(2, 1); break;
case 2: digitalWrite(2, 0); break;
case 3: digitalWrite(3, 1); break;
}
}