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