Drift on Digi-Key DS3231 (Arduino Newbie)

Hey, I'm new to arduino and recently purchased an Adafruit DS3231 from Digi-Key. I'm finding that the time seems to drift getting a few seconds worse. After a few mins it goes from 7 seconds off to 20 seconds and beyond. I'm using the RTC_DS3231 example sketch.

I'm starting to wonder if the module is the problem. How can I check?

Any help appreciated, I'm new to arduino.

Here is the example code I'm using:
// Date and time functions using a DS3231 RTC connected via I2C and Wire lib
#include "RTClib.h"

RTC_DS3231 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

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

#ifndef ESP8266
while (!Serial); // wait for serial port to connect. Needed for native USB
#endif

if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
abort();
}

if (rtc.lostPower()) {
Serial.println("RTC lost power, let's set the time!");
// When time needs to be set on a new device, or after a power loss, the
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(DATE), F(TIME)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

// When time needs to be re-set on a previously configured device, the
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(DATE), F(TIME)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

void loop () {
DateTime now = rtc.now();

Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(" (");
Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
Serial.print(") ");
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();

Serial.print(" since midnight 1/1/1970 = ");
Serial.print(now.unixtime());
Serial.print("s = ");
Serial.print(now.unixtime() / 86400L);
Serial.println("d");

// calculate a date which is 7 days, 12 hours, 30 minutes, 6 seconds into the future
DateTime future (now + TimeSpan(7,12,30,6));

Serial.print(" now + 7d + 12h + 30m + 6s: ");
Serial.print(future.year(), DEC);
Serial.print('/');
Serial.print(future.month(), DEC);
Serial.print('/');
Serial.print(future.day(), DEC);
Serial.print(' ');
Serial.print(future.hour(), DEC);
Serial.print(':');
Serial.print(future.minute(), DEC);
Serial.print(':');
Serial.print(future.second(), DEC);
Serial.println();

Serial.print("Temperature: ");
Serial.print(rtc.getTemperature());
Serial.println(" C");

Serial.println();
delay(3000);
}

Which Arduino board are you using ?

Try this sketch which does not use a library, not that the library is likely to be a problem.

You may need to change the clock address in the code to suit your project and you can set the time by editing the parameters in the call to setTime()

#include <Wire.h>
const byte CLOCK_ADDRESS = 0x68;
const byte clockSecond = 0x00;
const byte clockMinute = 0x01;
const byte clockHour = 0x02;

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  setClock(11, 15, 57); //hour, minute, second
}

void loop()
{
  printTime();
}

void setClock(byte hour, byte minute, byte second)
{
  Wire.begin();
  writeByte(clockHour, hour);
  writeByte(clockMinute, minute);
  writeByte(clockSecond, second);
}

void printTime()
{
  static byte previousSecond = 0;
  byte currentSecond = readClock(clockSecond);
  if ( currentSecond != previousSecond)
  {
    previousSecond = currentSecond;
    Serial.print(bcdToDec(readClock(clockHour)));
    Serial.print(":");
    Serial.print(bcdToDec(readClock(clockMinute)));
    Serial.print(":");
    byte currentSecond = readClock(clockSecond);
    Serial.println(bcdToDec(currentSecond));
  }
}

byte readClock(byte clockData)
{
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(clockData); //start reading here
  Wire.endTransmission();
  Wire.requestFrom(CLOCK_ADDRESS, 1); //get a byte
  return Wire.read();
}

byte decToBcd(byte val)
{
  return ( (val / 10 * 16) + (val % 10) );
}

byte bcdToDec(byte val)
{
  return ( (val / 16 * 10) + (val % 16) );
}

void writeByte(byte address, byte value)
{
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(address); //start writing here
  Wire.write(decToBcd(value)); //write this
  Wire.endTransmission();
}

I'm using an Uno.

UKHeliBob:
Which Arduino board are you using ?

Try this sketch which does not use a library, not that the library is likely to be a problem.

You may need to change the clock address in the code to suit your project and you can set the time by editing the parameters in the call to setTime()

#include <Wire.h>

const byte CLOCK_ADDRESS = 0x68;
const byte clockSecond = 0x00;
const byte clockMinute = 0x01;
const byte clockHour = 0x02;

void setup()
{
 Serial.begin(115200);
 while (!Serial);
 setClock(11, 15, 57); //hour, minute, second
}

void loop()
{
 printTime();
}

void setClock(byte hour, byte minute, byte second)
{
 Wire.begin();
 writeByte(clockHour, hour);
 writeByte(clockMinute, minute);
 writeByte(clockSecond, second);
}

void printTime()
{
 static byte previousSecond = 0;
 byte currentSecond = readClock(clockSecond);
 if ( currentSecond != previousSecond)
 {
   previousSecond = currentSecond;
   Serial.print(bcdToDec(readClock(clockHour)));
   Serial.print(":");
   Serial.print(bcdToDec(readClock(clockMinute)));
   Serial.print(":");
   byte currentSecond = readClock(clockSecond);
   Serial.println(bcdToDec(currentSecond));
 }
}

byte readClock(byte clockData)
{
 Wire.beginTransmission(CLOCK_ADDRESS);
 Wire.write(clockData); //start reading here
 Wire.endTransmission();
 Wire.requestFrom(CLOCK_ADDRESS, 1); //get a byte
 return Wire.read();
}

byte decToBcd(byte val)
{
 return ( (val / 10 * 16) + (val % 10) );
}

byte bcdToDec(byte val)
{
 return ( (val / 16 * 10) + (val % 16) );
}

void writeByte(byte address, byte value)
{
 Wire.beginTransmission(CLOCK_ADDRESS);
 Wire.write(address); //start writing here
 Wire.write(decToBcd(value)); //write this
 Wire.endTransmission();
}

So I've only been running this for a few minutes, but what started as a 2 second offset (between what I set it to vs when it uploaded) has become 6 seconds and I imagine it will continue. I'm on an Uno.

Are you using a battery or supplying the power with the Arduino Uno?

If you are using a battery to supply the Adafruit DS3231, check the voltage across the battery if you have a multimeter available, with the right voltage range selected. It should be above 2.3 Volts as stated on the specification sheet of the Adafruit website. Try swapping the CR1220 sized battery if you have more available or just use the Uno as the power supply.

Thanks

codematics:
Are you using a battery or supplying the power with the Arduino Uno?

If you are using a battery to supply the Adafruit DS3231, check the voltage across the battery if you have a multimeter available, with the right voltage range selected. It should be above 2.3 Volts as stated on the specification sheet of the Adafruit website. Try swapping the CR1220 sized battery if you have more available or just use the Uno as the power supply.

Thanks

I have a CR1220 in there and its reading 2.9v. I also have the Vin and Gnd hooked up to the DS3231 from the Uno. Is that correct?

No you shouldn’t have Vin connected to it , use the 5v pin and ground.

Run the example supplied with your library to check the clock performance to establish whether your code or the clock is at fault .

hammy:
No you shouldn’t have Vin connected to it , use the 5v pin and ground.

Run the example supplied with your library to check the clock performance to establish whether your code or the clock is at fault .

To clarify, the DS3231's Vin is attached to the 5V on the Uno. Is that correct? I've got the DS3231's Gnd, SCL and SDA to Uno's Gnd, SCL and SDA.

I've been using the code in my OP and the one added later and both drift.

That’s correct

So I've only been running this for a few minutes, but what started as a 2 second offset (between what I set it to vs when it uploaded) has become 6 seconds and I imagine it will continue. I'm on an Uno.

What are you using for the reference clock which you are drifting away from?

cattledog:
What are you using for the reference clock which you are drifting away from?

I'm having it print to the serial output and comparing it to the timestamp of the printed information.

16:18:25.685 -> 2021/4/1 (Thursday) 16:17:56
16:18:25.685 -> since midnight 1/1/1970 = 1617293876s = 18718d
16:18:25.718 -> now + 7d + 12h + 30m + 6s: 2021/4/9 4:48:2
16:18:25.718 -> Temperature: 23.50 C

So I've only been running this for a few minutes, but what started as a 2 second offset (between what I set it to vs when it uploaded) has become 6 seconds and I imagine it will continue.

I'm having it print to the serial output and comparing it to the timestamp of the printed information.

That timestamp is generated by the system clock on the computer and its not clear to me how accurate it really is and how often it synchronizes to an external reference.

Since you have a part from DigiKey and not some $2.00 module, I would expect it to be genuine.

How does your DS3231 look compared to a phone? I have also seen phone apps which will display the GPS time as well as the phone's system time.

If you have a GPS module you can get the 1 second pulse to compare the RTC against.

ryandude10:
I'm having it print to the serial output and comparing it to the timestamp of the printed information.

16:18:25.685 -> 2021/4/1 (Thursday) 16:17:56
16:18:25.685 -> since midnight 1/1/1970 = 1617293876s = 18718d
16:18:25.718 -> now + 7d + 12h + 30m + 6s: 2021/4/9 4:48:2
16:18:25.718 -> Temperature: 23.50 C

So, are you comparing it to the Arduino clock, via millis() time? That frequently drifts badly. The suggestion above to run a DS3231 library example sketch instead, is a very good one. I often compare times with an online UTC clock. There are a few around. A modern cell phone is also usually dead on.

So, are you comparing it to the Arduino clock, via millis() time?

No, the monitor timestamp comes from the PC not the Arduino.

cattledog:
No, the monitor timestamp comes from the PC not the Arduino.

Oh yeah, that...

How about a circuit diagram and/or pictures of the hardware? If it still doesn't seem right compared with something other than the PC...

aarg:
Oh yeah, that...

How about a circuit diagram and/or pictures of the hardware? If it still doesn't seem right compared with something other than the PC...

Sorry, I'm new and don't have a circuit diagram. I bought this model, but from Digi-Key since it's out of stock on Adafruit.

Where did you get the information to make the interconnections between the Uno and the RTC board? How did you actually implement it? The reason I asked for a diagram, is to see how your wires are connected. Not the schematic of the RTC board, we know that works.

That is a legitimate module, and should meet the published specifications for the chip. The drift should be a couple of seconds a week.

I would suspect your pc clock before the rtc. Install a gps or ntp timer app on your phone to check against.

A suggestion, compile and run the code you originally posted, then comment out the line that sets the time on the RTC, recompile and run that modified code. Its really hard to measure clock drift when you are setting the RTC every time the arduino is powered up or reset.

Update:
I think my DS3231 is correct and functioning within spec. After comparing my clock to time.gov for 24 hours I have noticed no drift. In fact I think the clock issue was with my computer. More accurately, the linux emulation running on my chromebook. I think the version of Linux on my chromebook doesn't keep time accurate enough and was the one that was drifting.

Thanks for all the help.