stm32duino / STM32LoRaWAN

Arduino library to support LoRaWAN communication using the STM32WL series.
https://stm32duino.github.io/STM32LoRaWAN/
Other
40 stars 9 forks source link

LowPower.deepSleep not working after modem.begin #36

Open janvrska opened 1 year ago

janvrska commented 1 year ago

Expected Behavior

After setting modem.begin(EU868) and LowPower.deepSleep() it goes sleep, and the chip will not wake up.

Current Behavior

After setting modem.begin(EU868) and LowPower.deepSleep() it goes sleep, but after around 900ms it wakes up.

If modem.begin(EU868) isn't used then it works as intended, and the chip will not wake up.

Minimal example

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

STM32RTC& rtc = STM32RTC::getInstance();
STM32LoRaWAN modem;

void setup() {
    Serial.begin(115200);
    modem.begin(EU868);
    LowPower.begin();
    LowPower.deepSleep();
}

void loop() {
    Serial.begin(115200);
    Serial.println("wakes up");
    delay(1000);
    LowPower.deepSleep();
}

Power Profiler screenshot with modem.begin() image

Power Profiler screenshot without modem.begin() image

Details (what I tried)

I tried to look on https://github.com/stm32duino/STM32LoRaWAN/blob/192c1234a8661f72f1635e5675db0d11fb110920/src/STM32LoRaWAN.cpp#L62-L81 and tried in my main code to:

rtc.detachInterrupt, rtc.detachSecondsInterrupt, rtc.disableAlarm (both A ,B), but none of it worked.

Only when I after modem.begin() called rtc.end() and then rtc.begin() it stops waking up, but after joining the network (which was successfull), modem.send() didn't send any message (propably some problem with RTC settings that should have been set in modem.begin() and was cleared after rtc.end())

Context

I'm using supported LORA-E5 chip (STM32WLE5JC) on custom PCB

platformio.ini:

[env:lora_e5_mini]
platform = ststm32
board = lora_e5_mini
framework = arduino
lib_deps = 
    https://github.com/stm32duino/STM32LoRaWAN.git#0.2.0
    https://github.com/stm32duino/STM32RTC.git#1.4.0
    https://github.com/stm32duino/STM32LowPower.git#5ae219c
monitor_speed = 115200
monitor_filters = time
upload_protocol = stlink
debug_tool = stlink
HelgeSeidel commented 1 year ago

Hi @janvrska, using rtc.begin(true) to reset the RTC after modem.begin(EU868) helped for me.

fpistm commented 1 year ago

Did you try to add a small delay after the modem begin? Did you try this example: https://github.com/stm32duino/STM32LoRaWAN/blob/main/examples/LowPowerBasic/LowPowerBasic.ino Is it working?

HelgeSeidel commented 1 year ago

Hi @fpistm, I tried the example on the RAK3172T and unfortunately it is not working. The example send the data package every 6 seconds. Using rtc.begin(true) to reset the RTC after modem.begin(EU868) helped here as well.

Screenshot

fpistm commented 1 year ago

Using rtc.begin(true) to reset the RTC after modem.begin(EU868) helped here as well.

Seems strange as the modem.begin() call the rtc.begin(true). https://github.com/stm32duino/STM32LoRaWAN/blob/192c1234a8661f72f1635e5675db0d11fb110920/src/STM32LoRaWAN.cpp#L73C1-L75

My guess is something is pending which wake up the mcu. Calling deepSleep() simply enter in STOP mode 2 but several source (event) can wake it (LPUART, PWR_WKUP pin, RTC...). So before entering in deepSleep you have to ensure you can.

@janvrska Some notes, we do not support PIO only Arduino IDE. We saw several times misalignment on PIO setup and so would not invest time on wrong configurations.

Your code is not correct:

void loop() {
    //Serial.begin(115200); --> Avoid to init each loop the instance
    Serial.println("wakes up");
    //delay(1000); --> Simply do a flush to prevent Serial IT to wakeup the board.
    Serial.flush();
    LowPower.deepSleep();
}

