Struggling with DS3231 alarm clock using interrupt

I'm making an alarm clock on the UNO, my code for the clock is pretty much done but now I've moved onto the alarm itself and I'm struggling with how to move forward.
The library I'm using is: DS3231 - Rinky-Dink Electronics
I know there are lots of DS3231 libraries but I'd like to stick with this one because all my code is written for it now.

My problem is that I don't actually know how I trigger the interrupt. In the manual for the library, the only related information is the command rtc.setOutput(OUTPUT_INT) and the RTC datasheet mentions it is an active low interrupt that is activated when the alarm occurs.

To me this seems contradictory, as I thought the alarm would be activated when the interrupt occurs. By my (admittedly limited) knowledge of interrupts, I thought that when the interrupt is triggered, the program would jump to the alarm function.

But unfortunately I (a) dont know if thats right, and (b) have no idea how to make my program actually execute the alarm function after the interrupt.

Any help is greatly appreciated!

Edit: the interrupt I'm talking about is an interrupt pin on the RTC itself, not the interrupt pins of the Arduino. This opens up another issue - what interrupts do I use?

My problem is that I don’t actually know how I trigger the interrupt. In the manual for the library, the only related information is the command rtc.setOutput(OUTPUT_INT) and the RTC datasheet mentions it is an active low interrupt that is activated when the alarm occurs.

The library you’ve chosen doesn’t support setting the alarms in the chip. So you either have to think about the decision for that specific library or implement that part yourself.

To me this seems contradictory, as I thought the alarm would be activated when the interrupt occurs.

That depends on what you think an alarm is. In the terminology of the DS3231 an alarm is a point in time that if reached by the internal clock, will trigger an special pin to be pulled LOW. That electrical signal can be used to trigger an interrupt in the Arduino, which in turn can be used to do some other action.

Pay attention that you cannot do everything inside the interrupt handler. As interrupts are disabled inside an ISR, everything that depends itself on interrupts are not allowed (serial interface, I2C, etc.). But you always have the option to set a flag in the ISR and react on that in the loop().

Edit: the interrupt I’m talking about is an interrupt pin on the RTC itself, not the interrupt pins of the Arduino. This opens up another issue - what interrupts do I use?

I have the impression you’re writing about something you didn’t understand yet. For us to understand your project it might be helpful that you post a wiring diagram and the code you currently working with (don’t forget the code tags, that’s the </> button in the editor!).

pylon:
The library you’ve chosen doesn’t support setting the alarms in the chip. So you either have to think about the decision for that specific library or implement that part yourself.

That depends on what you think an alarm is. In the terminology of the DS3231 an alarm is a point in time that if reached by the internal clock, will trigger an special pin to be pulled LOW. That electrical signal can be used to trigger an interrupt in the Arduino, which in turn can be used to do some other action.

Pay attention that you cannot do everything inside the interrupt handler. As interrupts are disabled inside an ISR, everything that depends itself on interrupts are not allowed (serial interface, I2C, etc.). But you always have the option to set a flag in the ISR and react on that in the loop().

I have the impression you’re writing about something you didn’t understand yet. For us to understand your project it might be helpful that you post a wiring diagram and the code you currently working with (don’t forget the code tags, that’s the </> button in the editor!).

Thanks for your reply! I’m aware of the restrictions of what can be done inside ISRs, but you’re right that I don’t fully understand interrupts, having only done a project using them once before, about 2 years ago. That was using an 8051 microcontroller, not arduino so my experience and knowledge is limited. Funnily enough that was an alarm clock too, but I don’t think I have my code for it any more.

I may need to just rewrite it using a better library as I have seen some that include an alarm function and its probably easier to just do it now before I write too much code.

I’ve pasted my code so far on ghostbin as its probably a little bit too long to put it here. All the set time functions are currently not working so any advice you have related to the code or interrupt stuff would be very helpful!
https://ghostbin.co/paste/czre6

I’ve pasted my code so far on ghostbin as its probably a little bit too long to put it here.

Wrong, always post code to the forum. I don’t open code from temporary storages as ghostbin because in a few months someone looking for a similar problem won’t be able to see the code anymore and we have to do the whole story once again.

I may need to just rewrite it using a better library as I have seen some that include an alarm function and its probably easier to just do it now before I write too much code.

That’s my recommendation too.

pylon:
I don't open code from temporary storages as ghostbin because in a few months someone looking for a similar problem won't be able to see the code anymore and we have to do the whole story once again.

As far as I'm aware ghostbin isn't temporary, as I'm still able to open code I saved on there from 2+ years ago, but you're the expert so here's my code. :slight_smile:
I've changed it slightly since my original post so that the loop function uses a do...while loop to repeatedly display the time until it detects a change in the default state of any of the setting buttons. Then it ends the loop and moves onto if statements to find out what button was pressed and what function should be called.

