ESPHome  2024.3.1
debug_component.cpp
Go to the documentation of this file.
1 #include "debug_component.h"
2 
3 #include <algorithm>
4 #include "esphome/core/log.h"
5 #include "esphome/core/hal.h"
6 #include "esphome/core/helpers.h"
7 #include "esphome/core/version.h"
8 #include <cinttypes>
9 #include <climits>
10 
11 #ifdef USE_ESP32
12 
13 #include <esp_heap_caps.h>
14 #include <esp_system.h>
15 
16 #include <esp_chip_info.h>
17 #if defined(USE_ESP32_VARIANT_ESP32)
18 #include <esp32/rom/rtc.h>
19 #elif defined(USE_ESP32_VARIANT_ESP32C3)
20 #include <esp32c3/rom/rtc.h>
21 #elif defined(USE_ESP32_VARIANT_ESP32C6)
22 #include <esp32c6/rom/rtc.h>
23 #elif defined(USE_ESP32_VARIANT_ESP32S2)
24 #include <esp32s2/rom/rtc.h>
25 #elif defined(USE_ESP32_VARIANT_ESP32S3)
26 #include <esp32s3/rom/rtc.h>
27 #endif
28 
29 #endif // USE_ESP32
30 
31 #ifdef USE_ARDUINO
32 #ifdef USE_RP2040
33 #include <Arduino.h>
34 #elif defined(USE_ESP32) || defined(USE_ESP8266)
35 #include <Esp.h>
36 #endif
37 #endif
38 
39 namespace esphome {
40 namespace debug {
41 
42 static const char *const TAG = "debug";
43 
44 static uint32_t get_free_heap() {
45 #if defined(USE_ESP8266)
46  return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
47 #elif defined(USE_ESP32)
48  return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
49 #elif defined(USE_RP2040)
50  return rp2040.getFreeHeap();
51 #elif defined(USE_LIBRETINY)
52  return lt_heap_get_free();
53 #elif defined(USE_HOST)
54  return INT_MAX;
55 #endif
56 }
57 
59 #ifndef ESPHOME_LOG_HAS_DEBUG
60  return; // Can't log below if debug logging is disabled
61 #endif
62 
63  std::string device_info;
64  std::string reset_reason;
65  device_info.reserve(256);
66 
67  ESP_LOGCONFIG(TAG, "Debug component:");
68 #ifdef USE_TEXT_SENSOR
69  LOG_TEXT_SENSOR(" ", "Device info", this->device_info_);
70 #endif // USE_TEXT_SENSOR
71 #ifdef USE_SENSOR
72  LOG_SENSOR(" ", "Free space on heap", this->free_sensor_);
73  LOG_SENSOR(" ", "Largest free heap block", this->block_sensor_);
74 #if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
75  LOG_SENSOR(" ", "Heap fragmentation", this->fragmentation_sensor_);
76 #endif // defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
77 #endif // USE_SENSOR
78 
79  ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
80  device_info += ESPHOME_VERSION;
81 
82  this->free_heap_ = get_free_heap();
83  ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
84 
85 #if defined(USE_ARDUINO) && (defined(USE_ESP32) || defined(USE_ESP8266))
86  const char *flash_mode;
87  switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
88  case FM_QIO:
89  flash_mode = "QIO";
90  break;
91  case FM_QOUT:
92  flash_mode = "QOUT";
93  break;
94  case FM_DIO:
95  flash_mode = "DIO";
96  break;
97  case FM_DOUT:
98  flash_mode = "DOUT";
99  break;
100 #ifdef USE_ESP32
101  case FM_FAST_READ:
102  flash_mode = "FAST_READ";
103  break;
104  case FM_SLOW_READ:
105  flash_mode = "SLOW_READ";
106  break;
107 #endif
108  default:
109  flash_mode = "UNKNOWN";
110  }
111  ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
112  ESP.getFlashChipSize() / 1024, // NOLINT
113  ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT
114  device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT
115  "kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT
116  device_info += flash_mode;
117 #endif // USE_ARDUINO && (USE_ESP32 || USE_ESP8266)
118 
119 #ifdef USE_ESP32
120  esp_chip_info_t info;
121  esp_chip_info(&info);
122  const char *model;
123 #if defined(USE_ESP32_VARIANT_ESP32)
124  model = "ESP32";
125 #elif defined(USE_ESP32_VARIANT_ESP32C3)
126  model = "ESP32-C3";
127 #elif defined(USE_ESP32_VARIANT_ESP32C6)
128  model = "ESP32-C6";
129 #elif defined(USE_ESP32_VARIANT_ESP32S2)
130  model = "ESP32-S2";
131 #elif defined(USE_ESP32_VARIANT_ESP32S3)
132  model = "ESP32-S3";
133 #elif defined(USE_ESP32_VARIANT_ESP32H2)
134  model = "ESP32-H2";
135 #else
136  model = "UNKNOWN";
137 #endif
138  std::string features;
139  if (info.features & CHIP_FEATURE_EMB_FLASH) {
140  features += "EMB_FLASH,";
141  info.features &= ~CHIP_FEATURE_EMB_FLASH;
142  }
143  if (info.features & CHIP_FEATURE_WIFI_BGN) {
144  features += "WIFI_BGN,";
145  info.features &= ~CHIP_FEATURE_WIFI_BGN;
146  }
147  if (info.features & CHIP_FEATURE_BLE) {
148  features += "BLE,";
149  info.features &= ~CHIP_FEATURE_BLE;
150  }
151  if (info.features & CHIP_FEATURE_BT) {
152  features += "BT,";
153  info.features &= ~CHIP_FEATURE_BT;
154  }
155  if (info.features & CHIP_FEATURE_EMB_PSRAM) {
156  features += "EMB_PSRAM,";
157  info.features &= ~CHIP_FEATURE_EMB_PSRAM;
158  }
159  if (info.features)
160  features += "Other:" + format_hex(info.features);
161  ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
162  info.revision);
163  device_info += "|Chip: ";
164  device_info += model;
165  device_info += " Features:";
166  device_info += features;
167  device_info += " Cores:" + to_string(info.cores);
168  device_info += " Revision:" + to_string(info.revision);
169 
170  ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
171  device_info += "|ESP-IDF: ";
172  device_info += esp_get_idf_version();
173 
174  std::string mac = get_mac_address_pretty();
175  ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str());
176  device_info += "|EFuse MAC: ";
177  device_info += mac;
178 
179  switch (rtc_get_reset_reason(0)) {
180  case POWERON_RESET:
181  reset_reason = "Power On Reset";
182  break;
183 #if defined(USE_ESP32_VARIANT_ESP32)
184  case SW_RESET:
185 #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
186  case RTC_SW_SYS_RESET:
187 #endif
188  reset_reason = "Software Reset Digital Core";
189  break;
190 #if defined(USE_ESP32_VARIANT_ESP32)
191  case OWDT_RESET:
192  reset_reason = "Watch Dog Reset Digital Core";
193  break;
194 #endif
195  case DEEPSLEEP_RESET:
196  reset_reason = "Deep Sleep Reset Digital Core";
197  break;
198 #if defined(USE_ESP32_VARIANT_ESP32)
199  case SDIO_RESET:
200  reset_reason = "SLC Module Reset Digital Core";
201  break;
202 #endif
203  case TG0WDT_SYS_RESET:
204  reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
205  break;
206  case TG1WDT_SYS_RESET:
207  reset_reason = "Timer Group 1 Watch Dog Reset Digital Core";
208  break;
209  case RTCWDT_SYS_RESET:
210  reset_reason = "RTC Watch Dog Reset Digital Core";
211  break;
212 #if !defined(USE_ESP32_VARIANT_ESP32C6)
213  case INTRUSION_RESET:
214  reset_reason = "Intrusion Reset CPU";
215  break;
216 #endif
217 #if defined(USE_ESP32_VARIANT_ESP32)
218  case TGWDT_CPU_RESET:
219  reset_reason = "Timer Group Reset CPU";
220  break;
221 #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
222  case TG0WDT_CPU_RESET:
223  reset_reason = "Timer Group 0 Reset CPU";
224  break;
225 #endif
226 #if defined(USE_ESP32_VARIANT_ESP32)
227  case SW_CPU_RESET:
228 #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
229  case RTC_SW_CPU_RESET:
230 #endif
231  reset_reason = "Software Reset CPU";
232  break;
233  case RTCWDT_CPU_RESET:
234  reset_reason = "RTC Watch Dog Reset CPU";
235  break;
236 #if defined(USE_ESP32_VARIANT_ESP32)
237  case EXT_CPU_RESET:
238  reset_reason = "External CPU Reset";
239  break;
240 #endif
241  case RTCWDT_BROWN_OUT_RESET:
242  reset_reason = "Voltage Unstable Reset";
243  break;
244  case RTCWDT_RTC_RESET:
245  reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
246  break;
247 #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
248  case TG1WDT_CPU_RESET:
249  reset_reason = "Timer Group 1 Reset CPU";
250  break;
251  case SUPER_WDT_RESET:
252  reset_reason = "Super Watchdog Reset Digital Core And RTC Module";
253  break;
254  case GLITCH_RTC_RESET:
255  reset_reason = "Glitch Reset Digital Core And RTC Module";
256  break;
257  case EFUSE_RESET:
258  reset_reason = "eFuse Reset Digital Core";
259  break;
260 #endif
261 #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3)
262  case USB_UART_CHIP_RESET:
263  reset_reason = "USB UART Reset Digital Core";
264  break;
265  case USB_JTAG_CHIP_RESET:
266  reset_reason = "USB JTAG Reset Digital Core";
267  break;
268  case POWER_GLITCH_RESET:
269  reset_reason = "Power Glitch Reset Digital Core And RTC Module";
270  break;
271 #endif
272  default:
273  reset_reason = "Unknown Reset Reason";
274  }
275  ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
276  device_info += "|Reset: ";
277  device_info += reset_reason;
278 
279  const char *wakeup_reason;
280  switch (rtc_get_wakeup_cause()) {
281  case NO_SLEEP:
282  wakeup_reason = "No Sleep";
283  break;
284  case EXT_EVENT0_TRIG:
285  wakeup_reason = "External Event 0";
286  break;
287  case EXT_EVENT1_TRIG:
288  wakeup_reason = "External Event 1";
289  break;
290  case GPIO_TRIG:
291  wakeup_reason = "GPIO";
292  break;
293  case TIMER_EXPIRE:
294  wakeup_reason = "Wakeup Timer";
295  break;
296  case SDIO_TRIG:
297  wakeup_reason = "SDIO";
298  break;
299  case MAC_TRIG:
300  wakeup_reason = "MAC";
301  break;
302  case UART0_TRIG:
303  wakeup_reason = "UART0";
304  break;
305  case UART1_TRIG:
306  wakeup_reason = "UART1";
307  break;
308  case TOUCH_TRIG:
309  wakeup_reason = "Touch";
310  break;
311  case SAR_TRIG:
312  wakeup_reason = "SAR";
313  break;
314  case BT_TRIG:
315  wakeup_reason = "BT";
316  break;
317  default:
318  wakeup_reason = "Unknown";
319  }
320  ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason);
321  device_info += "|Wakeup: ";
322  device_info += wakeup_reason;
323 #endif
324 
325 #if defined(USE_ESP8266) && !defined(CLANG_TIDY)
326  ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId());
327  ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion());
328  ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str());
329  ESP_LOGD(TAG, "Boot Version=%u Mode=%u", ESP.getBootVersion(), ESP.getBootMode());
330  ESP_LOGD(TAG, "CPU Frequency: %u", ESP.getCpuFreqMHz());
331  ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
332  ESP_LOGD(TAG, "Reset Reason: %s", ESP.getResetReason().c_str());
333  ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
334 
335  device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
336  device_info += "|SDK: ";
337  device_info += ESP.getSdkVersion();
338  device_info += "|Core: ";
339  device_info += ESP.getCoreVersion().c_str();
340  device_info += "|Boot: ";
341  device_info += to_string(ESP.getBootVersion());
342  device_info += "|Mode: " + to_string(ESP.getBootMode());
343  device_info += "|CPU: " + to_string(ESP.getCpuFreqMHz());
344  device_info += "|Flash: 0x" + format_hex(ESP.getFlashChipId());
345  device_info += "|Reset: ";
346  device_info += ESP.getResetReason().c_str();
347  device_info += "|";
348  device_info += ESP.getResetInfo().c_str();
349 
350  reset_reason = ESP.getResetReason().c_str();
351 #endif
352 
353 #ifdef USE_RP2040
354  ESP_LOGD(TAG, "CPU Frequency: %u", rp2040.f_cpu());
355  device_info += "CPU Frequency: " + to_string(rp2040.f_cpu());
356 #endif // USE_RP2040
357 
358 #ifdef USE_LIBRETINY
359  ESP_LOGD(TAG, "LibreTiny Version: %s", lt_get_version());
360  ESP_LOGD(TAG, "Chip: %s (%04x) @ %u MHz", lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz());
361  ESP_LOGD(TAG, "Chip ID: 0x%06X", lt_cpu_get_mac_id());
362  ESP_LOGD(TAG, "Board: %s", lt_get_board_code());
363  ESP_LOGD(TAG, "Flash: %u KiB / RAM: %u KiB", lt_flash_get_size() / 1024, lt_ram_get_size() / 1024);
364  ESP_LOGD(TAG, "Reset Reason: %s", lt_get_reboot_reason_name(lt_get_reboot_reason()));
365 
366  device_info += "|Version: ";
367  device_info += LT_BANNER_STR + 10;
368  device_info += "|Reset Reason: ";
369  device_info += lt_get_reboot_reason_name(lt_get_reboot_reason());
370  device_info += "|Chip Name: ";
371  device_info += lt_cpu_get_model_name();
372  device_info += "|Chip ID: 0x" + format_hex(lt_cpu_get_mac_id());
373  device_info += "|Flash: " + to_string(lt_flash_get_size() / 1024) + " KiB";
374  device_info += "|RAM: " + to_string(lt_ram_get_size() / 1024) + " KiB";
375 
376  reset_reason = lt_get_reboot_reason_name(lt_get_reboot_reason());
377 #endif // USE_LIBRETINY
378 
379 #ifdef USE_TEXT_SENSOR
380  if (this->device_info_ != nullptr) {
381  if (device_info.length() > 255)
382  device_info.resize(255);
383  this->device_info_->publish_state(device_info);
384  }
385  if (this->reset_reason_ != nullptr) {
386  this->reset_reason_->publish_state(reset_reason);
387  }
388 #endif // USE_TEXT_SENSOR
389 }
390 
392  // log when free heap space has halved
393  uint32_t new_free_heap = get_free_heap();
394  if (new_free_heap < this->free_heap_ / 2) {
395  this->free_heap_ = new_free_heap;
396  ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
397  this->status_momentary_warning("heap", 1000);
398  }
399 
400 #ifdef USE_SENSOR
401  // calculate loop time - from last call to this one
402  if (this->loop_time_sensor_ != nullptr) {
403  uint32_t now = millis();
404  uint32_t loop_time = now - this->last_loop_timetag_;
405  this->max_loop_time_ = std::max(this->max_loop_time_, loop_time);
406  this->last_loop_timetag_ = now;
407  }
408 #endif // USE_SENSOR
409 }
410 
412 #ifdef USE_SENSOR
413  if (this->free_sensor_ != nullptr) {
414  this->free_sensor_->publish_state(get_free_heap());
415  }
416 
417  if (this->block_sensor_ != nullptr) {
418 #if defined(USE_ESP8266)
419  // NOLINTNEXTLINE(readability-static-accessed-through-instance)
420  this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize());
421 #elif defined(USE_ESP32)
422  this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
423 #elif defined(USE_LIBRETINY)
424  this->block_sensor_->publish_state(lt_heap_get_max_alloc());
425 #endif
426  }
427 
428 #if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
429  if (this->fragmentation_sensor_ != nullptr) {
430  // NOLINTNEXTLINE(readability-static-accessed-through-instance)
431  this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation());
432  }
433 #endif
434 
435  if (this->loop_time_sensor_ != nullptr) {
437  this->max_loop_time_ = 0;
438  }
439 
440 #ifdef USE_ESP32
441  if (this->psram_sensor_ != nullptr) {
442  this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
443  }
444 #endif // USE_ESP32
445 #endif // USE_SENSOR
446 }
447 
449 
450 } // namespace debug
451 } // namespace esphome
std::string format_hex(const uint8_t *data, size_t length)
Format the byte array data of length len in lowercased hex.
Definition: helpers.cpp:349
void status_momentary_warning(const std::string &name, uint32_t length=5000)
Definition: component.cpp:173
text_sensor::TextSensor * device_info_
const float LATE
For components that should be initialized at the very end of the setup process.
Definition: component.cpp:28
void publish_state(const std::string &state)
Definition: text_sensor.cpp:9
sensor::Sensor * loop_time_sensor_
float get_setup_priority() const override
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
sensor::Sensor * fragmentation_sensor_
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
text_sensor::TextSensor * reset_reason_
std::string to_string(int value)
Definition: helpers.cpp:82
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
Definition: helpers.cpp:592