and tried in my main code to: rtc.detachInterrupt, rtc.detachSecondsInterrupt, rtc.disableAlarm (both A ,B), but none of it worked.

Only when I after modem.begin() called rtc.end() and then rtc.begin() it stops waking up, but after joining the network (which was successfull), modem.send() didn't send any message (propably some problem with RTC settings that should have been set in modem.begin() and was cleared after rtc.end())

That is normal you can't send anything, if you remove the callback required for the modem timing or by reseting yourself the RTC as is do not set those callback.

I tried within my LoRa-E5 mini and the low power is functional. Please try he example then give us your feedback (preferably with Arduino).

@HelgeSeidel I don't understand your point:

I tried the example on the RAK3172T and unfortunately it is not working. The example send the data package every 6 seconds. Using rtc.begin(true) to reset the RTC after modem.begin(EU868) helped here as well.

This mean that if you call rtc.begin(true) after modem.begin(), it works? If true as stated before, seems very strange as we already reset the RTC in the begin. Or one explanation, could be you have a battery, in that case RTC always running and alarm configured. So maybe an alarm IT still pending in this timeframe but I doubt on this as with begin(true) the backup domain is reset. This board/module is not supported, you even enter yourself this #34 so not very relevant here as maybe other issue related to this.

HelgeSeidel commented 1 year ago

@fpistm Same behaviour on the RAK3172 Evaluation Board which is supported. Running the example as is will send the packet every 6 seconds. Calling rtc.begin(true) after modem.begin() and the example worked as expected.

No battery connected to the board / RTC.

janvrska commented 1 year ago

Thank you for quick response. I tried LowPowerBasic example in ArduinoIDE and have same result as @HelgeSeidel image image

After adding rtc.begin(true) after modem.begin, example works as expected image

fpistm commented 1 year ago

@FRASTM do you have any idea on this subject?

slavendam commented 10 months ago

Just to confirm that I also have the same issue on RAK3172 module (waking up after ~1s). Adding rtc.begin(true) solved issue.

Whoever came to this (temporary) solution - thank you a lot for saving lot of nerves! @HelgeSeidel

FRASTM commented 9 months ago

In case of rtc reset, rtc.begin(true), the RCC BDCR register bits RTCEN and RTCSEL are 0. __HAL_RCC_RTC_ENABLE() will set the RTCEN bit but RTCSEL should also be set with else no source is selected to clock the RTC. The RTC peripheral is not supposed to be accessed before RTC clock is selected. Resetting the Backup domain earlier in the RTC_init will also give more clock cycles to access the RCC BDCR register

Can you please try with this:

diff --git a/src/rtc.c b/src/rtc.c
index 73c911f..23cb143 100644
--- a/src/rtc.c
+++ b/src/rtc.c
@@ -411,6 +411,20 @@ bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool
   bool isAlarmBSet = false;
 #endif

+  /* Ensure backup domain is enabled before we init the RTC so we can use the backup registers for date retention on stm32f1xx boards */
+  enableBackupDomain();
+
+  if (reset) {
+    resetBackupDomain();
+    /* After Backup domain reset, RTC is disabled and no RTC clock selected */
+  }
+
+#ifdef __HAL_RCC_RTCAPB_CLK_ENABLE
+  __HAL_RCC_RTCAPB_CLK_ENABLE();
+#endif
+  __HAL_RCC_RTC_ENABLE();
+  /* Also need to select the RTC clock source asap with RTC_initClock */
+
   initFormat = format;
   initMode = mode;
   /* Ensure all RtcHandle properly set */
@@ -431,22 +445,6 @@ bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool
 #endif /* RTC_OUTPUT_REMAP_NONE */
 #endif /* STM32F1xx */

