Simplest way of timekeeping?

Evening all!

I have a need for a "reasonably" accurate timekeeper. By "reasonable", I mean "comparable to any cheapo battery-powered clock".

I want to build a timekeeping circuit for a novelty clock - it only needs to display seconds, and then drive a few other electronic bits at the beginning of every minute.

I've done some tests with millis(), but it seems there's a noticeable drift after only a couple of hours so I suspect it wouldn't be suitable to be left running for weeks.

I'd be interested to know what other techniques people might recommend - is there a good solution that can be supported by the Arduino, or would I be better off buying a separate RTC module? Or just go nuts and install an atomic clock decoder?

Thanks in advance for any advice,

Kris.

I've done some tests with millis(), but it seems there's a noticeable drift after only a couple of hours

Could be your code is at fault.
But sadly, we can't see it.

If you can find an atomic clock decoder cheaply, I'd definately buy it :smiley: . Or, you could buy a GPS module and extract the time (atomic clock accuracy) from that. On the other hand, you could do what I did and buy a DS1307 IC or module of eBay, or somewhere else, download a library for it and use that. It is easy to do, and gives decent accuracy.

Onions.

AWOL:

I've done some tests with millis(), but it seems there's a noticeable drift after only a couple of hours

Could be your code is at fault.
But sadly, we can't see it.

#include <LiquidCrystal.h>

int g_iHour;
int g_iMin;
int g_iSec;
unsigned long g_uLastTick;

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup()
{
  
  // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  
  lcd.print("Simple LCD Clock");
  
  g_iHour = 0;
  g_iMin = 0;
  g_iSec = 0;
  
  g_uLastTick = millis();
}

void loop()
{
  // compose a string of the current time, then print it ... 
  char text[20];
  sprintf(text, "%02d:%02d:%02d", g_iHour, g_iMin, g_iSec);
  lcd.setCursor(4, 1);
  lcd.print(text);
  // (note that this is quite wasteful - we only need to update the screen every second,
  // or when a button is pressed)

  // has it been more than a second since the last update?   
  if (millis() >= (g_uLastTick + 1000))
  {
    // yes
    g_uLastTick += 1000;
    
    g_iSec++;
    if (g_iSec > 59)
    {
      g_iSec = 0;
      g_iMin++;
      if (g_iMin > 59)
      {
        g_iMin = 0;
        g_iHour++;
        if (g_iHour > 23)
        {
          g_iHour = 0;
        }
      }
    }
  }
}

You don't say what board is being used but most Unos, etc., use a ceramic resonator for the system clock, which will typically have a frequency tolerance on the order of ±0.5%. Changing to a crystal (say, ±20ppm) might be simplest, but if you're not in a position to do that, then I'd try a DS1307 RTC. DS1307 breakout boards can be had for under $10, some will plug directly into the headers on an Arduino Uno.

Lately I've been experimenting with running an ATmega328P on the internal oscillator, and connecting a 32.768kHz crystal which acts as a clock source for Timer/Counter2. A simple RTC can then be implemented in software. Fairly straightforward, keeps decent time.

Thanks for your comment - yes, I should have mentioned that I'm using an Uno.

I think the most straightforward thing to try next will be a 1307.

Cheers!

DS1307 need a crystal too, and pullup resistors on the SCL/SDA lines.
DS3234 doesn't need any of that
http://pdfserv.maxim-ic.com/en/ds/DS3234.pdf
but is surface mount - so get an adapter (1.27mm pitch pads)
http://www.ebay.com/itm/2PK-SO-SOP-SOIC-SSOP-14-16-20-Pitch-0-65mm-and-1-27mm-DIP-Adapter-Converter-/170754392487?pt=LH_DefaultDomain_0&hash=item27c1c141a7

Or the much cheaper DS1337, which does come in DIP form. It's I2C though, not SPI.

DS133 needs a crystal and I2C pullup resistors, which I was suggesting could be avoided.

Could also run a straight sketch.
I ran this one on a Duemilanove against the official US time here for 5 hours and it stayed in sync the whole time.
http://www.time.gov/timezone.cgi?Eastern/d/-5/java

unsigned long currentmillis = 0;
unsigned long previousmillis = 0;
unsigned long interval = 10000;

byte ones_seconds = 0;
byte prior_seconds = 0;
byte tens_seconds = 0;
byte ones_minutes = 0;
byte tens_minutes = 0;
byte tenths = 0;
byte hundredths= 0;

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

void loop()

{
  currentmillis = micros(); // read the time.
  while (currentmillis - previousmillis >= interval) // 10 milliseconds have gone by
  {

    hundredths = hundredths +1;
    if (hundredths == 10){
      hundredths = 0;
      tenths = tenths +1;
    }

    if (tenths == 10){
      tenths = 0;
      ones_seconds = ones_seconds +1;
    }

    if (ones_seconds == 10){
      ones_seconds = 0;
      tens_seconds = tens_seconds +1;
    }

    if (tens_seconds == 6){
      tens_seconds = 0;
      ones_minutes = ones_minutes +1;
    }

    if (ones_minutes == 10){
      ones_minutes = 0;
      tens_minutes = tens_minutes +1;
    }

    if (tens_minutes == 6){
      tens_minutes = 0;
    }

    previousmillis = previousmillis + interval; // save the time for the next comparison
  }

  // counters are all updated now,

if (prior_seconds != ones_seconds){

  Serial.print (tens_minutes, DEC);
  Serial.print (" ");
  Serial.print (ones_minutes, DEC);
  Serial.print (" : ");
  Serial.print (tens_seconds, DEC);
  Serial.print (" ");
  Serial.println (ones_seconds, DEC);
prior_seconds = ones_seconds;
}

} // end void loop

@Crossroads

typo or intentional?

  currentmillis = micros(); // read the time.

@oddbloke:

I have a need for a "reasonably" accurate timekeeper.

We have seen "clocks" on this forum that used the amount of light to detect morning midday and evening to get the right timing to feed chicken, an error of 30 minutes was no problem, so reasonable is project dependable.

So, can you specify reasonable? What is the maximum error that is acceptable?
1 second per minute, hour, day, week, month, year?

Or maybe you can tell more about the project itself so we can think about what is needed.

To complete the list of proposed solutions: you can add an ethernet shield and use NTP (network Time Protocol) to request time from an Atomic clock.

Rob Tillaart,
Not a typo - I had a millis based example that I changed to be micros based instead, didn't bother changing all the variable names.

I let this run for 5 hours the other day, fully expecting to see some drift away - and every time I checked the seconds were right in sync.

CrossRoads, is there a crystal or resonator on the Duemilanove?

Crystal.

I've found the DS1307 with a resonator and the RTClib library to be very noob friendly...