ESPHome 2026.4.0
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 {
20namespace 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_printf(buf, size, pos, "|Chip: %s Features:", model);
228 bool first_feature = true;
229 for (const auto &feature : CHIP_FEATURES) {
230 if (info.features & feature.bit) {
231 pos = buf_append_printf(buf, size, pos, "%s%s", first_feature ? "" : ", ", feature.name);
232 first_feature = false;
233 info.features &= ~feature.bit;
234 }
235 }
236 if (info.features != 0) {
237 pos = buf_append_printf(buf, size, pos, "%sOther:0x%" PRIx32, first_feature ? "" : ", ", info.features);
238 }
239 pos = buf_append_printf(buf, size, pos, " Cores:%u Revision:%u", info.cores, info.revision);
240
241 uint32_t cpu_freq_mhz = arch_get_cpu_freq_hz() / 1000000;
242 pos = buf_append_printf(buf, size, pos, "|CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
243
244 char reset_buffer[RESET_REASON_BUFFER_SIZE];
245 char wakeup_buffer[WAKEUP_CAUSE_BUFFER_SIZE];
246 const char *reset_reason = get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE>(reset_buffer));
247 const char *wakeup_cause = get_wakeup_cause_(std::span<char, WAKEUP_CAUSE_BUFFER_SIZE>(wakeup_buffer));
248
249 uint8_t mac[6];
251
252 ESP_LOGD(TAG,
253 "ESP32 debug info:\n"
254 " Chip: %s\n"
255 " Cores: %u\n"
256 " Revision: %u\n"
257 " CPU Frequency: %" PRIu32 " MHz\n"
258 " ESP-IDF Version: %s\n"
259 " EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X\n"
260 " Reset Reason: %s\n"
261 " Wakeup Cause: %s",
262 model, info.cores, info.revision, cpu_freq_mhz, esp_get_idf_version(), mac[0], mac[1], mac[2], mac[3],
263 mac[4], mac[5], reset_reason, wakeup_cause);
264#if defined(USE_ARDUINO)
265 ESP_LOGD(TAG, " Flash: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode);
266#endif
267 // Framework detection
268#ifdef USE_ARDUINO
269 ESP_LOGD(TAG, " Framework: Arduino");
270 pos = buf_append_printf(buf, size, pos, "|Framework: Arduino");
271#else
272 ESP_LOGD(TAG, " Framework: ESP-IDF");
273 pos = buf_append_printf(buf, size, pos, "|Framework: ESP-IDF");
274#endif
275
276 pos = buf_append_printf(buf, size, pos, "|ESP-IDF: %s", esp_get_idf_version());
277 pos = buf_append_printf(buf, size, pos, "|EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3],
278 mac[4], mac[5]);
279 pos = buf_append_printf(buf, size, pos, "|Reset: %s", reset_reason);
280 pos = buf_append_printf(buf, size, pos, "|Wakeup: %s", wakeup_cause);
281
282 return pos;
283}
284
286#ifdef USE_SENSOR
287 uint32_t max_alloc = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
288 if (this->block_sensor_ != nullptr) {
289 this->block_sensor_->publish_state(max_alloc);
290 }
291 if (this->min_free_sensor_ != nullptr) {
292 this->min_free_sensor_->publish_state(heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL));
293 }
294 if (this->fragmentation_sensor_ != nullptr) {
295 uint32_t free_heap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
296 if (free_heap > 0) {
297 float fragmentation = 100.0f - (100.0f * max_alloc / free_heap);
298 this->fragmentation_sensor_->publish_state(fragmentation);
299 }
300 }
301 if (this->psram_sensor_ != nullptr) {
302 this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
303 }
304#endif
305}
306
307} // namespace debug
308} // namespace esphome
309#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
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
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:777
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 core.cpp:57
size_t size_t pos
Definition helpers.h:1082
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