From e7741c20e4dd5b3345eace8e6604ed1e93bb3c28 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 9 Sep 2025 10:29:07 -0500 Subject: [PATCH 1/8] Add LOG_HEAP log type, and more heap debug messages (#7937) --- src/DebugConfiguration.h | 7 +++++++ src/Power.cpp | 6 +++--- src/concurrency/OSThread.cpp | 4 ++-- src/mesh/MemoryPool.h | 2 ++ src/mesh/MeshService.cpp | 13 +++++++++++-- src/mesh/ReliableRouter.cpp | 6 +++++- src/mesh/Router.cpp | 8 ++++++++ src/modules/NodeInfoModule.cpp | 4 ++++ src/modules/Telemetry/DeviceTelemetry.cpp | 5 +++++ 9 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h index a34710eb0..26f2db1f4 100644 --- a/src/DebugConfiguration.h +++ b/src/DebugConfiguration.h @@ -23,6 +23,7 @@ #define MESHTASTIC_LOG_LEVEL_ERROR "ERROR" #define MESHTASTIC_LOG_LEVEL_CRIT "CRIT " #define MESHTASTIC_LOG_LEVEL_TRACE "TRACE" +#define MESHTASTIC_LOG_LEVEL_HEAP "HEAP" #include "SerialConsole.h" @@ -62,6 +63,12 @@ #endif #endif +#if defined(DEBUG_HEAP) +#define LOG_HEAP(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_HEAP, __VA_ARGS__) +#else +#define LOG_HEAP(...) +#endif + /// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic extern "C" void logLegacy(const char *level, const char *fmt, ...); diff --git a/src/Power.cpp b/src/Power.cpp index 06c6a9089..7de82b8d6 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -851,9 +851,9 @@ void Power::readPowerStatus() running++; } } - LOG_DEBUG(threadlist); - LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(), - memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false)); + LOG_HEAP(threadlist); + LOG_HEAP("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(), + memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false)); lastheap = memGet.getFreeHeap(); } #ifdef DEBUG_HEAP_MQTT diff --git a/src/concurrency/OSThread.cpp b/src/concurrency/OSThread.cpp index d9bb901b2..5aee03bbf 100644 --- a/src/concurrency/OSThread.cpp +++ b/src/concurrency/OSThread.cpp @@ -86,9 +86,9 @@ void OSThread::run() #ifdef DEBUG_HEAP auto newHeap = memGet.getFreeHeap(); if (newHeap < heap) - LOG_DEBUG("------ Thread %s leaked heap %d -> %d (%d) ------", ThreadName.c_str(), heap, newHeap, newHeap - heap); + LOG_HEAP("------ Thread %s leaked heap %d -> %d (%d) ------", ThreadName.c_str(), heap, newHeap, newHeap - heap); if (heap < newHeap) - LOG_DEBUG("++++++ Thread %s freed heap %d -> %d (%d) ++++++", ThreadName.c_str(), heap, newHeap, newHeap - heap); + LOG_HEAP("++++++ Thread %s freed heap %d -> %d (%d) ++++++", ThreadName.c_str(), heap, newHeap, newHeap - heap); #endif runned(); diff --git a/src/mesh/MemoryPool.h b/src/mesh/MemoryPool.h index c4af3c4ac..ea7c8f583 100644 --- a/src/mesh/MemoryPool.h +++ b/src/mesh/MemoryPool.h @@ -84,6 +84,8 @@ template class MemoryDynamic : public Allocator virtual void release(T *p) override { assert(p); + LOG_HEAP("Freeing 0x%x", p); + free(p); } diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 7e35fccd8..157a2eda3 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -193,8 +193,12 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p) // (so we update our nodedb for the local node) // Send the packet into the mesh + auto heapBefore = memGet.getFreeHeap(); + auto a = packetPool.allocCopy(p); + auto heapAfter = memGet.getFreeHeap(); + LOG_HEAP("Alloc in MeshService::handleToRadio() pointer 0x%x, size: %u, free: %u", a, heapBefore - heapAfter, heapAfter); - sendToMesh(packetPool.allocCopy(p), RX_SRC_USER); + sendToMesh(a, RX_SRC_USER); bool loopback = false; // if true send any packet the phone sends back itself (for testing) if (loopback) { @@ -250,7 +254,12 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh } if ((res == ERRNO_OK || res == ERRNO_SHOULD_RELEASE) && ccToPhone) { // Check if p is not released in case it couldn't be sent - sendToPhone(packetPool.allocCopy(*p)); + auto heapBefore = memGet.getFreeHeap(); + auto a = packetPool.allocCopy(*p); + auto heapAfter = memGet.getFreeHeap(); + LOG_HEAP("Alloc in MeshService::sendToMesh() pointer 0x%x, size: %u, free: %u", a, heapBefore - heapAfter, heapAfter); + + sendToPhone(a); } // Router may ask us to release the packet if it wasn't sent diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index e9ceeaef1..890d42b00 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -2,6 +2,7 @@ #include "Default.h" #include "MeshTypes.h" #include "configuration.h" +#include "memGet.h" #include "mesh-pb-constants.h" #include "modules/NodeInfoModule.h" #include "modules/RoutingModule.h" @@ -21,8 +22,11 @@ ErrorCode ReliableRouter::send(meshtastic_MeshPacket *p) if (p->hop_limit == 0) { p->hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit); } - + auto heapBefore = memGet.getFreeHeap(); auto copy = packetPool.allocCopy(*p); + auto heapAfter = memGet.getFreeHeap(); + LOG_HEAP("Alloc in ReliableRouter::send() pointer 0x%x, size: %u, free: %u", copy, heapBefore - heapAfter, heapAfter); + startRetransmission(copy, NUM_RELIABLE_RETX); } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index c7e32c4a1..603dfda4a 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -275,7 +275,12 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) // If the packet is not yet encrypted, do so now if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it + + auto heapBefore = memGet.getFreeHeap(); meshtastic_MeshPacket *p_decoded = packetPool.allocCopy(*p); + auto heapAfter = memGet.getFreeHeap(); + + LOG_HEAP("Alloc in Router::send pointer 0x%x, size: %u, free: %u", p_decoded, heapBefore - heapAfter, heapAfter); auto encodeResult = perhapsEncode(p); if (encodeResult != meshtastic_Routing_Error_NONE) { @@ -608,7 +613,10 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) // Also, we should set the time from the ISR and it should have msec level resolution p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone // Store a copy of encrypted packet for MQTT + auto heapBefore = memGet.getFreeHeap(); meshtastic_MeshPacket *p_encrypted = packetPool.allocCopy(*p); + auto heapAfter = memGet.getFreeHeap(); + LOG_HEAP("Alloc in Router::handleReceived pointer 0x%x, size: %u, free: %u", p_encrypted, heapBefore - heapAfter, heapAfter); // Take those raw bytes and convert them back into a well structured protobuf we can understand auto decodedState = perhapsDecode(p); diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index 0060e99fa..82632f667 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -44,7 +44,11 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal) service->cancelSending(prevPacketId); shorterTimeout = _shorterTimeout; + auto heapBefore = memGet.getFreeHeap(); meshtastic_MeshPacket *p = allocReply(); + auto heapAfter = memGet.getFreeHeap(); + + LOG_HEAP("Alloc in NodeInfoModule::sendOurNodeInfo pointer 0x%x, size: %u, free: %u", p, heapBefore - heapAfter, heapAfter); if (p) { // Check whether we didn't ignore it p->to = dest; p->decoded.want_response = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER && diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 08fd09db0..8694de993 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -172,7 +172,12 @@ bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) telemetry.variant.device_metrics.battery_level, telemetry.variant.device_metrics.voltage, telemetry.variant.device_metrics.uptime_seconds); + auto heapBefore = memGet.getFreeHeap(); meshtastic_MeshPacket *p = allocDataProtobuf(telemetry); + auto heapAfter = memGet.getFreeHeap(); + LOG_HEAP("Alloc in DeviceTelemetryModule::sendTelemetry() pointer 0x%x, size: %u, free: %u", p, heapBefore - heapAfter, + heapAfter); + p->to = dest; p->decoded.want_response = false; p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; From 31fdb369874f30306fbb76f720884359eac1c5c4 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 9 Sep 2025 10:46:33 -0500 Subject: [PATCH 2/8] Detection sensor add module only when enabled --- src/modules/Modules.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 85d183aef..0b051687d 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -138,7 +138,9 @@ void setupModules() neighborInfoModule = new NeighborInfoModule(); #endif #if !MESHTASTIC_EXCLUDE_DETECTIONSENSOR - detectionSensorModule = new DetectionSensorModule(); + if (moduleConfig.has_detection_sensor && moduleConfig.detection_sensor.enabled) { + detectionSensorModule = new DetectionSensorModule(); + } #endif #if !MESHTASTIC_EXCLUDE_ATAK if (IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TAK, From 0cd860e30080e724d5207cfcc1d04bfdb1eb5195 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 9 Sep 2025 10:53:18 -0500 Subject: [PATCH 3/8] RangeTest must be enabled --- src/modules/Modules.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 0b051687d..6b3f44bed 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -276,7 +276,8 @@ void setupModules() externalNotificationModule = new ExternalNotificationModule(); #endif #if !MESHTASTIC_EXCLUDE_RANGETEST && !MESHTASTIC_EXCLUDE_GPS - new RangeTestModule(); + if (moduleConfig.has_range_test && moduleConfig.range_test.enabled) + new RangeTestModule(); #endif } else { #if !MESHTASTIC_EXCLUDE_ADMIN From f267b5f5f77c1911e7fd99af5cd9db0d814f3c11 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 9 Sep 2025 11:15:55 -0500 Subject: [PATCH 4/8] Exclude trackball if we aren't a trackball device --- src/modules/Modules.cpp | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 6b3f44bed..d4beb6824 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -6,9 +6,12 @@ #include "input/RotaryEncoderImpl.h" #include "input/RotaryEncoderInterruptImpl1.h" #include "input/SerialKeyboardImpl.h" -#include "input/TrackballInterruptImpl1.h" #include "input/UpDownInterruptImpl1.h" #include "modules/SystemCommandsModule.h" +#if HAS_TRACKBALL +#include "input/TrackballInterruptImpl1.h" +#endif + #if !MESHTASTIC_EXCLUDE_I2C #include "input/cardKbI2cImpl.h" #endif @@ -135,7 +138,9 @@ void setupModules() traceRouteModule = new TraceRouteModule(); #endif #if !MESHTASTIC_EXCLUDE_NEIGHBORINFO - neighborInfoModule = new NeighborInfoModule(); + if (moduleConfig.has_neighbor_info && moduleConfig.neighbor_info.enabled) { + neighborInfoModule = new NeighborInfoModule(); + } #endif #if !MESHTASTIC_EXCLUDE_DETECTIONSENSOR if (moduleConfig.has_detection_sensor && moduleConfig.detection_sensor.enabled) { @@ -212,7 +217,7 @@ void setupModules() aLinuxInputImpl->init(); } #endif -#if !MESHTASTIC_EXCLUDE_INPUTBROKER +#if !MESHTASTIC_EXCLUDE_INPUTBROKER && HAS_TRACKBALL if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { trackballInterruptImpl1 = new TrackballInterruptImpl1(); trackballInterruptImpl1->init(TB_DOWN, TB_UP, TB_LEFT, TB_RIGHT, TB_PRESS); @@ -232,11 +237,14 @@ void setupModules() #if HAS_TELEMETRY new DeviceTelemetryModule(); #endif -// TODO: How to improve this? #if HAS_SENSOR && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR - new EnvironmentTelemetryModule(); + if (moduleConfig.has_telemetry && + (moduleConfig.telemetry.environment_measurement_enabled || moduleConfig.telemetry.environment_screen_enabled)) { + new EnvironmentTelemetryModule(); + } #if __has_include("Adafruit_PM25AQI.h") - if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first > 0) { + if (moduleConfig.has_telemetry && moduleConfig.telemetry.air_quality_enabled && + nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first > 0) { new AirQualityTelemetryModule(); } #endif @@ -248,12 +256,16 @@ void setupModules() #endif #endif #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_POWER_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR - new PowerTelemetryModule(); + if (moduleConfig.has_telemetry && + (moduleConfig.telemetry.power_measurement_enabled || moduleConfig.telemetry.power_screen_enabled)) { + new PowerTelemetryModule(); + } #endif #if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_STM32WL)) && \ !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) #if !MESHTASTIC_EXCLUDE_SERIAL - if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { + if (moduleConfig.has_serial && moduleConfig.serial.enabled && + config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { new SerialModule(); } #endif @@ -264,16 +276,22 @@ void setupModules() audioModule = new AudioModule(); #endif #if !MESHTASTIC_EXCLUDE_PAXCOUNTER - paxcounterModule = new PaxcounterModule(); + if (moduleConfig.has_paxcounter && moduleConfig.paxcounter.enabled) { + paxcounterModule = new PaxcounterModule(); + } #endif #endif #if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) #if !MESHTASTIC_EXCLUDE_STOREFORWARD - storeForwardModule = new StoreForwardModule(); + if (moduleConfig.has_store_forward && moduleConfig.store_forward.enabled) { + storeForwardModule = new StoreForwardModule(); + } #endif #endif #if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION - externalNotificationModule = new ExternalNotificationModule(); + if (moduleConfig.has_external_notification && moduleConfig.external_notification.enabled) { + externalNotificationModule = new ExternalNotificationModule(); + } #endif #if !MESHTASTIC_EXCLUDE_RANGETEST && !MESHTASTIC_EXCLUDE_GPS if (moduleConfig.has_range_test && moduleConfig.range_test.enabled) From 108bdf7b0d9ebbb27c7c778d609c45695e5757be Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 9 Sep 2025 19:11:39 -0500 Subject: [PATCH 5/8] Close should set heartbeatReceived = false --- src/mesh/PhoneAPI.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index a3a8a2087..9fb1b589f 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -100,6 +100,7 @@ void PhoneAPI::close() config_nonce = 0; config_state = 0; pauseBluetoothLogging = false; + heartbeatReceived = false; } } From 701028b749f905fdeaba7c35cb8c777b008cc665 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 10 Sep 2025 15:29:50 -0500 Subject: [PATCH 6/8] Unify build epoch to add flag in platformio-custom.py (#7917) * Unify build_epoch replacement logic in platformio-custom * Missed one --- .github/actions/setup-base/action.yml | 5 ----- bin/build-firmware.sh | 2 -- bin/platformio-custom.py | 7 +++++++ platformio.ini | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml index 350ca290c..f6c1fd80c 100644 --- a/.github/actions/setup-base/action.yml +++ b/.github/actions/setup-base/action.yml @@ -11,11 +11,6 @@ runs: ref: ${{github.event.pull_request.head.ref}} repository: ${{github.event.pull_request.head.repo.full_name}} - - name: Uncomment build epoch - shell: bash - run: | - sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini - - name: Install dependencies shell: bash run: | diff --git a/bin/build-firmware.sh b/bin/build-firmware.sh index fdd7caa11..7bd19aaa9 100644 --- a/bin/build-firmware.sh +++ b/bin/build-firmware.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini - export PIP_BREAK_SYSTEM_PACKAGES=1 if (echo $2 | grep -q "esp32"); then diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index fc1b4bc2e..e54d1586f 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -6,6 +6,8 @@ from os.path import join import subprocess import json import re +import time +from datetime import datetime from readprops import readProps @@ -125,11 +127,16 @@ for pref in userPrefs: pref_flags.append("-D" + pref + "=" + env.StringifyMacro(userPrefs[pref]) + "") # General options that are passed to the C and C++ compilers +# Calculate unix epoch for current day (midnight) +current_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) +build_epoch = int(current_date.timestamp()) + flags = [ "-DAPP_VERSION=" + verObj["long"], "-DAPP_VERSION_SHORT=" + verObj["short"], "-DAPP_ENV=" + env.get("PIOENV"), "-DAPP_REPO=" + repo_owner, + "-DBUILD_EPOCH=" + str(build_epoch), ] + pref_flags print ("Using flags:") diff --git a/platformio.ini b/platformio.ini index 81f95a7e3..e2e5e1a18 100644 --- a/platformio.ini +++ b/platformio.ini @@ -53,7 +53,7 @@ build_flags = -Wno-missing-field-initializers -DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware -DMESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE=1 -D MAX_THREADS=40 ; As we've split modules, we have more threads to manage - #-DBUILD_EPOCH=$UNIX_TIME + #-DBUILD_EPOCH=$UNIX_TIME ; set in platformio-custom.py now #-D OLED_PL=1 monitor_speed = 115200 From abc0eb196a64190c220f4762e19b047a69f68cf4 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Mon, 8 Sep 2025 20:54:08 +1000 Subject: [PATCH 7/8] Fix build error in rak_wismesh_tap_v2 (#7905) In the logs was: "No screen resolution defined in build_flags. Please define DISPLAY_SIZE." set according to similar devices. --- variants/esp32s3/rak_wismesh_tap_v2/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/esp32s3/rak_wismesh_tap_v2/platformio.ini b/variants/esp32s3/rak_wismesh_tap_v2/platformio.ini index 8b86e0217..de4714efa 100644 --- a/variants/esp32s3/rak_wismesh_tap_v2/platformio.ini +++ b/variants/esp32s3/rak_wismesh_tap_v2/platformio.ini @@ -70,6 +70,7 @@ build_flags = ${ft5x06.build_flags} -D LGFX_SCREEN_WIDTH=240 -D LGFX_SCREEN_HEIGHT=320 + -D DISPLAY_SIZE=320x240 ; landscape mode -D LGFX_PANEL=ST7789 -D LGFX_ROTATION=1 -D LGFX_TOUCH_X_MIN=0 From e17c50bb86b66eec3bb8e5fe1574f712ac3b1330 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 11 Sep 2025 07:57:42 -0500 Subject: [PATCH 8/8] Put guards in place around debug heap operations (#7955) * Put guards in place around debug heap operations * Add macros to clean up code * Add pointer as well --- src/DebugConfiguration.h | 19 +++++++++++++++++++ src/memGet.cpp | 12 ++++++++++++ src/mesh/MeshService.cpp | 11 ++++------- src/mesh/ReliableRouter.cpp | 5 ++--- src/mesh/Router.cpp | 12 +++++------- src/modules/NodeInfoModule.cpp | 5 ++--- src/modules/Telemetry/DeviceTelemetry.cpp | 6 ++---- 7 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h index 26f2db1f4..98bbe0f72 100644 --- a/src/DebugConfiguration.h +++ b/src/DebugConfiguration.h @@ -2,6 +2,12 @@ #include "configuration.h" +// Forward declarations +#if defined(DEBUG_HEAP) +class MemGet; +extern MemGet memGet; +#endif + // DEBUG LED #ifndef LED_STATE_ON #define LED_STATE_ON 1 @@ -65,8 +71,21 @@ #if defined(DEBUG_HEAP) #define LOG_HEAP(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_HEAP, __VA_ARGS__) + +// Macro-based heap debugging +#define DEBUG_HEAP_BEFORE auto heapBefore = memGet.getFreeHeap(); +#define DEBUG_HEAP_AFTER(context, ptr) \ + do { \ + auto heapAfter = memGet.getFreeHeap(); \ + if (heapBefore != heapAfter) { \ + LOG_HEAP("Alloc in %s pointer 0x%x, size: %u, free: %u", context, ptr, heapBefore - heapAfter, heapAfter); \ + } \ + } while (0) + #else #define LOG_HEAP(...) +#define DEBUG_HEAP_BEFORE +#define DEBUG_HEAP_AFTER(context, ptr) #endif /// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic diff --git a/src/memGet.cpp b/src/memGet.cpp index e8cd177dd..14e614014 100644 --- a/src/memGet.cpp +++ b/src/memGet.cpp @@ -88,4 +88,16 @@ uint32_t MemGet::getPsramSize() #else return 0; #endif +} + +void displayPercentHeapFree() +{ + uint32_t freeHeap = memGet.getFreeHeap(); + uint32_t totalHeap = memGet.getHeapSize(); + if (totalHeap == 0 || totalHeap == UINT32_MAX) { + LOG_INFO("Heap size unavailable"); + return; + } + int percent = (int)((freeHeap * 100) / totalHeap); + LOG_INFO("Heap free: %d%% (%u/%u bytes)", percent, freeHeap, totalHeap); } \ No newline at end of file diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 157a2eda3..607766ab6 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -193,11 +193,9 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p) // (so we update our nodedb for the local node) // Send the packet into the mesh - auto heapBefore = memGet.getFreeHeap(); + DEBUG_HEAP_BEFORE; auto a = packetPool.allocCopy(p); - auto heapAfter = memGet.getFreeHeap(); - LOG_HEAP("Alloc in MeshService::handleToRadio() pointer 0x%x, size: %u, free: %u", a, heapBefore - heapAfter, heapAfter); - + DEBUG_HEAP_AFTER("MeshService::handleToRadio", a); sendToMesh(a, RX_SRC_USER); bool loopback = false; // if true send any packet the phone sends back itself (for testing) @@ -254,10 +252,9 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh } if ((res == ERRNO_OK || res == ERRNO_SHOULD_RELEASE) && ccToPhone) { // Check if p is not released in case it couldn't be sent - auto heapBefore = memGet.getFreeHeap(); + DEBUG_HEAP_BEFORE; auto a = packetPool.allocCopy(*p); - auto heapAfter = memGet.getFreeHeap(); - LOG_HEAP("Alloc in MeshService::sendToMesh() pointer 0x%x, size: %u, free: %u", a, heapBefore - heapAfter, heapAfter); + DEBUG_HEAP_AFTER("MeshService::sendToMesh", a); sendToPhone(a); } diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 890d42b00..6d098b669 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -22,10 +22,9 @@ ErrorCode ReliableRouter::send(meshtastic_MeshPacket *p) if (p->hop_limit == 0) { p->hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit); } - auto heapBefore = memGet.getFreeHeap(); + DEBUG_HEAP_BEFORE; auto copy = packetPool.allocCopy(*p); - auto heapAfter = memGet.getFreeHeap(); - LOG_HEAP("Alloc in ReliableRouter::send() pointer 0x%x, size: %u, free: %u", copy, heapBefore - heapAfter, heapAfter); + DEBUG_HEAP_AFTER("ReliableRouter::send", copy); startRetransmission(copy, NUM_RELIABLE_RETX); } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 603dfda4a..4442b5d50 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -276,11 +276,9 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it - auto heapBefore = memGet.getFreeHeap(); + DEBUG_HEAP_BEFORE; meshtastic_MeshPacket *p_decoded = packetPool.allocCopy(*p); - auto heapAfter = memGet.getFreeHeap(); - - LOG_HEAP("Alloc in Router::send pointer 0x%x, size: %u, free: %u", p_decoded, heapBefore - heapAfter, heapAfter); + DEBUG_HEAP_AFTER("Router::send", p_decoded); auto encodeResult = perhapsEncode(p); if (encodeResult != meshtastic_Routing_Error_NONE) { @@ -612,11 +610,11 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) bool skipHandle = false; // Also, we should set the time from the ISR and it should have msec level resolution p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone + // Store a copy of encrypted packet for MQTT - auto heapBefore = memGet.getFreeHeap(); + DEBUG_HEAP_BEFORE; meshtastic_MeshPacket *p_encrypted = packetPool.allocCopy(*p); - auto heapAfter = memGet.getFreeHeap(); - LOG_HEAP("Alloc in Router::handleReceived pointer 0x%x, size: %u, free: %u", p_encrypted, heapBefore - heapAfter, heapAfter); + DEBUG_HEAP_AFTER("Router::handleReceived", p_encrypted); // Take those raw bytes and convert them back into a well structured protobuf we can understand auto decodedState = perhapsDecode(p); diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index 82632f667..86ceaae24 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -44,11 +44,10 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal) service->cancelSending(prevPacketId); shorterTimeout = _shorterTimeout; - auto heapBefore = memGet.getFreeHeap(); + DEBUG_HEAP_BEFORE; meshtastic_MeshPacket *p = allocReply(); - auto heapAfter = memGet.getFreeHeap(); + DEBUG_HEAP_AFTER("NodeInfoModule::sendOurNodeInfo", p); - LOG_HEAP("Alloc in NodeInfoModule::sendOurNodeInfo pointer 0x%x, size: %u, free: %u", p, heapBefore - heapAfter, heapAfter); if (p) { // Check whether we didn't ignore it p->to = dest; p->decoded.want_response = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER && diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 8694de993..98d5b19d0 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -172,11 +172,9 @@ bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) telemetry.variant.device_metrics.battery_level, telemetry.variant.device_metrics.voltage, telemetry.variant.device_metrics.uptime_seconds); - auto heapBefore = memGet.getFreeHeap(); + DEBUG_HEAP_BEFORE; meshtastic_MeshPacket *p = allocDataProtobuf(telemetry); - auto heapAfter = memGet.getFreeHeap(); - LOG_HEAP("Alloc in DeviceTelemetryModule::sendTelemetry() pointer 0x%x, size: %u, free: %u", p, heapBefore - heapAfter, - heapAfter); + DEBUG_HEAP_AFTER("DeviceTelemetryModule::sendTelemetry", p); p->to = dest; p->decoded.want_response = false;