I’ve recently purchased the DS1307 real time clock for my data logging project only to find that it drifts about 3 seconds per day, which is not acceptable for me. I expect my data logger to be maintenance free for at least 10 years. In which time it would have drifted 7.6 days!

At first I was considering switching to the DS3231 RTC which should be accurate enough for me but then its more expensive, not available locally and I’d have to mess around with SOIC breakout boards.

So after looking around the net and not found any solution that I’m happy with I did some quick modifications to the Time and DS1307RTC Libraries to do drift correction and store the drift info in the RTC battery backed memory.

I’ve also written an Arduino sketch to do the RTC time setting and drift calibration. Take a look at the Arduino sketch below if you’re interested. Most of the codes are supporting codes. You only need two lines of code to get the drift corrected time. One line to load the DriftInfo and one line to read the drift corrected time. You’ll need the modified Time and DS1307RTC Libraries(included in the “Drift Corrected DS1307 codes” link).


Drift Corrected DS1307 codes
Arduino Time Library
Discussion about drift correction for DS1307 on Arduino.cc Forum
DS1307 RTC Tutorial at Ladyada
DS1307 RTC at bildr Blog
An I2C Bus Example Using the DS1307 Real-Time Clock
DS1307 Datasheet

Implementation Note:

  • I assume you are somewhat familiar with using the Arduino Time library and have a working DS1307 hookup to your Arduino. If not follow the links above first.
  • The time in your RTC is not changed in anyway as the drift correction is computed every time now2() or now3() functions are called.
  • DriftInfo contains the drift start datetime and the drift rate which is used by now2() and now3() to compute the drift corrected time. DriftInfo can be optionally stored on the DS1307 battery backed memory using 8 Bytes of memory starting at location 0x08.
  • This is my preferred solution as I don’t have to worry about accumulated rounding error, also switching the Arduino board off for extended period won’t affect the drift correction.
  • The sketch and libraries are written using Arduino 1.0.1 IDE. Just comment out the LCD and Temperature Sensor codes if you don’t have an LCD and LM35 attached.
  • The sketch below provides a user friendly interface to set, calibrate and monitor the DS1307. I hope others find it useful. Feedbacks, corrections, improvements are welcomed. Oh, I forgot one very important thing NO WARRANTY. Have fun! 🙂
//	Author		    : Howard Lim Chong Han
//	Project	            : RTC with Drift Correction
//	Project description : Sketch to set RTC and calibrate drift correction for DS1307
//                          : uses modified Time and DS1307RTC Library
//      Usage               : Open serial Monitor
//                            Command to set RTC time and update DriftInfo.DriftStart in the RTC battery back memory
//                            Command format: TYYMMDDHHMMSS
//                            Example: 2012 Oct 21 6:30am is T121021063000
//                            Command "I" read DriftInfo from RTC Memory

