15

I've made a clock using an Arduino, but the time seems to drift. I am aware of the rollover issue; the clock seems to drift by about 15 minutes over the course of a week.

I'm using a custom PCB with this resonator from Digi-key. The code reads the millis() function at the beginning of each loop, and works from that value.

My question is: How can I measure time with an Arduino, accurately enough to make a passable desk clock?

  • 3
    The millisecond function is supplied with data from an interrupt, which takes a couple clock cycles to run. This adds a miniscule amount of time to each tick. – TheDoctor Feb 12 '14 at 4:41
  • 3
    @TheDoctor: This is incorrect. The interrupt does not slow down the hardware timer that drives millis(). – Edgar Bonet Mar 3 '15 at 12:11
15

Note: although my answer was accepted and has a higher vote score, make sure you read Edgar Bonet's great answer on how to make your Arduino keep time without an RTC.

I've been quite successful in using the DS1307 Real Time Clock. Here's a link to its datasheet.

Below are some of its features:

  • It uses IC interface for communication with Arduino, making it easy to program using the right libraries (available on the Net).

  • It is connected to Arduino through the SCL and SDA pins (analog A4 and A5 respectively), thus only using 2 pins.

  • It requires very little external components to run.

  • IT can be connected to a coin cell battery so it will keep time even while the Arduino is turned off. In its low power mode, the coin cell battery lasts for years.

  • It drifts very little (in my case it only drifts a few seconds per week).

  • It's not very expensive.

If you don't intend to use an RTC, you can replace the crystal that is commonly used to provide clock to arduino for a crystal oscillator module like this one from Farnel or this other one. They come in 4 pin packages like in the images below. They will generate a much more precise clock for your arduino.

Crystal oscillator image Crystal oscillator image Crystal oscillator image

Both modules mentioned have tolerances of 50 ppm and operate at 5V.

Again, just to be clear, these crystal oscillator modules are not to be confused with regular 2 pin crystal like this below. Those are part of the circuitry of external clocks for MCUs, for example.

Crystal oscillator

  • Is the DS1302 good enough or should I move to the DS1307? – Kelly S. French Jun 15 '18 at 13:54
13

You do not need an RTC to build a clock: the ATmega chip has all the hardware needed to perform the duties of the RTC itself. Here is how:

  1. Get a 32768 Hz watch crystal: either buy it or disassemble an old clock. These crystals, specifically designed for time keeping, have an extremely small temperature drift. You would also need one of those if you wanted to use an RTC chip.

  2. Configure the fuses of your ATmega to run off the 8 MHz RC oscillator. This will make your millis() function horribly inaccurate, and also free the XTAL1 and XTAL2 pins.

  3. Connect the watch crystal to the TOSC1 and TOSC2 pins. These are the same pins as XTAL1 and XTAL2 (9 and 10 on the 328P). The different names are used to mean different functions.

  4. Configure the Timer/Counter 2 for asynchronous operation, normal counting mode, prescaler set to 128, and enable the timer overflow interrupt.

Now you will get a TIMER2_OVF interrupt at a very steady rate of once per second. You only need to advance the clock display by one second in the ISR. In between the interrupts, you can put the MCU in very deep sleep (power-save sleep mode: nothing runs but Timer/Counter 2) and run for years on a couple of AA cells. Unless the display is power-hungry, obviously.

I did exactly this to build my 24-hour one handed wall clock. This link points now to the English translation of the original documentation in French.

Quartz calibration

If you do not calibrate your quartz, you can expect a significant drift, typically a few seconds per week. The drift rate depends on the stray capacitance of the traces that connect the crystal to the MCU. In principle, it could be removed by adding some extra, finely tuned capacitance. It is worth noting that you would have the same drift problem with an RTC.

If you are satisfied with this kind of accuracy, then live with it and be happy. However, if you care to measure the drift, you will notice that it is very stable. You can then easily compensate for it in software, and achieve an accuracy of a few seconds per year.

The algorithm for correcting the drift is very simple. From the measured drift, you figure out the precise delay between the interrupts, which should be very close to 109 nanoseconds, then:

