Pages: [1] 2   Go Down
Author Topic: DS 1307 stays behind in time day by day...  (Read 1910 times)
0 Members and 1 Guest are viewing this topic.
manchester
Offline Offline
Newbie
*
Karma: 0
Posts: 22
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi All,

I am having an Arduino Uno Rev.3 connected with an RTC DS1307 and an LCD 2x16.
I am displaying the time and date using the RTC on the LCD.
After a week I noticed that the time displayed in the LCD and the time in my PC have a difference of a few minutes.
For example: PC time --> 11:04:17 and my RTC -->11:01:41
My RTC is not keeping the time as it should be but stays day by day a little behind from the "actual" time.
Do you have any ideas on how I can synchronize my RTC with my PC system time?

Many Thanks!
Logged

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4063
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The Time library, http://www.arduino.cc/playground/Code/Time has a Processing sketch that runs on the PC for this purpose. Have not used it myself. I might search the forum a bit too, as this is a question that comes up occasionally and there are likely other solutions available.

BTW, if your example is typical of your actual observations, i.e. a 156 second difference in a week's time, that is an unusually large error (258 ppm). I'd expect a DS1307 to be more accurate than that by a factor of ten or more. It might be worth posting your code. (I'm assuming the PC is being kept accurate via NTP, etc.)

Quote
After a week I noticed that the time displayed in the LCD and the time in my PC have a difference of a few minutes.
For example: PC time --> 11:04:17 and my RTC -->11:01:41

Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Norfolk UK
Offline Offline
Faraday Member
**
Karma: 67
Posts: 2521
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have always found the DS1307 to be a poor keeper of time.
The way I improved the timekeeping in a standalone project with no computer connection was to work out how much time the RTC loses per day and then at the midnight hour add to the current RTC time and save it. You could modify this to update hourly instead of daily.

Code:
//Enumerations
const int driftComp = 9;    //Seconds to adjust clock by per day to compensate for RTC drift
const int delayComp = 500;  //Micro seconds to delay clock adjust by (driftComp * 1000) - delayCom = 8.5 seconds per 24 hours

void adjustClock(){
    static boolean driftApplied = false;

    RTC.readClock();
    int h=RTC.getHours();

    // RTC Drift compensation
    if(h == 0)                              //Is it the midnight hour
    {
        if(!driftApplied) {                 //Have I already done drift compensation
            int seconds = RTC.getSeconds(); //Get seconds
            seconds = seconds + driftComp;  //Apply drift
            delay(delayComp);               //Apply delay
            RTC.setSeconds(seconds % 60);   //Put seconds
            RTC.setClock();                 //Set clock
            driftApplied=true;              //Signal drift applied
        }
    }
    else{                                   //Not midnight hour
        driftApplied = false;               //Reset drift applied
    }
}
Logged

manchester
Offline Offline
Newbie
*
Karma: 0
Posts: 22
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you Jack and Riva for your answers.
Riva because I am quite new to the Arduino. How can I use your code?
What I am asking is how can I use void adjustClock() function on my program?

My code is a very simple code and i state it below:
Code:
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
#include <LiquidCrystal.h>
#include <Wire.h>
#include "RTClib.h"
 
RTC_DS1307 RTC;
LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);
void setup () {
 
  lcd.begin(16, 2);
  lcd.clear();
  Serial.begin(57600);
  Wire.begin();
  RTC.begin();
 
  if (! RTC.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    //RTC.adjust(DateTime(__DATE__, __TIME__));
  }
 
}
 
