Trouble with both DS1307 and DS3231 RTCs and time slowing down

Hi All,

I am having problems with getting accurate time from my RTCs!

I have been searching for an answer to this question for several months, and have tried several things, but I have not managed to find a solution. I was wondering if I could ask you guys and gals for some help. I have described the project I have made previously (Alarm Clock with Lamp Dimmer and music Project - Finished, but input requested - Exhibition / Gallery - Arduino Forum), but I have included below the updated files. I have also added pull-up resistors to the I2C connections. Basically, my RTCs seem to get slower the longer my program is running for. I have tried changing the DS1307s and now upgraded to the temperature and age compensated DS3231. I have also added pull-up resistors of 4k7 to both lines of the I2C. I am not sure why my RTCs are slowing down. Is there some kind of interference going on with the RTC crystal, is there some problem with the I2C being busy and interfering with time keeping? Do I need to use Nick Gammon's new I2C library to overcome this problem, as some people have mentioned their RTCs "hanging" (I have tried to get the new I2C library (which replaces the Wire.h library on the master arduino) to work with this but I get lots of compiler errors trying to sort this out, and have given up on this idea for the time being)?

I would greatly appreciated people's input.

:smiley:

Complete_Alarm_Clock_V4_with_SparkfunMP3.ino (31.3 KB)

Receiver_Complete_Alarm_ClockV3.ino (8.72 KB)

Receiver_CompleteAlarmClock_Sparkfun_V1.ino (3.85 KB)

