Hi, after some troubleshooting I realised that the function to disable the alarm on DS3231 doesn't work. I'm using Adafruit's RTClib and to try it out their example code for DS3231_alarm. Even if I write rtc.disableAlarm(1); and rtc.disableAlarm(2); somehow alarm still triggers. Is my module faulty or am I doing something wrong. I tried using different library but I got the same results. Does anybody have DS3231 to confirm or deny my results?
/* Example implementation of an alarm using DS3231
*
* VCC and GND of RTC should be connected to some power source
* SDA, SCL of RTC should be connected to SDA, SCL of arduino
* SQW should be connected to CLOCK_INTERRUPT_PIN
* CLOCK_INTERRUPT_PIN needs to work with interrupts
*/
#include <RTClib.h>
// #include <Wire.h>
RTC_DS3231 rtc;
// the pin that is connected to SQW
#define CLOCK_INTERRUPT_PIN 2
void setup() {
Serial.begin(9600);
// initializing the rtc
if(!rtc.begin()) {
Serial.println("Couldn't find RTC!");
Serial.flush();
while (1) delay(10);
}
if(rtc.lostPower()) {
// this will adjust to the date and time at compilation
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
//we don't need the 32K Pin, so disable it
rtc.disable32K();
// Making it so, that the alarm will trigger an interrupt
pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, FALLING);
// set alarm 1, 2 flag to false (so alarm 1, 2 didn't happen so far)
// if not done, this easily leads to problems, as both register aren't reset on reboot/recompile
rtc.clearAlarm(1);
rtc.clearAlarm(2);
// stop oscillating signals at SQW Pin
// otherwise setAlarm1 will fail
rtc.writeSqwPinMode(DS3231_OFF);
// turn off alarm 2 (in case it isn't off already)
// again, this isn't done at reboot, so a previously set alarm could easily go overlooked
rtc.disableAlarm(2);
rtc.disableAlarm(1);
// schedule an alarm 10 seconds in the future
if(!rtc.setAlarm1(
rtc.now() + TimeSpan(10),
DS3231_A1_Second // this mode triggers the alarm when the seconds match. See Doxygen for other options
)) {
Serial.println("Error, alarm wasn't set!");
}else {
Serial.println("Alarm will happen in 10 seconds!");
}
}
void loop() {
// print current time
char date[10] = "hh:mm:ss";
rtc.now().toString(date);
Serial.print(date);
// the stored alarm value + mode
DateTime alarm1 = rtc.getAlarm1();
Ds3231Alarm1Mode alarm1mode = rtc.getAlarm1Mode();
char alarm1Date[12] = "DD hh:mm:ss";
alarm1.toString(alarm1Date);
Serial.print(" [Alarm1: ");
Serial.print(alarm1Date);
Serial.print(", Mode: ");
switch (alarm1mode) {
case DS3231_A1_PerSecond: Serial.print("PerSecond"); break;
case DS3231_A1_Second: Serial.print("Second"); break;
case DS3231_A1_Minute: Serial.print("Minute"); break;
case DS3231_A1_Hour: Serial.print("Hour"); break;
case DS3231_A1_Date: Serial.print("Date"); break;
case DS3231_A1_Day: Serial.print("Day"); break;
}
// the value at SQW-Pin (because of pullup 1 means no alarm)
Serial.print("] SQW: ");
Serial.print(digitalRead(CLOCK_INTERRUPT_PIN));
// whether a alarm fired
Serial.print(" Fired: ");
Serial.print(rtc.alarmFired(1));
// Serial.print(" Alarm2: ");
// Serial.println(rtc.alarmFired(2));
// control register values (see https://datasheets.maximintegrated.com/en/ds/DS3231.pdf page 13)
// Serial.print(" Control: 0b");
// Serial.println(read_i2c_register(DS3231_ADDRESS, DS3231_CONTROL), BIN);
// resetting SQW and alarm 1 flag
// using setAlarm1, the next alarm could now be configurated
if (rtc.alarmFired(1)) {
rtc.clearAlarm(1);
Serial.print(" - Alarm cleared");
}
Serial.println();
delay(2000);
}
void onAlarm() {
Serial.println("Alarm occured!");
}
/*static uint8_t read_i2c_register(uint8_t addr, uint8_t reg) {
Wire.beginTransmission(addr);
Wire.write((byte)reg);
Wire.endTransmission();
Wire.requestFrom(addr, (byte)1);
return Wire.read();
}*/
The only time you disable the alarms in your code is immediately followed by setting alarm1 again.
rtc.disableAlarm(2);
rtc.disableAlarm(1);
// schedule an alarm 10 seconds in the future
if(!rtc.setAlarm1(
rtc.now() + TimeSpan(10),
DS3231_A1_Second // this mode triggers the alarm when the seconds match. See Doxygen for other options
)) {
ClearAlarm only clears the alarm flag, letting the INT pin go back high. It doesn't disable the alarm.
I'm not sure what the problem is, but as of today, the library function that disables an alarm is as follows:
void RTC_DS3231::disableAlarm(uint8_t alarm_num) {
uint8_t ctrl = read_register(DS3231_CONTROL);
ctrl &= ~(1 << (alarm_num - 1));
write_register(DS3231_CONTROL, ctrl);
}
That's taken from the file RTC_DS3231.cpp in the Adafruit library. It looks like it should work. It clears the interrupt enable bit for the alarm in the Control register, which means when the time matches the alarm setting, nothing will happen on the INT/SQW pin.
But a match will always set the interrupt flag bit in the Status register. So maybe the problem is attempting to use the alarmFired function to determine if the alarm has triggered. For that to work you would also have to confirm that the interrupt enable bit in the control register is set. Without that, alarmFired only means the time matched the alarm, not that anything actually happened as a result, like triggering an interrupt. But this library doesn't appear to offer a function to read the Control register.
That was exactly my problem until I figured out that disableAlarm only disables SQW. I was testing alarmFired and it would always return true. Honestly, it would make more sense if you disabled the alarm that it disables everything.
I wonder if it might be possible to prevent the alarm time from ever matching the actual time. Maybe you could set the alarm seconds to 61 or something like that that isn't a valid time. Then alarmFired would never return true unless you enter a valid alarm time which eventually triggers.
Here is how I disable the alarms in my
DS3231_Simple Arduino library
uint8_t DS3231_Simple::disableAlarms()
{
// There's no way to actually disable the alarms from triggering, so
// we have to set them to some unreachable date
// (NB: you can disable the alarms from putting the SQW pin low, but they still trigger
// in the register itself, you can't stop that, hence this tom-foolery).
#if 0
DateTime invalid = { 0,0,0,0,31,2,0 };
#else
// This saves 4 bytes interestingly (assuming you are already going to be using read() somewhere)
DateTime invalid = read();
invalid.Day = 31;
invalid.Month = 2;
#endif
setAlarm(invalid, ALARM_MATCH_MINUTE_HOUR_DATE);
setAlarm(invalid, ALARM_MATCH_SECOND_MINUTE_HOUR_DATE);
checkAlarms(); // A dummy check alarm to kill the alarm flags.
return 1;
}
I guess I need to study your library, but I don't understand your code at all. Starting with #if 0. Also where does month 2 come into this? Neither alarm includes the month. Well, as I said, I need to look through your library.
Starting with #if 0.
`#if 0` is disabled code, the `#else` code following is used instead.
Now, the rest of your comment... ahhh... yeah, I suspect that is a bug indeed. I guess I forgot the alarms don't check month (probably should have named the define ..._DOM instead of ..._DATE).
Funny nobody has ever reported it in however many years. Probably nobody checks for alarms if they are not setting alarms I suppose.
Anyway, long and short, you can't disable the alarm in the register. Maybe if you set an invalid Day of Month or Hour it might work. I don't have one around to test with now.
Starting with #if 0.
`#if 0` is disabled code, the `#else` code following is used instead.
Now, the rest of your comment... ahhh... yeah, I suspect that is a bug indeed. I guess I forgot the alarms don't check month (probably should have named the define ..._DOM instead of ..._DATE).
Funny nobody has ever reported it in however many years. Probably nobody checks for alarms if they are not setting alarms I suppose.
Anyway, long and short, you can't disable the alarm in the register. Maybe if you set an invalid Day of Month or Hour it might work. I don't have one around to test with now.
I have several to test with. I guess the advantage of Feb 31 is that it has no value that is illegal per se, only invalid in combination. Well, my guess is that in the alarm fields illegal values won't be modified. I'll try to test that later today. But if that's true, then you could set the alarm seconds or minutes value to 61, and a match would never happen, so the flag bits wouldn't be set.
But you know, I've used these RTCs for years, and don't think I've ever tested the alarm flag bits. I've cleared them, but never tested them. If I'm using alarm interrupts, then INT/SQW will be low if there's been an alarm. So I don't see why would you need to see if the flags have been set.
Ok, I did a test writing the value 97 into register 7, which is the seconds register of Alarm1. 97 is 0b01100001, which is 61 in binary coded decimal. Then I read back the value in register 7 once a second to see if it changed. In more than a minute, it did not. I also read back the status register to see if the alarm flags were ever set. They were not. So it seems you can write an invalid number into an alarm register, and it will not be error-checked or changed. So since the actual time will never contain such an invalid number, no match will ever occur, and the alarm flags will never be set.
To do this test, I had to clear the A1M1 bit in register 7, and set the A1M2 - A1M4 bits in registers 8, 9 and 0xA. If A1M1 is also set, it will match every second no matter what value is in register 7.
If I'm using alarm interrupts, then INT/SQW will be low if there's been an alarm. So I don't see why would you need to see if the flags have been set.
I'm already using 1Hz SQW signal to update the display when second changes, so checking alarms using interrupts is not really possible. Writing invalid value to the alarm would be my first approach but I wanted to test if you could clear all alarms so that wouldn't be necessary.