Go Down

Topic: Keeping accurate time (Read 21322 times) previous topic - next topic

kdecker

I am using the following code to display a digital clock using my arduino uno:

#include <Time.h> 
#include <LiquidCrystal.h>

#define TIME_MSG_LEN  11   // time sync to PC is HEADER followed by Unix time_t as ten ASCII digits
#define TIME_HEADER  'T'   // Header tag for serial time sync message
#define TIME_REQUEST  7    // ASCII bell character requests a time sync message

// T1262347200  //noon Jan 1 2010
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int clockSet = 0;

void setup() 
{
  Serial.begin(9600);
  lcd.begin(16, 2);
}

void loop()
{   
  if(clockSet == 0)
  {
    if(Serial.available() )
    {
      processSyncMessage();
      clockSet = 1;
    }
  }
  digitalClockDisplay(); 
  delay(1000);
}

void digitalClockDisplay()
{
  lcd.setCursor(1,0);
  lcd.print(hour());
  lcdPrintDigits(minute());
  lcdPrintDigits(second());
}

void lcdPrintDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  lcd.print(":");
  if(digits < 10)
    lcd.print('0');
  lcd.print(digits);
}
void processSyncMessage() {
  // if time sync available from serial port, update time and return true
  while(Serial.available() >=  TIME_MSG_LEN ){  // time message consists of header & 10 ASCII digits
    char c = Serial.read() ;
    Serial.print(c); 
    if( c == TIME_HEADER ) {       
      time_t pctime = 0;
      for(int i=0; i < TIME_MSG_LEN -1; i++){   
        c = Serial.read();         
        if( c >= '0' && c <= '9'){   
          pctime = (10 * pctime) + (c - '0') ; // convert digits to a number   
        }
      }   
      setTime(pctime);   // Sync Arduino clock to the time received on the serial port
    } 
  }
}


Most of this is out of the example code for the time.h library. The problem is the clock loses about 2 minutes each day (i.e. it is 2 minutes slower than my computer clock). Any ideas on why this might be? I have cut down the code as much as I can such that it only calls processSyncMessage once (the first time I send the time to the arduino). I thought maybe executing processSyncMessage every time through the loop was slowing it down but that doesn't seem to be the case. Maybe I should change the delay to delayMicroseconds()?

Thanks.

RuggedCircuits

It could be your LCD printing code is just taking about 1ms or so. Then, your delay() function is not 1000ms from the last delay, it's 1000ms from the the last delay plus LCD time.

Try just using millis() directly instead of delay(). Something like (warning...untested code):

Code: [Select]

uint32_t lastTime;

void loop(void)
{
  if ((millis() - lastTime) >= 1000) {
    lastTime += 1000;
    digitalClockDisplay();
  }
  ......
}


--
The Gadget Shield: accelerometer, RGB LED, IR transmit/receive, speaker, microphone, light sensor, potentiometer, pushbuttons

robtillaart


A better accuracy can be obtained by using a RealTimeClock like the DS1307.

Further you can speed up the connection with the PC by using a higher baudrate Serial.begin(115200); that way it takes less time.





Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

kdecker

Thanks for the great suggestions. I would like the arduino to run standalone (i.e. not connected to any computer), otherwise I would definitely use the RealTimeClock. I will try the idea of checking the time it takes to write to the display. I was kind of thinking in that direction.

robtillaart

Quote
standalone

Is the Arduino connected to the internet (ethernetshield) ?
If yes you can use the NTP to sync time
If not you may use a DCF77 signal (or similar) to get accurate time 

- http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1232018156
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

johnwasser

Without an external precision clock your Arduino time measurement will only be as accurate as the processor clock.  Perhaps a high-precision 16MHz crystal instead of the standard one would help.

It looks like 10ppm (parts per million) is the best you can get off the shelf.  That's a little better than a second a day accuracy.  You can get such crystals for under $1.  Search Google Shopping for "16 MHz crystal 10ppm".
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Udo Klein

A 10ppm crystall will not give 10ppm accurarcy unless ALL other parameters will fit. Especially the load caps may get it much further out of tune than you would expect. Careless board layout and unstable power conditions will not improve things.
HIt is possible (but not easy) to compensate for frequency offset as well as temperature. However it is very much easier to use a DS3231 (e.g. a Chrono Dot) or to go for a time normal like DCF77 or GPS though.
In case of extraordinary precision requirements have a look at used Trimble Thunderbold Modules at Ebay.
Check out my experiments http://blog.blinkenlight.net

MarkT

The Uno, unlike the Duemilanove, doesn't use a crystal to clock the microcontroller - you can't rely on millis()/micros() being better than 0.1% accurate or whatever performance ceramic resonators have...  This is very unfortunate.  It is possible to replace the resonator since the board has pads for a standard crystal and load capacitors but you'll need a steady hand and a solder-sucker to clear the thru-holes.

