From 61cc7642444204df3eae16fe491046900e5a230d Mon Sep 17 00:00:00 2001 From: Andrew Yong Date: Sun, 12 Jul 2026 02:37:01 +0800 Subject: [PATCH 1/2] 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 --- src/Power.cpp | 2 +- src/platform/stm32wl/main-stm32wl.cpp | 21 ++++++++++++++++++++- variants/stm32/rak3172/platformio.ini | 2 ++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index 1c1f34cd406..4118dac538c 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 97b63a96582..e8c4458fc36 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,24 @@ bool stm32wlRtcAvailable() void stm32wlSetup() {} #endif -void cpuDeepSleep(uint32_t msecToWake) {} +void cpuDeepSleep(uint32_t msecToWake) +{ +#if HAS_LSE + if (!stm32wlRtcAvailable()) + return; + + if (Serial) + 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 514668a12db..5ed5be1d709 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 From 01297c7df21de1c9bffd42a5671c3360ef50f466 Mon Sep 17 00:00:00 2001 From: Andrew Yong Date: Sun, 12 Jul 2026 03:24:07 +0800 Subject: [PATCH 2/2] 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 --- src/platform/stm32wl/main-stm32wl.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/platform/stm32wl/main-stm32wl.cpp b/src/platform/stm32wl/main-stm32wl.cpp index e8c4458fc36..d429c5662de 100644 --- a/src/platform/stm32wl/main-stm32wl.cpp +++ b/src/platform/stm32wl/main-stm32wl.cpp @@ -143,11 +143,21 @@ void stm32wlSetup() {} void cpuDeepSleep(uint32_t msecToWake) { #if HAS_LSE - if (!stm32wlRtcAvailable()) - return; + 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) + if (Serial) { + Serial.flush(); Serial.end(); + } if (msecToWake != portMAX_DELAY) { LowPower.shutdown(msecToWake);