Make an Accurate Arduino Clock Using Only One Wire - NO External Hardware Needed!

144K9559

Intro: Make an Accurate Arduino Clock Using Only One Wire - NO External Hardware Needed!

How to make an Arduino clock - without using external oscillators or clock chips:

(more of my projects on our research website - click the arduino logo top of home page)


You will need:

An Arduino board (just about any flavor will work fine) and software
1 LED
1 Jumper Wire


(Wiring Example Updated Below...)

I'm an Electrical Engineer of 20+ years and just discovered the Arduino platform a few months ago.  Needless to say I fell in love with it and am now hooked on projects.  For Christmas this year, I wanted to make some very heart-felt, one-of-a-kind gifts for my parents.  For my Mother, it was definately a 'one-of' clock design.  I was not concerned with keeping up with daylight savings time or leap years - simply accurately keep the day of week and time.  

How to generate the clock pulses in a unique but accurate manner though?  Sure I could use a fancy embedded clock IC or the common 32,768 crystal oscillator, but I wanted something different than the rest.  I started using a 555 timer to output a steady 100Hz square wave.  This worked pretty well but I was losing about 1 second per hour.  No problem - I just wrote an algorithm to correct the missing second in software.  I was happy.  Then I discovered that although the Arduino's internal timer was not totally reliable for time keeping, the analog PWM outputs did have a very steady square wave of 490Hz.  The duty cycle is determined by the value written to the analog pin.  (i.e. - 0 is zero volts, 127 is a 50% duty cycle, 255 is a logic high/5V).

I decided to try the PWM analog output wired directly to the interrupt pin  2 and it worked great!  Plus, it kept up perfectly with my computer's real time clock to the second without need for software compensation.  I've been running it for days now and it continues to perform just as I had hoped....all with one wire and some code.

The finished gift will have Eagle CAD custom PCB's for the power supply and logic, housed in a plexiglass enclosure.  The entire theme is ice blue LED lighting to simulate an analog clock.  While the enclosure has a white on blue LCD displaying the day, time, and room temperature, a serial cable connects from the project box to the actual clock.  Using a 12 bit decade counter, a servo, and lots of blue LED's I am lighting the appropriate hour segments on a custom-designed analog clock face printed on plexiglass.  The servo is mounted in the center of the clock face and has an armature attached with an LED mounted to light up the minutes behind the glass.  (each minute the servo turns 6 degrees, lighting up the appropriate minute).  I hope to have it finished soon and will make a video of the final product....I hope she loves it !!

Simple Arduino wiring diagram and demo code to make your own clock using this method.  Also a link to Circuit Lab demonstrating how to use the 555 timer @ 100Hz method if you so choose.

Code and links below...

555 Timer Oscillator Circuit (if you wish to try this method)
https://www.circuitlab.com/circuit/b575r9/555-100hz-oscillator/

Clock Demo Code:


/*  Simple internal clock demo: by Joseph Unik aka Relic1974
    Uses analog PWM output of 490Hz with a 50% duty cycle to
    keep very accurate time ;).  Connect an LED to pin 13 to
    watch seconds blink.  Connect a jumper from Analog pin 0
    to Digital Pin 2 (interrupt 0). Minutes output to serial
    monitor.  http://www.planetxresearch.com 'Arduino' logo
    for more projects and tricks...


    (Feel free to use this code to expand into a fully-functional
    clock or other project under Creative Commons ;)
*/

int clockInt = 0;            // digital pin 2 is now interrupt 0
int masterClock = 0;         // counts rising edge clock signals
int seconds = 0;             // variable
int minutes = 0;             // variable
int ledPin = 13;

void setup()
{
  attachInterrupt(clockInt, clockCounter, RISING);
      //  clockInt is our interrupt, clockCounter function is called when
      //  invoked on a RISING clock edge
  analogReference(DEFAULT);
  pinMode(ledPin, OUTPUT);
  Serial.begin(57600);
  analogWrite(0, 127);   // this starts our PWM 'clock' with a 50% duty cycle
}

