Are hardware interrupts with sleep modes reliable?

Hi, I have worked really, really hard on some code for a water level detector to send a status via LoRa. I posted the problem last night May 23 at 9.03pm in the programming forum with no replies, so thought I would ask a simpler question here as I am going mad with my issue.

In your experience, do hardware interrupts work reliably with sleep modes?

I am using Adafruit Feather LoRa boards (32u4) as transmitter and recievers. The transmitter accepts an alarm interrupt from a DS3231 clock on hardware Int pin 1 and is connected to a float sensor on hardware pin 0 via the INPUT PULLUP function.

They initally seem to work fine and the interrupts seem to fire in response to the RTC and my (simulated) activation of the NO float switch.

However, on my receiver board I get a number of activations that I should not be getting! These include sensor high and low readings and RTC reports, at times when I am convinced they have not been generated, unless by errant code in my sketch. I have scoured the web, read and re-read Nick Gammon's guide on interrupts a dozen times (I confess I did not understand every bit of it, but I have tried hard to follow that guidance.

I am using the LowPower.h library and Sandeep Mistry's LoRa library.

I won't post the code here immediately as I dont want to hack anyone off by merely repeating last nights post, but I can if you wish.

Please can someone give me an idea if I should expect 100% reliability or not? Last night was my first post for help to the forum and it was a bit dis-heartening to see it viewed but with no suggestions recieved. I have genuinely exhausted all my brain power, google time and tweaks to code. I have nowhere left to turn. Thank you in advance for any pointers or wisdom :-[ .

G4USI:
Hi, I have worked really, really hard on some code for a water level detector to send a status via LoRa. I posted the problem last night May 23 at 9.03pm in the programming forum with no replies, so thought I would ask a simpler question here as I am going mad with my issue.

In your experience, do hardware interrupts work reliably with sleep modes?

Appologies for the very long delay in answering your question but do appreciate that the volunteers who support these forums are in different timezones and may not be available to provide free advice 24 hours a day at short notice.

Yes hardware interrupts are reliable if you set them up, and the board hardware, correctly.

Forget the LoRa for now and simplify your program.

At its most basic you need to have simple code that using a switch will reliably wake up your board, do something simple like print a message or flash and LED and go back to sleep.

If you think, for instance that the RTC is providing random interrupts, then change\simplify the sketch so that only the RTC is providing the wake up interrupts.

OK thanks very much for the reply. I understand. My impatience was not directed at anyone but myself! Never having used a forum before, to see loads of replies to others posts and none to your own leaves you feeling a bit helpless! I also wasn't sure if I was posting in the correct part of the forum.

I was using the main code without LoRa in the first instance and the code was reliably responding to my simulated sensor activations and the RTC. Based on your suggestion I will check that again over a prolonged period to see if there are false activations without the LoRa. Thanks for that.
advice.

One quick question on the interrupts though - I am attaching and detaching both interrupts in the same section of code - is that OK or does it need to be done differently with two interrupts? Snippet below, and thanks for the feedback.

void goToSleep() {
  noInterrupts();
  attachInterrupt(digitalPinToInterrupt(sensorWakePin), sensorWakeUp, CHANGE);
  attachInterrupt(digitalPinToInterrupt(RTCwakePin), RTCWakeUp, FALLING);
  //LoRa sleep???
  interrupts();
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
  //Disable external pin interrupts on wake up
  detachInterrupt(digitalPinToInterrupt(RTCwakePin));
  detachInterrupt(digitalPinToInterrupt(sensorWakePin));
}

If you have any sort of long cable on the float sensor it needs a lower value pull up resistor. - try 10k, or even better opto isolate it .
The internal pull-up resistor is high value making your circuit prone to noise pickup
You could also add some filtering in software to ensure the “ pulse”
You get is not Just a fast spike .

Snippet below, and thanks for the feedback.

Always post ALL the code.

  attachInterrupt(digitalPinToInterrupt(RTCwakePin), RTCWakeUp, FALLING);

There are several sleep modes and interrupt function depends on the mode. FALLING does not work in some of them.

Carefully study the ATMega32u4 data sheet to learn which interrupts work, in which sleep modes.

There is rarely a need to use detachInterrupt(), so avoid that complication.

Thanks for the helpful replies which I will act on.

As suggested, I will post the full code here in case anyone has the opportunity to look and can spot any errors.

I built the sketch modularly to try to ensure everything worked before bringing it together, but I will start to strip back to basics, checking and testing as I go (again!!)

At the moment I am foxed. This is the biggest thing I have attempted so far, and it will not beat me!

The interrupts all seem to fire correctly when I expect them to, but I am getting unexpected ones in the interim when nothing has been activated.

Thanks for the help so far. Any further suggestions gratefully received.

[code]

//Initialisation ==============================================

//for battery voltage ============
#define VBATPIN A9
float measuredvbat;
int healthCheck;

// For the RTC ===========
#include "Wire.h"
#include "RTClibExtended.h"
#define RTCwakePin 1
volatile bool clockAlarm = false;
RTC_DS3231 RTC;

// For Sleep mode ===========
#include "LowPower.h"

// For the sensor =============
#define sensorWakePin 0
int waterLevel;
volatile bool sensorAlarm = false;
volatile int valA;
int waterLevelSent;

// For LoRa ============
#include <SPI.h>
#include <LoRa.h>
#define RFM95_CS 8
#define RFM95_RST 4
#define RFM95_INT 7

void setup() {

  // For the RTC ====================================================
  pinMode(RTCwakePin, INPUT_PULLUP);
  //Initialize communication with the clock
  Wire.begin();
  RTC.begin();
  // dECIDE IF TO LEAVE THE BELOW OR REMOVE - NEED TO SET RTC SEPERATELY IF REMOVED
  //RTC.adjust(DateTime(__DATE__, __TIME__));   //set RTC date and time to COMPILE time
  //Set SQW pin to OFF
  RTC.writeSqwPinMode(DS3231_OFF);
  initiateAlarm(); //call fn to clear and set alarms
  //Set alarm1 every day at 09:00 - minutes first then hour, 0 for secs no account of daylight saving time
  //RTC.setAlarm(ALM1_MATCH_HOURS, 0, 0, 09, 0);
  //Set alarm1 at 1 min past the hour past the minute REMOVE IN FINAL
  RTC.setAlarm(ALM1_MATCH_MINUTES, 0, 01, 0, 0);///REMOVE IN FINAL
 
  // For the Water Sensor ====================================
  pinMode(sensorWakePin, INPUT_PULLUP);

  // For LoRa ===============================
  LoRa.setPins(RFM95_CS, RFM95_RST, RFM95_INT);
  //433E6 or 868E6
  // Change sync word (0xF3)ranges from 0-0xFF
  //LoRa.setSyncWord(0xF3);
    
  while (!LoRa.begin(868E6))
  {
    ;
  }
  LoRa.setTxPower(20); 
  LoRa.enableCrc(); //prevent bad packets
  //Set Spreading Factor to 12 for more robust, yet slowest signal
  //LoRa.setSpreadingFactor(12); 
  //Set coding rate for best signal immunity - range 5 - 8 - default 5 8 doubles TX time!!
  //LoRa.setCodingRate4(8); 
  //LoRa.setSignalBandwidth(); 
}

void RTCWakeUp() {
  clockAlarm = true;
}

void sensorWakeUp() {
  valA = digitalRead(sensorWakePin);
  sensorAlarm = true;
}

void loop() {
  USBDevice.attach();//remove in final
  
  // rtc interrupt response
  if (clockAlarm) {
    clockAlarm = !clockAlarm; //reset flag
    initiateAlarm();
    measuredvbat = analogRead(VBATPIN);
    measuredvbat *= 2;    // we divided by 2, so multiply back
    measuredvbat *= 3.3;  // Multiply by 3.3V, our reference voltage
    measuredvbat /= 1024; // convert to voltage
    if (measuredvbat >= 3.4) {
      healthCheck = 2;
    }
    else {
      healthCheck = 3;
    }
    LORATXHealth();
  }

  // sensor interrupt response
  if (sensorAlarm) {
    sensorAlarm = !sensorAlarm;// reset flag
    sensorDebounce(); 
      //only send packet via Lora if there has been a change to water level since last packet sent
      if ((waterLevel) != (waterLevelSent)) {
      LORATXSensor();
      }
  }

  // once dealt with interrupts, can go back to sleep
 goToSleep();
}

void goToSleep() {
  noInterrupts();
  attachInterrupt(digitalPinToInterrupt(sensorWakePin), sensorWakeUp, CHANGE);
  attachInterrupt(digitalPinToInterrupt(RTCwakePin), RTCWakeUp, FALLING);
  //LoRa sleep???
  interrupts();
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
  //Disable external pin interrupts on wake up
  detachInterrupt(digitalPinToInterrupt(RTCwakePin));
  detachInterrupt(digitalPinToInterrupt(sensorWakePin));
}

void initiateAlarm() {
  //clear any pending alarms and reset
  RTC.armAlarm(1, false);
  RTC.clearAlarm(1);
  RTC.alarmInterrupt(1, false);
  RTC.armAlarm(2, false);
  RTC.clearAlarm(2);
  RTC.alarmInterrupt(2, false);
  RTC.alarmInterrupt(1, true);//reset
}

void sensorDebounce() {
  //not really debounce. Just checking if water level is the same 3 secs later and only take action if so.
  delay(3000);
  int valB = digitalRead(sensorWakePin);
  if ((valA) != (valB)) {//tried surrounding this with 'noInterrupts' to preserve the volatile 'valA' but no effect
    goToSleep();
  }
  else {
   waterLevel = !valB; 
  }
}

void LORATXHealth() {
  noInterrupts();
  LoRa.beginPacket();
  LoRa.print(healthCheck);//state of battery, 3 = OK, 4 = re-charge
  LoRa.endPacket();
  //LORASLEEP?? if so need to wake on interrupt
  //what about an acknowledgement or re-send?
  interrupts();
}

void LORATXSensor() {
  noInterrupts();
  LoRa.beginPacket();
  LoRa.print(waterLevel);
  LoRa.endPacket();
  waterLevelSent = waterLevel; 
  //LORASLEEP?? if so need to wake on interrupt
  //what about an acknowledgement or re-send?
  interrupts();
}

[/code]