DS1307 RTC adjust drift automatically

Didn't know which category to put this in. I wrote some code to adjust the drift of my RTC. You only have to set it once. Mine drifted about 6 seconds per day. That's not good enough for me. Now it's less than 0.1 seconds. This works especially when the clock is powered off. Not sure how it works during extreme temperatures. It's simple check it out. It's only a few lines more than the original example. You only need to change the first 3 constants, and calculate the 4th constant over time. Any improvements? Any ideas how I can adjust the day overflow? The way it is now you can use since12 for logging.

#include "Wire.h"
#define DS1307_ADDRESS 0x68

const int setyearago=1;  //when was the RTC set?
const int setmonth=10;
const int setday=4;
const int spdx100=580;  // secs/day slow error measured over 11 months 580

int second,minute,hour,weekDay,monthDay,month,year;
int errsec,i,j;
long since12,since12wrong;

void ferrsec(){
long t=setyearago*365;
t+=(month-setmonth)*30;
t+=(monthDay-setday);
errsec=t*spdx100/100;
//Serial.println(errsec/60.0);
since12wrong=long(hour)*60*60+minute*60+second;
since12=since12wrong+errsec;
//since12%=(24*60*60); untested and wrong let it overflow
//return;  //skip adjusting
second+=errsec%60;
if(second>=60){
  minute++;
  second-=60;
  }
minute+=errsec/60; 
while(minute>=60){
  hour++;  //could overflow past 23
  minute-=60;
  }
if(hour>=24){
  monthDay++;  //still not good enough could overflow monthDay
  hour-=24;
  }
}
void setup(){
  Wire.begin();
  Serial.begin(9600);
}
void loop(){
  printDate();
  delay(1000);
}
byte bcdToDec(byte val){
  return ( (val/16*10) + (val%16) );
}
void printDate(){
  Wire.beginTransmission(DS1307_ADDRESS);
  Wire.send(0);
  Wire.endTransmission();
  Wire.requestFrom(DS1307_ADDRESS,7);
  second = bcdToDec(Wire.receive());
  minute = bcdToDec(Wire.receive());
  hour = bcdToDec(Wire.receive() & 0b111111); //24 hour time
  weekDay = bcdToDec(Wire.receive()); //0-6 -> sunday - Saturday
  monthDay = bcdToDec(Wire.receive());
  month = bcdToDec(Wire.receive());
  year = bcdToDec(Wire.receive());
  ferrsec();
  Serial.print(month);
  Serial.print("/");
  Serial.print(monthDay);
  Serial.print("/");
  Serial.print(year);
  Serial.print(" ");
  Serial.print(hour);
  Serial.print(":");
  Serial.print(minute);
  Serial.print(":");
  Serial.println(second);
}

Has anyone tried this? I thought it would be useful for all DS1307 users. Is there a problem with the code? Improvements?

OK, in this thread now, thanks. :slight_smile:

I’d like to give this code a try. Can you give me more directions please?

Say I set my ds1307 to the correct time right now. How would I set the first three params of your sketch before uploading? Like this maybe?

Thanks!

const int setyearago=0; //when was the RTC set?
const int setmonth=8;
const int setday=22;

You got it. The 4th constant is a little tricky. You have to calculate it! Using your old original sketches to read time, measure how far off it is from the PC time today, and again in 10 days. Let's say it's 45 seconds slower than it was today. Divide by the number of days (10), then multiply by 100. The answer is 450. That's what you put for spdx100. That stands for Secs/day x 100 that it is slow. -450 if it's faster after 10 days running. Let me know! The error seems consistent to me, so now there is much less error per day.

Nice work.

I imagine the DS1307 drift is going to be quite dependent on temperature. But as long as the temperature is relatively constant, you should be good to go.

Some day I'll try to do something similar with an Arduino, a radio receiver for the US time signal and the Chronodot... and then create a auto-correct function for the aging trim in the DS3231.

sbright33: Let me know! The error seems consistent to me, so now there is much less error per day.

OK, I'll setup the clock and load the sketch this weekend. It will be at least 10 days plus a few more to monitor results before I post back to this thread with my results, but I will.

Thanks for writing and sharing this code!

Jake

You could guess after 2 days. I also wrote some code which is related to this. It uses GPS to measure and calibrate millis() and the Arduino clock within hours, no need to wait 100 days. So long as you keep it inside where the temperature is 60-80 degrees it is easy to obtain accuracy of 1ppm, which is about 1 seconds every 11 days. Or even 10ppm with some effort which is 1 second every 100 days, or 3 seconds per year. The only hardware you need for this code is GPS. I also have code which uses Wifly to do the same thing with NIST server.

If you've got the time, I can help you keep it!

Sounds good. I need to put the ds1307 on a breadboard this weekend and set the time. I'll let you know how it goes.

Thanks!

sbright33: ...Or even 10ppm with some effort which is 1 second every 100 days, or 3 seconds per year....

Pardon my ignorance. I thought lower PPMs were better than higher PPMs re: clock drift. Why would an increase to 10PPM drift be preferable to 1PPM? Did you perhaps mean 0.1PPM or 100PPB?

I thought the whole point of adjusting the aging trim in the DS1307 or DS3201 was to minimize the clock drift.

Of course my mistake 0.1ppm

sbright,