and here is the beginning of the master arduino code that connects with the RTC, so that you can get an idea of what is going on (I could not copy the whole code, due to the website's word limitation)

// Alarm Clock and Lamp Dimmer

#include "LedControl.h"
#include <Time.h>  
#include <Wire.h>  
#include <DS3232RTC.h>  // DS3231 library that returns time as time_t
#include <EEPROM.h> // to read & write to EEPROM memory

// debugging elements, comment out the next 3 lines if debugging is not needed
/*
#define DEBUG 1
const unsigned long debugDelay = 10000;
unsigned long serialPrintTimer = 0;
*/

// Time LED display control elements
// pin 12 is connected to the DataIn 
// pin 11 is connected to the CLK 
// pin 10 is connected to LOAD 
// 1 = only a single MAX72XX
LedControl lc=LedControl(12,11,10,1);
boolean LEDTimeOff = false;
const unsigned long LEDTimeTurningOffLength = 7000; // time until to start turning off LED display
const unsigned long LEDTimeOffDelayIncrements = 10000; // time between LED intensity decreases
// delay to space out reducing LED display intensity. Delay incremended each intensity decrease
unsigned long LEDTimeOffDelay = LEDTimeOffDelayIncrements; 
const int LEDTimeMaxIntensity = 3; // max intensity (0-15).
int LEDTimeIntensity = LEDTimeMaxIntensity;

boolean showingTemperature = false;

// time element
tmElements_t storedTime;

// alarm elements
tmElements_t alarmTime;
tmElements_t stopAlarmTime;
const int stopAlarmOffset = 7; // number of minutes for alarm music to run for
boolean alarmOn = true;
boolean alarmRunning = false;
boolean alarmStopped = false;

// lamp elements. lampState 0 = Off, 1 = downwards, 2 = upwards, 3 = fully on
int lampState = 0;
tmElements_t startDawnTime;
int startDawnOffset = -10; // minutes before alarm music starts for the dimmer to start
int dawnDimmerLength = 20; // length in minutes 1-60 of dawn running time
boolean dawnSimulatorReady2Run = true;
tmElements_t stopDuskTime;
const int duskDimmerLength = 10; // length in minutes 1-60
boolean duskModeRunning = false;
tmElements_t lampTurnOffTime;
int mintutesOfNoInput = 120;

// settings elements
char minuteOrHour = 'm';
unsigned long noInputTime = 0;
unsigned long flashingTimer = 0;
unsigned long flashDelay = 600;
boolean flashingState = false;

int dawnTrackNumber = 0; // first dawn tract
int duskTrackNumber = 100; // first dusk tract
// 0-99 where 0 = one song
int numberOfDawnSongs = 44;
// 0-99 where 0 = song one. Need to add 100 to use the tract with
// the sound module functions.
int numberOfDuskSongs = 22;

// button elements
// array of pin numbers for the 4 buttons, including the tilt switch, which is last.
const int buttonPins[] = {A0, A1, A2, A3, 9};
int buttonState[] = {LOW, LOW, LOW, LOW, LOW};
int buttonLastState[] = {LOW, LOW, LOW, LOW, LOW};
// 0 is change to LOW, 1 change to HIGH, 2 = no change
int buttonChangedTo[] = {2, 2, 2, 2, 2};
unsigned long buttonLastDebounce[] = {0, 0, 0, 0, 0};
unsigned long buttonTimers[] = {0, 0, 0, 0, 0};
const long debounceDelay = 50;

// RGB LED elements
const int RGBLEDPins[] = {3, 6, 5}; // red, green and then blue
const int RGBMaxBrightness = 80; // max brightness (0-255)
int RGBLEDValues[] = {0, 0, 0};
// 0 = red, 1 = green, 2 = blue
int RGBLEDColour2Change = 1;
int RGBLEDUpOrDown = 1; //0 = down, 1 = up
unsigned long RGBLEDTimer = 0;
unsigned long RGBLEDTimer2 = 0;

void setup() 
{
  int EEPROMCounter = 0;
  int bytesFromMasterToSlave = 0;
  
  // MAX72XX LED controller wake up and show time
  lc.shutdown(0,false);
  /* Set the brightness 0-15 */
  lc.setIntensity(0,LEDTimeIntensity);
  /* and clear the display */
  lc.clearDisplay(0);
  
  // RGB LED setup
  for(int i = 0; i < 3; i++)
  {
    pinMode(RGBLEDPins[i], OUTPUT);
    analogWrite(RGBLEDPins[i], 0);
  }
  
  /*
  // time setup (comment out if you don't want to reset the time when power turned off)
  storedTime.Hour = 18;
  storedTime.Minute = 16;
  storedTime.Second = 00;
  storedTime.Day = 17;
  storedTime.Month = 7;
  storedTime.Year = CalendarYrToTm(2014); 
  RTC.write(storedTime);
  */

  // the function to get the time from the RTC
  // I have changed to using a DS3231
  setSyncProvider(RTC.get); 
  setSyncInterval(3600); // resyn interval (3600 = seconds in one hour, 86400 = seconds in one day)
  //to help with showing the time on the LED display
  storedTime.Hour = hour();
  storedTime.Minute = minute();
  
  // to check all of the LED screen elements are working
  lc.setDigit(0,0,8,false);
  lc.setDigit(0,1,8,false);
  lc.setDigit(0,2,8,true);
  lc.setDigit(0,3,8,true);
  delay(2000);
  lc.clearDisplay(0);
  delay(1000);
  // check RGB LEDs are working
  for(int i = 0; i < 3; i++)
  {
    analogWrite(RGBLEDPins[i], RGBMaxBrightness);
    delay(2000);
    analogWrite(RGBLEDPins[i], 0);
  }
  delay(1000);
  showTimeLED(storedTime);
  
  //alarm setup
  alarmTime.Hour = 12;
  alarmTime.Minute = 01;
  startDawnTime = alarmTime;
  changeTime(startDawnTime, startDawnOffset);
  stopAlarmTime = alarmTime;
  changeTime(stopAlarmTime, stopAlarmOffset);
  
  lampTurnOffTime.Hour = hour();
  lampTurnOffTime.Minute = minute();
  changeTime(lampTurnOffTime, mintutesOfNoInput);
  
  #ifdef DEBUG
  Serial.begin(9600);
  Serial.println(" ");
  Serial.println("Welcome to the Alarm clock serial interface");
  Serial.println(" ");
  if(timeStatus()!= timeSet) 
     Serial.println("Unable to sync with the RTC");
  else
     Serial.println("RTC has set the system time");
  #endif

  // button setup
  for(int i = 0; i < 5; i++)
    pinMode(buttonPins[i], INPUT);
  
  // I2C bus join and turn off the lamp
  Wire.begin();
  TWBR = 152;  // 50 kHz. TWBR = 72; // 100 kHz (default)
  Wire.beginTransmission(4); // transmit to device #4
  Wire.write(0); // turn off the lamp
  Wire.write(0); // zero minutes
  Wire.write(0); // zero minutes
  Wire.endTransmission();
  
  Wire.requestFrom(5, 1);
  bytesFromMasterToSlave = Wire.read();
  
  // stops all music
  Wire.beginTransmission(5);
  Wire.write(0);
  Wire.write(0);
  Wire.write(0);
  Wire.write(0);
  Wire.write(0);
  Wire.write(0);
  Wire.write(0);
  Wire.write(0);
  Wire.write(0);
  Wire.endTransmission();  
  
  #ifdef DEBUG
  Serial.print("Number of Bytes to send to I2C #5 = ");
  Serial.println(bytesFromMasterToSlave); 
  #endif

  randomSeed(analogRead(0));
  
  // checking if EEPROM has been previously written to and all
  // the variables have been set
  for(int i = 0; i < 7; i++)
    if(EEPROM.read(i) == 255)
      EEPROMCounter++;
      
  if(EEPROMCounter == 0) // i.e. no 255 values read (non set values)
  {
    // alarm on or off
    if(EEPROM.read(0) == 0) // off
      alarmOn = false;
    else // on
      alarmOn = true;
      
    // alarm time
    alarmTime.Hour = EEPROM.read(1);
    alarmTime.Minute = EEPROM.read(2);
    
    // number of dawn songs
    numberOfDawnSongs = EEPROM.read(3); 
    
    // number of dusk songs
    numberOfDuskSongs = EEPROM.read(4);
    
    // dawn start up before alarm
    startDawnOffset = -1 * EEPROM.read(5);
    
    // dawn simulation length
    dawnDimmerLength = EEPROM.read(6);
    
    #ifdef DEBUG
    Serial.println("EEPROM values used");
    #endif
  } 
  
  #ifdef DEBUG
  if(alarmOn == false)
    Serial.println("Alarm off");
  else
    Serial.println("Alarm on");
  Serial.print("Alarm time: ");
  Serial.print(alarmTime.Hour);
  Serial.print(":");
  Serial.println(alarmTime.Minute);
  Serial.print("Number of dawn songs = ");
  Serial.println(numberOfDawnSongs+1);
  Serial.print("Number of dusk songs = ");
  Serial.println(numberOfDuskSongs+1);
  Serial.print("Dawn start offset = ");
  Serial.print(startDawnOffset);
  Serial.println(" minutes");
  Serial.print("Dawn Dimmer run time = ");
  Serial.print(dawnDimmerLength);
  Serial.println(" minutes");
  #endif
  
}

void showTimeLED(tmElements_t tm)
{
  lc.setDigit(0,0,tm.Hour/10,false);
  lc.setDigit(0,1,tm.Hour%10,false);
  lc.setDigit(0,2,tm.Minute/10,true);
  lc.setDigit(0,3,tm.Minute%10,true);
}

Which of those .ino files are your project?

void receiveEvent(int howMany)
{
  // makes sure the corrent number of bytes is received.
  if(howMany != eventByteNumber)
  {
    MP3player.playTrack(99);
    delay(50);
    return;
  }

You can't do a delay inside an interrupt service routine.

Is there some kind of interference going on with the RTC crystal, is there some problem with the I2C being busy and interfering with time keeping?

That sounds very unlikely indeed. That's like asking if a clock slows down if you don't look at it (or look at it the wrong way).

Try a very simple sketch that simply reads the clock and reports the time. If that is showing the right time a day later then the problem is somewhere else.

Nick's suggestion is a good one.

I don't understand why you are going through the layer of setSyncProvider , updating every hour, and using some Arduino internal clock.

Why not just read the RTC directly whenever want to know the time?

wb Nick :slight_smile: seems you're stiring up some fuss in another thread lol

Are you sure the crystal meets the specs?- the 1307 datasheet specifies not only its frequency but its capacitance too.

cjdelphi:
wb Nick :slight_smile: seems you're stiring up some fuss in another thread lol

Not intentionally. What thread is that?

Hi Nick,

nice to get input from you. I have really appreciated your work elsewhere. Sorry I have taken so long to reply, but I have recently moved house, and I did not realise you do not get email notifications when new replies are added to a topic on the arduino forum. I thought I had received none, and only looked out of curiosity today on the forum.

Regarding the .ion files. All three are used in the project, I have three arduinos running different aspects of the project. I will also get rid of the delays in the interrupt.

cattledog:
Nick's suggestion is a good one.

I don't understand why you are going through the layer of setSyncProvider , updating every hour, and using some Arduino internal clock.

Why not just read the RTC directly whenever want to know the time?

Cattledog,

I am not sure, I thought that is the way it is done. Would that not just slow things down reading the time from the RTC every cycle of the loop function?

How would you change the program?

How would you change the program?

You have a very complex program with multiple uses of different functions within the Time.h library. so it's difficult to go in there and change one piece related to the RTC

Setting and reading the RTC certainly does not require Time.h , and for my DS1307 modules I use the library RTClib.h .
GitHub - adafruit/RTClib: A fork of Jeelab's fantastic RTC Arduino library.
I believe it should work for a DS3231.

Many people do not use any library for simple RTC uses and use Wire.h for direct reading and writing.

You can take a look at this thread which addresses the use of the Time.h with RTCs.
http://forum.arduino.cc/index.php?topic=257468.msg1820820#msg1820820

I'm not sure how time critical your program is, and the I2C reads do take some time. If you don't need the time every time through the loop, you can read the RTC with a millis timer like in the 'blink without delay" example.

Did you run the simplified bench mark test, and is there resolution of the issue of the RTC accuracy?

Basically, my RTCs seem to get slower the longer my program is running for.

cattledog:
Did you run the simplified bench mark test, and is there resolution of the issue of the RTC accuracy?

I am currently running the simplified version if my program. I will see what happens to the time in a few days time. Many thanks for your input on communicating with the RTC cattle dog, I will use it when I get the results from the simplified clock.

I agree that a simplified sketch that does pretty much nothing but display the time is the way to go to debug this. I work a lot with RTCs and I'm not sure I've ever seen the symptom described.

Just another alternative, I wrote this library and it is known to work well with DS3231 and DS3232. (It might even work with DS1307, but I can't swear to it, I haven't tried it.)

Anyway, download the library and just run one of the example sketches that come with it, either "SetSerial" or "TimeRTC".

Use a lithium phone battery as it's backup power source instead of a coin cell...

When I did that, I went from losing seconds a day to seconds a month!

cjdelphi:
Use a lithium phone battery as it's backup power source instead of a coin cell...

When I did that, I went from losing seconds a day to seconds a month!

And what do you attribute that to?

It's odd that I've never noticed any trouble with coin cells.

Purely observational... but it's a true story, kinda leads me to believe the battery's internal resistance plays a part.

cjdelphi:

[quote author=Jack Christensen link=topic=256037.msg1831879#msg1831879 date=1407294382]

cjdelphi:
Use a lithium phone battery as it's backup power source instead of a coin cell...

When I did that, I went from losing seconds a day to seconds a month!

And what do you attribute that to?

It's odd that I've never noticed any trouble with coin cells.

Purely observational... but it's a true story, kinda leads me to believe the battery's internal resistance plays a part.
[/quote]

It may be a true story, but that's not a conclusion that I could accept. It can't be internal resistance, something else must have been going on. These RTCs only draw nanoamps from the backup batteries, and then only when Vcc is not present. My Microchip RTCs use a 1000? resistor in series with the coin cell, and this drops less than a millivolt because the current is so low.

Coin cells work fine with RTCs and they last for years due to the low load.

I need a proxy, it costs me money to reply due to my ip ban.

Ok so it's nothing to do with it's internal resistance. .. but I remember leaving it for a couple of weeks, the coin cell drifted while the nokia battery was perfect.. maybe my coin cell was really low on voltage?

cjdelphi:
I need a proxy, it costs me money to reply due to my ip ban.

Ok so it's nothing to do with it's internal resistance. .. but I remember leaving it for a couple of weeks, the coin cell drifted while the nokia battery was perfect.. maybe my coin cell was really low on voltage?

Could be, I might have checked it with a meter. Was the RTC being powered only by the backup battery, or did this occur while the RTC was powered by VCC? If the latter, it shouldn't matter what the state of the battery was. The RTCs that I use say that if a backup battery is not used, the VBAT pin should be connected to ground. I've also seen it said that if a backup battery is used, then one has to be present, even if it's dead, for the RTC to operate properly on VCC. Haven't checked that myself, though.

Hi all,

so I have tried out Nick's suggestion of running a "bare" system, that does not over use the I2C. What I have basically done is commented out anything to do with starting, transmitting or receiving on the I2C (I have left the Wire.h header file though), apart from the RTC commands. I left the system running for a week, pressing buttons, allowing the alarm functions to run (but no music is played or light dimmed of course) and running the dusk setting. Well, that fixed it, the RTC shows perfect time keeping. So there is some problem with the I2C protocol. Any help people?

Regards

Mark