stm32duino/STM32LowPower

Never waking up when alarm is set without reseting RTC

ClementFoucher opened this issue · 12 comments

Hello,

Note that I'm confused about where to post this issue, as the problem described below is linked to the sleep modes, but the partial workaround I found involves the RTC library.

I'm experiencing an issue where after being put in sleep or deepSleep mode with a programed alarm time, the µC seems to never wake up.
The issue depicted here was tested with the following Nucleo boards: L476RG and G474RE; and the following Adafruit board: STM32F405 Feather Express.

When I try to use the sleep modes in combination with a RTC alarm-based wake up at a specific time, the board will never wake up unless the RTC has been reset when calling its begin() function. When true is passed to that function, the behavior is correct. However, what I would like to do involves preserving the RTC time across reset (the µC has an attached battery to preserve its time), thus resetting RTC at each boot is not a viable solution.

Note that I used the very latest versions of RTC and Low Power libraries: #f620b53 version for RTC and #1a17d41 version for Low Power. I even integrated the latest PR #79 of the Low Power library without more luck.

Here is a minimal test I set up, displaying current time every minute on the serial console. Behavior can be switched between correct and faulty behavior using the MODE macro as depicted in the comments:

#include <Arduino.h>
#include <STM32RTC.h>
#include <STM32LowPower.h>

// Mode 0: works correctly, but RTC has to be reset so time can't be preserved across reboot
//#define MODE 0
// Mode 1: RTC time preserved, but never wakes up
#define MODE 1

STM32RTC& rtc = STM32RTC::getInstance();

void displayTime(uint8_t h, uint8_t m, uint8_t s)
{
	String text;
	text  = String(h);
	text += ":";
	if (m < 10) text += "0";
	text += String(m);
	text += ":";
	if (s < 10) text += "0";
	text += String(s);
	text += '\n';

	Serial.print(text);
	Serial.flush();
}

void sleepTillNextMinute(uint8_t h, uint8_t m)
{
	m = (m+1)%60;
	if (m==0) h = (h+1)%24;

	rtc.setAlarmTime(h, m, 0);
	rtc.enableAlarm(rtc.MATCH_HHMMSS);
	LowPower.deepSleep(); // or .sleep(): same behavior
}

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

	rtc.setClockSource(STM32RTC::LSE_CLOCK);

#if MODE == 0
	// Working, but clock is reinitialized at every reset
	rtc.begin(true);
	rtc.setTime(7, 30, 45);
#elif MODE == 1
	// Time is correctly preserved, but board will never wake up
	rtc.begin();
	if (!rtc.isConfigured())
		rtc.setTime(7, 30, 45);
#endif

	LowPower.begin();
}

void loop()
{
	uint8_t h, m, s;
	rtc.getTime(&h, &m, &s, nullptr);
	displayTime(h, m, s);
	sleepTillNextMinute(h, m);
}

Thanks for your support.

Hi @ClementFoucher ,
It works well (tested on my Nucleo_L476rg), when replacing
rtc.isConfigured()
by
rtc.isTimeSet()

See details: https://github.com/stm32duino/STM32RTC#since-stm32-core-version--150

Thanks for your reply.

However, even when changing that function, I still can't manage to get it working.
The time is correctly preserved, as I can see it evolving when pressing the reset button, but the board still won't wake up after going to sleep mode, thus time is only displayed after reset, and not every minute as it should be.
I checked my STM32 Core version, it is 2.3.0 so well above the 1.5.0 mentioned in the link you provide.

Strange. I'll investigate my config to see if it comes from my settings.

OK, there is a misunderstanding, when I said that it works, I mean:
I plug the power cable, then clock is set to 7:30:45
then alarm wakeup MCU every minutes (7:31:00, 7:32:00, ...)
I don't press reset button !!
So pressing reset button is a different story ...

No misunderstanding: I understood that to you, the alarm wakes up every minute, but to me it doesn't.
I mentioned the reset button because in my case, it is the only way to display the time, thus the only way to make sure the clock still runs.

Thanks for your support, I'll then check for an error on my side since the library seems to behave OK.
Just to make sure: is the upload method (I use mass storage) susceptible to interfere with the correct behavior?

Just to make sure: is the upload method (I use mass storage) susceptible to interfere with the correct behavior?

No

Make sure to unplug/replug power supply after upload.

