Problems with DS3231 Library

I'm wondering if anyone is using this library that Eric Ayars released quite some time ago. I modified it so it would compile under v1.0.x of the IDE. Everything I've tried to do with this library so far worked without a problem, from setting the clock, setting simple alarms, reading the clock, etc., etc. Till now. I've run into what seems to be a bug but I can't verify that it is a bug or not, or if the example file provided with the library has a problem.

Basically, when setting the alarms, the syntax for Alarm 1 is:Clock.setA1Time(DoW, Hour, Minute+1, Second, 0x0, true, false, false);
... and the syntax for Alarm 2 is similar except it doesn't have a "Second" variable to pass. Alarm 2 doesn't support that.

The issue here is that whatever I put in for the AlarmBits part (in the above example it's the 0x0) for Alarm 2, it seems to get overwritten by whatever the bits are for Alarm 1. In the _set example file, I changed the setA1Time() syntax to have 0b1000 as AlarmBits and setA2Time syntax to have 0b111. However when I read it back, both of them had 0b1000 in them.

I thought it was a fluke so I changed the bits only on Alarm 2 but nothing changed when I read the data back. Then I changed the bits on Alarm 1 and sure enough, reading the data back and both alarms have the same information, namely what I just changed Alarm 1 to.

So at this point I don't know where the problem sits. For one, the AlarmBits for Alarm 2 should only be 0b000 to 0b111, so why do I see 0b0000 (four digits), I don't know. And second I don't understand why it's copying whatever the bits are for Alarm 1 into Alarm 2.

I've attached the library here in the hopes that someone can look over it and figure out what may be the problem. The author doesn't seem to be responding or supporting it anymore. At least, his last posting was early last year and I haven't gotten a response from him from the messages I've sent.

DS3231.zip (9.24 KB)

There is a problem with adding attachments to posts this week. Can you post a link to where you got the library?

Of course. The original download came from Eric Ayars' website, located here

Keep in mind that it was written pre 1.0x, so you have to make the necessary changes to the example files.

The AlarmBits variable defines the bits for BOTH alarms. You need to use bitRead() and bitWrite() to diddle with the bits for each alarm individually.

/* Retrieves everything you could want to know about alarm
 * one. 
 * A1Dy true makes the alarm go on A1Day = Day of Week,
 * A1Dy false makes the alarm go on A1Day = Date of month.
 *
 * byte AlarmBits sets the behavior of the alarms:
 *	Dy	A1M4	A1M3	A1M2	A1M1	Rate
 *	X	1		1		1		1		Once per second
 *	X	1		1		1		0		Alarm when seconds match
 *	X	1		1		0		0		Alarm when min, sec match
 *	X	1		0		0		0		Alarm when hour, min, sec match
 *	0	0		0		0		0		Alarm when date, h, m, s match
 *	1	0		0		0		0		Alarm when DoW, h, m, s match
 *
 *	Dy	A2M4	A2M3	A2M2	Rate
 *	X	1		1		1		Once per minute (at seconds = 00)
 *	X	1		1		0		Alarm when minutes match
 *	X	1		0		0		Alarm when hours and minutes match
 *	0	0		0		0		Alarm when date, hour, min match
 *	1	0		0		0		Alarm when DoW, hour, min match
 */

It shouldn't be. At least, if I understand the code correctly, it's not. If I read the top of A1's setA1Time function, I see this:

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(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));
        ...
}

And for A2:

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(0x0b);	// A1 starts at 0bh    <-- this should read A2 starts at 0bh
	// Send A2 Minute and A2M2
	Wire.write(decToBcd(A2Minute) | ((AlarmBits & 0b00010000) << 3));
        ...
}

So it appears that they each use the AlarmBits passed to their respective function as opposed to copying from A1. So what am I missing? Where does it set both Alarmbits to the same thing?

And even if, let's say A1 is already set to some previous value, and I only set A2, it completely ignores whatever I put in for AlarmBits. The time gets set properly, but AlarmBits does not.

<note_to_self>Dude, it's time to learn C/C++ ... get a book.</note_to_self>

It seems easy enough to test. Create a variable. Set the bits for alarm 1. Set the bits for alarm 2. Call the setA1Time() method. Call the setA2Time() method. Call the getA1Time() method. Check the flags for both alarms. Call the getA2Time() method. Check the flags for both alarms.

That's exactly how I figured out that there's a problem. I created two different variables in my sketch, ARLM1 and ALRM2, with different bits in each (one had 0b1111 and the other 0b100) and passed those variables to setA1Time and setA2Time. Setting A2 never takes the AlarmBits into account. When I read things back, A2 reported its bits as 0b1111 (or whatever A1 is set as.)

The only other thing I can think of attempting is modifying the .cpp file to use two different variables, for example A1AlarmBits and A2Alarmbits as opposed to one single one. But I still can't figure out why it's not working right now, as written.

Anyone else feel like taking a stab at this before I start my attempt at hacking the library, hoping I know what I'm actually doing and figure out what's going on?

Ok, having changed the variables in the different routines to specifically say Alarm1Bits and Alarm2Bits did not make a difference. Now what I'm seeing is, when I try to read the data back, the bits for Alarm1 get set, but Alarm 2 remains at 0. Nothing's set. So, I'm still stuck.

The relevant lines in my sketch that sets the alarms is:

      Clock.setA1Time(Clock.getDoW(), Clock.getHour(h12, PM), Clock.getMinute(), 30, ALRM1_SET, true, false, false);
      Clock.setA2Time(Clock.getDate(), Clock.getHour(h12, PM), Clock.getMinute(), ALRM2_SET, false, false, false);
      // Turn alarms on
      Clock.turnOnAlarm(1);
      Clock.turnOnAlarm(2);

The variables ALRM1_SET and ALRM2_SET are 0b1000 and 0b111 respectively. Alarm1 is set properly. Alarm2 returns 0 when I read the bits back. The relevant part in the .cpp is:

void DS3231::setA1Time(byte A1Day, byte A1Hour, byte A1Minute, byte A1Second, byte Alarm1Bits, 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(0x07);	// A1 starts at 07h
	// Send A1 second and A1M1
	Wire.write(decToBcd(A1Second) | ((Alarm1Bits & 0b00000001) << 7));
	// Send A1 Minute and A1M2
	Wire.write(decToBcd(A1Minute) | ((Alarm1Bits & 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 | ((Alarm1Bits & 0b00000100)<<5);
	// A1 hour is figured out, send it
	Wire.write(temp_buffer); 
	// Figure out A1 day/date and A1M4
	temp_buffer = ((Alarm1Bits & 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 Alarm2Bits, 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(0x0b);	// A1 starts at 0bh
	// Send A2 Minute and A2M2
	Wire.write(decToBcd(A2Minute) | ((Alarm2Bits & 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 | ((Alarm2Bits & 0b00100000)<<2);
	// A2 hour is figured out, send it
	Wire.write(temp_buffer); 
	// Figure out A2 day/date and A2M4
	temp_buffer = ((Alarm2Bits & 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();
}

Ideas anyone? I really need to get this second alarm working for my project.

What happens if you try to use only the alarm2? something like
setA2Time(...)
getA2Time(...)

Do the AlarmBits still come back as 0?

Also are the other values coming back ok? (day, hour, min, ...)

John

johncc:
What happens if you try to use only the alarm2? something like
setA2Time(...)
getA2Time(...)

Do the AlarmBits still come back as 0?

Yep.

johncc:
Also are the other values coming back ok? (day, hour, min, ...)

Those are fine. It correctly sets the time of the alarm. It just doesn't seem to set the alarm bits which determines when that alarm goes off.

In the library, the Alarmbits hold the values for BOTH alarms and are ordered like this:
0 A2.2 A2.1 A2.0 A1.3 A1.2 A1.1 A1.0

So if you want to set alarm 1 to zero and alarm 2 to 111 you would set Alarmbits to 0B01110000

Pete

Ok, now we're getting somewhere. I was under the impression I only needed to pass the bits relevant to each alarm, as opposed to everything together. So this appears to be working now. I need to rewrite my sketch now to account for that.

Thanks!

The AlarmBits are sent to the DS3231 correctly, but there is a bug in reading back.
The problem is that AlarmBits in getA1Time and getA2Time were not initialized to 0x00.
AlarmBits is ORd with the correct bit each time, but any random bits will remain.
Fix as follows:
Change DS3231.cpp line 275 (first time AlarmBits is used in getA1Time) from
AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>7;
to
AlarmBits = (temp_buffer & 0b10000000)>>7;

Similarly, also fix the bug in getA2Time, line 318
AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>3;
to
AlarmBits = (temp_buffer & 0b10000000)>>3;

Then it works 100%.
pvz