A quick experiment on my Uno showed the resonator frequency being pulled 0.05% (500ppm) just by putting a finger on it.  Only 4.5ppm on the Duemilanove's crystal/caps.
[ I won't respond to messages, use the forum please ]

MarkT

In fact I got motivated enough to replace the resonator on my Uno board, partly to see how tricky it was to do.

Here's the board before modding it:


Then I desoldered the resonator and the unnecesary resistor:


Had some 22pF SM caps (0805 size, about the smallest I like to solder :)


And the crystal - there seemed to be insufficient clearance for the standard 49U package from the caps (they weren't tiny enough!) but I wanted it clear of the bare resonator pads anyway to avoid shorts so its sticking up a little from the board:


Finally at some point I will add a blob of glue from a hot-glue gun to support it better mechanically.  Overall not too fiddly if you have tackled SM components before.  A solder sucker is required to clear the thru-holes for the crystal leads, the tiny resonator and resistor can be heated on alternate sides till it moves and can be pushed off the pads.  Only attempt at your own risk of course ;)
[ I won't respond to messages, use the forum please ]

Simpson_Jr

#9
Mar 08, 2011, 12:55 pm Last Edit: Mar 08, 2011, 01:03 pm by Simpson_Jr Reason: 1
One thing I found out correcting the errors of my 1307, was to keep an eye on my PC's clock.
Standard it's synchronized once a week and it took me a while to notice it was less accurate as my arduino-clock.

Synchronizing it too often per hour with time.windows.com can result in temporary denial of services by the way  :*




westfw

Quote
1) The microcontroller crystal (or ceramic resonator) IS NOT capable of serving as a real-time clock. It was simply never made for that purpose. You will fight with it the rest of your life if you try to do that.

Nonsense.  A crystal oscillator is a crystal oscillator.  Unless you specifically use a higher-precision crystal, carefully match your capacitances, and provide some sort of temperatures compensation, a DS1307 "RTC" chip is not going to be any more accurate than a crystal connected directly to the microcontroller.  (and you COULD use similar care in implementing the microcontroller crystal oscillator.)  (not to say that the RTC chip doesn't have other features.)
(now, there ARE other "RTC modules" that specifically DO attack the accuracy problem, by using fancier RTC chips, or ovens to keep the critical circuitry at a specific temperature, or other mechanisms.   One example is the "ChronoDot": http://macetech.com/store/index.php?main_page=product_info&cPath=5&products_id=8 )

The OP hasn't said how accurate he actually needs, and I don't think we've confirmed or eliminated software bugs as the cause of the original inaccuracies.  You should be able to get better than 5000ppm (0.5%) even with a ceramic resonator, and 100ppm or better with the microcontroller clock controlled by a crystal and millis() (or with a DS1307 based RTC module and different code.)  The "Chronodot" is supposed to be good for about 2.5ppm over a moderate temperature range (and someone has already done the hard parts!)[/color]

kdecker

Well this has generated quite a discussion. Thanks to all for your input. The upshot of all this appears to be that using the arduino as a standalone clock is not practical due to the issues described here. I won't pursue that further and will take the advice of getting an RTC module. That seems to be the most expedient path. I'm not terribly concerned about microsecond accuracy over great lengths of time. I just want it to keep time as well as the digital clock on my nightstand. That shouldn't be too difficult I should think. I was just surprised at how inaccurate the arduino's microcontroller clock is. But at least I understand why now.

BenF


The upshot of all this appears to be that using the arduino as a standalone clock is not practical due to the issues described here. I won't pursue that further and will take the advice of getting an RTC module.

I would add that for a USB connected Arduino, syncing to the PC clock is as good as anything you can buy in terms of RTC. You may have to sync frequently (say every hour or so) to maintain exact time, but this is not much different from reading time off a RTC module at regular intervals.

The two minute delay you observed is mainly due to the software issue pointed out by retrolefty above (the logic is incorrect). Change the program logic in line with his advice and you should be good for a day or so between updates.

johnwasser


I'm not terribly concerned about microsecond accuracy over great lengths of time. I just want it to keep time as well as the digital clock on my nightstand.


If your Arduino is powered by an AC outlet (like your clock radio) another source of accurate time is the 60Hz AC power grid.  The long-term accuracy of the 60Hz power is very good.  Possibly even atomic clock good.  I've seen examples somewhere on how to get a nice interrupt from an AC transformer.   Just count the cycles, seconds, minutes, hours, and days.  Put in rules for Daylight Savings Time and you'll never need to set the clock unless you lose AC for a long time.
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Jack Christensen

I have several DS1307 RTCs, and they work well, but aren't what I would call super accurate.  They can gain or lose a couple seconds per day.  The DS3231 is much better, check out the "Chronodot" breakout board at Adafruit, or Sparkfun has a "dead on" RTC using the DS3234, which accuracy-wise, is the same as the DS3231, but it has an SPI interface rather than an I2C interface.  From a software standpoint, I've found the DS1307 and DS3231 to be interchangeable, i.e. DS1307 software libraries work fine with the DS3231.
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Go Up