Who needs a Software Real Time Clock?

I was a little surprised not to get any response for a library that gives date and time functionality to an arduino without the use of a real time clock. It was mentioned in this thread http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210889307#8 as a way of bypassing the millis rollover problem for sketches that need a way of manipulating and/or displaying times in units of seconds or more.

I was wondering if the cost and effort of integrating a separate real time clock is really no barrier to people that want this kind of functionality so no point in a 'Software Real Time Clock'.

Here are the advantages and disadvantages as I see them of both approaches, I would appreciate hearing your view on the relative merits of a software only solution.

The main advantages of software only solution is that there is no hardware to buy or integrate, everything runs on the Arduino. The main advantage of an external clock board is that it will keep the correct time even if the Arduino resets (the arduino soft solution needs to be synchronized, typically via a time message on the serial port.)

Here is the functionality in the DateTime library that I am using on the arduino. It is built on the standard time_t (an unsigned long) that is the number of seconds since midnight Jan 1 1970 (aka unix time).

void sync(time_t time); // set the internal clock to the given time
time_t now(); // return the current time as seconds since midnight Jan 1 1970
boolean available(); // refreshes the Date and Time properties and returns true if clock synced
// date and time properties updated using the timer0 overflow interrupt
byte Hour;
byte Minute;
byte Second;
byte Day;
byte DayofWeek; // Sunday is day 0
byte Month; // Jan is month 0
byte Year; // the Year minus 1900

// optional utility functions to convert to and from time components (hrs, secs, days, years etc) to time_t

// extracts time components from time_t
void localTime(time_t *timep,byte *psec,byte *pmin,byte *phour,byte *pday,byte *pwday,byte *pmonth,byte *pyear);

// returns time_t from components
time_t makeTime(uint8_t sec, uint8_t min, uint8_t hour, uint8_t day, uint8_t month, int year );

It takes more time to properly document something like this then it does to code it so I would like to hear views on the usefulness or otherwise of this before embarking on writing an article for the playground and posting the code there.

This sounds like it would be a useful library. Can it be built on top of the millis() function?

This sounds like it would be a useful library. Can it be built on top of the millis() function?

Hi David,
Yes, it was designed to be driven from millis.
What is the latest thinking on the best way to avoid the overflow problems?

I would absolutely be interested in this library! I don't have a specific application at the moment, but I can see this would be very useful.

mem: I'm planning to stick with the solution that I implemented and posted to the other thread, unless someone comes up with a better implementation.

Just want to put in my two cents that the real time clock library is a great idea, and really useful for long running projects.

Like you say though the battery backed clock is much more of a set it and forget type of operation, and without an LCD for reboot warnings and setting the time, the utility of a software clock would be somewhat less.

EEPROM write for persistent (but not absolutely accurate time)? I have a feeling that most people using the library are going to care about keeping things relatively accurate over periods of weeks or months and bit of drift isn't too much of a concern.

Paul

Just want to put in my two cents that the real time clock library is a great idea, and really useful for long running projects.

Like you say though the battery backed clock is much more of a set it and forget type of operation, and without an LCD for reboot warnings and setting the time, the utility of a software clock would be somewhat less.

EEPROM write for persistent (but not absolutely accurate time)? I have a feeling that most people using the library are going to care about keeping things relatively accurate over periods of weeks or months and bit of drift isn't too much of a concern.

Paul

Persisting the time through a reset/power down cycle is something I had not thought of. It's a really good idea but a problem with using EEPROM to hold the previous value is that I think a single EEPROM address updated once per minute would wear in a few months. I wonder if it would be unrealistically selfish to reserve up to half the EEPROM for this purpose so that memory locations could be spread around?

The write-up and code for the software DateTime library is now in the playground:
http://www.arduino.cc/playground/Code/DateTime

First off, I really like the ability to control the time using arduino whitout the use of any RTC...
However I would like to have the ability to correct the time, for instance you know your arduino run 3sec fast in 48 hours, then the library correct the time, ider by adding one sec every 16 hour or something... This feature would be really useful when creating a clock that don't have any connection to the outside world and needs to keep time really acurate over long time...

/Jon

First off, I really like the ability to control the time using arduino whitout the use of any RTC...
However I would like to have the ability to correct the time, for instance you know your arduino run 3sec fast in 48 hours, then the library correct the time, ider by adding one sec every 16 hour or something... This feature would be really useful when creating a clock that don't have any connection to the outside world and needs to keep time really acurate over long time...

/Jon

Hi Jon, would a method like this do what you want?

void setTimeAdjust(int adjSecsPerDay); // number of seconds per day added/subtracted to correct time

example usage:

SetTmeAdjust(1); // this will add 1 second per day by adding 1 millisecond every 86.4 seconds
SetTmeAdjust(4); // this will add 4 second per day by adding 1 millisecond every 21.6 seconds

SetTmeAdjust(-1); // this will subtract 1 second per day by subtracting 1 millisecond every 86.4 seconds
SetTmeAdjust(-4); // this will subtract 4 second per day by subtracting 1 millisecond every 21.6 seconds

