Diagnosing DS3231

Hi, I have wasted a considerable amount of time trying to get a DS3231 module to work, I have a genuine Maxim Module MPMB1 - so no fake issues, new battery, Maxim recommend the Adafruit library, so loaded that, and yet still it lost time on power off.

Inside the time setting function in the library the OSF bit is tested and the !EOSC bit is cleared. this is the correct behavious according to the datasheet, and yet it did not keep time. very strange.

I decided I needed to explore the status registers both before and after setting the time, so came up with this code based on the DS3231 example from the Adafruit RTClib.

I would recommend getting hold of the data sheet and understanding the function of these two registers DS3231 datasheet - from MAXIM

Hope it helps someone else, it displays the control and status registers bit by bit before and after the time set (or not if power not lost)

I am sure some of those with more experience could produce more elegant code, and I know there are read register routines in the library, but they are not public, and I did not want to change the library.

/* Date and time functions using a DS3231 RTC connected via I2C and Wire lib
Original library by JeeLabs http://news.jeelabs.org/code/, released to the  public domain
 This is a fork of JeeLab's fantastic real time clock library for Arduino. supplied by Adafruit
*/
#include "RTClib.h"
#include <Wire.h>

#define DS3231_address 0x68
#define DS3231_control_reg 0x0E
#define DS3231_status_reg 0x0F

RTC_DS3231 rtc;
  byte Status_reg_b4;
  byte Control_reg_b4;
  byte Status_reg_aft;
  byte Control_reg_aft;

