I have been playing around with Bluetooth and a water flow meter. I want to extend battery life by putting the Nano 33 IoT to sleep at night. If there's a water flow event while it's asleep, I want it to wake up, deal with it and then go back to sleep again.
The problem I'm having is that when a water flow event wakes it from sleep, very frequently the internal rtc year somehow has been set to Zero.
I'm using the Arduino LowPower library and have an external sensor configured via an interrupt on pin 2:
LowPower.attachInterruptWakeup(waterFlowSensorPulseIn, countPulse, RISING);
If it's not asleep and a water flow event happens, the countPulse function executes just fine.
I send it to sleep with:
LowPower.sleep(( (wakeupTimeHourTemp * 60) - (rtc.getHours() * 60 + rtc.getMinutes()) ) * 60000);
If no water flow event occurs it wakes up at the time set by the wakeup time.
If it's asleep and a water flow event happens, it wakes up and, more often than not, the rtc getYear() function returns 0.
I've minimised the code to try to make it easy on anyone who wants to take a look - so all bluetooth and non-relevant functionality has been removed. I've tested it and the issue still remains.
Any help greatly appreciated. Here's the minimal code:
#include <RTCZero.h>
#include "ArduinoLowPower.h"
#define waterFlowSensorPulseIn 2
const byte ledPinGreen = 5;
const byte ledPinOrange = 6;
const byte ledPinWhite = 7;
const int ignorePulsesThreshold = 10; // 50 unless debugging
const uint8_t bedTimeHour = 23, wakeupTimeHour = 1; // 23 and 9 normally - unless debugging
RTCZero rtc;
const uint8_t mySeconds = 45;
const uint8_t myMinutes = 58;
const uint8_t myHours = 22;
const uint8_t myDay = 15;
const uint8_t myMonth = 6;
const uint8_t myYear = 26;
volatile int pulseCount = 0; // 'volatile' is required for interrupt variables
const int pulsesStoppedTimeout = 50; // 50 milliseconds
long previousMillis = 0;
byte loopActiveCount = 0;
void setup() {
rtc.begin(); // initialize RTC
delay(1000); // make sure rtc is fully initialized
pinMode(LED_BUILTIN, OUTPUT);
// initialize the built-in LED pin
pinMode(waterFlowSensorPulseIn, INPUT_PULLUP);
pinMode(ledPinGreen, OUTPUT);
pinMode(ledPinOrange, OUTPUT);
pinMode(ledPinWhite, OUTPUT);
LowPower.attachInterruptWakeup(waterFlowSensorPulseIn, countPulse, RISING);
rtc.setTime(myHours, myMinutes, mySeconds);
rtc.setDate(myDay, myMonth, myYear);
}
void countPulse() {
pulseCount++;
}
void blinker(long mySpeed, int numBlinks, byte ledNum) {
long myCount = 0, previousMillis = 0, currentMillis = millis();
bool blinkingIsToContinue = true, ledState = LOW;
while (blinkingIsToContinue) {
currentMillis = millis();
if (currentMillis - previousMillis > mySpeed) {
previousMillis = currentMillis;
ledState = !ledState;
myCount++;
digitalWrite(ledNum, ledState);
if (myCount == numBlinks * 2) {
blinkingIsToContinue = false;
}
}
}
}
void loop() {
static unsigned long timePulseReceived = 0;
static int previousPulseCount = 0;
static long elapsedTime = 0;
static bool flowEventInProgress = false;
uint8_t wakeupTimeHourTemp;
// deal with possible water flow event. The interrupt service routine, countPulse, is fired
// whenever a pulse comes in from the sensor and increments pulseCount
if (pulseCount > 0) { // water is flowing, wait until it stops before calculating volumes etc.
// Debug: zero year - trying to find out why date sometimes gets messed up but time is ok
if (rtc.getYear() == 0) { // this test always fails here
while (1) {
blinker(80, 5, ledPinOrange);
blinker(120, 2, ledPinOrange);
}
}
flowEventInProgress = true;
if (pulseCount == previousPulseCount) {
// no additional pulse received yet, keep waiting / allowing for another
// pulse to arrive within pulsesStoppedTimeout
elapsedTime = millis() - timePulseReceived;
if (elapsedTime > pulsesStoppedTimeout) { // the flow event is finished
if (pulseCount > ignorePulsesThreshold) { // connecting a hosepipe can cause a few pulses so ignore them
// this is where all the bluetooth characteristics would get updated (removed in this minimal debug version)
}
// prepare for next flow event
flowEventInProgress = false;
pulseCount = 0;
previousPulseCount = 0;
elapsedTime = 0;
}
} else { // another pulse has arrived
timePulseReceived = millis();
previousPulseCount = pulseCount;
}
}
if (!flowEventInProgress) {
wakeupTimeHourTemp = wakeupTimeHour;
if (bedTimeHour > wakeupTimeHour) wakeupTimeHourTemp += 24; // wakeup time must be on following day
if ((rtc.getHours() >= bedTimeHour) && (rtc.getHours() < wakeupTimeHourTemp)) {
// Debug: zero year - trying to find out why date sometimes gets messed up
if (rtc.getYear() == 0) { // this test always fails here ie never == 0
while (1) {
blinker(80, 10, ledPinOrange);
blinker(120, 2, ledPinGreen);
}
}
// It should be asleep so send it to bed!
wakeupTimeHourTemp = wakeupTimeHour;
if ((rtc.getHours() * 60 + rtc.getMinutes()) > (wakeupTimeHour * 60)) wakeupTimeHourTemp += 24;
LowPower.sleep(((wakeupTimeHourTemp * 60) - (rtc.getHours() * 60 + rtc.getMinutes())) * 60000); // sleep until wakeup time
// NB The Nano will also wake up on the arrival of the first pulse of a new flow event.
// When the flow event is finished (and as long as it's still bedtime) the Nano
// will be put back to sleep.
blinker(120, 4, ledPinOrange); // show that it's woken up
}
}
// Debug: trying to find out why date sometimes gets messed up but time is ok
if (rtc.getYear() == 0) {
// if the Nano is woken up by pulses arriving, the test very frequently
// succeeds ie rtc.getYear() == 0. Why??
while (rtc.getYear() == 0) { // just in case, give the rtc time to settle down???
blinker(480, 4, ledPinWhite);
blinker(480, 4, ledPinOrange);
blinker(480, 4, ledPinGreen);
}
} else {
loopActiveCount++;
if (loopActiveCount == 64) blinker(20, 1, ledPinGreen);
// Show that it's looping, ie not asleep.
// As it's a byte, it'll reach 255 then go back to zero
}
}