There will always be some drift in the crystal frequency so I doubt its realistic to try to get better than 1 second per day without resorting to a more accurate crystal and temperature compensation.

BTW, I added a link to the DateTime library from the main libraries page: Libraries - Arduino Reference

mem>wear out EEPROM.
I wouldn't worry. I had the same worry but others put my fears to rest. See here - http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1214379954

First off, I really like the ability to control the time using arduino whitout the use of any RTC...
However I would like to have the ability to correct the time, for instance you know your arduino run 3sec fast in 48 hours, then the library correct the time, ider by adding one sec every 16 hour or something... This feature would be really useful when creating a clock that don't have any connection to the outside world and needs to keep time really acurate over long time...

/Jon

Hi Jon, would a method like this do what you want?

void setTimeAdjust(int adjSecsPerDay); // number of seconds per day added/subtracted to correct time

example usage:

SetTmeAdjust(1); // this will add 1 second per day by adding 1 millisecond every 86.4 seconds
SetTmeAdjust(4); // this will add 4 second per day by adding 1 millisecond every 21.6 seconds

SetTmeAdjust(-1); // this will subtract 1 second per day by subtracting 1 millisecond every 86.4 seconds
SetTmeAdjust(-4); // this will subtract 4 second per day by subtracting 1 millisecond every 21.6 seconds

There will always be some drift in the crystal frequency so I doubt its realistic to try to get better than 1 second per day without resorting to a more accurate crystal and temperature compensation.

Yea that method will probably work perfect... Thanks a lot mem :smiley:

/Jon

I am testing the proposed impementation for time adjustment, there is some code below you can run if you want to test it on your board

Edit the variable adjSeconds to a value appropriate for your board, it is the number of seconds to add or subtract each day.

int adjSecs = 120; // set this to the number of seconds to adjust per day, positive values speed up clock, negative values slow clock down

Compile and run the sketch (no library necessary).

You can see adjusted and unadjusted time updated every half second in the serial monitor (baud rate is 9600) and compare this to an accurate clock to verify that the adjusted value tracks the correct time.

The technique used here is to add the number of seconds set by adjSecs divided equally over a day. It does this by adding .001 times the value of adjSecs every 86 seconds (there are approx 86 thousand seconds per day)

I will update the DateTime library with an adjTime property (minus debug code) after I have completed testing, please let me know you find any issues.

Note that there is no handling of millis rollover in this code. I am testing using the proposed 0012 code as posted here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210889307/#3

Feel free to implement your favorite technique for handling millis rollover if testing for longer than 9 hours.

// sketch to test time adjustment

int adjSeconds = 120; // set this to the number of seconds to adjust per day, positive values speed up clock, negative values slow clock down

// system variables
unsigned long prevMillis = 0;
unsigned long sysTime = 0;  // increments once per second

unsigned long adjustedSysTime = 0;  // increments once per second plus/minus adjsutment
unsigned long adjustedPrevMillis = 0;

void adjTime(unsigned long now){
  // this function adds milliseconds times adjSecs every 86 seconds
  if( now - adjustedPrevMillis >= 1000){ 
    adjustedPrevMillis += 1000;
    if( (++adjustedSysTime) % 86 == 0)  {         
      adjustedPrevMillis += adjSeconds;
      Serial.print("At adjustedSysTime= ");
      Serial.print(adjustedSysTime,DEC);
      Serial.print(" now= ");
      Serial.print(now,DEC);
      Serial.print(" now - adj=");
      Serial.print(now - adjustedPrevMillis, DEC);
      Serial.print(" adding ");
      Serial.print(adjSeconds,DEC);
      Serial.print(" msecs, adjPrevMs= ");
      Serial.println(adjustedPrevMillis,DEC);         
    }    
  }      
}

void printTime(unsigned long time){

  int second = time % 60;
  int minute = (time / 60) % 60;
  int hours =  (time  / 3600) % 24;

  Serial.print(hours,DEC);
  Serial.print( ":");
  Serial.print(minute,DEC);
  Serial.print(":");
  Serial.print(second,DEC);
}

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

void loop() {
  unsigned long now = millis();
  while( now - prevMillis >= 1000){
    sysTime++;
    prevMillis += 1000;
  }  
  adjTime(now);  
  
  Serial.print("Sys time= ");  
  printTime(sysTime);
  Serial.print(", Adjusted time= ");
  printTime(adjustedSysTime);
  Serial.print(", delta= ");
  Serial.print(sysTime - adjustedSysTime, DEC);
  Serial.println(" seconds");
  delay(500);
}

Quick question,
I'm using the example sketch found at Arduino Playground - DateTime but am having some strange occurrences. It works flawlessly for what seems like a random period of time but then stalls, no longer reporting anything or responding to updates. After another seemingly random period of time it starts up again but at a completely different date and time. It can be updated again at this point but ultimately stalls again later. Does anyone else see this kind of activity or do I maybe have a hardware problem with my Arduino Diecimila? It always happens within 30 minutes of a reset.

