Implement memory monitoring and queue size reporting in MQTTBridge and CommonCLI

- Added `getQueueSize` method to `MyMesh` and `CommonCLI` for better queue management.
- Introduced memory logging functionality in `MQTTBridge` to monitor heap usage and detect potential memory leaks.
- Adjusted maximum queue size in `MQTTBridge` from 50 to 10 for improved resource management.
- Enhanced command handling in `CommonCLI` to report memory status upon request.
This commit is contained in:
agessaman committed 2026-01-02 13:36:41 -08:00
1 parent 1219668fb1
commit 2b0475998a
5 files changed
+111 -24

No files matched your search

+4
View File
@@ -314,6 +314,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
} else {
strcpy(reply, "ERR: clock cannot go backwards");
}
} else if (memcmp(command, "memory", 6) == 0) {
sprintf(reply, "Free: %d, Min: %d, Max: %d, Queue: %d",
ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap(),
_callbacks->getQueueSize());
} else if (memcmp(command, "start ota", 9) == 0) {
if (!_board->startOTAUpdate(_prefs->node_name, reply)) {
strcpy(reply, "Error");
+86 -21
View File
@@ -315,6 +315,39 @@ void MQTTBridge::loop() {
publishStatus();
_last_status_publish = millis();
}
// Memory monitoring (every 5 minutes)
static unsigned long last_memory_log = 0;
if (millis() - last_memory_log > 300000) { // 5 minutes
logMemoryStatus();
last_memory_log = millis();
}
// Critical memory check (every 15 minutes)
static unsigned long last_critical_check = 0;
if (millis() - last_critical_check > 900000) { // 15 minutes
if (ESP.getMaxAllocHeap() < 60000) { // Less than 60KB max alloc
MQTT_DEBUG_PRINTLN("WARNING: Max alloc heap below 60KB - potential memory leak detected!");
MQTT_DEBUG_PRINTLN("Free: %d, Min: %d, Max: %d", ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap());
// Attempt memory defragmentation by forcing garbage collection
MQTT_DEBUG_PRINTLN("Attempting memory defragmentation...");
// Force a small allocation and immediate free to trigger defrag
void* temp = malloc(1024);
if (temp) {
free(temp);
MQTT_DEBUG_PRINTLN("Defragmentation complete. New Max Alloc: %d", ESP.getMaxAllocHeap());
}
}
// Critical threshold check
if (ESP.getMaxAllocHeap() < 40000) { // Less than 40KB max alloc
MQTT_DEBUG_PRINTLN("CRITICAL: Max alloc heap below 40KB - severe memory leak!");
MQTT_DEBUG_PRINTLN("Free: %d, Min: %d, Max: %d", ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap());
}
last_critical_check = millis();
}
}
void MQTTBridge::onPacketReceived(mesh::Packet *packet) {
@@ -459,6 +492,12 @@ void MQTTBridge::processPacketQueue() {
publishRaw(queued.packet);
}
// Free packet memory before removing from queue
if (queued.packet) {
_mgr->free(queued.packet);
queued.packet = nullptr;
}
// Remove from queue
dequeuePacket();
processed++;
@@ -533,7 +572,7 @@ void MQTTBridge::publishStatus() {
void MQTTBridge::publishPacket(mesh::Packet* packet, bool is_tx) {
if (!packet) return;
char json_buffer[1024];
char json_buffer[512];
char origin_id[65];
// Use actual device ID
@@ -1057,26 +1096,41 @@ void MQTTBridge::syncTimeWithNTP() {
MQTT_DEBUG_PRINTLN("Time synced: %lu", epochTime);
// Set timezone from string (with DST support)
MQTT_DEBUG_PRINTLN("Setting timezone: %s", _prefs->timezone_string);
// Clean up old timezone object to prevent memory leak
if (_timezone) {
delete _timezone;
_timezone = nullptr;
}
// Create timezone object based on timezone string
Timezone* tz = createTimezoneFromString(_prefs->timezone_string);
if (tz) {
MQTT_DEBUG_PRINTLN("Timezone created successfully");
// Store timezone for later use in message building
_timezone = tz;
} else {
MQTT_DEBUG_PRINTLN("Failed to create timezone, using UTC");
// Create UTC timezone as fallback
TimeChangeRule utc = {"UTC", Last, Sun, Mar, 0, 0};
_timezone = new Timezone(utc, utc);
// Set timezone from string (with DST support) - only if changed
static char last_timezone[64] = "";
if (strcmp(_prefs->timezone_string, last_timezone) != 0) {
MQTT_DEBUG_PRINTLN("Setting timezone: %s", _prefs->timezone_string);
// Clean up old timezone object to prevent memory leak
if (_timezone) {
delete _timezone;
_timezone = nullptr;
}
// Create timezone object based on timezone string
Timezone* tz = createTimezoneFromString(_prefs->timezone_string);
if (tz) {
MQTT_DEBUG_PRINTLN("Timezone created successfully");
// Store timezone for later use in message building
_timezone = tz;
} else {
MQTT_DEBUG_PRINTLN("Failed to create timezone, using UTC");
// Create UTC timezone as fallback
TimeChangeRule utc = {"UTC", Last, Sun, Mar, 0, 0};
_timezone = new Timezone(utc, utc);
}
// Remember this timezone string
strncpy(last_timezone, _prefs->timezone_string, sizeof(last_timezone) - 1);
last_timezone[sizeof(last_timezone) - 1] = '\0';
// Force memory defragmentation after timezone recreation
MQTT_DEBUG_PRINTLN("Forcing memory defragmentation after timezone change");
void* temp = malloc(1024);
if (temp) {
free(temp);
MQTT_DEBUG_PRINTLN("Defragmentation complete. Max Alloc: %d", ESP.getMaxAllocHeap());
}
}
// Show current time in both UTC and local
@@ -1227,4 +1281,15 @@ Timezone* MQTTBridge::createTimezoneFromString(const char* tz_string) {
}
}
void MQTTBridge::logMemoryStatus() {
MQTT_DEBUG_PRINTLN("=== Memory Status ===");
MQTT_DEBUG_PRINTLN("Free heap: %d bytes", ESP.getFreeHeap());
MQTT_DEBUG_PRINTLN("Min free heap: %d bytes", ESP.getMinFreeHeap());
MQTT_DEBUG_PRINTLN("Max alloc heap: %d bytes", ESP.getMaxAllocHeap());
MQTT_DEBUG_PRINTLN("Heap size: %d bytes", ESP.getHeapSize());
MQTT_DEBUG_PRINTLN("Free PSRAM: %d bytes", ESP.getFreePsram());
MQTT_DEBUG_PRINTLN("Queue size: %d/%d packets", _queue_count, MAX_QUEUE_SIZE);
MQTT_DEBUG_PRINTLN("===================");
}
#endif
+9 -3
View File
@@ -86,7 +86,7 @@ private:
bool is_tx;
};
static const int MAX_QUEUE_SIZE = 50;
static const int MAX_QUEUE_SIZE = 10;
QueuedPacket _packet_queue[MAX_QUEUE_SIZE];
int _queue_head;
int _queue_tail;
@@ -111,8 +111,8 @@ private:
// Let's Mesh Analyzer support
bool _analyzer_us_enabled;
bool _analyzer_eu_enabled;
char _auth_token_us[1024]; // JWT token for US server authentication
char _auth_token_eu[1024]; // JWT token for EU server authentication
char _auth_token_us[512]; // JWT token for US server authentication
char _auth_token_eu[512]; // JWT token for EU server authentication
char _analyzer_username[70]; // Username in format v1_{UPPERCASE_PUBLIC_KEY}
// Device identity for JWT token creation
@@ -312,6 +312,12 @@ public:
* @return Number of queued packets
*/
int getQueueSize() const;
private:
/**
* Log memory status for debugging
*/
void logMemoryStatus();
};
#endif
+6
View File
@@ -51,6 +51,9 @@ build_flags =
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_repeater>
-<helpers/bridges/MQTTBridge.cpp>
-<helpers/MQTTMessageBuilder.cpp>
-<helpers/JWTHelper.cpp>
lib_deps =
${Heltec_lora32_v3.lib_deps}
${esp32_ota.lib_deps}
@@ -275,6 +278,9 @@ build_flags =
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<../examples/simple_repeater>
-<helpers/bridges/MQTTBridge.cpp>
-<helpers/MQTTMessageBuilder.cpp>
-<helpers/JWTHelper.cpp>
lib_deps =
${Heltec_lora32_v3.lib_deps}
${esp32_ota.lib_deps}
+6
View File
@@ -54,6 +54,9 @@ build_flags =
; -D MESH_DEBUG=1
build_src_filter = ${Station_G2.build_src_filter}
+<../examples/simple_repeater>
-<helpers/bridges/MQTTBridge.cpp>
-<helpers/MQTTMessageBuilder.cpp>
-<helpers/JWTHelper.cpp>
lib_deps =
${Station_G2.lib_deps}
${esp32_ota.lib_deps}
@@ -115,6 +118,9 @@ build_flags =
; -D MESH_DEBUG=1
build_src_filter = ${Station_G2.build_src_filter}
+<../examples/simple_repeater>
-<helpers/bridges/MQTTBridge.cpp>
-<helpers/MQTTMessageBuilder.cpp>
-<helpers/JWTHelper.cpp>
lib_deps =
${Station_G2.lib_deps}
${esp32_ota.lib_deps}