From 0ee5777c1593cf35e7137740e930b74fffb61c5a Mon Sep 17 00:00:00 2001 From: Andrew Yong Date: Thu, 16 Apr 2026 10:35:05 +0800 Subject: [PATCH] stm32wl(mem): fix getFreeHeap() underreporting on dynamic sbrk heap mallinfo().fordblks counts only free bytes within the committed arena. On STM32WL (newlib sbrk heap) the arena grows lazily from _end toward SP, so fordblks reads near-zero at early boot even when ~48 KB of addressable space remains. This caused NodeDB::isFull() to fire prematurely and evict nodes on a freshly booted device. Fix getFreeHeap() to include uncommitted sbrk headroom (SP - sbrk(0)) so the returned value reflects true available memory throughout the boot lifecycle. Introduce MESHTASTIC_DYNAMIC_SBRK_HEAP as an opt-in build flag (set in stm32.ini) so the fix is gated to platforms with a dynamic sbrk heap rather than a static heap. Future platforms with the same heap model can opt in by adding this flag. Signed-off-by: Andrew Yong Assisted-by: Claude Sonnet 4.6 --- src/memGet.cpp | 22 +++++++++++++++++----- variants/stm32/stm32.ini | 1 + 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/memGet.cpp b/src/memGet.cpp index 14e614014..570bbf5b2 100644 --- a/src/memGet.cpp +++ b/src/memGet.cpp @@ -10,8 +10,20 @@ #include "memGet.h" #include "configuration.h" -#ifdef ARCH_STM32WL +#if defined(MESHTASTIC_DYNAMIC_SBRK_HEAP) #include +#include // sbrk + +// Returns the uncommitted sbrk headroom: addressable space between the current heap +// break and the stack pointer that has not yet been committed to the arena. +// Currently used on: ARCH_STM32WL +static uint32_t sbrkHeadroom() +{ + uint32_t sp; + __asm volatile("mov %0, sp" : "=r"(sp)); + uint32_t heap_end = (uint32_t)sbrk(0); + return (sp > heap_end) ? (sp - heap_end) : 0; +} #endif MemGet memGet; @@ -28,9 +40,9 @@ uint32_t MemGet::getFreeHeap() return dbgHeapFree(); #elif defined(ARCH_RP2040) return rp2040.getFreeHeap(); -#elif defined(ARCH_STM32WL) +#elif defined(MESHTASTIC_DYNAMIC_SBRK_HEAP) // Currently: ARCH_STM32WL struct mallinfo m = mallinfo(); - return m.fordblks; // Total free space (bytes) + return m.fordblks + sbrkHeadroom(); // Free space within arena + uncommitted sbrk headroom #else // this platform does not have heap management function implemented return UINT32_MAX; @@ -49,9 +61,9 @@ uint32_t MemGet::getHeapSize() return dbgHeapTotal(); #elif defined(ARCH_RP2040) return rp2040.getTotalHeap(); -#elif defined(ARCH_STM32WL) +#elif defined(MESHTASTIC_DYNAMIC_SBRK_HEAP) // Currently: ARCH_STM32WL struct mallinfo m = mallinfo(); - return m.arena; // Non-mmapped space allocated (bytes) + return m.arena + sbrkHeadroom(); // Non-mmapped space allocated + uncommitted sbrk headroom #else // this platform does not have heap management function implemented return UINT32_MAX; diff --git a/variants/stm32/stm32.ini b/variants/stm32/stm32.ini index 542d08800..c49db27f3 100644 --- a/variants/stm32/stm32.ini +++ b/variants/stm32/stm32.ini @@ -36,6 +36,7 @@ build_flags = -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 -DRADIOLIB_EXCLUDE_LR2021=1 + -DMESHTASTIC_DYNAMIC_SBRK_HEAP -DHAL_DAC_MODULE_ONLY -DHAL_RNG_MODULE_ENABLED -Wl,--wrap=__assert_func