/* 16x2 LCD Display wiring:
 * LCD RS pin to digital pin 8
 * LCD Enable pin to digital pin 9
 * LCD D4 pin to digital pin 4
 * LCD D5 pin to digital pin 5
 * LCD D6 pin to digital pin 6
 * LCD D7 pin to digital pin 7
 * LCD R/W pin to ground

#include <LiquidCrystal.h>
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
float temp;
int tempAnalogPin = 1;
tmDriftInfo di;

void setup ()
  temp = (float(analogRead(tempAnalogPin))/1024.0)*500.0;  // initialize temp using first reading

  lcd.begin(16, 2);                         // set the lcd dimension
  lcd.clear();                              // LCD screen clear

  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  if(timeStatus()!= timeSet) 
     Serial.println("Unable to sync with the RTC");
     Serial.println("RTC has set the system time");

  di = RTC.read_DriftInfo();
  di.DriftDays = 1000;     // valid value 0 to 65,535
  di.DriftSeconds = 2800;  // fine tune this until your RTC sync with your reference time, valid value -32,768 to 32,767
  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


void loop()
  if(Serial.available()) {processSerialCommand();}
//  SerialDisplayDateTime(now3(di))    // use this if you don't have an LCD Display

void LcdDisplayTemperature(){
  // read LM35 temperature sensor using Arduino anolog pin
  // takes the average of 300 readings
  temp = (temp*299/300)+((float(analogRead(tempAnalogPin))/1024.0)*500.0*1/300);
  lcd.setCursor(11, 1); lcd.print(temp); lcd.setCursor(15, 1); //lcd.print("C");

void LcdDisplayDateTime(time_t timeToDisplay){
  lcd.setCursor(0, 0);
  lcd.print(year(timeToDisplay)); lcd.print("/"); lcd.print(month(timeToDisplay)); lcd.print("/"); lcd.print(day(timeToDisplay));

  lcd.setCursor(11, 0);

  lcd.setCursor(0, 1);

  int myHour = hourFormat12(timeToDisplay);
  if (myHour < 10) { lcd.print(" "); lcd.print(myHour); } else { lcd.print(myHour); }

  int myMinute = minute(timeToDisplay);
  if (myMinute < 10) { lcd.print("0"); lcd.print(myMinute); } else { lcd.print(myMinute); }

  int mySec = second(timeToDisplay);
  if ( mySec < 10) { lcd.print("0"); lcd.print(mySec); } else { lcd.print(mySec); }

  if(isAM(timeToDisplay)) {lcd.print("AM");} else {lcd.print("PM");};

void SerialDisplayDateTime(time_t timeToDisplay){
  Serial.print(year(timeToDisplay)); Serial.print("/");
  Serial.print(day(timeToDisplay));Serial.print(" ");
  if(isAM(timeToDisplay)) {Serial.print("AM");} else {Serial.print("PM");}; Serial.print(" ");

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  if(digits < 10)

void processSerialCommand() {
  char c = Serial.read(); 
    case 'T':
      // Command to set RTC time and update DriftInfo.DriftStart in the RTC battery back memory
      // Command format: TYYMMDDHHMMSS
      // Example: 2012 Oct 21 1:23pm is T121021132300
      delay(100); // Wait for all data to arrive
      if( Serial.available() == 12 ){  // process only if all expected data is available
        tmElements_t tme;
        time_t newTime;

        // Parse incomming 12 ASCII charaters into time_t
        // no error checking for numeric values in YYMDDHHMMSS fields, so be carefull!
        c = Serial.read(); tme.Year = c - '0';
        c = Serial.read(); tme.Year = 10*tme.Year; tme.Year += c-'0'; tme.Year += 30;
        c = Serial.read(); tme.Month = c - '0';
        c = Serial.read(); tme.Month = 10*tme.Month; tme.Month += c-'0';
        c = Serial.read(); tme.Day = c - '0';
        c = Serial.read(); tme.Day = 10*tme.Day; tme.Day += c-'0';
        c = Serial.read(); tme.Hour = c - '0';
        c = Serial.read(); tme.Hour = 10*tme.Hour; tme.Hour += c-'0';
        c = Serial.read(); tme.Minute = c - '0';
        c = Serial.read(); tme.Minute = 10*tme.Minute; tme.Minute += c-'0';
        c = Serial.read(); tme.Second = c - '0';
        c = Serial.read(); tme.Second = 10*tme.Second; tme.Second += c-'0';
        newTime = makeTime(tme);
        RTC.set(newTime);   // set the RTC and the system time to the received value

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

        Serial.print("RTC Set to: "); SerialDisplayDateTime(newTime);
    case 'I':
      // read and display DriftInfo from RTC memory
      tmDriftInfo diRead = RTC.read_DriftInfo();
      if (diRead.DriftStart == 0 | diRead.DriftDays == 0 | diRead.DriftSeconds == 0) {
        Serial.println("DriftInfo not set yet!");
      Serial.println("*** DriftInfo Read from RTC Memory ***");
      Serial.print("DriftStart   : ");
      SerialDisplayDateTime(diRead.DriftStart); Serial.println();
      Serial.print("DriftDays    : ");
      Serial.print("DriftSeconds : ");
      Serial.print("Day(s) since drift start: ");
      long tmp = now() - diRead.DriftStart;
      tmp *= diRead.DriftSeconds;
      Serial.print("Your RTC has Drifted(seconds): ");
      Serial.println(float(tmp)/float(SECS_PER_DAY * diRead.DriftDays));
  while(Serial.available()){ Serial.read(); } // clear serial buffer
This entry was posted in Arduino and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s