DS3231 resets when the power is turned off

I use a board with memory and a battery charger (DS3231). I have tested several modules with LIR2032 battery and CR2032 battery (after removing the diode and the resistance of the charger).

#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

LiquidCrystal_I2C lcd(0x3f, 20, 4);
RTC_DS3231 rtc;

unsigned long timeForAction = millis();
int initialDay;
int utcOffset;

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

  rtc.begin();
  lcd.init();
  lcd.backlight();
  lcd.clear();

//  initClock();

  DateTime nowTime = rtc.now();
  initialDay = nowTime.day();
  utcOffset = calcUtcOffset(nowTime);
  showDate(nowTime);
}

void loop() { 
  int secondsInterval = 1; 
  if (millis() > timeForAction) {
    timeForAction = millis() + (secondsInterval * 1000); 
    
    DateTime nowTime = rtc.now();
    if (initialDay != nowTime.day()) {
      initialDay = nowTime.day();
      showDate(nowTime);
    }

    showHour(nowTime); 
  }
}

The power supply for the module, display and Arduino NANO is external with an additional 5V regulator.
Additional file:

String dayOfWeekName(int d) {
  switch (d) {
    case 0: return "DOMINGO";
    case 1: return "LUNES";
    case 2: return "MARTES";
    case 3: return "MIERCOLES";
    case 4: return "JUEVES";
    case 5: return "VIERNES";
    case 6: return "SABADO";
  }
}

void initClock() {
  int initDay = 17;
  int initMonth = 9;
  int initYear = 2019;
  int initHour = 18;
  int initMinute = 17;
  rtc.adjust(DateTime(initYear, initMonth, initDay, initHour, initMinute, 0));
}

int calcUtcOffset(DateTime t) {
  int offset;
  if (t.month() >= 4 && t.month() <= 10) {
    return 2;
  } else {
    return 1;
  }
}

void showDate(DateTime t) {
char buf_date[11];
  snprintf(buf_date, sizeof(buf_date), "%02d/%02d/%4d ", 
  t.day(), t.month(), t.year());

  lcd.setCursor(0, 0);
  lcd.print(buf_date);
  lcd.setCursor(11, 0);
  lcd.print(dayOfWeekName(t.dayOfTheWeek()).c_str());
}

int calcUtcHour(int h) {
  if (h < utcOffset) {
    return h - utcOffset + 24;
  } else {
    return h - utcOffset;
  }
}

void showHour(DateTime t) {
  int utcHour = calcUtcHour(t.hour());
  
  char buf_hour_local[9];
    snprintf(buf_hour_local, sizeof(buf_hour_local), "%02d:%02d:%02d",
      t.hour(), t.minute(), t.second());

  char buf_hour_utc[6];
    snprintf(buf_hour_utc, sizeof(buf_hour_utc), "%02d:%02d",
      utcHour, t.minute());

  lcd.setCursor(0, 1);
  lcd.print(buf_hour_local);
  lcd.print(" ");
  lcd.print(buf_hour_utc);
  lcd.print(" (UTC)");
}

If I disconnect the external power, when I connect the RTC again it recovers the compilation time. In this situation, the voltage measured on pin 14 (Vbat) is always greater than 3V.

The library I use is RTClib.h.

I have reviewed the queries in several forums and although similar problems are described I have not found a way to solve them.

I would appreciate any help because I've been stuck for a while.

To me sound like the battery power isn't connected (or low etc) to the DS3231. Or no connection. You can check last by disconnecting the DS and see if that changes anything.

General tips for your code:

  • always use subtraction for millis() to get around the roll over (millis() - previousMillis >= period)
  • get rid of String :wink:

If I disconnect the external power, when I connect the RTC again it recovers the compilation time.

There is no compilation time in your sketch. Please explain what time is being shown when you reconnect the power. Is it the initClock() time?

I can run your sketch with no resetting of the time on power disconnect. You have some sort of hardware or battery issue with Vbat getting to the chip.

It's possible that you damaged something with your modification of the module.

septillion:
To me sound like the battery power isn't connected (or low etc) to the DS3231. Or no connection. You can check last by disconnecting the DS and see if that changes anything.