My setup:
Nucleo_l476rg
RTC: f620b534f7bffe24f031fc5935324027cfe51320 like you
LowPower: 5ef966f (including #81)

I did not do the unplug/replug part, because my device has a battery attached.

However, when I just tested it with my Nucleo board with that additional step, it seemed to show a correct behavior, as the time is correctly updated every minute. However, since by unplugging the RTC time is lost, we actually fall in the case "time not set", thus time is reset to the default value (if (!rtc.isTimeSet()) rtc.setTime(7, 30, 45);), which is not what I'm aiming for.

Thus, I probably misinterpreted the error in the first place: actually, it may not be linked to the fact that I pass or not pass the true parameter to the clock, but due to the fact that the time is set by software.
Does the library not use the hardware flag to understand if time is set ? I think it may be why I used the isConfigured() function in the fist part, as I thought this function used the hardware flag.

I need to dig it up. Thanks for all your help.

Ok, after digging up a little more, I can see why your use case (unplugging/replugging) works:
When calling begin() without parameters, if RTC hasn't been initialized (which is the case after a power loss), the behavior is the same as if we called begin(true) due to this test.
We then fall in the case "time not set", and time is reinitialized because rtc.isTimeSet() will return false.

As stated n my original post, I have a battery attached to my device and want the time to be preserved across board reset or firmware update. I know this is possible because of the test provided below. In MODE 2, it will work correctly, meaning time is preserved across reset or new code upload. In that mode, I artificially emulate sleep mode by doing no print on the console.

Mode 1 has the same RTC configuration, thus should expose the same behavior. Mode 0 works for the sleeping part, but does not preserve time across reboot.

#include <Arduino.h>
#include <STM32RTC.h>
#include <STM32LowPower.h>

// Mode 0: works correctly, but RTC has to be reset so time can't be preserved across reboot
//#define MODE 0
// Mode 1: RTC time preserved, but never wakes up
//#define MODE 1
// Mode 2: RTC time preserved, works OK, but no sleep mmode
#define MODE 2

STM32RTC& rtc = STM32RTC::getInstance();

void displayTime(uint8_t h, uint8_t m, uint8_t s)
{
  String text;
  text  = String(h);
  text += ":";
  if (m < 10) text += "0";
  text += String(m);
  text += ":";
  if (s < 10) text += "0";
  text += String(s);
  text += '\n';

  Serial.print(text);
  Serial.flush();
}

void sleepTillNextMinute(uint8_t h, uint8_t m)
{
  m = (m+1)%60;
  if (m==0) h = (h+1)%24;

  rtc.setAlarmTime(h, m, 0);
  rtc.enableAlarm(rtc.MATCH_HHMMSS);
  LowPower.sleep(); // or .sleep(): same behavior
}

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

  rtc.setClockSource(STM32RTC::LSE_CLOCK);

#if MODE == 0
  // Working, but clock is reinitialized at every reset
  rtc.begin(true);
  rtc.setTime(7, 30, 45);
#elif (MODE == 1) || (MODE == 2)
  // Time is correctly preserved, but board will never wake up if put in sleep mode
  rtc.begin();
  if (!rtc.isTimeSet())
    rtc.setTime(7, 30, 45);
#endif

  LowPower.begin();
}

void loop()
{
  uint8_t h, m, s;
  rtc.getTime(&h, &m, &s, nullptr);
#if (MODE == 0) || (MODE == 1)
  displayTime(h, m, s);
  sleepTillNextMinute(h, m);
#else
  static uint8_t latest_minute = m;
  static bool firstStart = true;
  if  ( (m != latest_minute) || (firstStart == true) )
  {
    rtc.getTime(&h, &m, &s, nullptr);
    displayTime(h, m, s);
    latest_minute = m;
	firstStart = false;
  }
#endif
}

Ok, my bad from the beginning!

I simply omitted to set the day on which to wake up by calling the function setAlarmDate(). I thought this was facultative since mode 0 above (first tests I did before trying to preserve time) worked correctly without setting it, but it turns out it is not. The fact that it works correctly in this mode is still unknown to me, but I won't investigate more as I found my error.

Sorry for wasting your time.

Actuallly, there is an issue here:

since I set rtc.enableAlarm(rtc.MATCH_HHMMSS), the day shouldn't matter in the alarm. The problem is located in function programRtcWakeUp(): here, alarm configuration is changed. Thus LowPower library overrides RTC settings for its own purpose. This makes the combination of the two libraries an issue in such a case.

Perhaps the LowPower library should provide an alarm() function based on an absolute time, so that we don't have to mix calls between RTC an LowPower?

I agree with you, since you set the mask, it is not mandatory to set the day.
And so yes, there is a bug ... in the case of reset (reset button)

I don't think programRtcWakeUp() is the issue:
programRtcWakeUp() is called conditionally

  if ((ms != 0) || _rtc_wakeup) {
    programRtcWakeUp(ms, DEEP_SLEEP_MODE);
  }

and in your case, ms is 0 and _rtc_wakeup is false (because it is set true only in enableWakeupFrom())
Thus I think programRtcWakeUp() is not called

Specially in your case, the problem comes from
https://github.com/stm32duino/STM32RTC/blob/f620b534f7bffe24f031fc5935324027cfe51320/src/rtc.c#L604
After the reset, the day indirectly inherit from the default reset value 0 which is considered invalid by IS_RTC_DATE(day)

I will soon propose a fix to set a correct value in the begin(), and by the way cover tricky cases including reset.

Thank you for your support.

Yes, you're right about programRtcWakeUp(), I was a bit too quick when reading the functions.

@ABOSTM:
Thank you very much for your work, I can confirm this new version is fully functional for my use case.