#define ONE_SECOND    1000000000  // in nanoseconds
#define ONE_INTERRUPT  999993482  // for example

ISR(TIMER2_OVF_vect)
{
    static uint32_t unaccounted_time;

    unaccounted_time += ONE_INTERRUPT;
    while (unaccounted_time >= ONE_SECOND) {
        advance_display_by_one_second();
        unaccounted_time -= ONE_SECOND;
    }
}

In the above example, the quartz is slightly too fast, and the software compensates by “missing” a tick every few days. If the quartz was too slow, the same code would instead double-tick once every few days.

This kind of calibration could also be done for an RTC, but it would be significantly more complex because the RTC reports the time in a broken-down form that does not naturally lend itself to arithmetic operations.

  • Wow that's a really slick design! I really like how you've put up enough photos to make the design clear, even for us silly monoglot americans :) I really love seeing explicit project documentation like this! – John Walthour Mar 5 '15 at 14:21
  • 2
    @JohnWalthour: Thanks! Now you are encouraging me to write a translation. :-) – Edgar Bonet Mar 5 '15 at 14:33
  • 2
    @JohnWalthour: Done! The link now points to the English translation. – Edgar Bonet Mar 10 '15 at 19:41
  • Just to be clear, when you say, "ATmega chip has all the hardware needed", that isn't entirely true when you have to obtain a new crystal. I think your solution is slick and am not above replacing the crystal but I was a bit confused when you say I don't need hardware and then turn around and say I need to replace a piece of hardware. – Kelly S. French Jun 7 '18 at 16:16
  • @KellyS.French: My sentence was “the ATmega chip has all the hardware needed to perform the duties of the RTC itself” (emphasis added). But then it is important to notice that most RTCs, including the ubiquitous DS1307, need an external crystal to operate. The ATmega is no different: it has all that's needed to replace the RTC itself, but not to replace the crystal that you would have to connect to the RTC anyway. Please note that an RTC module is more than just an RTC, as it does include the crystal. – Edgar Bonet Jun 7 '18 at 19:00
6

The resonator you specified has a 0.3% stability, where the crystal or crystal oscillator (as mentioned by Ricardo) is 50ppm. Many times more stable. Not to even mention the temperature drift of resonator is horrible. Heating by sunlight will change it. Hence a resonator should not be used for keeping time over long periods.

Hence using either a crystal or crystal oscillator will get what you want. Either using it on the ATmega and set the fuses respectively or us one connected to an RTC.

  • Where 50ppm is 0.005% stability? – Matthew G. Feb 12 '14 at 2:50
  • I generalize on that spec, to keep answer brief. Note stability aside Res. have a much larger tolerance and can be quite off. As John W is experiencing. "the right part for the right job" – mpflaga Feb 12 '14 at 2:53
  • Oh, I was just curious about terminology @mpflaga ... new to me. – Matthew G. Feb 12 '14 at 4:38
4

If you don't want to use extra hardware like an Real Time Clock (eg. DSDS1307), you can significantly improve timing accuracy by disabling all unused interrupts. By default Arduino sketches come with various interrupt routines enabled and often they are not used for actually your sketch. Quickest way to find out if you can do without it to try and disable them by issuing noInterrupts();

  • 3
    −1 (although this deserves −4) because: 1. Unless you actually need them, all interrupts are disabled by default, with the sole exception of TIMER0_OVF, which is needed for time keeping. 2. The timing accuracy of the Arduinos is limited mainly by the quality of the resonator. 3. Interrupts do no affect the accuracy of millis() unless you manage to spend more than one millisecond at a time servicing them, in which case you have other problems... 4. Disabling interrupts with noInterrupts() will prevent millis() from keeping time at all! – Edgar Bonet Mar 5 '15 at 11:47
2

I understand a lot of the spirit with Arduino is being frugal and occasionally trudging through a problem. I use Arduino (and now chipKIT, since it's got 10x the RAM and 10x the clock speed) for my workplace and I need "peripheral functions" to be up to speed and working as quickly as possible.

I use the sparkfun real time clock in one of my projects and am very happy with it. They also have a "Dead on" variant.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.