From 662f14e10ba7355054aed80c8b7eb0a9075d7126 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 12 Mar 2026 12:16:16 -1000 Subject: [PATCH] [debug] Implement reset reason for RP2040/RP2350 The debug component's reset reason text sensor was returning an empty string on RP2040/RP2350 platforms. Read the chip reset registers to report the actual reset source. RP2040: reads VREG_AND_CHIP_RESET for POR, RUN pin, and debug port. RP2350: reads POWMAN chip_reset for POR, brown-out, RUN pin, watchdog variants, glitch detect, debugger, rescue, and core powerdown. Both: checks watchdog_caused_reboot() from the Pico SDK. Also adds buf_append_str() helper to helpers.h for efficient plain string appends without format parsing overhead. --- esphome/components/debug/debug_rp2040.cpp | 65 ++++++++++++++++++++++- esphome/core/helpers.h | 22 ++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/esphome/components/debug/debug_rp2040.cpp b/esphome/components/debug/debug_rp2040.cpp index c9d41942dbc..24757b8810e 100644 --- a/esphome/components/debug/debug_rp2040.cpp +++ b/esphome/components/debug/debug_rp2040.cpp @@ -2,12 +2,75 @@ #ifdef USE_RP2040 #include "esphome/core/log.h" #include +#include +#if defined(PICO_RP2350) +#include +#else +#include +#endif namespace esphome { namespace debug { static const char *const TAG = "debug"; -const char *DebugComponent::get_reset_reason_(std::span buffer) { return ""; } +const char *DebugComponent::get_reset_reason_(std::span buffer) { + char *buf = buffer.data(); + const size_t size = RESET_REASON_BUFFER_SIZE; + size_t pos = 0; + +#if defined(PICO_RP2350) + uint32_t chip_reset = powman_hw->chip_reset; + if (chip_reset & 0x10000000) // HAD_WATCHDOG_RESET_RSM + pos = buf_append_str(buf, size, pos, "Watchdog (RSM)|"); + if (chip_reset & 0x08000000) // HAD_HZD_SYS_RESET_REQ + pos = buf_append_str(buf, size, pos, "Hazard debugger reset|"); + if (chip_reset & 0x04000000) // HAD_GLITCH_DETECT + pos = buf_append_str(buf, size, pos, "Power supply glitch|"); + if (chip_reset & 0x02000000) // HAD_SWCORE_PD + pos = buf_append_str(buf, size, pos, "Switched core powerdown|"); + if (chip_reset & 0x01000000) // HAD_WATCHDOG_RESET_SWCORE + pos = buf_append_str(buf, size, pos, "Watchdog (SWCORE)|"); + if (chip_reset & 0x00800000) // HAD_WATCHDOG_RESET_POWMAN + pos = buf_append_str(buf, size, pos, "Watchdog (POWMAN)|"); + if (chip_reset & 0x00400000) // HAD_WATCHDOG_RESET_POWMAN_ASYNC + pos = buf_append_str(buf, size, pos, "Watchdog (POWMAN async)|"); + if (chip_reset & 0x00200000) // HAD_RESCUE + pos = buf_append_str(buf, size, pos, "Rescue reset|"); + if (chip_reset & 0x00080000) // HAD_DP_RESET_REQ + pos = buf_append_str(buf, size, pos, "Debugger reset|"); + if (chip_reset & 0x00040000) // HAD_RUN_LOW + pos = buf_append_str(buf, size, pos, "RUN pin|"); + if (chip_reset & 0x00020000) // HAD_BOR + pos = buf_append_str(buf, size, pos, "Brown-out|"); + if (chip_reset & 0x00010000) // HAD_POR + pos = buf_append_str(buf, size, pos, "Power-on reset|"); +#else + uint32_t chip_reset = vreg_and_chip_reset_hw->chip_reset; + if (chip_reset & 0x00100000) // HAD_PSM_RESTART + pos = buf_append_str(buf, size, pos, "Debug port restart|"); + if (chip_reset & 0x00010000) // HAD_RUN + pos = buf_append_str(buf, size, pos, "RUN pin|"); + if (chip_reset & 0x00000100) // HAD_POR + pos = buf_append_str(buf, size, pos, "Power-on reset|"); +#endif + + if (watchdog_caused_reboot()) { + if (watchdog_enable_caused_reboot()) { + pos = buf_append_str(buf, size, pos, "Watchdog timeout|"); + } else { + pos = buf_append_str(buf, size, pos, "Watchdog reboot|"); + } + } + + // Remove trailing '|' + if (pos > 0 && buf[pos - 1] == '|') { + buf[pos - 1] = '\0'; + } else if (pos == 0) { + return "Unknown"; + } + + return buf; +} const char *DebugComponent::get_wakeup_cause_(std::span buffer) { return ""; } diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 70ac1574f0c..d28d53be023 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -942,6 +942,28 @@ __attribute__((format(printf, 4, 5))) inline size_t buf_append_printf(char *buf, } #endif +/// Safely append a string to buffer without format parsing, returning new position (capped at size). +/// More efficient than buf_append_printf for plain string literals. +/// @param buf Output buffer +/// @param size Total buffer size +/// @param pos Current position in buffer +/// @param str String to append +/// @return New position after appending (capped at size on overflow) +inline size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str) { + if (pos >= size) { + return size; + } + size_t remaining = size - pos - 1; // reserve space for null terminator + size_t len = strlen(str); + if (len > remaining) { + len = remaining; + } + memcpy(buf + pos, str, len); + pos += len; + buf[pos] = '\0'; + return pos; +} + /// Concatenate a name with a separator and suffix using an efficient stack-based approach. /// This avoids multiple heap allocations during string construction. /// Maximum name length supported is 120 characters for friendly names.