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!
