ESPHome 2026.8.1
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"
8#include <esp_sleep.h>
9#include <esp_idf_version.h>
10
11#include <esp_heap_caps.h>
12#include <esp_system.h>
13#include <esp_chip_info.h>
14#include <esp_partition.h>
15
16#ifdef USE_ARDUINO
17#include <Esp.h>
18#endif
19
20namespace esphome::debug {
21
22static const char *const TAG = "debug";
23
24// index by values returned by esp_reset_reason
25
26static const char *const RESET_REASONS[] = {
27 "unknown source",
28 "power-on event",
29 "external pin",
30 "software via esp_restart",
31 "exception/panic",
32 "interrupt watchdog",
33 "task watchdog",
34 "other watchdogs",
35 "exiting deep sleep mode",
36 "brownout",
37 "SDIO",
38 "USB peripheral",
39 "JTAG",
40 "efuse error",
41 "power glitch detected",
42 "CPU lock up",
43};
44
45static const char *const REBOOT_KEY = "reboot_source";
46static const size_t REBOOT_MAX_LEN = 24;
47
48// on shutdown, store the source of the reboot request
51 char buffer[REBOOT_MAX_LEN]{};
52 auto pref = global_preferences->make_preference(REBOOT_MAX_LEN,
53 fnv1_hash_extend(fnv1_hash(REBOOT_KEY), App.get_name().c_str()));
54 if (component != nullptr) {
55 strncpy(buffer, LOG_STR_ARG(component->get_component_log_str()), REBOOT_MAX_LEN - 1);
56 buffer[REBOOT_MAX_LEN - 1] = '\0';
57 }
58 ESP_LOGD(TAG, "Storing reboot source: %s", buffer);
59 pref.save(&buffer);
61}
62
63const char *DebugComponent::get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
64 char *buf = buffer.data();
65 const size_t size = RESET_REASON_BUFFER_SIZE;
66
67 unsigned reason = esp_reset_reason();
68 if (reason < sizeof(RESET_REASONS) / sizeof(RESET_REASONS[0])) {
69 if (reason == ESP_RST_SW) {
70 auto pref = global_preferences->make_preference(REBOOT_MAX_LEN,
71 fnv1_hash_extend(fnv1_hash(REBOOT_KEY), App.get_name().c_str()));
72 char reboot_source[REBOOT_MAX_LEN]{};
73 if (pref.load(&reboot_source)) {
74 reboot_source[REBOOT_MAX_LEN - 1] = '\0';
75 snprintf(buf, size, "Reboot request from %s", reboot_source);
76 } else {
77 snprintf(buf, size, "%s", RESET_REASONS[reason]);
78 }
79 } else {
80 snprintf(buf, size, "%s", RESET_REASONS[reason]);
81 }
82 } else {
83 snprintf(buf, size, "unknown source");
84 }
85 return buf;
86}
87
88#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
89static const char *const WAKEUP_CAUSES[] = {
90 "undefined", // ESP_SLEEP_WAKEUP_UNDEFINED (0)
91 "undefined", // ESP_SLEEP_WAKEUP_ALL (1)
92 "external signal using RTC_IO", // ESP_SLEEP_WAKEUP_EXT0 (2)
93 "external signal using RTC_CNTL", // ESP_SLEEP_WAKEUP_EXT1 (3)
94 "timer", // ESP_SLEEP_WAKEUP_TIMER (4)
95 "touchpad", // ESP_SLEEP_WAKEUP_TOUCHPAD (5)
96 "ULP program", // ESP_SLEEP_WAKEUP_ULP (6)
97 "GPIO", // ESP_SLEEP_WAKEUP_GPIO (7)
98 "UART", // ESP_SLEEP_WAKEUP_UART (8)
99 "UART1", // ESP_SLEEP_WAKEUP_UART1 (9)
100 "UART2", // ESP_SLEEP_WAKEUP_UART2 (10)
101 "WIFI", // ESP_SLEEP_WAKEUP_WIFI (11)
102 "COCPU int", // ESP_SLEEP_WAKEUP_COCPU (12)
103 "COCPU crash", // ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG (13)
104 "BT", // ESP_SLEEP_WAKEUP_BT (14)
105 "VAD", // ESP_SLEEP_WAKEUP_VAD (15)
106 "VBAT under voltage", // ESP_SLEEP_WAKEUP_VBAT_UNDER_VOLT (16)
107};
108#else
109static const char *const WAKEUP_CAUSES[] = {
110 "undefined", // ESP_SLEEP_WAKEUP_UNDEFINED (0)
111 "undefined", // ESP_SLEEP_WAKEUP_ALL (1)
112 "external signal using RTC_IO", // ESP_SLEEP_WAKEUP_EXT0 (2)
113 "external signal using RTC_CNTL", // ESP_SLEEP_WAKEUP_EXT1 (3)
114 "timer", // ESP_SLEEP_WAKEUP_TIMER (4)
115 "touchpad", // ESP_SLEEP_WAKEUP_TOUCHPAD (5)
116 "ULP program", // ESP_SLEEP_WAKEUP_ULP (6)
117 "GPIO", // ESP_SLEEP_WAKEUP_GPIO (7)
118 "UART", // ESP_SLEEP_WAKEUP_UART (8)
119 "WIFI", // ESP_SLEEP_WAKEUP_WIFI (9)
120 "COCPU int", // ESP_SLEEP_WAKEUP_COCPU (10)
121 "COCPU crash", // ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG (11)
122 "BT", // ESP_SLEEP_WAKEUP_BT (12)
123};
124#endif
125
126const char *DebugComponent::get_wakeup_cause_(std::span<char, WAKEUP_CAUSE_BUFFER_SIZE> buffer) {
127 static constexpr auto NUM_CAUSES = sizeof(WAKEUP_CAUSES) / sizeof(WAKEUP_CAUSES[0]);
128#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
129 // IDF 6.0+ returns a bitmap of all wakeup sources
130 uint32_t causes = esp_sleep_get_wakeup_causes();
131 if (causes == 0) {
132 return WAKEUP_CAUSES[0]; // "undefined"
133 }
134 char *p = buffer.data();
135 char *end = p + buffer.size();
136 *p = '\0';
137 const char *sep = "";
138 for (unsigned i = 0; i < NUM_CAUSES && p < end; i++) {
139 if (causes & (1U << i)) {
140 size_t needed = strlen(sep) + strlen(WAKEUP_CAUSES[i]);
141 if (p + needed >= end) {
142 break;
143 }
144 p += snprintf(p, end - p, "%s%s", sep, WAKEUP_CAUSES[i]);
145 sep = ", ";
146 }
147 }
148 return buffer.data();
149#else
150 unsigned reason = esp_sleep_get_wakeup_cause();
151 if (reason < NUM_CAUSES) {
152 return WAKEUP_CAUSES[reason];
153 }
154 return "unknown source";
155#endif
156}
157
159 ESP_LOGCONFIG(TAG,
160 "Partition table:\n"
161 " %-12s %-4s %-8s %-10s %-10s",
162 "Name", "Type", "Subtype", "Address", "Size");
163 esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
164 while (it != NULL) {
165 const esp_partition_t *partition = esp_partition_get(it);
166 ESP_LOGCONFIG(TAG, " %-12s %-4d %-8d 0x%08" PRIX32 " 0x%08" PRIX32, partition->label, partition->type,
167 partition->subtype, partition->address, partition->size);
168 it = esp_partition_next(it);
169 }
170 esp_partition_iterator_release(it);
171}
172
173uint32_t DebugComponent::get_free_heap_() { return heap_caps_get_free_size(MALLOC_CAP_INTERNAL); }
174
175struct ChipFeature {
176 int bit;
177 const char *name;
178};
179
180static constexpr ChipFeature CHIP_FEATURES[] = {
181 {CHIP_FEATURE_BLE, "BLE"},
182 {CHIP_FEATURE_BT, "BT"},
183 {CHIP_FEATURE_EMB_FLASH, "EMB Flash"},
184 {CHIP_FEATURE_EMB_PSRAM, "EMB PSRAM"},
185 {CHIP_FEATURE_WIFI_BGN, "2.4GHz WiFi"},
186};
187
188size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE> buffer, size_t pos) {
189 constexpr size_t size = DEVICE_INFO_BUFFER_SIZE;
190 char *buf = buffer.data();
191
192#if defined(USE_ARDUINO)
193 const char *flash_mode;
194 switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
195 case FM_QIO:
196 flash_mode = "QIO";
197 break;
198 case FM_QOUT:
199 flash_mode = "QOUT";
200 break;
201 case FM_DIO:
202 flash_mode = "DIO";
203 break;
204 case FM_DOUT:
205 flash_mode = "DOUT";
206 break;
207 case FM_FAST_READ:
208 flash_mode = "FAST_READ";
209 break;
210 case FM_SLOW_READ:
211 flash_mode = "SLOW_READ";
212 break;
213 default:
214 flash_mode = "UNKNOWN";
215 }
216 uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT
217 uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT
218 pos = buf_append_printf(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed,
219 flash_mode);
220#endif
221
222 esp_chip_info_t info;
223 esp_chip_info(&info);
224 const char *model = ESPHOME_VARIANT;
225
226 // Build features string
227 pos = buf_append_str(buf, size, pos, "|Chip: ");
228 pos = buf_append_str(buf, size, pos, model);
229 pos = buf_append_str(buf, size, pos, " Features:");
230 bool first_feature = true;
231 for (const auto &feature : CHIP_FEATURES) {
232 if (info.features & feature.bit) {
233 pos = buf_append_str(buf, size, pos, first_feature ? "" : ", ");
234 pos = buf_append_str(buf, size, pos, feature.name);
235 first_feature = false;
236 info.features &= ~feature.bit;
237 }
238 }
239 if (info.features != 0) {
240 pos = buf_append_str(buf, size, pos, first_feature ? "" : ", ");
241 pos = buf_append_printf(buf, size, pos, "Other:0x%" PRIx32, info.features);
242 }
243 pos = buf_append_printf(buf, size, pos, " Cores:%u Revision:%u", info.cores, info.revision);
244
245 uint32_t cpu_freq_mhz = arch_get_cpu_freq_hz() / 1000000;
246 pos = buf_append_printf(buf, size, pos, "|CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
247
248 char reset_buffer[RESET_REASON_BUFFER_SIZE];
249 char wakeup_buffer[WAKEUP_CAUSE_BUFFER_SIZE];
250 const char *reset_reason = get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE>(reset_buffer));
251 const char *wakeup_cause = get_wakeup_cause_(std::span<char, WAKEUP_CAUSE_BUFFER_SIZE>(wakeup_buffer));
252
253 uint8_t mac[MAC_ADDRESS_SIZE];
255
256 ESP_LOGD(TAG,
257 "ESP32 debug info:\n"
258 " Chip: %s\n"
259 " Cores: %u\n"
260 " Revision: %u\n"
261 " CPU Frequency: %" PRIu32 " MHz\n"
262 " ESP-IDF Version: %s\n"
263 " EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X\n"
264 " Reset Reason: %s\n"
265 " Wakeup Cause: %s",
266 model, info.cores, info.revision, cpu_freq_mhz, esp_get_idf_version(), mac[0], mac[1], mac[2], mac[3],
267 mac[4], mac[5], reset_reason, wakeup_cause);
268#if defined(USE_ARDUINO)
269 ESP_LOGD(TAG, " Flash: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode);
270#endif
271 // Framework detection
272#ifdef USE_ARDUINO
273 ESP_LOGD(TAG, " Framework: Arduino");
274 pos = buf_append_str(buf, size, pos, "|Framework: Arduino");
275#else
276 ESP_LOGD(TAG, " Framework: ESP-IDF");
277 pos = buf_append_str(buf, size, pos, "|Framework: ESP-IDF");
278#endif
279
280 pos = buf_append_str(buf, size, pos, "|ESP-IDF: ");
281 pos = buf_append_str(buf, size, pos, esp_get_idf_version());
282 pos = buf_append_printf(buf, size, pos, "|EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3],
283 mac[4], mac[5]);
284 pos = buf_append_str(buf, size, pos, "|Reset: ");
285 pos = buf_append_str(buf, size, pos, reset_reason);
286 pos = buf_append_str(buf, size, pos, "|Wakeup: ");
287 pos = buf_append_str(buf, size, pos, wakeup_cause);
288
289 return pos;
290}
291
293#ifdef USE_SENSOR
294 uint32_t max_alloc = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
295 if (this->block_sensor_ != nullptr) {
296 this->block_sensor_->publish_state(max_alloc);
297 }
298 if (this->min_free_sensor_ != nullptr) {
299 this->min_free_sensor_->publish_state(heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL));
300 }
301 if (this->fragmentation_sensor_ != nullptr) {
302 uint32_t free_heap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
303 if (free_heap > 0) {
304 float fragmentation = 100.0f - (100.0f * max_alloc / free_heap);
305 this->fragmentation_sensor_->publish_state(fragmentation);
306 }
307 }
308 if (this->psram_sensor_ != nullptr) {
309 this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
310 }
311#endif
312}
313
314} // namespace esphome::debug
315
316#endif // USE_ESP32
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
Component * get_current_component()
constexpr const char * c_str() const
Definition string_ref.h:73
const char * get_wakeup_cause_(std::span< char, WAKEUP_CAUSE_BUFFER_SIZE > buffer)
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_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:68
const Component * component
Definition component.cpp:34
size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str)
Safely append a string to buffer, returning new position (capped at size).
Definition helpers.h:1111
constexpr uint32_t fnv1_hash_extend(uint32_t hash, T value)
Extend a FNV-1 hash with an integer (hashes each byte).
Definition helpers.h:813
uint16_t size
Definition helpers.cpp:25
ESPPreferences * global_preferences
uint32_t fnv1_hash(const char *str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:160
uint32_t arch_get_cpu_freq_hz()
Definition hal.cpp:63
size_t size_t pos
Definition helpers.h:1062
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:74
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t
ESPPreferenceObject make_preference(size_t, uint32_t, bool)
Definition preferences.h:24
bool sync()
Commit pending writes to flash.
Definition preferences.h:33
uint8_t end[39]
Definition sun_gtil2.cpp:17