void loop () {
   
  DateTime now = RTC.now();
   
  lcd.clear(); // Clear the LCD screen
  lcd.print("Time: ");
  if (now.hour() < 10) lcd.print("0"); // Check if we need to add leading zero
  lcd.print(now.hour(), DEC); // Output current hour
  lcd.print(":");
  if (now.minute() < 10) lcd.print("0"); // Check if we need to add leading zero
  lcd.print(now.minute(), DEC); // Output current minute
  lcd.print(":");
  if (now.second() < 10) lcd.print("0"); // Check if we need to add leading zero
  lcd.print(now.second(), DEC);
  lcd.setCursor(0, 1);
  lcd.print("Date: ");
  if (now.day() < 10) lcd.print("0"); // Check if we need to add leading zero
  lcd.print(now.day(), DEC); // Output current hour
  lcd.print(":");
  if (now.month() < 10) lcd.print("0"); // Check if we need to add leading zero
  lcd.print(now.month(), DEC); // Output current minute
  lcd.print(":");
  if (now.year() < 10) lcd.print("0"); // Check if we need to add leading zero
  lcd.print(now.year(), DEC);
 
  Serial.println();
  delay(1000);
  lcd.clear();
}

Thank you!!!
Logged

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4063
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Well the code does seem pretty straightforward. DS1307 accuracy is governed by the crystal's accuracy, circuit layout, and temperature. Many crystals used with DS1307s have specs of ±20 or ±30 ppm. My expectation would be for the circuit to operate reasonably close to that. While I don't consider 20-30 ppm to be great accuracy, practically, it's probably OK for many applications, but represents a minimum standard IMHO.
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Norfolk UK
Offline Offline
Faraday Member
**
Karma: 67
Posts: 2521
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I use a different RTC library to you but I think I have altered your program to suit. You will need to work out the correct driftComp & delayComp values to suit your DS1307. The values you give indicate a 22.286 second loss per day so once every 24 hours (assuming your clock is 24 hour mode) I add 23 seconds and then wait 714 milliseconds = 22.286 and then set the clock. As you have such a large variation you could maybe alter the code to adjust the clock every hour instead.

Code:
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
#include <LiquidCrystal.h>
#include <Wire.h>
#include "RTClib.h"

const int driftComp = 23;    //Seconds to adjust clock by per day to compensate for RTC drift
const int delayComp = 714;  //Milliseconds to delay clock adjust by (driftComp * 1000) - delayCom = 8.5 seconds per 24 hours

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

void setup () {
   
    lcd.begin(16, 2);
    lcd.clear();
    Serial.begin(57600);
    Wire.begin();
    RTC.begin();
   
    if (! RTC.isrunning()) {
        Serial.println("RTC is NOT running!");
        // following line sets the RTC to the date & time this sketch was compiled
        //RTC.adjust(DateTime(__DATE__, __TIME__));
    }
   
}

void loop () {
    static boolean driftApplied = false;
   
    DateTime now = RTC.now();
   
    // RTC Drift compensation
    if(now.hour == 0) {                     //Is it the midnight hour
        if(!driftApplied) {                 //Have I already done drift compensation
            int seconds = now.second();     //Get seconds
            seconds = seconds + driftComp;  //Apply drift
            delay(delayComp);               //Subtract delay
            now.Seconds(second % 60);       //Put seconds
            RTC.adjust(now);                //Set clock
            driftApplied=true;              //Signal drift applied
        }
    }
    else{                                   //Not midnight hour
        driftApplied = false;               //Reset drift applied
    }
   
    lcd.clear(); // Clear the LCD screen
    lcd.print("Time: ");
    if (now.hour() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.hour(), DEC); // Output current hour
    lcd.print(":");
    if (now.minute() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.minute(), DEC); // Output current minute
    lcd.print(":");
    if (now.second() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.second(), DEC);
    lcd.setCursor(0, 1);
    lcd.print("Date: ");
    if (now.day() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.day(), DEC); // Output current hour
    lcd.print(":");
    if (now.month() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.month(), DEC); // Output current minute
    lcd.print(":");
    if (now.year() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.year(), DEC);
   
     delay(1000);
}
Logged

Leeds, England
Offline Offline
God Member
*****
Karma: 14
Posts: 639
Quick, chuck it in the bin before the boss finds out...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