ytsirk, are you testing it using the Processing example sketch to set the clock or some other code. I wonder if there is some serial data being received by the arduino that is causing the symptoms you describe.

You could test your Arduino by running a simple sketch like this one to see if it survives beyond 30 minutes or so

int i = 0;
void setup(){
  Serial.begin(19200);
  pinMode(13,OUTPUT); // we flash the LED each second
}

void  loop(){
   delay(1000); 
   Serial.print(i++,DEC);
}

edit: I have been running the playground example sketch for just over an hour without problem on my board. Please let me know if the sketch in this post runs ok or not on your board.

That code worked for well over a half an hour without a hitch and I haven't had any trouble with any other code running on it before. Your question about serial data had me thinking so I tried a modified version of the playground code to try to eliminate that possibility.
I came up with this:

#include <DateTime.h>
#include <DateTimeStrings.h>

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

void  loop(){  
 DateTime.sync(1218757280);   // Sync DateTime clock to the time received on the serial port
 while (1){
  if(DateTime.available()) { // update clocks if time has been synced
    unsigned long prevtime = DateTime.now();
    while( prevtime == DateTime.now() )  // wait for the second to rollover
      ;
    DateTime.available(); //refresh the Date and time properties
    digitalClockDisplay( );   // update digital clock

    // send our time to an app listening on the serial port
    Serial.println(DateTime.now());      
  }
 }
}

void digitalClockDisplay(){
  // digital clock display of current time
  Serial.print(DateTime.Hour,DEC);  
  printDigits(DateTime.Minute);  
  printDigits(DateTime.Second);
  Serial.print(" ");
  Serial.print(DateTimeStrings.dayStr(DateTime.DayofWeek));
  Serial.print(" ");  
  Serial.print(DateTimeStrings.monthStr(DateTime.Month));  
  Serial.print(" ");
  Serial.println(DateTime.Day, DEC);  
}

void printDigits(byte digits){
  // utility function for digital clock display: prints colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits,DEC);  
}

Unfortunately I got the same problem, a freeze, and then a bit later a seemingly random date and time started up again. If you had it running with no problems then I'm wondering if it might be a hardware issue of some sort.

edit: I'm starting to wonder if it is indeed a random date and time it restarts at: with this code it always seems to jump to October 3rd when it starts up again... hmm.

Is the time after restart always exactly the same to the second ? Not sure what that means but it may help us track things down.

It may be worth adding a print statement in setup to see if it resets after freezing (although I don't think it has)

Also adding a few lines to the sketch you posted above will tell us if its freezing while waiting for the seconds to change:

unsigned long prevtime = DateTime.now();
while( prevtime == DateTime.now() ) // wait for the second to rollover
** { // the following lines to be added to your test sketch**
** Serial.print(millis(),DEC);**
** delay(500); // this reduces the number of serial message sent **
** }**

if the millis value doesn't change when it freezes, we can look for something that may be blocking interrupts.

FYI, the playground sketch has been running continuously now on my board for 12 hours without problem.

There seems to be three issues being discussed here:

  1. the need for a date, hour, minute and second time keeping feature.
  2. the synchronization of this feature with the outside world.
  3. should this feature be basic to the arduino?

I would suggest time is fundamental to our every day life; hence, flows over into the devices we build with the arduino.

As for synchronization, the network people have studied this problem to dead about 20+ years ago. NTP or Network Time Protocol resulted and is extremely accurate to milliseconds. Arduino is testing an ethernet stack, thus, access to NTP will follow.

Another alternative for time syncronization other than a RTC chip is via a GPS chip which produces NMEA output.

The NMEA 0183 data stream consists of a series of "sentences" delimited by a newline character. Each sentence begins with a six character identifier, the first character of which is always "$". The NMEA 0183 standard defines dozens of sentences, but only a fraction apply directly to GPS devices. The most useful sentences include:

$GPAAM - Waypoint Arrival Alarm
$GPBOD - Bearing, Origin to Destination
$GPBWW - Bearing, Waypoint to Waypoint
$GPGGA - Global Positioning System Fix Data
$GPGLL - Geographic Position, Latitude/Longitude
$GPGSA - GPS DOP and Active Satellites
$GPGST - GPS Pseudorange Noise Statistics
$GPGSV - GPS Satellites in View
$GPHDG - Heading, Deviation & Variation
$GPHDT - Heading, True
$GPRMB - Recommended Minimum Navigation Information
$GPRMC - Recommended Minimum Specific GPS/TRANSIT Data
$GPRTE - Routes
$GPVTG - Track Made Good and Ground Speed
$GPWCV - Waypoint Closure Velocity
$GPWNC - Distance, Waypoint to Waypoint
$GPWPL - Waypoint Location
$GPXTE - Cross-Track Error, Measured
$GPXTR - Cross-Track Error, Dead Reckoning
$GPZDA - UTC Date/Time and Local Time Zone Offset
$GPZFO - UTC and Time from Origin Waypoint
$GPZTG - UTC and Time to Destination Waypoint