I'm still reluctant to change my library and cause myself loads of extra work, so my alarm function now tests for multiple operands before activating (Time is equal to alarm setting, and alarm has been toggled on.) This is done with an if statement within the do...while loop.

What do you think?

void loop() {
  do{
    // Do...while loop runs code at least once, then repeats until a button is pressed
    // Time display here
    t = rtc.getTime();
    lcd.setCursor(6,0);
    lcd.print(t.hour, DEC);
    lcd.print(":");
    lcd.print(t.min, DEC);
    lcd.setCursor(0,1);
    lcd.print(t.dow, DEC);
    lcd.print("    ");
    lcd.print(t.date, DEC);
    lcd.print("/");
    lcd.print(t.mon, DEC);
    lcd.print("/");
    lcd.print(t.year, DEC);

    // Alarm function
    if(ALARM_ACTIVE == 1 && a.hour == t.hour && a.min == t.min && t.sec == 0){
      activateAlarm();      
    }

   
    delay(1000); // 1 second delay before repeat    
  }while(digitalRead(SET_CLOCK)==1 && digitalRead(SET_ALARM)==1 && digitalRead(ALARM_TOG)==1);
  
  if(digitalRead(SET_CLOCK)==0){
    clockSet();
  }
  if(digitalRead(SET_ALARM)==0){
    alarmSet();
  }
  if(digitalRead(ALARM_TOG)==0){
    alarmToggle();
  }
}

GolamMostafa:
Tell me the time of of the day of 24-hrs/12-hrs clock at which you want the DS3231 to initiate an alarm signal and let that signal to interrupt the UNO to maintain the alarm tone for 10 seconds (say:).

I get that as a basic idea of how it works, my problem is actually how I would use the DS3231 to initiate the alarm signal.

I know the DS3231 has a built in alarm interrupt, but the library I'm using doesn't allow any alarm settings.
Is it possible to use 2 different libraries for the DS3231?
I don't want to change all my code that was written using the current library, but I would like to be able to use the alarm interrupt.

What do you think?

That code doesn't compile because it isn't complete.

Does the code work for you? I would expect it to fail if you push one of the buttons only shortly. Remove the do-while loop. Remove the delay() call. Learn to use the millis() call to do things periodically (the BlinkWithoutDelay example shows you how to do that).

Is it possible to use 2 different libraries for the DS3231?

Theoretically yes but it's nonsense. Use a library that provides all the functionality you need.

I don't want to change all my code that was written using the current library, but I would like to be able to use the alarm interrupt.

If it's just the code you posted, it is changed in a few seconds...

You will need support for alarms in your library before doing anything else. After that, it’s pretty easy.

In my project I use both alarms - one goes off each minute, so I can update the time display. The other is used to trigger action at some point in the future. To set things up, it goes a bit like this:
(not a complete, just as an example). The interrupt pin from the clock is connected to pin 19 on my mega.

	//  Timers

        //  Interrupt pin from RTC.
        constexpr byte RTC_INTERRUPT = 19;

	// MUST be volatile so the compiler is forced to re-read it each time.
	volatile bool timerTriggered = false;

	void RTCTimerISR(void)
	{
		// Can't reset the alarms for the ISR here.  Note the alarm and bail.
		timerTriggered = true;
		return;
	}


        //  Called from setup()
	void ResetRTC()
	{
		//  Reset RTC and its alarms
		DS3231::begin(true);  // Reset everything, disables alarms

		DS3231::setAlarm2(0, 0, 0, DS3231_EVERY_MINUTE, true);

		attachInterrupt(digitalPinToInterrupt(RTC_INTERRUPT), RTCTimerISR, FALLING);
	}


//  Called to set a time in the future to close the gate.
void SetGateCloseTime(time_t tCloseTime, EEPromWrite ew)
{
	RTCDateTime dt = DS3231::unixtimeToRTCDateTime(tCloseTime);

	DS3231::setAlarm1(dt.day, dt.hour, dt.minute, dt.second, DS3231_MATCH_DT_H_M_S, true);
	
}
        
void CheckTimers()
	{
		// volatile, set by RTC ISR.
		if (timerTriggered)
		{
			timerTriggered = false;

			//  Service the interrupts first, then do our tasks
			bool timer1Triggered = DS3231::isAlarm1();
			bool timer2Triggered = DS3231::isAlarm2();

			// Timer 1 is used to signal the close time for the gate.
			if (timer1Triggered)
			{
				SetGate(GateClosed, noWrite);
				ClearGateCloseTime();

				LogGateAction(glaRelease, gasTimer);
			}

			if (timer2Triggered)
			{
				//  Expect this to go off each minute
				lcd.UpdateLCD();
                         }
                     }
              }

I’m using a modified ‘DS3231’ library that you are welcome to.

DS3231.h (4.83 KB)

DS3231.cpp (23.7 KB)