In the code that sets the time you will notice __DATE__ and __TIME__. Are you using these to set the time? If so then these are known as preprocessor directives. What happens (in brief) is that the first thing that happens when you start to compile and download your sketch is that these two items are replaced by the current date and time according to your pc. The problem is that then the sketch is compiled and squirted down to the processor, all of which takes time. Hence the RTC ends up several seconds behind the pc.

To get round this you can specify the time you want to set the RTC to:

RTC.adjust(DateTime(__DATE__, "13:00:00"));

If you remove the test to check if the RTC is running and upload the sketch then as soon as you run the Serial Monitor it will set the time to that specified. So using the example above, about thirty seconds before one o'clock I would upload the sketch and then, a couple of seconds before time, start up the Serial Monitor. Don't run the Serial Monitor again otherwise you will reset the time again!

With a little trial and error you should be able to time it just right!

However, setting it right won't stop the time from drifting over a week due to the factors outlined by Jack. If accuracy is important you might wish to consider a DS3232 based module.
Logged

Beginners guide to using the Seeedstudio SIM900 GPRS/GSM Shield

Saskatchewan
Offline Offline
Sr. Member
****
Karma: 19
Posts: 364
When the going gets weird, the weird turn pro. - Hunter S. Thompson
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have a 1307 in my project and it runs for days and days between getting checked out by me. I only display the time accurate to a minute but it matches my PC whenever I check. I used part of a sample sketch that sets the time by sending Unix time to it via serial. I've only set it the once when I got a battery for it, probably a good month ago.

Did I get lucky?   smiley-grin

Logged

manchester
Offline Offline
Newbie
*
Karma: 0
Posts: 22
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you all for your help and ideas!
Regarding Riva:
Thank you for modifying the code. Unfortunately when I am verifying the code I am having an error on line
Code:
if(now.hour == 0) {                     //Is it the midnight hour
.
I changed it to
Code:
if(now.hour() == 0) {                     //Is it the midnight hour
and now it gives me an error on
Code:
  now.seconds(second % 60);       //Put seconds
. The error I get is the following:
Quote
sketch_oct04c.cpp: In function 'void loop()':
sketch_oct04c:38: error: 'class DateTime' has no member named 'seconds'
sketch_oct04c:38: error: 'second' was not declared in this scope
I have stuck with this error.

My code now is :
Code:
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
#include <LiquidCrystal.h>
#include <Wire.h>
#include "RTClib.h"

const int driftComp = 23;    //Seconds to adjust clock by per day to compensate for RTC drift
const int delayComp = 714;  //Milliseconds to delay clock adjust by (driftComp * 1000) - delayCom = 8.5 seconds per 24 hours

RTC_DS1307 RTC;
 LiquidCrystal lcd(8, 7, 5, 4, 3, 2);

void setup () {
   
    lcd.begin(16, 2);
    lcd.clear();
    Serial.begin(57600);
    Wire.begin();
    RTC.begin();
   
    if (! RTC.isrunning()) {
        Serial.println("RTC is NOT running!");
        // following line sets the RTC to the date & time this sketch was compiled
        //RTC.adjust(DateTime(__DATE__, __TIME__));
    }
   
}

void loop () {
    static boolean driftApplied = false;
   
    DateTime now = RTC.now();
   
    // RTC Drift compensation
    if(now.hour() == 0) {                     //Is it the midnight hour
        if(!driftApplied) {                 //Have I already done drift compensation
            int seconds = now.second();     //Get seconds
            seconds = seconds + driftComp;  //Apply drift
            delay(delayComp);               //Subtract delay
            now.seconds(second % 60);       //Put seconds
            RTC.adjust(now);                //Set clock
            driftApplied=true;              //Signal drift applied
        }
    }
    else{                                   //Not midnight hour
        driftApplied = false;               //Reset drift applied
    }
   
    lcd.clear(); // Clear the LCD screen
    lcd.print("Time: ");
    if (now.hour() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.hour(), DEC); // Output current hour
    lcd.print(":");
    if (now.minute() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.minute(), DEC); // Output current minute
    lcd.print(":");
    if (now.second() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.second(), DEC);
    lcd.setCursor(0, 1);
    lcd.print("Date: ");
    if (now.day() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.day(), DEC); // Output current hour
    lcd.print(":");
    if (now.month() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.month(), DEC); // Output current minute
    lcd.print(":");
    if (now.year() < 10) lcd.print("0"); // Check if we need to add leading zero
    lcd.print(now.year(), DEC);
   
     delay(1000);
}
I have also changed
Quote
LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);
to
LiquidCrystal lcd(8, 7, 5, 4, 3, 2);

Regarding Jimmy60:
Your way of dealing with the DS1307 sounds very interesting. Could you please sent me your code to have a look?

Many Thanks to all of you!

Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 611
Posts: 49101
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
            int seconds = now.second();     //Get seconds
            seconds = seconds + driftComp;  //Apply drift
            delay(delayComp);               //Subtract delay
            now.seconds(second % 60);       //Put seconds
The now.second() call with no argument returns the current value. The same method, with a value, sets the value. The compiler is right, just so you know.
Logged

0
Offline Offline
Shannon Member
****
Karma: 206
Posts: 12113
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

One possible issue is that _watch_ 32768Hz crystals are designed for the average temperature of a wristwatch (close to body heat for 2/3 of the time).  You might find it keeps better time if kept at 30C rather than room temp.

Also crystals are cut for a specific load-capacitance - if the crystal is not designed for the oscillator it will run somewhat out of spec.
« Last Edit: October 04, 2012, 10:18:35 am by MarkT » Logged

[ I won't respond to messages, use the forum please ]

Saskatchewan
Offline Offline
Sr. Member
****
Karma: 19
Posts: 364
When the going gets weird, the weird turn pro. - Hunter S. Thompson
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm using the Time.h library I found here:

http://www.arduino.cc/playground/Code/time

The included sample, TimeRTCSet, has the method I used to set it.

As far as ambient air temps go my Arduino has spent most of it's running time around 20C or lower. The last week has been closer to 15C. It's in an unheated shop.

Time.h includes a DS1307RTC.h, I notice you aren't using Time.h
Logged

Norfolk UK
Offline Offline
Faraday Member
**
Karma: 67
Posts: 2521
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
  now.seconds(second % 60);       //Put seconds
. The error I get is the following:
Quote
sketch_oct04c.cpp: In function 'void loop()':
sketch_oct04c:38: error: 'class DateTime' has no member named 'seconds'
sketch_oct04c:38: error: 'second' was not declared in this scope
I have stuck with this error.
From the error message try this line instead.
Code:
now.second(seconds % 60);
Logged

Left Coast, CA (USA)
Offline Offline
Brattain Member
*****
Karma: 361
Posts: 17293
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have a 1307 in my project and it runs for days and days between getting checked out by me. I only display the time accurate to a minute but it matches my PC whenever I check. I used part of a sample sketch that sets the time by sending Unix time to it via serial. I've only set it the once when I got a battery for it, probably a good month ago.

Did I get lucky?   smiley-grin



Yes. Accuracy of a 1307 RTC or any RTC for that matter, is based on the accuracy of the crystal and how well it's matched electrically to the chip. Even your PC internal RTC will drift some amount over time and needs to be reset either manually or via some on-line updating method. Some 1307 designs have a small trimmer cap wired to one of the crystal pins and ground to allow a tweeking control to try and get better long term accuracy but even with that temperature changes will effect long term accuracy.
Logged

Offline Offline
Edison Member
*
Karma: 33
Posts: 1438
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This story seems appropriate here. The contractor was the dad of someone I used to work with...

A contractor working on a nuclear sub noticed that the ships chronometer was a few minutes off. He set it to WWV, and didn't think any more about it. A few days later he noted that it was off by a few minutes again, this was way out of spec, so he started to get concerned. He set again and kept checking it. A few hours later, the CPO came in, glanced at his wristwatch and reset the chronometer by it.
Logged

Pages: [1] 2   Go Up
Jump to: