Skip to content

Instantly share code, notes, and snippets.

@deveth0
Last active January 5, 2022 13:59
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save deveth0/796afa5d35a6c9d79e30008938d42e4e to your computer and use it in GitHub Desktop.
Save deveth0/796afa5d35a6c9d79e30008938d42e4e to your computer and use it in GitHub Desktop.
Arduino Demonstration of an alarm triggered by a DS3231 RTC
/**
* Sets an alarm using a DS3231 device, goes to sleep and wakes up again
* @author: dev-eth0
* @url: https://www.dev-eth0.de/
*/
#include <DS3231.h> // https://github.com/NorthernWidget/DS3231
#include <Wire.h>
#include <LowPower.h> // https://github.com/rocketscream/Low-Power
DS3231 Clock;
// Some static test-date for the RTC
byte Year = 2017;
byte Month = 9;
byte Date = 17;
byte Hour = 19;
byte Minute = 29;
byte Second = 30;
// Interrupt Pin used
static const byte wakeUpPin = 2;
// Those are the ALARM Bits that can be used
// They need to be combined into a single value (see below)
// Found here: https://github.com/mlepard/ArduinoChicken/blob/master/roboCoop/alarmControl.ino
#define ALRM1_MATCH_EVERY_SEC 0b1111 // once a second
#define ALRM1_MATCH_SEC 0b1110 // when seconds match
#define ALRM1_MATCH_MIN_SEC 0b1100 // when minutes and seconds match
#define ALRM1_MATCH_HR_MIN_SEC 0b1000 // when hours, minutes, and seconds match
#define ALRM2_ONCE_PER_MIN 0b111 // once per minute (00 seconds of every minute)
#define ALRM2_MATCH_MIN 0b110 // when minutes match
#define ALRM2_MATCH_HR_MIN 0b100 // when hours and minutes match
int ledState = HIGH;
void setup() {
// Start the serial port
Serial.begin(115200);
Serial.println("Alarm Test");
// Configure Interrupt Pin
pinMode(wakeUpPin, INPUT_PULLUP);
digitalWrite(wakeUpPin, HIGH);
// Start the I2C interface
Wire.begin();
// Set time
Clock.setClockMode(false);
Clock.setYear(Year);
Clock.setMonth(Month);
Clock.setDate(Date);
Clock.setHour(Hour);
Clock.setMinute(Minute);
Clock.setSecond(Second);
// Set alarm
Serial.println("Setting alarm");
// This is the interesting part which sets the AlarmBits and configures, when the Alarm be triggered
byte ALRM1_SET = ALRM1_MATCH_MIN_SEC; // trigger A1 when minute and second match
byte ALRM2_SET = ALRM2_MATCH_MIN; // trigger A2 when minute matches (and second is 0 as A2 does not support seconds)
// combine the AlarmBits
int ALARM_BITS = ALRM2_SET;
ALARM_BITS <<= 4;
ALARM_BITS |= ALRM1_SET;
// Trigger Alarm when Minute == 30 or 0
// Clock.setA1Time(Day, Hour, Minute, Second, AlarmBits, DayOfWeek, 12 hour mode, PM)
Clock.setA1Time(0, 0, 0, 0, ALARM_BITS, false, false, false);
// Clock.setA2Time(Day, Hour, Minute, AlarmBits, DayOfWeek, 12 hour mode, PM)
Clock.setA2Time(0, 0, 30, ALARM_BITS, false, false, false);
// Turn on Alarm
Clock.turnOnAlarm(1);
Clock.turnOnAlarm(2);
Serial.println(ALARM_BITS, BIN);
Serial.println("Alarm 1:");
Serial.println(Clock.checkAlarmEnabled(1));
Serial.println("Alarm 2:");
Serial.println(Clock.checkAlarmEnabled(2));
// Attach interrupt
attachInterrupt(digitalPinToInterrupt(wakeUpPin), wakeUp, FALLING);
// sleep
delay(500);
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
}
// loop is started once the device wakes up again
void loop() {
blinkLED();
delay(1000);
}
void blinkLED() {
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
digitalWrite(LED_BUILTIN, ledState);
}
void wakeUp() {
// wake up again
Serial.println("Woke up this morning...");
}
@deveth0
Copy link
Author

deveth0 commented Aug 15, 2019

Is there anything happening on the rtc after the times have been set? For me it sounds like either an incompatible rtc or probably a broken one.

@manuelescotech
Copy link

manuelescotech commented Aug 15, 2019 via email

@deveth0
Copy link
Author

deveth0 commented Aug 15, 2019

Hm weird. Both rtc from the same distributor? I'm from Europe

@manuelescotech
Copy link

manuelescotech commented Aug 15, 2019 via email

@deveth0
Copy link
Author

deveth0 commented Aug 15, 2019

Ahh OK, that's a completely new use case. The example just triggers once at a defined time.

If you want to trigger an alarm after 30 seconds, you need to set the time again. There are multiple get methods which allow you to get the time from your rtc, then add 30s and set alarm 1 again.

@manuelescotech
Copy link

manuelescotech commented Aug 15, 2019 via email

@deveth0
Copy link
Author

deveth0 commented Aug 15, 2019

Exactly. You could also get a GPS device and use this as time source :)

@manuelescotech
Copy link

manuelescotech commented Aug 15, 2019 via email

@deveth0
Copy link
Author

deveth0 commented Aug 15, 2019

Nope, when the uno wakes up, the defined method (wakeup in my example) is called, afterwards the loop.
You can also set the time in wakeup and sleep again.

There are also some cool GPS / 3G combi devices if you need internet access

@manuelescotech
Copy link

manuelescotech commented Aug 15, 2019 via email

@deveth0
Copy link
Author

deveth0 commented Aug 15, 2019

It should automatically be reset if you set the alarm again

@manuelescotech
Copy link

manuelescotech commented Aug 15, 2019 via email

@flostuerzer
Copy link

Good Evening Sir,

I have one little problem with this Code. I can run it once, so when I start the program, the pin D2 is HIGH and after some seconds, it drops to low. But it never rises again to about 4,5mA, do you have any idea what the reason could be?
Only if I remove the DS3231 module from the setup and remove the battery cell, reassemble everything, it works again for one single interrupt. Is there any command to use to set the pin to high again?

Everytime I am looping, a new Interrupt is being attached, it goes straight into sleep mode, detaches the interrupt and does some things, delays for 3s and starts the loop again. Can I put a line in there to put the SQW Pin to HIGH again?

Thanks a lot in advance!

Greetings,

Flo

@deveth0
Copy link
Author

deveth0 commented May 28, 2020

Hey Flo,

I'm afraid you stumbled upon this bug: NorthernWidget/DS3231#17

You might try to modify your local version of the DS3231 library as mentioned there.

@flostuerzer
Copy link

Thanks a lot for your answer!

I changed the code in DS3231.cpp to this :

`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);

//Modification from https://github.com/NorthernWidget/DS3231/issues/17
// clear A1F/A2F flags for previously set alarms
temp_buffer = readControlByte(1);
// modify status byte
if (Alarm == 1) {
	temp_buffer = temp_buffer & 0b11111110;
} else {
	temp_buffer = temp_buffer & 0b11111101;
}
writeControlByte(temp_buffer, 1);

}`

But I am not 100% sure if this was right because actually nothing really changed. Pin still stays low after the first interrupt, ist there anything else I have to change?
How long should the LOW signal from the SQW pin usually be?

And one more question:
In the end, I just want to trigger the interrupt once every hour, e.g. at 16:00:00, 17:00:00,..
Is it correct in this case to use

ALRM1_MATCH_MIN_SEC 0b1100 // when minutes and seconds match or
ALRM2_MATCH_MIN 0b110 // when minutes match

And how to I get only one of two Alarm running?

Really appreciate your help, I'm unsuccessfully trying for days now...

@flostuerzer
Copy link

Okay looks like I am not used to writing code in a comment but it should look like everything written in code in my comment is now in the .cpp file. So basically I've added the code from the issue you've sent me to the existing code in the DS3231::turnOffAlarm(byte Alarm) method.

@deveth0
Copy link
Author

deveth0 commented May 28, 2020

I did not test the workaround, sorry. Are you sure, that you use the changed version?

You need to set the alarm after each trigger again, simply add another hour then. The ALRM1_* and ALRM2_* are only relevant, if you want to trigger on the second or if it's ok, to trigger on a minute (accuracy). You can use whichever you want.

@flostuerzer
Copy link

I am 100% sure I use the changed version and I cant really figure out why it won't work.

Okay thanks!

I'll let you know when I'll get it working. Is there anything else to change, maybe in the DS3231.h file? I could send you my whole code if you would like to have a look on that.

@deveth0
Copy link
Author

deveth0 commented May 28, 2020

i did not really look into the workaround, sorry. I'd assume that you only need to change the c file.

@renstanford
Copy link

this is what i need.. thank you for sharing about using built-in alarms on DS3231

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment