Help with False Hardware Interrupts

I am at my wits end! I am using two Adafruit LoRa Feather 32u4 boards with RFM95 chips. The project is to monitor water levels. The sensor board will be connected to a float switch - for now I am using a push button switch for testing). The basic idea is that if a change takes place at the water sensor then a LoRa message is sent to the receiver board, indicating the water level is either high (button closed and pulled to ground) or Low, button open and pulled-up via the internal pull-up resistor. It is intended a packet is only sent if it is different to the last packet sent. Unless there is a change, the board is asleep to save power.

This sensor button is connected to Hardware Interrupt Pin 0 (highest priority.)

I am also using Hardware Interrupt Pin 1 (lower priority) to pull the Feather from sleep on an alarm from the Feather DS3231 RTC pulling this pin LOW. The purpose of this is to send a daily 'Health Check and battery status'. For testing purposes, it is currently waking the Feather hourly rather than daily.

The problem - well the activation of the packets and notifications works perfectly when I press or release the push button, no problem there. The issue is that I am getting FALSE notifications. I am getting LoRa messages at the receiver when the button has been untouched. There does not appear to be a pattern to this. Sometimes I will get a Water Low, sometimes a Water High, sometimes I will get a 'recharge battery' message at a time when I am not expecting one (i.e. on the RTC ISR).

I have tried for hours and hours various methods and changes but cant crack it. I have read Nick Gammon's Interrupt tutorial endelssly and I just cannot see what is wrong.

Can someone more experienced than I please cast a fresh pair of eyes on this and spot my mistake(s)?

Code for the sensor is here:

//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 for the Receiver is here:

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

void setup() {
  LoRa.setPins(RFM95_CS, RFM95_RST, RFM95_INT);
  Serial.begin(9600);
  while (!Serial);

  Serial.println("LoRa Receiver");

  if (!LoRa.begin(868E6)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
  LoRa.setTxPower(20); 
  LoRa.enableCrc(); //prevent bad packets
}

void loop() {
  // try to parse packet
  int packetSize = LoRa.parsePacket();
  if (packetSize) {
    // received a packet
    Serial.print("Received packet ");
  
    // read packet
    while (LoRa.available()) {
      int message = ((char)LoRa.read());
      switch (message) {
        case 48:
          Serial.println("Water LOW");
          break;
        case 49:
          Serial.println("Water HIGH");
          break;
        case 50:
          Serial.println("Health OK");
          break;
        case 51:
          Serial.println("Recharge battery");
          break;
      }
    }

    // print RSSI of packet
    //Serial.print("' with RSSI ");
    //Serial.println(LoRa.packetRssi());
    
  }
}

And the Serial Monitor output from the Receiver is attached. Please ignore upto and including 21:01:13:109 as these are genuine and expected - the others are not. Also note the Recharge Battery packet is erroneous - the battery is fully charged and being charged by USB at the time!

I think your trouble is with the delay ... try a millis() timer instead.

Do you want the question answered in here, or in the Sensors topic where you seem to have asked the same question ?