DS3231 - How to recover from false triggers?

Hi all,

I’m hoping to ask for some advice on how to recover from a false trigger on an external interrupt pin.

I’ve been working on an Ardunio weather station project that will operate for long periods of time in remote areas. It’s a relatively simple design, based on the Adafruit Pro Trinket 3V and DS3231 Precision RTC Breakout.

I have connected the SQW/INT pin of the DS3231 to the external interrupt pin (D3) of the Pro Trinket. I am using Jack Christensen’s DS3232RTC library to set alarms to wake the Trinket from sleep every 5 minutes, at which point measurements from a thermistor and anemometer will be recorded. I am using RocketScream’s LowPower.h library to put the Trinket to sleep inbetween measurements.

I have spent a considerable amount of time studying both the DS3232RTC library and the DS3231 datasheet, but I am struggling to understand to how best deal with false triggers (or why they happen).

Using the code below, I have observed that if there is a significant source of noise, or I manually trigger the external interrupt by momentarily connecting pin D3 to GND, the Trinket will wake and execute the main loop, but the alarm flag will not reset and the external interrupt pin is permanently brought LOW. This effectively means that the Trinket will never wake again. I am very curious as to what could be causing this to happen.

I’ve included an oscilloscope screenshot below that should help to illustrate my problem. While one should always ensure their design is free of noise and other possible sources that could cause false triggers, I am hoping there may be a programmatic solution that would be able to deal with such an event.

I would greatly appreciate any feedback or thoughts on how to improve overall operation.

Cheers,
Adam

#include <DS3232RTC.h>                        // https://github.com/JChristensen/DS3232RTC
#include <LowPower.h>                         // https://github.com/rocketscream/Low-Power

#define RTC_ALARM_PIN         3               

time_t t, alarmTime;
time_t alarmInterval = 10;                    // Alarm interval in seconds.

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

  pinMode(RTC_ALARM_PIN, INPUT_PULLUP);       // Enable internal pull-up resistor.
  RTC.setAlarm(ALM1_MATCH_SECONDS, 0, 0, 0, 1);  // Set initial alarm 1 to occur at the next instance of 00 seconds.
  RTC.alarm(ALARM_1);                         // Ensure RTC interrupt flag is cleared.
  RTC.alarm(ALARM_2);                         // Ensure RTC interrupt flag is cleared.
  RTC.squareWave(SQWAVE_NONE);                // Configure INT/SQW pin for interrupt operation. Disable default square wave.
  RTC.alarmInterrupt(ALARM_1, true);          // Enable interrupt output for alarm 1.
  RTC.alarmInterrupt(ALARM_2, false);         // Disable interrupt output for alarm 2.
}

void loop()
{
  sleepNow();                                 // Sleep.
  
  /*
    Read sensors, etc., upon wake.
  */

  RTC.alarm(ALARM_1);                         // Reset alarm 1 flag.
  t = RTC.get();                              // Read date and time from RTC.
  alarmTime = t + alarmInterval;              // Calculate next alarm.
  RTC.setAlarm(ALM1_MATCH_DATE, second(alarmTime), minute(alarmTime), hour(alarmTime), day(alarmTime)); // Set alarm 1.
  
}

// Enable sleep until RTC alarm interrupt is generated.
void sleepNow()
{
  noInterrupts();                                             // Disable interrupts before entering sleep.
  attachInterrupt(digitalPinToInterrupt(RTC_ALARM_PIN), wakeUpNowISR, FALLING); // Wake on falling edge of RTC_ALARM_PIN.
  interrupts();                                               // Enable interrupts to ensure next instruction is executed.
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);        // Enter sleep and await external interrupt.
}

// RTC alarm interrupt service routine.
void wakeUpNowISR()
{
  detachInterrupt(digitalPinToInterrupt(RTC_ALARM_PIN));      // Disable interrupts on RTC_ALARM_PIN
}

Hi Adam,

I've got no experience with this RTC unfortunately, but I do see a couple of things that look like worthy troubleshooting points. My hunch is that the program you've written doesn't like how long the line is held low for. I suspect that either the arduino or the DS3231 are holding that line low after setting up the intterupts. I can't see anything in the library for the DS3231 that would cause this. Because you're only kicking the timer down the road and not actually resetting the interrupt.

On thing you might try, actually look at the result of the call for RTC.alarm(ALARM_1), if it returns false, which would indicate a manual trigger, wait until the line goes low again and figure out some method of resetting the RTC to a known state. This might be a re-initialization or just waiting until the alarm has triggered on the RTC and going from there.

My personal strategy to tackle this type of problem would be to get it working again, and then try and break it again. This might help reveal whatever mechanism is behind the fault, since you should have two of the same failure states with a different set of inputs.

Do update us on whatever you find.

V/r
James Whitlock

I don't know if this is causing the problem but you do not have to detach and re-attach the interrupt each time it occurs.
attach the ISR once at the end of the setup function and comment out the other attach/detach and the interrupts/noInterrupts.

Pete

+1 for removing detach/attach. Do not use detach.

The question is, where is this unusual noise coming from? Do you have long wires connecting the RTC and the Arduino?

Are the ground connections secure?

Using a lower value, external pullup resistor on the (open drain/collector) SQW/*INT line can reduce noise.

Does the alarm ever reset with an induced interrupt?

Are you using anything other than INPUT_PULLUP as the pullup on the alarm interrupt pin? Perhaps try an external pullup to Vbat.

If its a random occurance, perhaps you could put a secondary test to see if the alarm is actually cleared and try to clear it again if not.

RTC.alarm(ALARM_1);  // Reset alarm 1 flag.

delay(50);                        

if (digitalRead(RTC_ALARM_PIN) == LOW)
  {
      RTC.alarm(ALARM_1);   
      
  }

Hi all,

Thanks for the great feedback!

el_supremo:
I don't know if this is causing the problem but you do not have to detach and re-attach the interrupt each time it occurs.
attach the ISR once at the end of the setup function and comment out the other attach/detach and the interrupts/noInterrupts.

Pete

I started testing the different suggestions in this thread and I noticed that as soon as I moved the attachInterrupt to setup() and removed the attachInterrupt/detachInterrupt and interrupts()/noInterrupts() from the ISR and sleep functions, the Pro Trinket was able to recover and continue to operate no matter how many times I manually tied the interrupt line to GND (see screenshot below).

I had originally based my sleep/wake/interrupt cycles on an example from Nick Gammon's interrupts page, but this example made us of the avr sleep commands. When I started using the LowPower.h library, I just adapted the code to use the simplified sleep command. It looks like this is where I may have made my mistake.

@cattledog I tried to incorporate the extra check, but it didn't seem to be able to reset the interrupt line, even with longer delays.

@jremington Noise doesn't appear to be an problem at the moment, however I did experience some issues when learning how to connect the anemometer's reed switch wind sensor. It was here I discovered the susceptibility of my circuit to noise and false triggers, and this was something I definitely wanted to address. I'm currently using the internal pullup on the interrupt pin, and while I'll get a slightly stronger voltage when using an external pullup resistor (3.166 V up to to 3.273 V), this doesn't appear to be able to resolve the original false trigger issue. I believe the internal pullup should suffice, which also helps to reduce the quiescent draw.

@KeitaruSM Thanks for the advice! I had started to watch the status of the RTC.alarm and was planning on further checks, but it looks like removing attach/detach did the trick!

Now that my circuit and code appears to be able to recover from a false trigger, I'm wondering if there's a way to ignore them all together. Granted they shouldn't be happening in the first place, but could a debounce check be added to my now empty ISR that would filter out triggers that occur before the expected RTC alarm? I've looked at examples of millis() timers intended for debouncing mechanical switches, but I'm unsure if these would be suitable.

Thanks everyone for all the help!
Adam

I have observed that if there is a significant source of noise,

@jremington Noise doesn't appear to be an problem,

Please explain the discrepancy in the above statements. Note: "false triggers" = noise.

jremington:
Please explain the discrepancy in the above statements. Note: "false triggers" = noise.

Hi Jim,

I mean to say that my circuit is no longer generating noise that is causing a problem. When I was learning how to connect the reed switch from the anemometer to Pro Trinket, which also involved learning how to configure pin change interrupts on one of the digital pins, I discovered that every time the anemometer cups would complete a rotation and close the switch, it would trigger the RTC alarm. This led to me reading up on noise in circuits, which led me to star grounds, debouncing, etc., and finally to watching the outputs of all the pins on the oscilloscope. It turns out that powering the reed switch from a digital pin with an external pullup and writing the pin LOW when not in use did the trick to eliminate all the noise.

However, I've noticed that the DS3231 is still susceptible to false triggering when, for example, I connect the oscilloscope probe to the interrupt pin. This appears to bring the pin low momentarily on contact, and it triggers the RTC alarm. I'm unsure if this is expected behaviour when connecting a probe, or indicative of a separate problem all together.

Cheers,
Adam