1

I am developing code to run a word clock and have noticed a slight problem. I am using a DS3231 to keep time, and using the Wire library to communicate with it. I ping the DS3231 every second to get the time and check a few other things. This is all working fine except when the clock loses power. It seems that occasionally upon loss of power to the Arduino, the time that that the DS3231 reports when it regains power, is incorrect.

My thought is that the power is getting cut in the middle of various Wire.read() commands and this is causing the chip to mess up and reset its own time, but I am not sure. The DS3231 has a battery backup attached to it, and that battery has only been plugged in for a month or two, so I don't imagine that is the problem.

I am wondering if anyone has an idea how I can deal with this potential problem, or if there is some way I can clear the Wire library before it starts up again.

I have put some sample code below to give an idea of what I am doing. There is of course much more code, but this is the only relevant part.

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

void readDS3231time(byte * second, byte * minute, byte * hour, byte * dayOfWeek, byte * dayOfMonth, byte * month, byte * year) {
  Wire.beginTransmission(kClockAddress);
  Wire.write(0); // set DS3231 register pointer to 00h
  Wire.endTransmission();
  Wire.requestFrom(kClockAddress, 7); // request seven bytes of data from DS3231 starting from register 00h
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}

void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) { // sets time and date data to DS3231
  Wire.beginTransmission(kClockAddress);
  Wire.write(0); // set next input to start at the seconds register
  Wire.write(decToBcd(second)); // set seconds
  Wire.write(decToBcd(minute)); // set minutes
  Wire.write(decToBcd(hour)); // set hours
  Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
  Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
  Wire.write(decToBcd(month)); // set month
  Wire.write(decToBcd(year)); // set year (0 to 99)
  Wire.endTransmission();
}

void setup() {
  // put your setup code here, to run once:
  Wire.begin()
   // DS3231 seconds, minutes, hours, day, date, month, year
   //setDS3231time(0, 36, 10, 2, 21, 12, 15);
}

void loop() {
  // put your main code here, to run repeatedly:
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; // retrieve data from DS3231
  readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  delay(1000);
}
  • By incorrect, do you mean 01/01/00 01 00:00:00? – Gerben Dec 21 '15 at 19:11
  • @Gerben no, I mean off as in like a few hours or a few days. – MZimmerman6 Dec 22 '15 at 0:28
  • That's weird. I'd check the battery voltage. The DS3231 should transition to battery power just fine. – Gerben Dec 22 '15 at 14:33
1
  Wire.requestFrom(kClockAddress, 7); // request seven bytes of data

Just because you request 7 bytes doesn't necessarily mean you get them. I suggest before reading the date and time you set all those variables to something invalid (like 0xFF). Then after reading, if you get 0xFF in any of them, ignore the entire lot.


we are sending a start bit again, when we never sent a stop bit previosuly. ... Does this sound reasonable

Not really, because I2C doesn't have start and stop bits, it has start condition and stop condition which are specific changes to SDA and SCL which don't happen at any other times. See I2C for a description. I would be checking the data for validity, as I said. There is no other way of knowing for sure that a transmission wasn't interrupted. If you request 7 bytes of data, the library tells you that you got 7, even if you did not. However if it cannot even find the target device, it returns 0 as the number of bytes (you aren't testing for that anyway).

The very least you should do is test for a valid connection, eg.

if (Wire.requestFrom(kClockAddress, 7) == 7)
  {
  // read the data
  }

That at least confirms that the clock chip responded in some way. Not checking error returns from function calls, where documented, is not wise.

Similarly when you send your command.

  Wire.endTransmission();

That should be:

if (Wire.endTransmission() != 0)
  {
  // handle error here
  return;
  }

You have no tests in your code for either the sending or receiving to be working. And then you are unconditionally using the resulting time values. That's like opening a file, and reading it, without checking if the file exists.

|improve this answer|||||
  • I think the actual problem came about with the I2C interface with the DS3231. If the power is lost to the device in the middle of an I2C read, it never gets the stop signal or some other signal. Because of this, some bogus data either ends up getting written, or the next time we attempt to start a transmission (upon return of power) we are sending a start bit again, when we never sent a stop bit previosuly. At least this is my running assumption as of right now. Does this sound reasonable, or is there a way to fix that? – MZimmerman6 Feb 1 '16 at 17:40
  • See amended answer. – Nick Gammon Feb 1 '16 at 19:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.