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