ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
debug_esp32.cpp
Go to the documentation of this file.
1#include "debug_component.h"
2
3#ifdef USE_ESP32
5#include "esphome/core/log.h"
6#include "esphome/core/hal.h"
7#include <esp_sleep.h>
8
9#include <esp_heap_caps.h>
10#include <esp_system.h>
11#include <esp_chip_info.h>
12#include <esp_partition.h>
13
14#ifdef USE_ARDUINO
15#include <Esp.h>
16#endif
17
18namespace esphome {
19namespace debug {
20
21static const char *const TAG = "debug";
22
23// index by values returned by esp_reset_reason
24
25static const char *const RESET_REASONS[] = {
26 "unknown source",
27 "power-on event",
28 "external pin",
29 "software via esp_restart",
30 "exception/panic",
31 "interrupt watchdog",
32 "task watchdog",
33 "other watchdogs",
34 "exiting deep sleep mode",
35 "brownout",
36 "SDIO",
37 "USB peripheral",
38 "JTAG",
39 "efuse error",
40 "power glitch detected",
41 "CPU lock up",
42};
43
44static const char *const REBOOT_KEY = "reboot_source";
45static const size_t REBOOT_MAX_LEN = 24;
46
47// on shutdown, store the source of the reboot request
50 char buffer[REBOOT_MAX_LEN]{};
51 auto pref = global_preferences->make_preference(REBOOT_MAX_LEN, fnv1_hash(REBOOT_KEY + App.get_name()));
52 if (component != nullptr) {
53 strncpy(buffer, LOG_STR_ARG(component->get_component_log_str()), REBOOT_MAX_LEN - 1);
54 buffer[REBOOT_MAX_LEN - 1] = '\0';
55 }
56 ESP_LOGD(TAG, "Storing reboot source: %s", buffer);
57 pref.save(&buffer);
59}
60
61const char *DebugComponent::get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
62 char *buf = buffer.data();
63 const size_t size = RESET_REASON_BUFFER_SIZE;
64
65 unsigned reason = esp_reset_reason();
66 if (reason < sizeof(RESET_REASONS) / sizeof(RESET_REASONS[0])) {
67 if (reason == ESP_RST_SW) {
68 auto pref = global_preferences->make_preference(REBOOT_MAX_LEN, fnv1_hash(REBOOT_KEY + App.get_name()));
69 char reboot_source[REBOOT_MAX_LEN]{};
70 if (pref.load(&reboot_source)) {
71 reboot_source[REBOOT_MAX_LEN - 1] = '\0';
72 snprintf(buf, size, "Reboot request from %s", reboot_source);
73 } else {
74 snprintf(buf, size, "%s", RESET_REASONS[reason]);
75 }
76 } else {
77 snprintf(buf, size, "%s", RESET_REASONS[reason]);
78 }
79 } else {
80 snprintf(buf, size, "unknown source");
81 }
82 return buf;
83}
84
85static const char *const WAKEUP_CAUSES[] = {
86 "undefined",
87 "undefined",
88 "external signal using RTC_IO",
89 "external signal using RTC_CNTL",
90 "timer",
91 "touchpad",
92 "ULP program",
93 "GPIO",
94 "UART",
95 "WIFI",
96 "COCPU int",
97 "COCPU crash",
98 "BT",
99};
100
101const char *DebugComponent::get_wakeup_cause_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
102 const char *wake_reason;
103 unsigned reason = esp_sleep_get_wakeup_cause();
104 if (reason < sizeof(WAKEUP_CAUSES) / sizeof(WAKEUP_CAUSES[0])) {
105 wake_reason = WAKEUP_CAUSES[reason];
106 } else {
107 wake_reason = "unknown source";
108 }
109 // Return the static string directly - no need to copy to buffer
110 return wake_reason;
111}
112
114 ESP_LOGCONFIG(TAG,
115 "Partition table:\n"
116 " %-12s %-4s %-8s %-10s %-10s",
117 "Name", "Type", "Subtype", "Address", "Size");
118 esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
119 while (it != NULL) {
120 const esp_partition_t *partition = esp_partition_get(it);
121 ESP_LOGCONFIG(TAG, " %-12s %-4d %-8d 0x%08" PRIX32 " 0x%08" PRIX32, partition->label, partition->type,
122 partition->subtype, partition->address, partition->size);
123 it = esp_partition_next(it);
124 }
125 esp_partition_iterator_release(it);
126}
127
128uint32_t DebugComponent::get_free_heap_() { return heap_caps_get_free_size(MALLOC_CAP_INTERNAL); }
129
130struct ChipFeature {
131 int bit;
132 const char *name;
133};
134
135static constexpr ChipFeature CHIP_FEATURES[] = {
136 {CHIP_FEATURE_BLE, "BLE"},
137 {CHIP_FEATURE_BT, "BT"},
138 {CHIP_FEATURE_EMB_FLASH, "EMB Flash"},
139 {CHIP_FEATURE_EMB_PSRAM, "EMB PSRAM"},
140 {CHIP_FEATURE_WIFI_BGN, "2.4GHz WiFi"},
141};
142
143size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE> buffer, size_t pos) {
144 constexpr size_t size = DEVICE_INFO_BUFFER_SIZE;
145 char *buf = buffer.data();
146
147#if defined(USE_ARDUINO)
148 const char *flash_mode;
149 switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
150 case FM_QIO:
151 flash_mode = "QIO";
152 break;
153 case FM_QOUT:
154 flash_mode = "QOUT";
155 break;
156 case FM_DIO:
157 flash_mode = "DIO";
158 break;
159 case FM_DOUT:
160 flash_mode = "DOUT";
161 break;
162 case FM_FAST_READ:
163 flash_mode = "FAST_READ";
164 break;
165 case FM_SLOW_READ:
166 flash_mode = "SLOW_READ";
167 break;
168 default:
169 flash_mode = "UNKNOWN";
170 }
171 uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT
172 uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT
173 pos = buf_append_printf(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed,
174 flash_mode);
175#endif
176
177 esp_chip_info_t info;
178 esp_chip_info(&info);
179 const char *model = ESPHOME_VARIANT;
180
181 // Build features string
182 pos = buf_append_printf(buf, size, pos, "|Chip: %s Features:", model);
183 bool first_feature = true;
184 for (const auto &feature : CHIP_FEATURES) {
185 if (info.features & feature.bit) {
186 pos = buf_append_printf(buf, size, pos, "%s%s", first_feature ? "" : ", ", feature.name);
187 first_feature = false;
188 info.features &= ~feature.bit;
189 }
190 }
191 if (info.features != 0) {
192 pos = buf_append_printf(buf, size, pos, "%sOther:0x%" PRIx32, first_feature ? "" : ", ", info.features);
193 }
194 pos = buf_append_printf(buf, size, pos, " Cores:%u Revision:%u", info.cores, info.revision);
195
196 uint32_t cpu_freq_mhz = arch_get_cpu_freq_hz() / 1000000;
197 pos = buf_append_printf(buf, size, pos, "|CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
198
199 char reason_buffer[RESET_REASON_BUFFER_SIZE];
200 const char *reset_reason = get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE>(reason_buffer));
201 const char *wakeup_cause = get_wakeup_cause_(std::span<char, RESET_REASON_BUFFER_SIZE>(reason_buffer));
202
203 uint8_t mac[6];
205
206 ESP_LOGD(TAG,
207 "ESP32 debug info:\n"
208 " Chip: %s\n"
209 " Cores: %u\n"
210 " Revision: %u\n"
211 " CPU Frequency: %" PRIu32 " MHz\n"
212 " ESP-IDF Version: %s\n"
213 " EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X\n"
214 " Reset Reason: %s\n"
215 " Wakeup Cause: %s",
216 model, info.cores, info.revision, cpu_freq_mhz, esp_get_idf_version(), mac[0], mac[1], mac[2], mac[3],
217 mac[4], mac[5], reset_reason, wakeup_cause);
218#if defined(USE_ARDUINO)
219 ESP_LOGD(TAG, " Flash: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode);
220#endif
221 // Framework detection
222#ifdef USE_ARDUINO
223 ESP_LOGD(TAG, " Framework: Arduino");
224 pos = buf_append_printf(buf, size, pos, "|Framework: Arduino");
225#else
226 ESP_LOGD(TAG, " Framework: ESP-IDF");
227 pos = buf_append_printf(buf, size, pos, "|Framework: ESP-IDF");
228#endif
229
230 pos = buf_append_printf(buf, size, pos, "|ESP-IDF: %s", esp_get_idf_version());
231 pos = buf_append_printf(buf, size, pos, "|EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3],
232 mac[4], mac[5]);
233 pos = buf_append_printf(buf, size, pos, "|Reset: %s", reset_reason);
234 pos = buf_append_printf(buf, size, pos, "|Wakeup: %s", wakeup_cause);
235
236 return pos;
237}
238
240#ifdef USE_SENSOR
241 uint32_t max_alloc = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
242 if (this->block_sensor_ != nullptr) {
243 this->block_sensor_->publish_state(max_alloc);
244 }
245 if (this->min_free_sensor_ != nullptr) {
246 this->min_free_sensor_->publish_state(heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL));
247 }
248 if (this->fragmentation_sensor_ != nullptr) {
249 uint32_t free_heap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
250 if (free_heap > 0) {
251 float fragmentation = 100.0f - (100.0f * max_alloc / free_heap);
252 this->fragmentation_sensor_->publish_state(fragmentation);
253 }
254 }
255 if (this->psram_sensor_ != nullptr) {
256 this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
257 }
258#endif
259}
260
261} // namespace debug
262} // namespace esphome
263#endif // USE_ESP32
Component * get_current_component()
const std::string & get_name() const
Get the name of this Application set by pre_setup().
virtual bool sync()=0
Commit pending writes to flash.
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
void log_partition_info_()
Logs information about the device's partition table.
size_t get_device_info_(std::span< char, DEVICE_INFO_BUFFER_SIZE > buffer, size_t pos)
sensor::Sensor * fragmentation_sensor_
const char * get_wakeup_cause_(std::span< char, RESET_REASON_BUFFER_SIZE > buffer)
const char * get_reset_reason_(std::span< char, RESET_REASON_BUFFER_SIZE > buffer)
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
const Component * component
Definition component.cpp:37
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
size_t size
Definition helpers.h:854
ESPPreferences * global_preferences
uint32_t fnv1_hash(const char *str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:148
uint32_t arch_get_cpu_freq_hz()
Definition core.cpp:53
size_t size_t pos
Definition helpers.h:854
void get_mac_address_raw(uint8_t *mac)
Get the device MAC address as raw bytes, written into the provided byte array (6 bytes).
Definition helpers.cpp:73
Application App
Global storage of Application pointer - only one Application can exist.