void clockCounter()      // called by interrupt
{
  masterClock ++;        // with each clock rise add 1 to masterclock count
  if(masterClock == 489) // 490Hz reached     
  {                         
    seconds ++;          // after one 490Hz cycle add 1 second ;)
    masterClock = 0;     // Reset after 1 second is reached
    tone(13, 100, 500);  // using tone to pulse LED without delay call ;)
   }
  return;
}

void loop()
{
  if(seconds == 60)      // NOW GETTING IN TO REAL TIME KEEPING
  {
    minutes ++;          // increment minutes by 1
    seconds = 0;         // reset the seconds variable
    Serial.print("Minutes = ");
    Serial.println(minutes);
  }
}

Demo of my project in the works here



58 Comments

I tried to use this clock with a project I'm working on. The only differance I really noticed was that I'm running an UNO and that I'm running at 9600 baud. I'm very new at this so I may have done something else wrong that I don't even realize but anyway the end result is that my clock gains about 17 seconds every hour. Any suggestions? I'm working on getting my code transfered to this PC so I can post it but in the mean time was wondering if my baud rate would have any effect.
OK, some differences were found that I forgot to mention (my fault) between different arduino boards.  I have only tried this with success with the mega2560 and the ATMega328-based Nano v3.0. 

On the mega2560, which is the board that I developed my uber clock upon, a few of the first analog pins DO have PWM output ability.  For that board, my interrupt routine was counting only the RISING clock signals @ 490hz.  When I ported the code to the nano, I realized that it did not have the same PWM on analog 0 as the mega did.  So I kept the same interrupt pin (D2, int 0) and moved the masterClock pulses to D3 on the nano. 

Then another issue appeared.  When counting RISING clock edges @ 490hz on the nano, my clock was almost double speed!   So I changed the interrupt to look for CHANGE in clock signal instead of RISING and changed the frequency (number of clock CHANGES) to 979, or 0 thru 979 which = 980.  I had to change the frequency not because the PWM signal was 980hz on the nano, but because I was now counting any CHANGE in PWM signal.  So either a low to high OR  a high to low clock edge would invoke the interrupt.  WHY this worked I still am puzzled about. 

Your serial port baud rate should not affect the clock at all btw.

Here's some example code (just the basics) of what I did differently when using the nano.   The same might apply to your UNO board.

In setup try this instead:

attachInterrupt(0, clockCounter, CHANGE);   // Still using interrupt 0 pin D2
analogReference(DEFAULT);

analogWrite(3, 127);          // PWM clock heartbeat !!!  (or any other PWM pin)


Then try this instead for your interrupt / masterClock function:

void clockCounter()             // called by interrupt
{
  masterClock ++;               // with each clock rise add 1 to masterclock count
  if(masterClock >= 979)      
  {                         
    seconds ++;                 // also add one second
    masterClock = 0;            // Reset after 1 second is reached
    ledpinstate = !ledpinstate;
  }

  digitalWrite(ledPin, ledpinstate);

}


This got the nano keeping EXACT time with my PC's real time clock, which is very hard to believe, but I have the results to prove it ! 