I have my cheap ds1307 setup. I'll be installing your code either tonight or tomorrow and will let you know how it works out.

Jake

Looks like I'll have to use a GPS receiver since sourcing the radio-based receivers appears difficult, plus I get the benefit of a GPS system, if I want it. The breakout board from adafruit appears enticing, it'll be interesting to see to what extent trimming the oscillator will work. What is the minimum time you think you need to trim with reasonable perfection? For something as accurate as the DS3231, I figured I'd need at least a month.

Using PPS pin you can measure 1us drift each second. So you get 1ppm in about... 1 second!

It makes more sense to wait 20 minutes and use 1ms drift method instead.

http://arduino.cc/forum/index.php/topic,120288

Sbright,

I have your code installed and it appears to be working :D I'll monitor it for a few days.

Thanks, Jeff

Hi Guys,

I have modified the Time and DS1307RTC Libraries to do drift correction and store the drift info in the RTC battery backed memory. I’ve also included an Arduino sketch to do the RTC time setting and drift calibration. Take a look at the link below if you’re interested.

Howard

http://limchonghan.wordpress.com/driftcorrectedds1307/

Code posted here about drifting of ds1307 is dead ! anybody have that code please share

http://limchonghan.wordpress.com/driftcorrectedds1307/

I saved it few years ago… and still have it ;D

Here
Project GitHub - gion86/PlantLight: Automatic light switch based on light sensor and date/time RTC
Library GitHub - gion86/DS1307_RTC: DS1307 Real time clock library for the ATTiny85 (ATTinyX5)
I made a little project with that code, slightly modified: it’s working pretty well once you get the drift correction for you clock/oscillator…

DS1307 Drift Corrected.zip (26 KB)

@gion86

Thanks for sharing the code !

Can you please explain these 2 lines of code!

di.DriftDays = 1000;     // valid value 0 to 65,535
di.DriftSeconds = 2725;  // fine tune this until your RTC sync with your reference time, valid value -32,768 to 32,767

also how to calculate these values , if my clock is 6 sec/day fast.

Please explain any other code lines related to correction !

The variable DriftSeconds holds the "quantity" of second your clock is going to drift in DriftDays days. So if you clock is 6 sec/day fast you have to set:

di.DriftDays = 1;
di.DriftSeconds = 6;

If you clock was slow by 6 it should be (signed):

di.DriftDays = 1;
di.DriftSeconds = -6;

If you a have a not integer number of seconds you can multiply both variable by the same factor. So for example if you have 25.67 sec/day:

di.DriftDays = 100;
di.DriftSeconds = 2567;

In the internal of the library those 2 variables will be divided by each other:

float driftCorrection = float(rtcTime - driftInfo.DriftStart);
driftCorrection /= float(SECS_PER_DAY * driftInfo.DriftDays);
driftCorrection *= float(driftInfo.DriftSeconds);

In driftCorrection you will have the floating point number of seconds per day of you drift, which is then used to calculate the current time:

// due to conversion of driftCorrection to long, accuracy of time returned is +/- 0.5sec
time_t curTime = rtcTime - long(driftCorrection);

I have to make clear that this is not my code, but I took it from the blog post above http://limchonghan.wordpress.com/driftcorrectedds1307/. But I have tested and I'm using in a project so I can say that is working. ;)

I'm trying to run the above code, but unsuccessfully.

sketch_jul28a:34: error: 'tmDriftInfo' does not name a type

 tmDriftInfo di;


sketch_jul28a.ino: In function 'void setup()':

sketch_jul28a:48: error: 'di' was not declared in this scope

   di = RTC.read_DriftInfo();


sketch_jul28a:48: error: 'class DS1307RTC' has no member named 'read_DriftInfo'

   di = RTC.read_DriftInfo();

sketch_jul28a:51: error: 'class DS1307RTC' has no member named 'write_DriftInfo'

   RTC.write_DriftInfo(di); // once you're happy with the drift setting you only have to read the DriftInfo and pass it to now2() or now3() function to get your drift corrected time

sketch_jul28a.ino: In function 'void loop()':

sketch_jul28a:59: error: 'di' was not declared in this scope

   LcdDisplayDateTime(now3(di));

sketch_jul28a:59: error: 'now3' was not declared in this scope

   LcdDisplayDateTime(now3(di));

sketch_jul28a.ino: In function 'void processSerialCommand()':

sketch_jul28a:142: error: 'tmDriftInfo' was not declared in this scope

         tmDriftInfo diUpdate = RTC.read_DriftInfo();  // update DriftInfo in RTC

sketch_jul28a:143: error: 'diUpdate' was not declared in this scope

         diUpdate.DriftStart = newTime;

sketch_jul28a:144: error: 'class DS1307RTC' has no member named 'write_DriftInfo'

         RTC.write_DriftInfo(diUpdate);

sketch_jul28a:151: error: 'tmDriftInfo' was not declared in this scope

       tmDriftInfo diRead = RTC.read_DriftInfo();

sketch_jul28a:152: error: 'diRead' was not declared in this scope

       if (diRead.DriftStart == 0 | diRead.DriftDays == 0 | diRead.DriftSeconds == 0) {

sketch_jul28a:158: error: 'diRead' was not declared in this scope

       SerialDisplayDateTime(diRead.DriftStart); Serial.println();

exit status 1
'tmDriftInfo' does not name a type