diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index f54bf82eae..615d4a18ce 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -28,24 +28,23 @@ void DebugComponent::dump_config() { #endif // defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) #endif // USE_SENSOR - std::string device_info; - device_info.reserve(256); + char device_info_buffer[DEVICE_INFO_BUFFER_SIZE]; ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION); - device_info += ESPHOME_VERSION; + size_t pos = buf_append(device_info_buffer, DEVICE_INFO_BUFFER_SIZE, 0, "%s", ESPHOME_VERSION); this->free_heap_ = get_free_heap_(); ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_); - get_device_info_(device_info); + pos = get_device_info_(std::span(device_info_buffer), pos); #ifdef USE_TEXT_SENSOR if (this->device_info_ != nullptr) { - if (device_info.length() > 255) - device_info.resize(255); - this->device_info_->publish_state(device_info); + this->device_info_->publish_state(std::string(device_info_buffer, pos)); } if (this->reset_reason_ != nullptr) { - this->reset_reason_->publish_state(get_reset_reason_()); + char reset_reason_buffer[RESET_REASON_BUFFER_SIZE]; + this->reset_reason_->publish_state( + get_reset_reason_(std::span(reset_reason_buffer))); } #endif // USE_TEXT_SENSOR diff --git a/esphome/components/debug/debug_component.h b/esphome/components/debug/debug_component.h index 96306f7cdf..5783bc5418 100644 --- a/esphome/components/debug/debug_component.h +++ b/esphome/components/debug/debug_component.h @@ -4,6 +4,13 @@ #include "esphome/core/defines.h" #include "esphome/core/helpers.h" #include "esphome/core/macros.h" +#include +#include +#include +#include +#ifdef USE_ESP8266 +#include +#endif #ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" @@ -15,6 +22,44 @@ namespace esphome { namespace debug { +static constexpr size_t DEVICE_INFO_BUFFER_SIZE = 256; +static constexpr size_t RESET_REASON_BUFFER_SIZE = 128; + +#ifdef USE_ESP8266 +// ESP8266: Use vsnprintf_P to keep format strings in flash (PROGMEM) +// Format strings must be wrapped with PSTR() macro +inline size_t buf_append_p(char *buf, size_t size, size_t pos, PGM_P fmt, ...) { + if (pos >= size) { + return size; + } + va_list args; + va_start(args, fmt); + int written = vsnprintf_P(buf + pos, size - pos, fmt, args); + va_end(args); + if (written < 0) { + return pos; // encoding error + } + return std::min(pos + static_cast(written), size); +} +#define buf_append(buf, size, pos, fmt, ...) buf_append_p(buf, size, pos, PSTR(fmt), ##__VA_ARGS__) +#else +/// Safely append formatted string to buffer, returning new position (capped at size) +__attribute__((format(printf, 4, 5))) inline size_t buf_append(char *buf, size_t size, size_t pos, const char *fmt, + ...) { + if (pos >= size) { + return size; + } + va_list args; + va_start(args, fmt); + int written = vsnprintf(buf + pos, size - pos, fmt, args); + va_end(args); + if (written < 0) { + return pos; // encoding error + } + return std::min(pos + static_cast(written), size); +} +#endif + class DebugComponent : public PollingComponent { public: void loop() override; @@ -81,10 +126,10 @@ class DebugComponent : public PollingComponent { text_sensor::TextSensor *reset_reason_{nullptr}; #endif // USE_TEXT_SENSOR - std::string get_reset_reason_(); - std::string get_wakeup_cause_(); + const char *get_reset_reason_(std::span buffer); + const char *get_wakeup_cause_(std::span buffer); uint32_t get_free_heap_(); - void get_device_info_(std::string &device_info); + size_t get_device_info_(std::span buffer, size_t pos); void update_platform_(); }; diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index 25852b32a7..ebb6abf4da 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -58,24 +58,29 @@ void DebugComponent::on_shutdown() { global_preferences->sync(); } -std::string DebugComponent::get_reset_reason_() { - std::string reset_reason; +const char *DebugComponent::get_reset_reason_(std::span buffer) { + char *buf = buffer.data(); + const size_t size = RESET_REASON_BUFFER_SIZE; + unsigned reason = esp_reset_reason(); if (reason < sizeof(RESET_REASONS) / sizeof(RESET_REASONS[0])) { - reset_reason = RESET_REASONS[reason]; if (reason == ESP_RST_SW) { auto pref = global_preferences->make_preference(REBOOT_MAX_LEN, fnv1_hash(REBOOT_KEY + App.get_name())); - char buffer[REBOOT_MAX_LEN]{}; - if (pref.load(&buffer)) { - buffer[REBOOT_MAX_LEN - 1] = '\0'; - reset_reason = "Reboot request from " + std::string(buffer); + char reboot_source[REBOOT_MAX_LEN]{}; + if (pref.load(&reboot_source)) { + reboot_source[REBOOT_MAX_LEN - 1] = '\0'; + snprintf(buf, size, "Reboot request from %s", reboot_source); + } else { + snprintf(buf, size, "%s", RESET_REASONS[reason]); } + } else { + snprintf(buf, size, "%s", RESET_REASONS[reason]); } } else { - reset_reason = "unknown source"; + snprintf(buf, size, "unknown source"); } - ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str()); - return reset_reason; + ESP_LOGD(TAG, "Reset Reason: %s", buf); + return buf; } static const char *const WAKEUP_CAUSES[] = { @@ -94,7 +99,7 @@ static const char *const WAKEUP_CAUSES[] = { "BT", }; -std::string DebugComponent::get_wakeup_cause_() { +const char *DebugComponent::get_wakeup_cause_(std::span buffer) { const char *wake_reason; unsigned reason = esp_sleep_get_wakeup_cause(); if (reason < sizeof(WAKEUP_CAUSES) / sizeof(WAKEUP_CAUSES[0])) { @@ -103,6 +108,7 @@ std::string DebugComponent::get_wakeup_cause_() { wake_reason = "unknown source"; } ESP_LOGD(TAG, "Wakeup Reason: %s", wake_reason); + // Return the static string directly - no need to copy to buffer return wake_reason; } @@ -136,7 +142,10 @@ static constexpr ChipFeature CHIP_FEATURES[] = { {CHIP_FEATURE_WIFI_BGN, "2.4GHz WiFi"}, }; -void DebugComponent::get_device_info_(std::string &device_info) { +size_t DebugComponent::get_device_info_(std::span buffer, size_t pos) { + constexpr size_t size = DEVICE_INFO_BUFFER_SIZE; + char *buf = buffer.data(); + #if defined(USE_ARDUINO) const char *flash_mode; switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance) @@ -161,68 +170,66 @@ void DebugComponent::get_device_info_(std::string &device_info) { default: flash_mode = "UNKNOWN"; } - ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s", - ESP.getFlashChipSize() / 1024, // NOLINT - ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT - device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT - "kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT - device_info += flash_mode; + uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT + uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT + ESP_LOGD(TAG, "Flash Chip: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode); + pos = buf_append(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed, + flash_mode); #endif esp_chip_info_t info; esp_chip_info(&info); const char *model = ESPHOME_VARIANT; - std::string features; - // Check each known feature bit + // Build features string + pos = buf_append(buf, size, pos, "|Chip: %s Features:", model); + bool first_feature = true; for (const auto &feature : CHIP_FEATURES) { if (info.features & feature.bit) { - features += feature.name; - features += ", "; + pos = buf_append(buf, size, pos, "%s%s", first_feature ? "" : ", ", feature.name); + first_feature = false; info.features &= ~feature.bit; } } - if (info.features != 0) - features += "Other:" + format_hex(info.features); - ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores, - info.revision); - device_info += "|Chip: "; - device_info += model; - device_info += " Features:"; - device_info += features; - device_info += " Cores:" + to_string(info.cores); - device_info += " Revision:" + to_string(info.revision); - device_info += str_sprintf("|CPU Frequency: %" PRIu32 " MHz", arch_get_cpu_freq_hz() / 1000000); - ESP_LOGD(TAG, "CPU Frequency: %" PRIu32 " MHz", arch_get_cpu_freq_hz() / 1000000); + if (info.features != 0) { + pos = buf_append(buf, size, pos, "%sOther:0x%" PRIx32, first_feature ? "" : ", ", info.features); + } + ESP_LOGD(TAG, "Chip: Model=%s, Cores=%u, Revision=%u", model, info.cores, info.revision); + pos = buf_append(buf, size, pos, " Cores:%u Revision:%u", info.cores, info.revision); + + uint32_t cpu_freq_mhz = arch_get_cpu_freq_hz() / 1000000; + ESP_LOGD(TAG, "CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz); + pos = buf_append(buf, size, pos, "|CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz); // Framework detection - device_info += "|Framework: "; #ifdef USE_ARDUINO ESP_LOGD(TAG, "Framework: Arduino"); - device_info += "Arduino"; + pos = buf_append(buf, size, pos, "|Framework: Arduino"); #elif defined(USE_ESP32) ESP_LOGD(TAG, "Framework: ESP-IDF"); - device_info += "ESP-IDF"; + pos = buf_append(buf, size, pos, "|Framework: ESP-IDF"); #else ESP_LOGW(TAG, "Framework: UNKNOWN"); - device_info += "UNKNOWN"; + pos = buf_append(buf, size, pos, "|Framework: UNKNOWN"); #endif ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version()); - device_info += "|ESP-IDF: "; - device_info += esp_get_idf_version(); + pos = buf_append(buf, size, pos, "|ESP-IDF: %s", esp_get_idf_version()); - std::string mac = get_mac_address_pretty(); - ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str()); - device_info += "|EFuse MAC: "; - device_info += mac; + uint8_t mac[6]; + get_mac_address_raw(mac); + ESP_LOGD(TAG, "EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + pos = buf_append(buf, size, pos, "|EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], + mac[5]); - device_info += "|Reset: "; - device_info += get_reset_reason_(); + char reason_buffer[RESET_REASON_BUFFER_SIZE]; + const char *reset_reason = get_reset_reason_(std::span(reason_buffer)); + pos = buf_append(buf, size, pos, "|Reset: %s", reset_reason); - std::string wakeup_reason = this->get_wakeup_cause_(); - device_info += "|Wakeup: "; - device_info += wakeup_reason; + const char *wakeup_cause = get_wakeup_cause_(std::span(reason_buffer)); + pos = buf_append(buf, size, pos, "|Wakeup: %s", wakeup_cause); + + return pos; } void DebugComponent::update_platform_() { diff --git a/esphome/components/debug/debug_esp8266.cpp b/esphome/components/debug/debug_esp8266.cpp index 7427b32290..274f77e20d 100644 --- a/esphome/components/debug/debug_esp8266.cpp +++ b/esphome/components/debug/debug_esp8266.cpp @@ -8,19 +8,31 @@ namespace debug { static const char *const TAG = "debug"; -std::string DebugComponent::get_reset_reason_() { +const char *DebugComponent::get_reset_reason_(std::span buffer) { + char *buf = buffer.data(); #if !defined(CLANG_TIDY) - return ESP.getResetReason().c_str(); + String reason = ESP.getResetReason(); // NOLINT + snprintf_P(buf, RESET_REASON_BUFFER_SIZE, PSTR("%s"), reason.c_str()); + return buf; #else - return ""; + buf[0] = '\0'; + return buf; #endif } +const char *DebugComponent::get_wakeup_cause_(std::span buffer) { + // ESP8266 doesn't have detailed wakeup cause like ESP32 + return ""; +} + uint32_t DebugComponent::get_free_heap_() { return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance) } -void DebugComponent::get_device_info_(std::string &device_info) { +size_t DebugComponent::get_device_info_(std::span buffer, size_t pos) { + constexpr size_t size = DEVICE_INFO_BUFFER_SIZE; + char *buf = buffer.data(); + const char *flash_mode; switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance) case FM_QIO: @@ -38,42 +50,45 @@ void DebugComponent::get_device_info_(std::string &device_info) { default: flash_mode = "UNKNOWN"; } - ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s", - ESP.getFlashChipSize() / 1024, // NOLINT - ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT - device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT - "kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT - device_info += flash_mode; + uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT + uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT + ESP_LOGD(TAG, "Flash Chip: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode); + pos = buf_append(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed, + flash_mode); #if !defined(CLANG_TIDY) - auto reset_reason = get_reset_reason_(); + char reason_buffer[RESET_REASON_BUFFER_SIZE]; + const char *reset_reason = get_reset_reason_(std::span(reason_buffer)); + uint32_t chip_id = ESP.getChipId(); + uint8_t boot_version = ESP.getBootVersion(); + uint8_t boot_mode = ESP.getBootMode(); + uint8_t cpu_freq = ESP.getCpuFreqMHz(); + uint32_t flash_chip_id = ESP.getFlashChipId(); + ESP_LOGD(TAG, - "Chip ID: 0x%08X\n" + "Chip ID: 0x%08" PRIX32 "\n" "SDK Version: %s\n" "Core Version: %s\n" "Boot Version=%u Mode=%u\n" "CPU Frequency: %u\n" - "Flash Chip ID=0x%08X\n" + "Flash Chip ID=0x%08" PRIX32 "\n" "Reset Reason: %s\n" "Reset Info: %s", - ESP.getChipId(), ESP.getSdkVersion(), ESP.getCoreVersion().c_str(), ESP.getBootVersion(), ESP.getBootMode(), - ESP.getCpuFreqMHz(), ESP.getFlashChipId(), reset_reason.c_str(), ESP.getResetInfo().c_str()); + chip_id, ESP.getSdkVersion(), ESP.getCoreVersion().c_str(), boot_version, boot_mode, cpu_freq, flash_chip_id, + reset_reason, ESP.getResetInfo().c_str()); - device_info += "|Chip: 0x" + format_hex(ESP.getChipId()); - device_info += "|SDK: "; - device_info += ESP.getSdkVersion(); - device_info += "|Core: "; - device_info += ESP.getCoreVersion().c_str(); - device_info += "|Boot: "; - device_info += to_string(ESP.getBootVersion()); - device_info += "|Mode: " + to_string(ESP.getBootMode()); - device_info += "|CPU: " + to_string(ESP.getCpuFreqMHz()); - device_info += "|Flash: 0x" + format_hex(ESP.getFlashChipId()); - device_info += "|Reset: "; - device_info += reset_reason; - device_info += "|"; - device_info += ESP.getResetInfo().c_str(); + pos = buf_append(buf, size, pos, "|Chip: 0x%08" PRIX32, chip_id); + pos = buf_append(buf, size, pos, "|SDK: %s", ESP.getSdkVersion()); + pos = buf_append(buf, size, pos, "|Core: %s", ESP.getCoreVersion().c_str()); + pos = buf_append(buf, size, pos, "|Boot: %u", boot_version); + pos = buf_append(buf, size, pos, "|Mode: %u", boot_mode); + pos = buf_append(buf, size, pos, "|CPU: %u", cpu_freq); + pos = buf_append(buf, size, pos, "|Flash: 0x%08" PRIX32, flash_chip_id); + pos = buf_append(buf, size, pos, "|Reset: %s", reset_reason); + pos = buf_append(buf, size, pos, "|%s", ESP.getResetInfo().c_str()); #endif + + return pos; } void DebugComponent::update_platform_() { diff --git a/esphome/components/debug/debug_host.cpp b/esphome/components/debug/debug_host.cpp index 09ad34ef88..2fa88f0909 100644 --- a/esphome/components/debug/debug_host.cpp +++ b/esphome/components/debug/debug_host.cpp @@ -5,11 +5,13 @@ namespace esphome { namespace debug { -std::string DebugComponent::get_reset_reason_() { return ""; } +const char *DebugComponent::get_reset_reason_(std::span buffer) { return ""; } + +const char *DebugComponent::get_wakeup_cause_(std::span buffer) { return ""; } uint32_t DebugComponent::get_free_heap_() { return INT_MAX; } -void DebugComponent::get_device_info_(std::string &device_info) {} +size_t DebugComponent::get_device_info_(std::span buffer, size_t pos) { return pos; } void DebugComponent::update_platform_() {} diff --git a/esphome/components/debug/debug_libretiny.cpp b/esphome/components/debug/debug_libretiny.cpp index e823ac6c77..4f07a4cc17 100644 --- a/esphome/components/debug/debug_libretiny.cpp +++ b/esphome/components/debug/debug_libretiny.cpp @@ -7,31 +7,43 @@ namespace debug { static const char *const TAG = "debug"; -std::string DebugComponent::get_reset_reason_() { return lt_get_reboot_reason_name(lt_get_reboot_reason()); } +const char *DebugComponent::get_reset_reason_(std::span buffer) { + // Return the static string directly + return lt_get_reboot_reason_name(lt_get_reboot_reason()); +} + +const char *DebugComponent::get_wakeup_cause_(std::span buffer) { return ""; } uint32_t DebugComponent::get_free_heap_() { return lt_heap_get_free(); } -void DebugComponent::get_device_info_(std::string &device_info) { - std::string reset_reason = get_reset_reason_(); +size_t DebugComponent::get_device_info_(std::span buffer, size_t pos) { + constexpr size_t size = DEVICE_INFO_BUFFER_SIZE; + char *buf = buffer.data(); + + char reason_buffer[RESET_REASON_BUFFER_SIZE]; + const char *reset_reason = get_reset_reason_(std::span(reason_buffer)); + uint32_t flash_kib = lt_flash_get_size() / 1024; + uint32_t ram_kib = lt_ram_get_size() / 1024; + uint32_t mac_id = lt_cpu_get_mac_id(); + ESP_LOGD(TAG, "LibreTiny Version: %s\n" "Chip: %s (%04x) @ %u MHz\n" - "Chip ID: 0x%06X\n" + "Chip ID: 0x%06" PRIX32 "\n" "Board: %s\n" - "Flash: %u KiB / RAM: %u KiB\n" + "Flash: %" PRIu32 " KiB / RAM: %" PRIu32 " KiB\n" "Reset Reason: %s", - lt_get_version(), lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz(), lt_cpu_get_mac_id(), - lt_get_board_code(), lt_flash_get_size() / 1024, lt_ram_get_size() / 1024, reset_reason.c_str()); + lt_get_version(), lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz(), mac_id, + lt_get_board_code(), flash_kib, ram_kib, reset_reason); - device_info += "|Version: "; - device_info += LT_BANNER_STR + 10; - device_info += "|Reset Reason: "; - device_info += reset_reason; - device_info += "|Chip Name: "; - device_info += lt_cpu_get_model_name(); - device_info += "|Chip ID: 0x" + format_hex(lt_cpu_get_mac_id()); - device_info += "|Flash: " + to_string(lt_flash_get_size() / 1024) + " KiB"; - device_info += "|RAM: " + to_string(lt_ram_get_size() / 1024) + " KiB"; + pos = buf_append(buf, size, pos, "|Version: %s", LT_BANNER_STR + 10); + pos = buf_append(buf, size, pos, "|Reset Reason: %s", reset_reason); + pos = buf_append(buf, size, pos, "|Chip Name: %s", lt_cpu_get_model_name()); + pos = buf_append(buf, size, pos, "|Chip ID: 0x%06" PRIX32, mac_id); + pos = buf_append(buf, size, pos, "|Flash: %" PRIu32 " KiB", flash_kib); + pos = buf_append(buf, size, pos, "|RAM: %" PRIu32 " KiB", ram_kib); + + return pos; } void DebugComponent::update_platform_() { diff --git a/esphome/components/debug/debug_rp2040.cpp b/esphome/components/debug/debug_rp2040.cpp index 497547e30d..a426a73bc2 100644 --- a/esphome/components/debug/debug_rp2040.cpp +++ b/esphome/components/debug/debug_rp2040.cpp @@ -7,13 +7,21 @@ namespace debug { static const char *const TAG = "debug"; -std::string DebugComponent::get_reset_reason_() { return ""; } +const char *DebugComponent::get_reset_reason_(std::span buffer) { return ""; } + +const char *DebugComponent::get_wakeup_cause_(std::span buffer) { return ""; } uint32_t DebugComponent::get_free_heap_() { return rp2040.getFreeHeap(); } -void DebugComponent::get_device_info_(std::string &device_info) { - ESP_LOGD(TAG, "CPU Frequency: %u", rp2040.f_cpu()); - device_info += "CPU Frequency: " + to_string(rp2040.f_cpu()); +size_t DebugComponent::get_device_info_(std::span buffer, size_t pos) { + constexpr size_t size = DEVICE_INFO_BUFFER_SIZE; + char *buf = buffer.data(); + + uint32_t cpu_freq = rp2040.f_cpu(); + ESP_LOGD(TAG, "CPU Frequency: %" PRIu32, cpu_freq); + pos = buf_append(buf, size, pos, "|CPU Frequency: %" PRIu32, cpu_freq); + + return pos; } void DebugComponent::update_platform_() {} diff --git a/esphome/components/debug/debug_zephyr.cpp b/esphome/components/debug/debug_zephyr.cpp index 6abf983e9e..85880595b6 100644 --- a/esphome/components/debug/debug_zephyr.cpp +++ b/esphome/components/debug/debug_zephyr.cpp @@ -15,14 +15,14 @@ static const char *const TAG = "debug"; constexpr std::uintptr_t MBR_PARAM_PAGE_ADDR = 0xFFC; constexpr std::uintptr_t MBR_BOOTLOADER_ADDR = 0xFF8; -static void show_reset_reason(std::string &reset_reason, bool set, const char *reason) { +static size_t append_reset_reason(char *buf, size_t size, size_t pos, bool set, const char *reason) { if (!set) { - return; + return pos; } - if (!reset_reason.empty()) { - reset_reason += ", "; + if (pos > 0) { + pos = buf_append(buf, size, pos, ", "); } - reset_reason += reason; + return buf_append(buf, size, pos, "%s", reason); } static inline uint32_t read_mem_u32(uintptr_t addr) { @@ -56,33 +56,47 @@ static inline uint32_t sd_version_get() { return 0; } -std::string DebugComponent::get_reset_reason_() { +const char *DebugComponent::get_reset_reason_(std::span buffer) { + char *buf = buffer.data(); + const size_t size = RESET_REASON_BUFFER_SIZE; + uint32_t cause; auto ret = hwinfo_get_reset_cause(&cause); if (ret) { ESP_LOGE(TAG, "Unable to get reset cause: %d", ret); - return ""; + buf[0] = '\0'; + return buf; } - std::string reset_reason; + size_t pos = 0; - show_reset_reason(reset_reason, cause & RESET_PIN, "External pin"); - show_reset_reason(reset_reason, cause & RESET_SOFTWARE, "Software reset"); - show_reset_reason(reset_reason, cause & RESET_BROWNOUT, "Brownout (drop in voltage)"); - show_reset_reason(reset_reason, cause & RESET_POR, "Power-on reset (POR)"); - show_reset_reason(reset_reason, cause & RESET_WATCHDOG, "Watchdog timer expiration"); - show_reset_reason(reset_reason, cause & RESET_DEBUG, "Debug event"); - show_reset_reason(reset_reason, cause & RESET_SECURITY, "Security violation"); - show_reset_reason(reset_reason, cause & RESET_LOW_POWER_WAKE, "Waking up from low power mode"); - show_reset_reason(reset_reason, cause & RESET_CPU_LOCKUP, "CPU lock-up detected"); - show_reset_reason(reset_reason, cause & RESET_PARITY, "Parity error"); - show_reset_reason(reset_reason, cause & RESET_PLL, "PLL error"); - show_reset_reason(reset_reason, cause & RESET_CLOCK, "Clock error"); - show_reset_reason(reset_reason, cause & RESET_HARDWARE, "Hardware reset"); - show_reset_reason(reset_reason, cause & RESET_USER, "User reset"); - show_reset_reason(reset_reason, cause & RESET_TEMPERATURE, "Temperature reset"); + pos = append_reset_reason(buf, size, pos, cause & RESET_PIN, "External pin"); + pos = append_reset_reason(buf, size, pos, cause & RESET_SOFTWARE, "Software reset"); + pos = append_reset_reason(buf, size, pos, cause & RESET_BROWNOUT, "Brownout (drop in voltage)"); + pos = append_reset_reason(buf, size, pos, cause & RESET_POR, "Power-on reset (POR)"); + pos = append_reset_reason(buf, size, pos, cause & RESET_WATCHDOG, "Watchdog timer expiration"); + pos = append_reset_reason(buf, size, pos, cause & RESET_DEBUG, "Debug event"); + pos = append_reset_reason(buf, size, pos, cause & RESET_SECURITY, "Security violation"); + pos = append_reset_reason(buf, size, pos, cause & RESET_LOW_POWER_WAKE, "Waking up from low power mode"); + pos = append_reset_reason(buf, size, pos, cause & RESET_CPU_LOCKUP, "CPU lock-up detected"); + pos = append_reset_reason(buf, size, pos, cause & RESET_PARITY, "Parity error"); + pos = append_reset_reason(buf, size, pos, cause & RESET_PLL, "PLL error"); + pos = append_reset_reason(buf, size, pos, cause & RESET_CLOCK, "Clock error"); + pos = append_reset_reason(buf, size, pos, cause & RESET_HARDWARE, "Hardware reset"); + pos = append_reset_reason(buf, size, pos, cause & RESET_USER, "User reset"); + pos = append_reset_reason(buf, size, pos, cause & RESET_TEMPERATURE, "Temperature reset"); - ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str()); - return reset_reason; + // Ensure null termination if nothing was written + if (pos == 0) { + buf[0] = '\0'; + } + + ESP_LOGD(TAG, "Reset Reason: %s", buf); + return buf; +} + +const char *DebugComponent::get_wakeup_cause_(std::span buffer) { + // Zephyr doesn't have detailed wakeup cause like ESP32 + return ""; } uint32_t DebugComponent::get_free_heap_() { return INT_MAX; } @@ -118,175 +132,183 @@ void DebugComponent::log_partition_info_() { flash_area_foreach(fa_cb, nullptr); } -void DebugComponent::get_device_info_(std::string &device_info) { - std::string supply = "Main supply status: "; - if (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_NORMAL) { - supply += "Normal voltage."; - } else { - supply += "High voltage."; - } - ESP_LOGD(TAG, "%s", supply.c_str()); - device_info += "|" + supply; +size_t DebugComponent::get_device_info_(std::span buffer, size_t pos) { + constexpr size_t size = DEVICE_INFO_BUFFER_SIZE; + char *buf = buffer.data(); - std::string reg0 = "Regulator stage 0: "; + // Main supply status + const char *supply_status = + (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_NORMAL) ? "Normal voltage." : "High voltage."; + ESP_LOGD(TAG, "Main supply status: %s", supply_status); + pos = buf_append(buf, size, pos, "|Main supply status: %s", supply_status); + + // Regulator stage 0 if (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_HIGH) { - reg0 += nrf_power_dcdcen_vddh_get(NRF_POWER) ? "DC/DC" : "LDO"; - reg0 += ", "; + const char *reg0_type = nrf_power_dcdcen_vddh_get(NRF_POWER) ? "DC/DC" : "LDO"; + const char *reg0_voltage; switch (NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) { case (UICR_REGOUT0_VOUT_DEFAULT << UICR_REGOUT0_VOUT_Pos): - reg0 += "1.8V (default)"; + reg0_voltage = "1.8V (default)"; break; case (UICR_REGOUT0_VOUT_1V8 << UICR_REGOUT0_VOUT_Pos): - reg0 += "1.8V"; + reg0_voltage = "1.8V"; break; case (UICR_REGOUT0_VOUT_2V1 << UICR_REGOUT0_VOUT_Pos): - reg0 += "2.1V"; + reg0_voltage = "2.1V"; break; case (UICR_REGOUT0_VOUT_2V4 << UICR_REGOUT0_VOUT_Pos): - reg0 += "2.4V"; + reg0_voltage = "2.4V"; break; case (UICR_REGOUT0_VOUT_2V7 << UICR_REGOUT0_VOUT_Pos): - reg0 += "2.7V"; + reg0_voltage = "2.7V"; break; case (UICR_REGOUT0_VOUT_3V0 << UICR_REGOUT0_VOUT_Pos): - reg0 += "3.0V"; + reg0_voltage = "3.0V"; break; case (UICR_REGOUT0_VOUT_3V3 << UICR_REGOUT0_VOUT_Pos): - reg0 += "3.3V"; + reg0_voltage = "3.3V"; break; default: - reg0 += "???V"; + reg0_voltage = "???V"; } + ESP_LOGD(TAG, "Regulator stage 0: %s, %s", reg0_type, reg0_voltage); + pos = buf_append(buf, size, pos, "|Regulator stage 0: %s, %s", reg0_type, reg0_voltage); } else { - reg0 += "disabled"; + ESP_LOGD(TAG, "Regulator stage 0: disabled"); + pos = buf_append(buf, size, pos, "|Regulator stage 0: disabled"); } - ESP_LOGD(TAG, "%s", reg0.c_str()); - device_info += "|" + reg0; - std::string reg1 = "Regulator stage 1: "; - reg1 += nrf_power_dcdcen_get(NRF_POWER) ? "DC/DC" : "LDO"; - ESP_LOGD(TAG, "%s", reg1.c_str()); - device_info += "|" + reg1; + // Regulator stage 1 + const char *reg1_type = nrf_power_dcdcen_get(NRF_POWER) ? "DC/DC" : "LDO"; + ESP_LOGD(TAG, "Regulator stage 1: %s", reg1_type); + pos = buf_append(buf, size, pos, "|Regulator stage 1: %s", reg1_type); - std::string usb_power = "USB power state: "; + // USB power state + const char *usb_state; if (nrf_power_usbregstatus_vbusdet_get(NRF_POWER)) { if (nrf_power_usbregstatus_outrdy_get(NRF_POWER)) { - /**< From the power viewpoint, USB is ready for working. */ - usb_power += "ready"; + usb_state = "ready"; } else { - /**< The USB power is detected, but USB power regulator is not ready. */ - usb_power += "connected (regulator is not ready)"; + usb_state = "connected (regulator is not ready)"; } } else { - /**< No power on USB lines detected. */ - usb_power += "disconected"; + usb_state = "disconnected"; } - ESP_LOGD(TAG, "%s", usb_power.c_str()); - device_info += "|" + usb_power; + ESP_LOGD(TAG, "USB power state: %s", usb_state); + pos = buf_append(buf, size, pos, "|USB power state: %s", usb_state); + // Power-fail comparator bool enabled; - nrf_power_pof_thr_t pof_thr; - - pof_thr = nrf_power_pofcon_get(NRF_POWER, &enabled); - std::string pof = "Power-fail comparator: "; + nrf_power_pof_thr_t pof_thr = nrf_power_pofcon_get(NRF_POWER, &enabled); if (enabled) { + const char *pof_voltage; switch (pof_thr) { case POWER_POFCON_THRESHOLD_V17: - pof += "1.7V"; + pof_voltage = "1.7V"; break; case POWER_POFCON_THRESHOLD_V18: - pof += "1.8V"; + pof_voltage = "1.8V"; break; case POWER_POFCON_THRESHOLD_V19: - pof += "1.9V"; + pof_voltage = "1.9V"; break; case POWER_POFCON_THRESHOLD_V20: - pof += "2.0V"; + pof_voltage = "2.0V"; break; case POWER_POFCON_THRESHOLD_V21: - pof += "2.1V"; + pof_voltage = "2.1V"; break; case POWER_POFCON_THRESHOLD_V22: - pof += "2.2V"; + pof_voltage = "2.2V"; break; case POWER_POFCON_THRESHOLD_V23: - pof += "2.3V"; + pof_voltage = "2.3V"; break; case POWER_POFCON_THRESHOLD_V24: - pof += "2.4V"; + pof_voltage = "2.4V"; break; case POWER_POFCON_THRESHOLD_V25: - pof += "2.5V"; + pof_voltage = "2.5V"; break; case POWER_POFCON_THRESHOLD_V26: - pof += "2.6V"; + pof_voltage = "2.6V"; break; case POWER_POFCON_THRESHOLD_V27: - pof += "2.7V"; + pof_voltage = "2.7V"; break; case POWER_POFCON_THRESHOLD_V28: - pof += "2.8V"; + pof_voltage = "2.8V"; + break; + default: + pof_voltage = "???V"; break; } if (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_HIGH) { - pof += ", VDDH: "; + const char *vddh_voltage; switch (nrf_power_pofcon_vddh_get(NRF_POWER)) { case NRF_POWER_POFTHRVDDH_V27: - pof += "2.7V"; + vddh_voltage = "2.7V"; break; case NRF_POWER_POFTHRVDDH_V28: - pof += "2.8V"; + vddh_voltage = "2.8V"; break; case NRF_POWER_POFTHRVDDH_V29: - pof += "2.9V"; + vddh_voltage = "2.9V"; break; case NRF_POWER_POFTHRVDDH_V30: - pof += "3.0V"; + vddh_voltage = "3.0V"; break; case NRF_POWER_POFTHRVDDH_V31: - pof += "3.1V"; + vddh_voltage = "3.1V"; break; case NRF_POWER_POFTHRVDDH_V32: - pof += "3.2V"; + vddh_voltage = "3.2V"; break; case NRF_POWER_POFTHRVDDH_V33: - pof += "3.3V"; + vddh_voltage = "3.3V"; break; case NRF_POWER_POFTHRVDDH_V34: - pof += "3.4V"; + vddh_voltage = "3.4V"; break; case NRF_POWER_POFTHRVDDH_V35: - pof += "3.5V"; + vddh_voltage = "3.5V"; break; case NRF_POWER_POFTHRVDDH_V36: - pof += "3.6V"; + vddh_voltage = "3.6V"; break; case NRF_POWER_POFTHRVDDH_V37: - pof += "3.7V"; + vddh_voltage = "3.7V"; break; case NRF_POWER_POFTHRVDDH_V38: - pof += "3.8V"; + vddh_voltage = "3.8V"; break; case NRF_POWER_POFTHRVDDH_V39: - pof += "3.9V"; + vddh_voltage = "3.9V"; break; case NRF_POWER_POFTHRVDDH_V40: - pof += "4.0V"; + vddh_voltage = "4.0V"; break; case NRF_POWER_POFTHRVDDH_V41: - pof += "4.1V"; + vddh_voltage = "4.1V"; break; case NRF_POWER_POFTHRVDDH_V42: - pof += "4.2V"; + vddh_voltage = "4.2V"; + break; + default: + vddh_voltage = "???V"; break; } + ESP_LOGD(TAG, "Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage); + pos = buf_append(buf, size, pos, "|Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage); + } else { + ESP_LOGD(TAG, "Power-fail comparator: %s", pof_voltage); + pos = buf_append(buf, size, pos, "|Power-fail comparator: %s", pof_voltage); } } else { - pof += "disabled"; + ESP_LOGD(TAG, "Power-fail comparator: disabled"); + pos = buf_append(buf, size, pos, "|Power-fail comparator: disabled"); } - ESP_LOGD(TAG, "%s", pof.c_str()); - device_info += "|" + pof; auto package = [](uint32_t value) { switch (value) { @@ -373,6 +395,8 @@ void DebugComponent::get_device_info_(std::string &device_info) { "NRFFW %s\n" "NRFHW %s", uicr(NRF_UICR->NRFFW, 13).c_str(), uicr(NRF_UICR->NRFHW, 12).c_str()); + + return pos; } void DebugComponent::update_platform_() {}