From a707cc6da239794865835f95c7b4629871aa3ae3 Mon Sep 17 00:00:00 2001 From: cottongin Date: Mon, 26 Jan 2026 13:56:36 -0500 Subject: [PATCH] debug mode added for memory testing --- platformio.ini | 8 +++++ .../browser/OpdsBookBrowserActivity.cpp | 1 + .../dictionary/DictionaryMenuActivity.cpp | 1 + .../dictionary/DictionaryResultActivity.cpp | 1 + .../dictionary/DictionarySearchActivity.cpp | 1 + .../dictionary/EpubWordSelectionActivity.cpp | 1 + src/activities/home/HomeActivity.cpp | 14 ++++++++ src/activities/home/ListViewActivity.cpp | 1 + src/activities/home/MyLibraryActivity.cpp | 1 + .../network/CalibreWirelessActivity.cpp | 2 ++ .../network/CrossPointWebServerActivity.cpp | 33 ++++++++++++++----- .../network/NetworkModeSelectionActivity.cpp | 1 + .../network/WifiSelectionActivity.cpp | 1 + src/activities/reader/EpubReaderActivity.cpp | 1 + .../EpubReaderChapterSelectionActivity.cpp | 1 + src/activities/reader/TxtReaderActivity.cpp | 1 + src/activities/reader/XtcReaderActivity.cpp | 1 + .../XtcReaderChapterSelectionActivity.cpp | 1 + .../settings/CalibreSettingsActivity.cpp | 1 + .../settings/CategorySettingsActivity.cpp | 1 + .../settings/ClearCacheActivity.cpp | 1 + src/activities/settings/OtaUpdateActivity.cpp | 1 + src/activities/settings/SettingsActivity.cpp | 1 + src/activities/util/KeyboardEntryActivity.cpp | 1 + src/main.cpp | 21 ++++++++++++ 25 files changed, 89 insertions(+), 9 deletions(-) diff --git a/platformio.ini b/platformio.ini index 080350d..45ccb8b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -63,3 +63,11 @@ extends = base build_flags = ${base.build_flags} -DCROSSPOINT_VERSION=\"${crosspoint.version}\" + +[env:debug_memory] +extends = base +build_flags = + ${base.build_flags} + -DCROSSPOINT_VERSION=\"${crosspoint.version}-dev\" + -DDEBUG_MEMORY=1 + -DCORE_DEBUG_LEVEL=4 diff --git a/src/activities/browser/OpdsBookBrowserActivity.cpp b/src/activities/browser/OpdsBookBrowserActivity.cpp index 555cba9..51a3bc6 100644 --- a/src/activities/browser/OpdsBookBrowserActivity.cpp +++ b/src/activities/browser/OpdsBookBrowserActivity.cpp @@ -60,6 +60,7 @@ void OpdsBookBrowserActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/dictionary/DictionaryMenuActivity.cpp b/src/activities/dictionary/DictionaryMenuActivity.cpp index ddda380..943f3c6 100644 --- a/src/activities/dictionary/DictionaryMenuActivity.cpp +++ b/src/activities/dictionary/DictionaryMenuActivity.cpp @@ -45,6 +45,7 @@ void DictionaryMenuActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/dictionary/DictionaryResultActivity.cpp b/src/activities/dictionary/DictionaryResultActivity.cpp index 46be775..365c009 100644 --- a/src/activities/dictionary/DictionaryResultActivity.cpp +++ b/src/activities/dictionary/DictionaryResultActivity.cpp @@ -40,6 +40,7 @@ void DictionaryResultActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/dictionary/DictionarySearchActivity.cpp b/src/activities/dictionary/DictionarySearchActivity.cpp index c2f1e1e..8fe6b5d 100644 --- a/src/activities/dictionary/DictionarySearchActivity.cpp +++ b/src/activities/dictionary/DictionarySearchActivity.cpp @@ -88,6 +88,7 @@ void DictionarySearchActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/dictionary/EpubWordSelectionActivity.cpp b/src/activities/dictionary/EpubWordSelectionActivity.cpp index 61dc279..d9c233d 100644 --- a/src/activities/dictionary/EpubWordSelectionActivity.cpp +++ b/src/activities/dictionary/EpubWordSelectionActivity.cpp @@ -42,6 +42,7 @@ void EpubWordSelectionActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index ce873f8..e7c9275 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -39,6 +39,10 @@ int HomeActivity::getMenuItemCount() const { void HomeActivity::onEnter() { Activity::onEnter(); + Serial.printf("[%lu] [HOME] [MEM] onEnter - Free: %d, Largest: %d, CoverBuffer: %s (%d bytes)\n", millis(), + ESP.getFreeHeap(), ESP.getMaxAllocHeap(), coverBufferStored ? "allocated" : "null", + coverBufferStored ? static_cast(GfxRenderer::getBufferSize()) : 0); + renderingMutex = xSemaphoreCreateMutex(); // Check if we have a book to continue reading @@ -153,17 +157,24 @@ void HomeActivity::onEnter() { void HomeActivity::onExit() { Activity::onExit(); + Serial.printf("[%lu] [HOME] [MEM] onExit start - Free: %d, Largest: %d, CoverBuffer: %s\n", millis(), + ESP.getFreeHeap(), ESP.getMaxAllocHeap(), coverBufferStored ? "allocated" : "null"); + // Wait until not rendering to delete task to avoid killing mid-instruction to EPD xSemaphoreTake(renderingMutex, portMAX_DELAY); if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; // NOTE: Do NOT free cover buffer here - keep it cached for fast re-entry // The buffer will be freed/replaced when the book changes + + Serial.printf("[%lu] [HOME] [MEM] onExit end - Free: %d, Largest: %d\n", millis(), ESP.getFreeHeap(), + ESP.getMaxAllocHeap()); } bool HomeActivity::storeCoverBuffer() { @@ -202,8 +213,11 @@ bool HomeActivity::restoreCoverBuffer() { void HomeActivity::freeCoverBuffer() { if (coverBuffer) { + Serial.printf("[%lu] [HOME] [MEM] Freeing cover buffer (%d bytes), heap before: %d\n", millis(), + static_cast(GfxRenderer::getBufferSize()), ESP.getFreeHeap()); free(coverBuffer); coverBuffer = nullptr; + Serial.printf("[%lu] [HOME] [MEM] Cover buffer freed, heap after: %d\n", millis(), ESP.getFreeHeap()); } coverBufferStored = false; } diff --git a/src/activities/home/ListViewActivity.cpp b/src/activities/home/ListViewActivity.cpp index 18079f7..63903b5 100644 --- a/src/activities/home/ListViewActivity.cpp +++ b/src/activities/home/ListViewActivity.cpp @@ -96,6 +96,7 @@ void ListViewActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/home/MyLibraryActivity.cpp b/src/activities/home/MyLibraryActivity.cpp index 283dfbd..513d15b 100644 --- a/src/activities/home/MyLibraryActivity.cpp +++ b/src/activities/home/MyLibraryActivity.cpp @@ -188,6 +188,7 @@ void MyLibraryActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/network/CalibreWirelessActivity.cpp b/src/activities/network/CalibreWirelessActivity.cpp index 0ad9094..8646897 100644 --- a/src/activities/network/CalibreWirelessActivity.cpp +++ b/src/activities/network/CalibreWirelessActivity.cpp @@ -82,6 +82,7 @@ void CalibreWirelessActivity::onExit() { if (networkTaskHandle) { vTaskDelete(networkTaskHandle); networkTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } xSemaphoreGive(stateMutex); @@ -90,6 +91,7 @@ void CalibreWirelessActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/network/CrossPointWebServerActivity.cpp b/src/activities/network/CrossPointWebServerActivity.cpp index c8c7d0f..c5ca6a6 100644 --- a/src/activities/network/CrossPointWebServerActivity.cpp +++ b/src/activities/network/CrossPointWebServerActivity.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -94,15 +95,18 @@ void CrossPointWebServerActivity::onEnter() { void CrossPointWebServerActivity::onExit() { ActivityWithSubactivity::onExit(); - Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit start: %d bytes\n", millis(), ESP.getFreeHeap()); + Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit start: %d, Largest: %d\n", millis(), ESP.getFreeHeap(), + ESP.getMaxAllocHeap()); state = WebServerActivityState::SHUTTING_DOWN; // Stop the web server first (before disconnecting WiFi) stopWebServer(); + Serial.printf("[%lu] [WEBACT] [MEM] After stopWebServer: %d bytes\n", millis(), ESP.getFreeHeap()); // Stop mDNS MDNS.end(); + Serial.printf("[%lu] [WEBACT] [MEM] After MDNS.end: %d bytes\n", millis(), ESP.getFreeHeap()); // Stop DNS server if running (AP mode) if (dnsServer) { @@ -110,6 +114,7 @@ void CrossPointWebServerActivity::onExit() { dnsServer->stop(); delete dnsServer; dnsServer = nullptr; + Serial.printf("[%lu] [WEBACT] [MEM] After DNS server cleanup: %d bytes\n", millis(), ESP.getFreeHeap()); } // Brief wait for LWIP stack to flush pending packets @@ -120,16 +125,24 @@ void CrossPointWebServerActivity::onExit() { Serial.printf("[%lu] [WEBACT] Stopping WiFi AP...\n", millis()); WiFi.softAPdisconnect(true); } else { - Serial.printf("[%lu] [WEBACT] Disconnecting WiFi (graceful)...\n", millis()); - WiFi.disconnect(false); // false = don't erase credentials, send disconnect frame + Serial.printf("[%lu] [WEBACT] Disconnecting WiFi...\n", millis()); + WiFi.disconnect(false); // false = don't erase credentials, just send disconnect frame } - delay(30); // Allow disconnect frame to be sent + delay(100); // Allow disconnect frame to be sent and event callbacks to complete + Serial.printf("[%lu] [WEBACT] [MEM] After WiFi disconnect: %d bytes\n", millis(), ESP.getFreeHeap()); Serial.printf("[%lu] [WEBACT] Setting WiFi mode OFF...\n", millis()); WiFi.mode(WIFI_OFF); - delay(30); // Allow WiFi hardware to power down + delay(50); + Serial.printf("[%lu] [WEBACT] [MEM] After WiFi.mode(OFF): %d bytes\n", millis(), ESP.getFreeHeap()); - Serial.printf("[%lu] [WEBACT] [MEM] Free heap after WiFi disconnect: %d bytes\n", millis(), ESP.getFreeHeap()); + // Aggressive WiFi cleanup: stop the WiFi driver to recover more memory + // WiFi is on-demand for File Transfer only, so full cleanup is safe + Serial.printf("[%lu] [WEBACT] Stopping WiFi driver (esp_wifi_stop)...\n", millis()); + esp_wifi_stop(); + delay(100); // Allow LWIP stack to fully release + Serial.printf("[%lu] [WEBACT] [MEM] After esp_wifi_stop: %d, Largest: %d\n", millis(), ESP.getFreeHeap(), + ESP.getMaxAllocHeap()); // Acquire mutex before deleting task Serial.printf("[%lu] [WEBACT] Acquiring rendering mutex before task deletion...\n", millis()); @@ -140,16 +153,18 @@ void CrossPointWebServerActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; - Serial.printf("[%lu] [WEBACT] Display task deleted\n", millis()); + // Allow idle task to free the task stack + vTaskDelay(10 / portTICK_PERIOD_MS); + Serial.printf("[%lu] [WEBACT] [MEM] After task delete + yield: %d bytes\n", millis(), ESP.getFreeHeap()); } // Delete the mutex Serial.printf("[%lu] [WEBACT] Deleting mutex...\n", millis()); vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; - Serial.printf("[%lu] [WEBACT] Mutex deleted\n", millis()); - Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit end: %d bytes\n", millis(), ESP.getFreeHeap()); + Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit end: %d, Largest: %d, MinFree: %d\n", millis(), + ESP.getFreeHeap(), ESP.getMaxAllocHeap(), ESP.getMinFreeHeap()); } void CrossPointWebServerActivity::onNetworkModeSelected(const NetworkMode mode) { diff --git a/src/activities/network/NetworkModeSelectionActivity.cpp b/src/activities/network/NetworkModeSelectionActivity.cpp index ad05f5b..ca87328 100644 --- a/src/activities/network/NetworkModeSelectionActivity.cpp +++ b/src/activities/network/NetworkModeSelectionActivity.cpp @@ -44,6 +44,7 @@ void NetworkModeSelectionActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/network/WifiSelectionActivity.cpp b/src/activities/network/WifiSelectionActivity.cpp index 07d4441..5565a97 100644 --- a/src/activities/network/WifiSelectionActivity.cpp +++ b/src/activities/network/WifiSelectionActivity.cpp @@ -82,6 +82,7 @@ void WifiSelectionActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack Serial.printf("[%lu] [WIFI] Display task deleted\n", millis()); } diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index b997fe2..630105d 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -192,6 +192,7 @@ void EpubReaderActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp index 0031dc7..a1b68d4 100644 --- a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp +++ b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp @@ -88,6 +88,7 @@ void EpubReaderChapterSelectionActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index aa1175a..db0114e 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -131,6 +131,7 @@ void TxtReaderActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/reader/XtcReaderActivity.cpp b/src/activities/reader/XtcReaderActivity.cpp index ab1a8a6..61b6485 100644 --- a/src/activities/reader/XtcReaderActivity.cpp +++ b/src/activities/reader/XtcReaderActivity.cpp @@ -109,6 +109,7 @@ void XtcReaderActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/reader/XtcReaderChapterSelectionActivity.cpp b/src/activities/reader/XtcReaderChapterSelectionActivity.cpp index b2cfeca..50b2019 100644 --- a/src/activities/reader/XtcReaderChapterSelectionActivity.cpp +++ b/src/activities/reader/XtcReaderChapterSelectionActivity.cpp @@ -69,6 +69,7 @@ void XtcReaderChapterSelectionActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/settings/CalibreSettingsActivity.cpp b/src/activities/settings/CalibreSettingsActivity.cpp index 4f614ff..0c581c9 100644 --- a/src/activities/settings/CalibreSettingsActivity.cpp +++ b/src/activities/settings/CalibreSettingsActivity.cpp @@ -44,6 +44,7 @@ void CalibreSettingsActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/settings/CategorySettingsActivity.cpp b/src/activities/settings/CategorySettingsActivity.cpp index 1341e62..a805885 100644 --- a/src/activities/settings/CategorySettingsActivity.cpp +++ b/src/activities/settings/CategorySettingsActivity.cpp @@ -63,6 +63,7 @@ void CategorySettingsActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/settings/ClearCacheActivity.cpp b/src/activities/settings/ClearCacheActivity.cpp index 1e10c14..4edcb56 100644 --- a/src/activities/settings/ClearCacheActivity.cpp +++ b/src/activities/settings/ClearCacheActivity.cpp @@ -35,6 +35,7 @@ void ClearCacheActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/settings/OtaUpdateActivity.cpp b/src/activities/settings/OtaUpdateActivity.cpp index 0393847..cb1d57b 100644 --- a/src/activities/settings/OtaUpdateActivity.cpp +++ b/src/activities/settings/OtaUpdateActivity.cpp @@ -90,6 +90,7 @@ void OtaUpdateActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 2bf2e63..8abd8a6 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -115,6 +115,7 @@ void SettingsActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/activities/util/KeyboardEntryActivity.cpp b/src/activities/util/KeyboardEntryActivity.cpp index 4ac8dc2..fa3b66a 100644 --- a/src/activities/util/KeyboardEntryActivity.cpp +++ b/src/activities/util/KeyboardEntryActivity.cpp @@ -55,6 +55,7 @@ void KeyboardEntryActivity::onExit() { if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + vTaskDelay(10 / portTICK_PERIOD_MS); // Let idle task free stack } vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; diff --git a/src/main.cpp b/src/main.cpp index addb427..c0b80e1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -114,17 +114,38 @@ EpdFontFamily ui12FontFamily(&ui12RegularFont, &ui12BoldFont); unsigned long t1 = 0; unsigned long t2 = 0; +// Memory debugging helper - logs heap state for tracking leaks +#ifdef DEBUG_MEMORY +void logMemoryState(const char* tag, const char* context) { + Serial.printf("[%lu] [%s] [MEM] %s - Free: %d, Largest: %d, MinFree: %d\n", + millis(), tag, context, + ESP.getFreeHeap(), + ESP.getMaxAllocHeap(), + ESP.getMinFreeHeap()); +} +#else +// No-op when not in debug mode +#define logMemoryState(tag, context) ((void)0) +#endif + void exitActivity() { if (currentActivity) { + logMemoryState("MAIN", "Before onExit"); currentActivity->onExit(); + logMemoryState("MAIN", "After onExit, before delete"); delete currentActivity; currentActivity = nullptr; + // Allow idle task to free FreeRTOS task stacks + delay(50); + logMemoryState("MAIN", "After delete + delay"); } } void enterNewActivity(Activity* activity) { + logMemoryState("MAIN", "Before enterNewActivity"); currentActivity = activity; currentActivity->onEnter(); + logMemoryState("MAIN", "After onEnter"); } // Verify long press on wake-up from deep sleep