Alarm time settings on a DS3231

Hi,

I just cannot get to store the alarm time in the DS3231 flash memory.

The getA1Time routine is not returning the data I previously ‘stored’.

Any help is appreciated.
TIA

#include <DS3231.h>

/* Clock chip settings */
DS3231 Clock;
byte ADay, AHour, AMinute, ASecond, ABits; // Alarm variables
bool ADy, A12h, Apm;  // Alarm variables

// setting AL1 and AL2 registers
// http://forum.arduino.cc/index.php?topic=168421.0
#define ALRM1_MATCH_HR_MIN_SEC 0b1000  // when hours, minutes, and seconds match
byte ALRM1_SET = ALRM1_MATCH_HR_MIN_SEC;
#define ALRM2_MATCH_HR_MIN     0b100   // when hours and minutes match
byte ALRM2_SET = ALRM2_MATCH_HR_MIN;
// Set AlarmBits, ALRM2 first, followed by ALRM1

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

  /* set alarm1 time to 17:15*/
  AHour = byte(17);
  AMinute = byte(15);

  setAlarms();  // store alarm variables NOT WORKING

  Clock.getA1Time(ADay, AHour, AMinute, ASecond, ABits, ADy, A12h, Apm); // read alarm stored values
  
}

void loop()
{

  Serial.print(AHour, DEC);
  Serial.print(" ");
  Serial.println(AMinute, DEC);

}

void setAlarms() {

  // Set AlarmBits, ALRM2 first, followed by ALRM1
  int AlarmBits = ALRM2_SET;
  AlarmBits <<= 4;
  AlarmBits |= ALRM1_SET;

  // set both alarms to :00 and :30 seconds, every minute
  // Format: .setA*Time(DoW|Date, Hour, Minute, Second, 0x0, DoW|Date, 12h|24h, am|pm)
  //                    |                                    |         |        |
  //                    |                                    |         |        +--> when set for 12h time, true for pm, false for am
  //                    |                                    |         +--> true if setting time based on 12 hour, false if based on 24 hour
  //                    |                                    +--> true if you're setting DoW, false for absolute date
  //                    +--> INTEGER representing day of the week, 1 to 7 (Monday to Sunday)
  //
  Clock.setA1Time(Clock.getDoW(), AHour, AMinute, 0, AlarmBits, false, false, false);
  Clock.setA2Time(Clock.getDate(), AHour, AMinute, AlarmBits, false, false, false);

}

Are you sure that the I2C interface to the RTC is working? Can you actually read the time from the RTC?

Yes, the time works fine. Even the alarm soft settings work fine. By soft settings I mean setting alarm time variables at power up but if turned off, the settings are lost. So I know that the alarm hour/minutes are not stored in the flash memory.

which flash memory? If I remember correctly, the DS3231 just has Time keeping Registers which needs power.

ebolisa:
but if turned off, the settings are lost

so... are you sure the battery is fine?

Hum, you're right, there's no flash memory but if I turn off the Arduino and power it back on, the time is kept but not the alarm settings. So the battery is fine.

is the battery a LIR2032 (rechargeable?) or a basic CR2032?

Basic CR2032. So you're suggesting that the code is ok?

Yes at first look helicopter view your code feels OK - I trusted your comment that the alarm triggers at the right time if you don't cut power supply.

Hence my suspicion on battery. have a look at this topic and answer #1 about cutting the trace

I would recommend the rechargeable battery

Ok, will cut the trace but I don't think will solve my problem. Thank you.

Well - double check if there is a current charging the battery first.

Looking at your code you are missing at the end of setAlarms()

      // // Enables alarm 1 and 2 and the external interrupt pin.
      Clock.turnOnAlarm(1);
      Clock.turnOnAlarm(2);

I've not checked but this might be what stores the value really in the registers.

Yep - agree that's somewhat fishy as the Timekeeping Battery Current = 0.84μA while Data-Retention Current = 0,1 μA

So if there is enough current for the crystal then the memory should be working too.

in the library there is a oscillatorCheck() function which returns false if the oscillator has been off for some reason (and thus if this is the case, the time is probably not correct).

could you call that function in your code and see what it tells you when you switch off the arduino? if the battery is working, then the Oscillator Stop Flag (OSF) should not be set and the function call should return true.

I usually either code the I2C myself or use JC library because I'm using DS3232 (which does have 236 bytes of battery-backed SRAM) but that library supports as well DS3231

Will do that and report it later. However, the setA1Time does write hour/minute into the registers based on the following routines from the library:

void DS3231::setA1Time(byte A1Day, byte A1Hour, byte A1Minute, byte A1Second, byte AlarmBits, bool A1Dy, bool A1h12, bool A1PM) {
	//	Sets the alarm-1 date and time on the DS3231, using A1* information
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x07));	// A1 starts at 07h
	// Send A1 second and A1M1
	Wire.write(decToBcd(A1Second) | ((AlarmBits & 0b00000001) << 7));
	// Send A1 Minute and A1M2
	Wire.write(decToBcd(A1Minute) | ((AlarmBits & 0b00000010) << 6));
	// Figure out A1 hour 
	if (A1h12) {
		// Start by converting existing time to h12 if it was given in 24h.
		if (A1Hour > 12) {
			// well, then, this obviously isn't a h12 time, is it?
			A1Hour = A1Hour - 12;
			A1PM = true;
		}
		if (A1PM) {
			// Afternoon
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A1Hour) | 0b01100000;
		} else {
			// Morning
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A1Hour) | 0b01000000;
		}
	} else {
		// Now for 24h
		temp_buffer = decToBcd(A1Hour); 
	}
	temp_buffer = temp_buffer | ((AlarmBits & 0b00000100)<<5);
	// A1 hour is figured out, send it
	Wire.write(temp_buffer); 
	// Figure out A1 day/date and A1M4
	temp_buffer = ((AlarmBits & 0b00001000)<<4) | decToBcd(A1Day);
	if (A1Dy) {
		// Set A1 Day/Date flag (Otherwise it's zero)
		temp_buffer = temp_buffer | 0b01000000;
	}
	Wire.write(temp_buffer);
	// All done!
	Wire.endTransmission();
}

void DS3231::setA2Time(byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits, bool A2Dy, bool A2h12, bool A2PM) {
	//	Sets the alarm-2 date and time on the DS3231, using A2* information
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x0b));	// A1 starts at 0bh
	// Send A2 Minute and A2M2
	Wire.write(decToBcd(A2Minute) | ((AlarmBits & 0b00010000) << 3));
	// Figure out A2 hour 
	if (A2h12) {
		// Start by converting existing time to h12 if it was given in 24h.
		if (A2Hour > 12) {
			// well, then, this obviously isn't a h12 time, is it?
			A2Hour = A2Hour - 12;
			A2PM = true;
		}
		if (A2PM) {
			// Afternoon
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A2Hour) | 0b01100000;
		} else {
			// Morning
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A2Hour) | 0b01000000;
		}
	} else {
		// Now for 24h
		temp_buffer = decToBcd(A2Hour); 
	}
	// add in A2M3 bit
	temp_buffer = temp_buffer | ((AlarmBits & 0b00100000)<<2);
	// A2 hour is figured out, send it
	Wire.write(temp_buffer); 
	// Figure out A2 day/date and A2M4
	temp_buffer = ((AlarmBits & 0b01000000)<<1) | decToBcd(A2Day);
	if (A2Dy) {
		// Set A2 Day/Date flag (Otherwise it's zero)
		temp_buffer = temp_buffer | 0b01000000;
	}
	Wire.write(temp_buffer);
	// All done!
	Wire.endTransmission();
}

void DS3231::turnOnAlarm(byte Alarm) {
	// turns on alarm number "Alarm". Defaults to 2 if Alarm is not 1.
	byte temp_buffer = readControlByte(0);
	// modify control byte
	if (Alarm == 1) {
		temp_buffer = temp_buffer | 0b00000101;
	} else {
		temp_buffer = temp_buffer | 0b00000110;
	}
	writeControlByte(temp_buffer, 0);
}

void DS3231::turnOffAlarm(byte Alarm) {
	// turns off alarm number "Alarm". Defaults to 2 if Alarm is not 1.
	// Leaves interrupt pin alone.
	byte temp_buffer = readControlByte(0);
	// modify control byte
	if (Alarm == 1) {
		temp_buffer = temp_buffer & 0b11111110;
	} else {
		temp_buffer = temp_buffer & 0b11111101;
	}
	writeControlByte(temp_buffer, 0);
}

Hello again,

Removed the diode as per suggestion and modified the code a bit. The alarm trips on time but, if power is removed from the Arduino, the alarm time settings are lost. The battery is at 3.7V with Arduino off.

Here’s a simple code where the problem can be reproduced…

#include <DS3231.h>

/* Clock chip settings */
DS3231 Clock;
byte ADay, AHour, AMinute, ASecond, ABits; // Alarm variables
bool ADy, A12h, Apm;  // Alarm variables

// setting AL1 and AL2 registers
// http://forum.arduino.cc/index.php?topic=168421.0
#define ALRM1_MATCH_HR_MIN_SEC 0b1000  // when hours, minutes, and seconds match
byte ALRM1_SET = ALRM1_MATCH_HR_MIN_SEC;
#define ALRM2_MATCH_HR_MIN     0b100   // when hours and minutes match
byte ALRM2_SET = ALRM2_MATCH_HR_MIN;
// Set AlarmBits, ALRM2 first, followed by ALRM1

String readString;            // storing data received from serial

void setup()
{
  Serial.begin(9600);
  delay(50);
}

void loop()
{
  Clock.getA1Time(ADay, AHour, AMinute, ASecond, ABits, ADy, A12h, Apm); // read alarm stored values
  Serial.print(AHour, DEC); Serial.print(":"); Serial.println(AMinute, DEC);  // print alarm time erroneous
  delay(2000); // so I can see it

  setAlarmTime();
  Serial.print(AHour); Serial.print(":"); Serial.println(AMinute);  // prints alarm time ok
  delay(1000);

}

void setAlarmTime() {

  while (Serial.available() )     // Send data only when you receive data
  {
    delay(3);
    char c = Serial.read();        //Read the incoming data
    readString += c;
  }
  if (readString.length() > 0) {
    delay(100);
    Serial.println(readString);          //print value on Serial monitor to ensure data is correct text => "alHHMM" ex.: al1534 => 15:34 

    /*set alarm hours/mins */
    if (readString.substring(0, 2) == "al") { // are the first 2 chars = "al"?
      if (readString.length() == 6) { // if so storethe time in the variables
        AHour = readString.substring(2, 4).toInt();
        AMinute = readString.substring(4, 6).toInt();
        setAlarms(); // move variables data to DS registers
      }
    }
  }

  readString = "";  // clear variable

}


void setAlarms() {

  // Set AlarmBits, ALRM2 first, followed by ALRM1
  int AlarmBits = ALRM2_SET;
  AlarmBits <<= 4;
  AlarmBits |= ALRM1_SET;

  // set both alarms to :00 and :30 seconds, every minute
  // Format: .setA*Time(DoW|Date, Hour, Minute, Second, 0x0, DoW|Date, 12h|24h, am|pm)
  //                    |                                    |         |        |
  //                    |                                    |         |        +--> when set for 12h time, true for pm, false for am
  //                    |                                    |         +--> true if setting time based on 12 hour, false if based on 24 hour
  //                    |                                    +--> true if you're setting DoW, false for absolute date
  //                    +--> INTEGER representing day of the week, 1 to 7 (Monday to Sunday)
  //
  Clock.setA1Time(Clock.getDoW(), AHour, AMinute, 0, AlarmBits, false, false, false);
  Clock.setA2Time(Clock.getDate(), AHour, AMinute, AlarmBits, false, false, false);

  // Turns alarms on
  Clock.turnOnAlarm(1);
  Clock.turnOnAlarm(2);
}

And battery plugged in the right way?

Ha, ha, ha, yes!! but I solved my problem. Apparently, the library Wire is needed for the data to be stored into the DS’ alarm time registers. Said library is not needed, however, to store time data and that is what threw me off. Thank you for your time.

that's weird you have to add it yourself. it's included on line 23 of DS3231.h which you import in your code when you do

#include <DS3231.h>

and if you go check the library function code for getA1Time() that you call in the loop

void DS3231::getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM) {
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x07);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 4);

	temp_buffer	= Wire.read();	// Get A1M1 and A1 Seconds
	A1Second	= bcdToDec(temp_buffer & 0b01111111);
	// put A1M1 bit in position 0 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>7;

	temp_buffer		= Wire.read();	// Get A1M2 and A1 minutes
	A1Minute	= bcdToDec(temp_buffer & 0b01111111);
	// put A1M2 bit in position 1 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>6;

	temp_buffer	= Wire.read();	// Get A1M3 and A1 Hour
	// put A1M3 bit in position 2 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>5;
	// determine A1 12/24 mode
	A1h12		= temp_buffer & 0b01000000;
	if (A1h12) {
		A1PM	= temp_buffer & 0b00100000;			// determine am/pm
		A1Hour	= bcdToDec(temp_buffer & 0b00011111);	// 12-hour
	} else {
		A1Hour	= bcdToDec(temp_buffer & 0b00111111);	// 24-hour
	}

	temp_buffer	= Wire.read();	// Get A1M4 and A1 Day/Date
	// put A1M3 bit in position 3 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>4;
	// determine A1 day or date flag
	A1Dy		= (temp_buffer & 0b01000000)>>6;
	if (A1Dy) {
		// alarm is by day of week, not date.
		A1Day	= bcdToDec(temp_buffer & 0b00001111);
	} else {
		// alarm is by date, not day of week.
		A1Day	= bcdToDec(temp_buffer & 0b00111111);
	}
}

you can see it uses the Wire library and calls all over the place as this is the way to communicate with the RTC module. check if you have the most recent library... So the whole thing would not work at all if Wire was not there in the first place...

but glad it works!