cattledog:
There is no compilation time in your sketch. Please explain what time is being shown when you reconnect the power. Is it the initClock() time?

I can run your sketch with no resetting of the time on power disconnect. You have some sort of hardware or battery issue with Vbat getting to the chip.

It's possible that you damaged something with your modification of the module.

A battery problem is discarded at 99%, I use two module one with LIR2032 and other with CR2032 (modified circuit). The time when I connect is the initClock(), indeed.

All, the volts in the Vbat (pin 14) is 3V or more.

Sorry I d'not upload pictures

I found that my DS3231 kept losing it's settings, even after changing the battery to a brand new one.
On mine the battery holder doesn't have a very strong spring and wiggling the board or touching the battery was enough to lose the time.

I soldered around the parts of the battery holder to make it a tighter fit and then jammed the battery in so it can't move and it's worked fine since.

Please try this sketch, and tell us exactly what your display shows.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3f, 20, 4);

// variables for storing the time
//   second  minute  hour    weekday  date    month   year
byte ss=0,   mi=0,   hh=0,   wd=6,    dd=1,   mo=1,   yy=0;
 
void setup()
{
  delay(20); // maybe I don't really need this line
 
  Wire.begin();
 
  // clear /EOSC bit
  // Sometimes necessary to ensure that the clock
  // keeps running on just battery power. Once set,
  // it shouldn't need to be reset but it's a good
  // idea to make sure.
//  Wire.beginTransmission(0x68); // address DS3231
//  Wire.write(0x0E); // select register
//  Wire.write(0b00011100); // write register bitmap, bit 7 is /EOSC
//  Wire.endTransmission();

  delay(20); // maybe I don't really need this line either
 
  lcd.setBacklight(HIGH);
  lcd.clear();
 
  delay(20); // or this line, for that matter
}
 
void loop()
{
  // read the time from the RTC, if we can
  boolean gotTheTime = grabTime();
 
  if (gotTheTime) {
    // if we are here, then the time has been successfully read
    // and stored in global variables (ss, mi, hh, wd, dd, mo, yy)
    printTime(); 
  }
  else {
    // if we are here, then we tried to read the time but couldn't
    lcd.setCursor(0,0);
    lcd.print("Unable to read  ");
    lcd.setCursor(0,1);
    lcd.print("time from RTC.  ");
    //         01234567890123456
  }
 
  delay(100);
}


boolean grabTime() {
  // get time from the RTC and put it in global variables

  // send request to receive data starting at register 0
  Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
  Wire.write((byte)0); // start at register 0
  Wire.endTransmission();
  Wire.requestFrom(0x68, 7); // request seven bytes (ss, mi, hh, wd, dd, mo, yy)
  // check for a reply from the RTC, and use it if we can
  if (Wire.available() >= 7) {
    // if we're here, we got a reply and it is long enough
    // so now we read the time
    ss = bcd2bin(Wire.read()); // get seconds
    mi = bcd2bin(Wire.read()); // get minutes
    hh = bcd2bin(Wire.read()); // get hours
    wd = bcd2bin(Wire.read()); // get day of week
    dd = bcd2bin(Wire.read()); // get day of month
    mo = bcd2bin(Wire.read()); // get month
    yy = bcd2bin(Wire.read()); // get year (two digits)
    // indicate that we successfully got the time
    return true;
  }
  else {
    // indicate that we were unable to read the time
    return false;
  }
}


byte bcd2bin(byte x) {
  // converts from binary-coded decimal to a "regular" binary number
  return ((((x >> 4) & 0xF) * 10) + (x & 0xF)) ;
}


void printTime() {
  // just like the name says 
  char buf[35]; // this is more than we really need, but so what?
  lcd.setCursor(0,0);
  //            0 12 3 45 6 78 9 0 1 23456
  sprintf(buf,"\'%02d.%02d.%02d(%c%c%c)  ",yy,mo,dd,
   "BSMTWTFS"[(wd<7)?wd:0],"auouehra"[(wd<7)?wd:0],"dnneduit"[(wd<7)?wd:0]);
  lcd.print(buf);
  lcd.setCursor(0,1);
  //            01 2 34 5 67 890123456
  sprintf(buf,"%02d:%02d:%02d        ",hh,mi,ss);
  lcd.print(buf);   
}

