From 9c8f884f11f7bb226f05f4793e0761fb4dcd3cc0 Mon Sep 17 00:00:00 2001 From: "Valentin V. Bartenev" Date: Sun, 31 May 2026 02:41:57 +0300 Subject: [PATCH] ESP32: add reset reason to boot event, CLI, and web panel Implement getResetReason() and getResetReasonString() for ESP32Board, covering all esp_reset_reason_t values: power-on, external reset, software reset, panic/exception, interrupt watchdog, task watchdog, generic watchdog, deep sleep wake, brownout, and SDIO reset. Extend the pwrmgt.bootreason CLI command to support ESP32 alongside NRF52. On ESP32 only the reset reason is reported (no shutdown reason). Pass the reset reason as the value field when recording the HISTORY_EVENT_BOOT stats event so the reason is stored in the in-memory ring buffer and persisted to the archive events log. Add bootReasonLabel() to the web panel and update renderEventsSection() to annotate boot events with the human-readable reset reason string, e.g. "boot (panic/exception)", when a non-null value is present. --- examples/simple_repeater/MyMesh.cpp | 2 +- src/helpers/CommonCLI.cpp | 5 ++++- src/helpers/ESP32Board.h | 20 ++++++++++++++++++++ src/helpers/web/WebPanelServer.cpp | 21 +++++++++++++++++---- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index d0b4d3c8..234d471e 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1331,7 +1331,7 @@ void MyMesh::begin(FILESYSTEM *fs, ArchiveStorage* archive) { next_archive_neighbours_flush_ms = millis() + kArchiveNeighboursFlushIntervalMs; } if (web.isWebStatsEnabled()) { - recordStatsEvent(HISTORY_EVENT_BOOT); + recordStatsEvent(HISTORY_EVENT_BOOT, board.getResetReason()); if (_archive != nullptr) { recordStatsEvent(_archive->isMounted() ? HISTORY_EVENT_ARCHIVE_MOUNTED : HISTORY_EVENT_ARCHIVE_UNAVAILABLE); } diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index e7b8a5a0..84e8e06c 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -898,10 +898,13 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep strcpy(reply, "ERROR: Power management not supported"); #endif } else if (memcmp(config, "pwrmgt.bootreason", 17) == 0) { -#ifdef NRF52_POWER_MANAGEMENT +#if defined(NRF52_POWER_MANAGEMENT) sprintf(reply, "> Reset: %s; Shutdown: %s", _board->getResetReasonString(_board->getResetReason()), _board->getShutdownReasonString(_board->getShutdownReason())); +#elif defined(ESP_PLATFORM) + sprintf(reply, "> Reset: %s", + _board->getResetReasonString(_board->getResetReason())); #else strcpy(reply, "ERROR: Power management not supported"); #endif diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index ba906f55..f7643d6f 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -84,6 +84,26 @@ public: uint8_t getStartupReason() const override { return startup_reason; } + uint32_t getResetReason() const override { + return (uint32_t)esp_reset_reason(); + } + + const char* getResetReasonString(uint32_t reason) override { + switch ((esp_reset_reason_t)reason) { + case ESP_RST_POWERON: return "Power On"; + case ESP_RST_EXT: return "External Reset"; + case ESP_RST_SW: return "Software Reset"; + case ESP_RST_PANIC: return "Panic/Exception"; + case ESP_RST_INT_WDT: return "Interrupt Watchdog"; + case ESP_RST_TASK_WDT: return "Task Watchdog"; + case ESP_RST_WDT: return "Watchdog"; + case ESP_RST_DEEPSLEEP: return "Deep Sleep Wake"; + case ESP_RST_BROWNOUT: return "Brownout"; + case ESP_RST_SDIO: return "SDIO Reset"; + default: return "Unknown"; + } + } + #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on diff --git a/src/helpers/web/WebPanelServer.cpp b/src/helpers/web/WebPanelServer.cpp index 961c8206..b0b5085d 100644 --- a/src/helpers/web/WebPanelServer.cpp +++ b/src/helpers/web/WebPanelServer.cpp @@ -1627,6 +1627,14 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML( ${renderMetricList(metrics, "metric-grid-4")} `; } + function bootReasonLabel(value) { + const ESP_RESET_REASONS = { + 1: "power on", 2: "external reset", 3: "software reset", + 4: "panic/exception", 5: "interrupt watchdog", 6: "task watchdog", + 7: "watchdog", 8: "deep sleep wake", 9: "brownout", 10: "SDIO reset" + }; + return ESP_RESET_REASONS[value] || ("reason " + value); + } function renderEventsSection(events) { if (!Array.isArray(events) || !events.length) { return `
@@ -1634,10 +1642,15 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
No recent events
`; } - const rows = events.map((event) => ` - ${escapeHtml(event.type || "event")} - ${escapeHtml(formatDuration(event.t || 0))} - `).join(""); + const rows = events.map((event) => { + const label = event.type === "boot" && event.value != null + ? `boot (${bootReasonLabel(event.value)})` + : (event.type || "event"); + return ` + ${escapeHtml(label)} + ${escapeHtml(formatDuration(event.t || 0))} + `; + }).join(""); return `

Events