void setup () 
{
  Serial.begin(57600);
  Wire.begin();
  while (!Serial); // wait for serial port to connect. Needed for native USB

  if (! rtc.begin())
 {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
  }
  Serial.println("********************************************************");
  Serial.println("trying to diagnose DS3231 that seems to loose time.");
  Serial.println("this snapshots the registers before the OSF check in 'lost power' function,");
  Serial.println("then snapshots again after the function has fired (or not).");
  Serial.print("PC date/time of this run: ");Serial.print(F(__DATE__));Serial.print("");
  Serial.println(F(__TIME__));
  Serial.println();
  Control_reg_b4=state_of_register(DS3231_address,DS3231_control_reg,1);
  Status_reg_b4=state_of_register(DS3231_address,DS3231_status_reg,1);
  if (rtc.lostPower()) 
  {
    Serial.println("RTC lost power, let's set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  else {Serial.println("OSF flag in status reg not set - no power loss");}
  Serial.println("State of registers;");
  Control_reg_aft=state_of_register(DS3231_address,DS3231_control_reg,1);
  Status_reg_aft=state_of_register(DS3231_address,DS3231_status_reg,1);
  display_reg();
  Serial.println();
  // just print data row once - so you can see the time held by DS3231 at restart
  DateTime now = rtc.now();
    Serial.print("RTC Data: ");
    Serial.print(now.day(), DEC);Serial.print('/');Serial.print(now.month(), DEC);Serial.print('/');Serial.print(now.year(), DEC);
    Serial.print("\t");
    Serial.print(now.hour(), DEC);Serial.print(':');Serial.print(now.minute(), DEC);Serial.print(':');Serial.print(now.second(), DEC);
    Serial.print("\tTemp: ");Serial.print(rtc.getTemperature());
    Serial.println(" C");
    Serial.print("PC date/time of this run: ");Serial.print(F(__DATE__));Serial.print(" ");Serial.println(F(__TIME__));
}

void loop () 
{
// do nothing!
}

byte state_of_register(byte device_address, byte this_register, int bytes)
{
  Wire.beginTransmission(device_address);
  Wire.write(this_register);
  Wire.requestFrom(device_address, bytes);
  Wire.endTransmission();
  return(Wire.read());
}

void display_reg()
{
  String Control_bit_name[8]=  {"A1F    ","A2F    ","BSY    ","EN32kHz","NA     ","NA     ","NA     ","OSF    "};
  String Control_bit_descr [8]={"Alarm 1 Flag","Alarm 2 Flag","Busy","Enable 32kHz Output","not used","not used","not used","Oscillator Stop Flag"}; 
  String Status_bit_name[8]=   {"A1IE   ","A2IE   ","INTCN  ","RS1    ","RS2    ","CONV   ","BBSQW  ","!EOSC  "};
  String Status_bit_descr [8]= {"Alarm 1 Interrupt Enable","Alarm 2 Interrupt Enable","Interrupt Control","Rate Select","Rate Select","Convert Temperature","Battery-Backed Square-Wave Enable","Enable Oscillator"}; 
  Serial.println("                           \tB4\tAfter");
  for (int i=7;i>=0;i--)
    {
      Serial.print("  Status.bit ");
      Serial.print(i);
      Serial.print(" ");
      Serial.print(Status_bit_name[i]);
      Serial.print(" =\t");
      Serial.print(Status_reg_b4>>i & 1);
      Serial.print("\t");
      Serial.print(Status_reg_aft>>i & 1);
      Serial.print ("\t");
      Serial.println(Status_bit_descr[i]);
    }

  Serial.println();
  for (int i=7;i>=0;i--)
    {
      Serial.print("  Control.bit ");
      Serial.print(i);
      Serial.print(" ");
      Serial.print(Control_bit_name[i]);
      Serial.print(" =\t");
      Serial.print(Control_reg_b4>>i & 1);
      Serial.print("\t");
      Serial.print(Control_reg_aft>>i & 1);
      Serial.print ("\t");
      Serial.println(Control_bit_descr[i]);
    }

}

This is the base sketch that I use for the DS3231

#include <DS3231.h>

DS3231 rtc(SDA, SCL);

void setup() {
  Serial.begin(9600);
  
  rtc.begin();  // Initialize the RTC object
  
  // The following lines can be uncommented to set the date and time manually
  //rtc.setDOW(THURSDAY);     // Set Day-of-Week
  //rtc.setTime(12, 0, 0);     // Set the time (24hr format)
  //rtc.setDate(1, 1, 2020);   // Set the date
}

void loop() {
  Serial.print(rtc.getDOWStr());  // Send Day
  Serial.print(" ");
  
  Serial.print(rtc.getDateStr()); // Send Date
  Serial.print(" -- ");
  
  Serial.println(rtc.getTimeStr()); // Send time
  
  delay (1000); // Evil, I know
}

I think that is a totally different library, the Maxim recommended Adafruit library functions are not the same.

Which is it?

You can use any compatible library. This is just the one that I use with these boards.
I put them in a lot of projects and it works well.
Hope it helps.

FWIW I didn't use any library: (note I was using an M0)
This code only reads the data/time + setup. It does not write the data/time.

/*
   Rev 01 operational on Arduino M0 board !


 - Our board is 3.3v
-  Write 0x00 to register 0x0E		setup clock, disable 32k output, enable sq wave output @ 1 Hz, disable alarms
-  Read  0x0F	(only interested in OSF, bit 7)
-          if OSF bit is 1 it means the oscillator was or is stopped and time is suspect.
-  Write 0x00 to register 0x0F, Resets OSF if it was set, disable the 32k osc. Clear any alarm flags.

*/

#include "Wire.h"

// **** constants **************************************************************

#define RTC_ADDRESS 0x68
#define RTC_0E 0x0E
#define RTC_0F 0x0F

// **** variables **************************************************************

uint8_t Reg0x0E;
uint8_t Reg0x0F;  // bit 7 is OSF (0 = OK, 1 = Oscillator had/is stopped )
bool RTC_Error = false;

// **** function Prototypes ****************************************************
uint8_t rtc_i2c_read_byte( uint8_t Address, uint8_t &Data);
uint8_t rtc_i2c_write_byte( uint8_t Address, uint8_t Data);
void ReadRTC();

// **** function macros ********************************************************
uint8_t bcd2bin (uint8_t val) { return ((val >> 4) * 10) + (val & 0x0F); }
//uint8_t bin2bcd (uint8_t val) { return (val / 10) << 4 | (val % 10);     }


// **** Program Code ************************************************************


void setup()
{

Wire.begin();  // Connect to I2C bus
SerialUSB.begin (115200);
delay(2000);    // USB serial seems to need this.  The If loop didn't work
SerialUSB.println("....starting");
rtc_i2c_read_byte(RTC_0E, Reg0x0E);
SerialUSB.print("Register 0x0E =  ");
SerialUSB.print(Reg0x0E, BIN);

rtc_i2c_read_byte(RTC_0F, Reg0x0F);
SerialUSB.print(",          Register 0x0F =  ");
SerialUSB.println(Reg0x0F, BIN);
if (Reg0x0F & 0b10000000)
  {
    SerialUSB.print("..must reset clock   ");
    RTC_Error = true;
  }
// don't need the else but keep in for now as we add code and troubleshoot.
  else
  {
   SerialUSB.print( "..RTC Clock time is valid..    ");
  }

// send desired configuration bytes
//rtc_i2c_write_byte(RTC_0E,0x00);
//rtc_i2c_write_byte(RTC_0F,0x00);

ReadRTC();
}


void loop()
{}


// ********************************************************************
// **** Functions  ****************************************************
// ********************************************************************

// read 1 byte:
uint8_t rtc_i2c_read_byte(const uint8_t Address, uint8_t &Data)
{
 	Wire.beginTransmission(RTC_ADDRESS);
  	Wire.write(Address);
  	Wire.endTransmission(false);
    Wire.requestFrom(RTC_ADDRESS, 1);
    Data = Wire.read();
    Wire.endTransmission(true);
    return 1;
}


void ReadRTC()
 {
  Wire.beginTransmission(RTC_ADDRESS);
  Wire.write(0);  // This is the first register address (Seconds)
        // We'll read from here on for 7 bytes: secs reg, minutes reg, hours, days, months and years.
  Wire.endTransmission(false);
  Wire.requestFrom(RTC_ADDRESS, 7);
  uint8_t  ss = bcd2bin(Wire.read() & 0x7F); // why mask 8th bit here?
  uint8_t  mm = bcd2bin(Wire.read());
  uint8_t  hh = bcd2bin(Wire.read());
  Wire.read();                               // day of week
  uint8_t  d = bcd2bin(Wire.read());
  uint8_t  m = bcd2bin(Wire.read());
  uint16_t y = bcd2bin(Wire.read()) + 2000;

//  String sTimeStamp = String(y) + "-" + String(m) + "-" + String(d) + " " + String (hh) + ":" + String(mm) + ":" + String(ss);
//  SerialUSB.println(sTimeStamp);

  SerialUSB.print(String(y));
  SerialUSB.print("-");
  SerialUSB.print(String(m));
  SerialUSB.print("-");
  SerialUSB.print(String(d));
  SerialUSB.print(" ");
  SerialUSB.print(String(hh));
  SerialUSB.print(":");
  SerialUSB.print(String(mm));
  SerialUSB.print(":");
  SerialUSB.println(String(ss));
}


uint8_t rtc_i2c_write_byte(const uint8_t Address, const uint8_t Data)
{
  Wire.beginTransmission(RTC_ADDRESS);
  Wire.write(Address);
  Wire.write(Data);
  Wire.endTransmission();   // return Wire.endTransmission();
  return 1;
}


/*
Sketch uses 5698 bytes (18%) of program storage space. Maximum is 30720 bytes.
Global variables use 512 bytes (25%) of dynamic memory, leaving 1536 bytes for local variables. Maximum is 2048 bytes.
*/

Hardcore! That's the way to do it! I had hardware issues rather than software hence wanting to see the state of the registers.

The issue was cut power, and the date/time registers still had the values from power cut time. Not quite a reset but as if the clock was frozen on battery.

I knew the flag needed clearing on 1st power up, but still the problem persisted. Oddly gone away now. Dunno why, which is equally frustrating

It's not so much hardcore but I don't like "bloated" libraries and I like to understand the code I'm using.

Oh and the posted code is device prove-out sketch I write for every device I use. No trying to do everything all at once :slight_smile:

Yes, the adafruit library is vast, covering a number of RTC models. I have always assumed that only the code that is used is included in the final binary.

My priority is getting the higher level logic right, so I often rely on libraries.

I build boxes for industrial use, so end up spending a lot of time on overall function and user interaction. This particular box, at the over view level it is quite simple, count inputs from two beam break sensors and send the output to a PC which moves the data to a server based SQL database for analysis or whatever.

Surprising how complex that simple ask gets! If only comms and PCs were reliable!

You are likely correct, however I prefer to understand the code in my project. And without the need for libraries or perhaps a local file I needn't worry about someone else's library being changed.

1 Like