Here is another thing to try IF you have an oscilloscope (don't have mine yet).  Perfrom the same analogWrite(pin, 127) on any PWM pin and note the frequency.  It SHOULD be 490hz on all boards but I could be wrong.  So, say the UNO's PWM freq is a bit different ... then you would need to adjust your clock pulse counts to match.

I hope this helps, and feel free to contact again if more assistance is needed.  I also have many other projects, from beginner to advanced, on my website at this URL - http://www.planetxresearch.org/#!__arduino

I'll be adding a guide in the next day or two on getting results with the infamouse JY-MCU cheap bluetooth breakout board.  I finally have code for the radio that is reliable, allows for board programming with AT commands, and also provides 2-way communication.

I'm trying to create the same results u have to make the led blink, in this case switch a relay on and off to the timing to the second.

So far It just stays on.

any suggestions?

Hi, I need output clock with variable frequency (100KHz +- 10%)...using timers

Hi, I need output clock with variable frequency (100KHz +- 10%)...using timers

Your PWM is powered from the exact same clock that drives your timers, since the PWM signals are generated by the timers. Using the timers you will achieve the exact same accuracy as using the PWM - just without the need of an external wire...

This fact might be hidden behind the shell of the Arduino programming environment, but it becomes clear if you look into the details on how PWM works on an ATmega microcontroller...

People, really. There is no need for this method. Just use the timer interrupt. It is exactly the same, if not better. *Facepalm*

Ahan ahan just use the internal interrupt based on a timer to accomplish the same, and I am not an electrical engineer but mechanical... This intractable should be deleted.

I don't really see the point of this. The PWM is directly divided out of the main system clock. You can just program the interrupt registers to do the exact same thing without having to touch hardware.

you can use the millis() function instead and obtain similar results...
here's the code-



unsigned long lastTick=0;
unsigned int sec=1;
unsigned int minute=0;
unsigned int hour=0;

void setup()
{
Serial.begin(9600);
}

void loop()
{


if (millis() - lastTick >= 1000) {


Serial.print(hour,DEC);
Serial.print(" : ");
Serial.print(minute,DEC);
Serial.print(" : ");
Serial.print(sec,DEC);
Serial.println();

sec++;
if (sec==60)
{minute++;
sec=0;
}
if(minute==60)
{ hour++;
minute=0;
}
lastTick = millis();
}

}

Does this code cater for Rollover? otherwise you can have glitches every 50days, when Millis - resets? Thats the great trick with using this PWM...

I discovered that my Arduino nano is actually producing 490.03Hz. So that means that every 2592 seconds, I need to skip 1 second to keep the clock accurate. Still, not bad at all for not using an RTC.

Very interesting idea! I have a question though... Based on the Atmel documentation, it looks like the PWM timers are 'divided down' from the main CPU clock speed. (e.g., "The clock source for the [PWM] timer is prepared – the PLL [timer hardware] is started and locked to the system clock", http://www.atmel.com/Images/doc2542.pdf )

This means that if the main CPU clock drifts (which as sadly we all know it does), then the exact timing of the PWM timers will drift proportionately. This would explain why some people are seeing different exact speeds of their PWM output: it's no more frequency-controlled than the main CPU speed, and people are having to make small adjustments and compensations for their particular device and environment.

So basically my question is this: do you have a link for any documentation showing where the timebase of the ATmega PWM timers comes from, whether divided from CPU clock or otherwise? Thanks! I'm very interested in how we can improve the stability and accuracy of Arduino clocks, and this is a clever idea to pursue!

hello

i just tried your method but im not sure it works

mi timmer was very fast or very slow , i do not know why

any suggestion ?

Don't you lose time as soon as you power it off?

I'm also a 20+yr Electrical & Embedded Software Engineer who loves Arduino, and I'm working on using one for a simple project at work. I needed a reasonably accurate periodic interrupt timer, so I did something similar to what you did, but instead of using PWM output command (which only works on certain pins and at a fixed rate), I used the tone() command on Digital pin 4 and wired it to Digital pin 3.

I use attachInterrupt() for interrupt#1 (on pin#3). The tone() command can be of whatever frequency that I need - there's no need to be locked into the 490Hz interrupts. Note that the tone() command has a lower limit of about 31 Hz on my board  from what I've read. I've been using a tone() of 8000Hz successfully on an Arduino Uno board. I don't know what the upper limit is, but it will depend on how much code is in the interrupt service routine and the crystal.
Brian, has the tone command been keeping time properly for you since your post?
I may be a little late to the party, but my curiosity was stroked by the mechanical side of your project. I am very intreged by your armature with the LED. Could you explain how you went about that? Also I thought servos were not accurate enough for something like a clock. I mean accuracy as far as being off a fraction of a degree after so many movements. Seems like you would have to have some method of checking it's position to ensure accuracy?
More Comments