-  /* Ensure backup domain is enabled before we init the RTC so we can use the backup registers for date retention on stm32f1xx boards */
-  enableBackupDomain();
-
-  if (reset) {
-    resetBackupDomain();
-  }
-
-#ifdef __HAL_RCC_RTCAPB_CLK_ENABLE
-  __HAL_RCC_RTCAPB_CLK_ENABLE();
-#endif
-  __HAL_RCC_RTC_ENABLE();
-
-  isAlarmASet = RTC_IsAlarmSet(ALARM_A);
-#ifdef RTC_ALARM_B
-  isAlarmBSet = RTC_IsAlarmSet(ALARM_B);
-#endif
 #if defined(STM32F1xx)
   uint32_t BackupDate;
   BackupDate = getBackupRegister(RTC_BKP_DATE) << 16;
@@ -510,6 +508,12 @@ bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool
 #else
       RTC_setPrediv(predivAsync, predivSync);
 #endif
+
+      isAlarmASet = RTC_IsAlarmSet(ALARM_A);
+#ifdef RTC_ALARM_B
+      isAlarmBSet = RTC_IsAlarmSet(ALARM_B);
+#endif
+
       if (isAlarmASet) {
         RTC_GetAlarm(ALARM_A, &alarmDay, &alarmHours, &alarmMinutes, &alarmSeconds, &alarmSubseconds, &alarmPeriod, &alarmMask);
       }
slavendam commented 9 months ago

In case of rtc reset, rtc.begin(true), the RCC BDCR register bits RTCEN and RTCSEL are 0. __HAL_RCC_RTC_ENABLE() will set the RTCEN bit but RTCSEL should also be set with else no source is selected to clock the RTC. The RTC peripheral is not supposed to be accessed before RTC clock is selected. Resetting the Backup domain earlier in the RTC_init will also give more clock cycles to access the RCC BDCR register

Can you please try with this: ...

I've tried suggested change, but behaviour is the same.

I also saw that setting up LoRaWAN (modem.begin) will reset time and date which are set up prior to that call

FRASTM commented 9 months ago

If the rtc has already been initialized, we could skip the rtc init sequence requested by the Lorawan (calling a _rtc.begin(false,STM32RTC::HOUR_24); instead ) and only set the binary mode to MIX mode (if differs). The Lorawan setting the binary mode will only update the RTC binary mode bits, without changing calendar registers. This is possible when the first rtc initialisation has configured the same LSE clock as the lorawan requires the LSE

FRASTM commented 9 months ago

Change the STM32RTC : RTC_init function to force the update of the RTC ICS BIN and BDCU register in case of RTC is already initiliazed. See https://github.com/stm32duino/STM32RTC/pull/106

nmaas87 commented 4 months ago

I had the same issue and as reported, calling rtc.begin(true) after modem.begin() made it work. Thanks a lot for reporting it in this issues, I was already on the verge of questioning my sanity :).

FRASTM commented 1 month ago

One difference seen inside the modem.begin() is that after the _rtc.begin(), there are two attachInterrupt :

  _rtc.attachInterrupt(UTIL_TIMER_IRQ_MAP_PROCESS, STM32RTC::ALARM_B);
  _rtc.attachSecondsInterrupt(TIMER_IF_SSRUCallback);

Especially the attachSecondsInterrupt which also set the RTC WakeUpTimer interrupt.

When rtc.begin() is called (independently) after the modem.init, the rtc.begin(true) is resetting the RTC registers, including wakeup Interrupt Enable bit, etc. The wakeUp Interrupt is no more enabled

Could that explain the issue

Does the issue change if the STM32LoRaWAN::begin has not rtc.attachSecondsInterrupt(TIMER_IF_SSRUCallback); ?

mrschuster commented 2 weeks ago

This indeed explains the issue, why the STM wakes up every second. Removing the attachSecondsInterrupt line solves this issue, as then the deepSleep works as expected. I tested this on a RAK3172.

I also looked into why rtc.detachSecondsInterrupt after the modem.begin() does not work. In the RTC library, the detach seems to only detach the callback, but lets the one-second-wakeup in place.