OK, thanks, the display show the time of the last initClock() setting!

Only hour, minute and seconds on the line 0 (HH:MM:SS), delay(40)

I think that maybe, in my code, I failed to initialize the LCD properly.
I just made some small changes to my sketch. (Yes, I copied a few lines from your sketch, for initializing the LCD.)
Please try this, and see what you get.
What time does it show? Does it show a "frozen" clock, or a running clock?

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3f, 20, 4);

// variables for storing the time
//   second  minute  hour    weekday  date    month   year
byte ss=0,   mi=0,   hh=0,   wd=6,    dd=1,   mo=1,   yy=0;
 
void setup()
{
  delay(20); // maybe I don't really need this line
 
  Wire.begin();
 
  // clear /EOSC bit
  // Sometimes necessary to ensure that the clock
  // keeps running on just battery power. Once set,
  // it shouldn't need to be reset but it's a good
  // idea to make sure.
//  Wire.beginTransmission(0x68); // address DS3231
//  Wire.write(0x0E); // select register
//  Wire.write(0b00011100); // write register bitmap, bit 7 is /EOSC
//  Wire.endTransmission();

  delay(20); // maybe I don't really need this line either

  rtc.begin();
  lcd.init();
  lcd.backlight();
  lcd.clear();
 
  delay(20); // or this line, for that matter
}
 
void loop()
{
  // read the time from the RTC, if we can
  boolean gotTheTime = grabTime();
 
  if (gotTheTime) {
    // if we are here, then the time has been successfully read
    // and stored in global variables (ss, mi, hh, wd, dd, mo, yy)
    printTime();
  }
  else {
    // if we are here, then we tried to read the time but couldn't
    lcd.setCursor(0,0);
    lcd.print("Unable to read  ");
    lcd.setCursor(0,1);
    lcd.print("time from RTC.  ");
    //         01234567890123456
  }
 
  delay(100);
}


boolean grabTime() {
  // get time from the RTC and put it in global variables

  // send request to receive data starting at register 0
  Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
  Wire.write((byte)0); // start at register 0
  Wire.endTransmission();
  Wire.requestFrom(0x68, 7); // request seven bytes (ss, mi, hh, wd, dd, mo, yy)
  // check for a reply from the RTC, and use it if we can
  if (Wire.available() >= 7) {
    // if we're here, we got a reply and it is long enough
    // so now we read the time
    ss = bcd2bin(Wire.read()); // get seconds
    mi = bcd2bin(Wire.read()); // get minutes
    hh = bcd2bin(Wire.read()); // get hours
    wd = bcd2bin(Wire.read()); // get day of week
    dd = bcd2bin(Wire.read()); // get day of month
    mo = bcd2bin(Wire.read()); // get month
    yy = bcd2bin(Wire.read()); // get year (two digits)
    // indicate that we successfully got the time
    return true;
  }
  else {
    // indicate that we were unable to read the time
    return false;
  }
}


byte bcd2bin(byte x) {
  // converts from binary-coded decimal to a "regular" binary number
  return ((((x >> 4) & 0xF) * 10) + (x & 0xF)) ;
}


void printTime() {
  // just like the name says
  char buf[35]; // this is more than we really need, but so what?
  lcd.setCursor(0,0);
  //            0 12 3 45 6 78 9 0 1 23456
  sprintf(buf,"\'%02d.%02d.%02d(%c%c%c)  ",yy,mo,dd,
   "BSMTWTFS"[(wd<7)?wd:0],"auouehra"[(wd<7)?wd:0],"dnneduit"[(wd<7)?wd:0]);
  lcd.print(buf);
  lcd.setCursor(0,1);
  //            01 2 34 5 67 890123456
  sprintf(buf,"%02d:%02d:%02d        ",hh,mi,ss);
  lcd.print(buf);   
}