From a3c8778032a1c247b6382bbae68a4b3e20a7c3ca Mon Sep 17 00:00:00 2001 From: Andrew Yong Date: Fri, 17 Jul 2026 18:26:12 +0800 Subject: [PATCH] stm32wl(rak3172): implement cpuDeepSleep()/shutdown() via STM32LowPower (#10993) * stm32wl(rak3172): implement cpuDeepSleep()/shutdown() via STM32LowPower cpuDeepSleep() was an empty stub, so the SDS deep-sleep PowerFSM state and low-battery shutdown did nothing but leave the CPU running at full power. Power::shutdown() also didn't include STM32WL in its arch list, so an explicit shutdown command just logged a FIXME warning. Adds STM32LowPower as a lib_dep (rak3172 only, mirroring STM32RTC) and implements cpuDeepSleep() using it. Standby mode is used for both the finite-wake (SDS/low battery) and forever (shutdown) paths, via LowPower.shutdown(), with or without an RTC alarm. If the LSE-backed hardware RTC never came up (stm32wlRtcAvailable() false), this is a no-op - safer than sleeping without a confirmed wake source. LowPower.shutdown() resets the MCU on wake and should never return; if it somehow does, force a reset via HAL_NVIC_SystemReset() rather than hanging the device silently forever. Gated behind the existing HAS_LSE flag, so this is inert on every variant but rak3172. Assisted-by: Claude Sonnet 5 Signed-off-by: Andrew Yong * feat(stm32wl): Warn and reset when trying to deepSleep without RTC This is to prevent leaving the firmware catanonic when firmware has run its shutdown routine but doesn't actually shutdown. Signed-off-by: Andrew Yong --------- Signed-off-by: Andrew Yong --- src/Power.cpp | 2 +- src/platform/stm32wl/main-stm32wl.cpp | 31 ++++++++++++++++++++++++++- variants/stm32/rak3172/platformio.ini | 2 ++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index 1c1f34cd4..4118dac53 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -906,7 +906,7 @@ void Power::shutdown() #if HAS_SCREEN messageStore.saveToFlash(); #endif -#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040) +#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_STM32WL) #ifdef PIN_LED1 ledOff(PIN_LED1); #endif diff --git a/src/platform/stm32wl/main-stm32wl.cpp b/src/platform/stm32wl/main-stm32wl.cpp index 97b63a965..d429c5662 100644 --- a/src/platform/stm32wl/main-stm32wl.cpp +++ b/src/platform/stm32wl/main-stm32wl.cpp @@ -7,6 +7,7 @@ #include #if HAS_LSE +#include #include // LSEDRV is a 2-bit RCC_BDCR field where every combination is a legal drive level, so this covers all 4 values. @@ -120,6 +121,7 @@ void stm32wlSetup() rtc.setClockSource(STM32RTC::LSE_CLOCK); rtc.begin(); stm32wlRtcValid = true; + LowPower.begin(); LOG_INFO("STM32WL: LSE locked, hardware RTC available"); } else { // Don't leave a failed oscillator burning current. @@ -138,7 +140,34 @@ bool stm32wlRtcAvailable() void stm32wlSetup() {} #endif -void cpuDeepSleep(uint32_t msecToWake) {} +void cpuDeepSleep(uint32_t msecToWake) +{ +#if HAS_LSE + if (!stm32wlRtcAvailable()) { + // Hardware can't shutdown, but firmware has already prepared itself for shutdown + // Do not leave the device unresponsive, reset instead + LOG_WARN("STM32WL: hardware RTC failed, cannot deep sleep/shutdown"); + if (Serial) { + Serial.flush(); + Serial.end(); + } + HAL_NVIC_SystemReset(); + } + + if (Serial) { + Serial.flush(); + Serial.end(); + } + + if (msecToWake != portMAX_DELAY) { + LowPower.shutdown(msecToWake); + } else { + LowPower.shutdown(); + } + // RTC wakes from shutdown into MCU reset, so this code should never be reached + HAL_NVIC_SystemReset(); +#endif +} // Hacks to force more code and data out. diff --git a/variants/stm32/rak3172/platformio.ini b/variants/stm32/rak3172/platformio.ini index 514668a12..5ed5be1d7 100644 --- a/variants/stm32/rak3172/platformio.ini +++ b/variants/stm32/rak3172/platformio.ini @@ -20,5 +20,7 @@ lib_deps = ${stm32_base.lib_deps} # renovate: datasource=github-tags depName=STM32RTC packageName=stm32duino/STM32RTC https://github.com/stm32duino/STM32RTC/archive/refs/tags/1.9.0.zip + # renovate: datasource=github-tags depName=STM32LowPower packageName=stm32duino/STM32LowPower + https://github.com/stm32duino/STM32LowPower/archive/refs/tags/1.5.0.zip upload_port = stlink