ESPHome  2022.12.8
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 
9 #ifdef USE_ESP32
10 
11 #include <esp_heap_caps.h>
12 #include <esp_system.h>
13 
14 #if ESP_IDF_VERSION_MAJOR >= 4
15 #include <esp32/rom/rtc.h>
16 #else
17 #include <rom/rtc.h>
18 #endif
19 
20 #endif // USE_ESP32
21 
22 #ifdef USE_ARDUINO
23 #include <Esp.h>
24 #endif
25 
26 namespace esphome {
27 namespace debug {
28 
29 static const char *const TAG = "debug";
30 
31 static uint32_t get_free_heap() {
32 #if defined(USE_ESP8266)
33  return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
34 #elif defined(USE_ESP32)
35  return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
36 #endif
37 }
38 
40  std::string device_info;
41  std::string reset_reason;
42  device_info.reserve(256);
43 
44  ESP_LOGCONFIG(TAG, "Debug component:");
45 #ifdef USE_TEXT_SENSOR
46  LOG_TEXT_SENSOR(" ", "Device info", this->device_info_);
47 #endif // USE_TEXT_SENSOR
48 #ifdef USE_SENSOR
49  LOG_SENSOR(" ", "Free space on heap", this->free_sensor_);
50  LOG_SENSOR(" ", "Largest free heap block", this->block_sensor_);
51 #if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
52  LOG_SENSOR(" ", "Heap fragmentation", this->fragmentation_sensor_);
53 #endif // defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
54 #endif // USE_SENSOR
55 
56  ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
57  device_info += ESPHOME_VERSION;
58 
59  this->free_heap_ = get_free_heap();
60  ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_);
61 
62 #ifdef USE_ARDUINO
63  const char *flash_mode;
64  switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
65  case FM_QIO:
66  flash_mode = "QIO";
67  break;
68  case FM_QOUT:
69  flash_mode = "QOUT";
70  break;
71  case FM_DIO:
72  flash_mode = "DIO";
73  break;
74  case FM_DOUT:
75  flash_mode = "DOUT";
76  break;
77 #ifdef USE_ESP32
78  case FM_FAST_READ:
79  flash_mode = "FAST_READ";
80  break;
81  case FM_SLOW_READ:
82  flash_mode = "SLOW_READ";
83  break;
84 #endif
85  default:
86  flash_mode = "UNKNOWN";
87  }
88  ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
89  ESP.getFlashChipSize() / 1024, // NOLINT
90  ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT
91  device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT
92  "kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT
93  device_info += flash_mode;
94 #endif // USE_ARDUINO
95 
96 #ifdef USE_ESP32
97  esp_chip_info_t info;
98  esp_chip_info(&info);
99  const char *model;
100  switch (info.model) {
101  case CHIP_ESP32:
102  model = "ESP32";
103  break;
104  default:
105  model = "UNKNOWN";
106  }
107  std::string features;
108  if (info.features & CHIP_FEATURE_EMB_FLASH) {
109  features += "EMB_FLASH,";
110  info.features &= ~CHIP_FEATURE_EMB_FLASH;
111  }
112  if (info.features & CHIP_FEATURE_WIFI_BGN) {
113  features += "WIFI_BGN,";
114  info.features &= ~CHIP_FEATURE_WIFI_BGN;
115  }
116  if (info.features & CHIP_FEATURE_BLE) {
117  features += "BLE,";
118  info.features &= ~CHIP_FEATURE_BLE;
119  }
120  if (info.features & CHIP_FEATURE_BT) {
121  features += "BT,";
122  info.features &= ~CHIP_FEATURE_BT;
123  }
124  if (info.features)
125  features += "Other:" + format_hex(info.features);
126  ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
127  info.revision);
128  device_info += "|Chip: ";
129  device_info += model;
130  device_info += " Features:";
131  device_info += features;
132  device_info += " Cores:" + to_string(info.cores);
133  device_info += " Revision:" + to_string(info.revision);
134 
135  ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
136  device_info += "|ESP-IDF: ";
137  device_info += esp_get_idf_version();
138 
139  std::string mac = get_mac_address_pretty();
140  ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str());
141  device_info += "|EFuse MAC: ";
142  device_info += mac;
143 
144  switch (rtc_get_reset_reason(0)) {
145  case POWERON_RESET:
146  reset_reason = "Power On Reset";
147  break;
148  case SW_RESET:
149  reset_reason = "Software Reset Digital Core";
150  break;
151  case OWDT_RESET:
152  reset_reason = "Watch Dog Reset Digital Core";
153  break;
154  case DEEPSLEEP_RESET:
155  reset_reason = "Deep Sleep Reset Digital Core";
156  break;
157  case SDIO_RESET:
158  reset_reason = "SLC Module Reset Digital Core";
159  break;
160  case TG0WDT_SYS_RESET:
161  reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
162  break;
163  case TG1WDT_SYS_RESET:
164  reset_reason = "Timer Group 1 Watch Dog Reset Digital Core";
165  break;
166  case RTCWDT_SYS_RESET:
167  reset_reason = "RTC Watch Dog Reset Digital Core";
168  break;
169  case INTRUSION_RESET:
170  reset_reason = "Intrusion Reset CPU";
171  break;
172  case TGWDT_CPU_RESET:
173  reset_reason = "Timer Group Reset CPU";
174  break;
175  case SW_CPU_RESET:
176  reset_reason = "Software Reset CPU";
177  break;
178  case RTCWDT_CPU_RESET:
179  reset_reason = "RTC Watch Dog Reset CPU";
180  break;
181  case EXT_CPU_RESET:
182  reset_reason = "External CPU Reset";
183  break;
184  case RTCWDT_BROWN_OUT_RESET:
185  reset_reason = "Voltage Unstable Reset";
186  break;
187  case RTCWDT_RTC_RESET:
188  reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
189  break;
190  default:
191  reset_reason = "Unknown Reset Reason";
192  }
193  ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
194  device_info += "|Reset: ";
195  device_info += reset_reason;
196 
197  const char *wakeup_reason;
198  switch (rtc_get_wakeup_cause()) {
199  case NO_SLEEP:
200  wakeup_reason = "No Sleep";
201  break;
202  case EXT_EVENT0_TRIG:
203  wakeup_reason = "External Event 0";
204  break;
205  case EXT_EVENT1_TRIG:
206  wakeup_reason = "External Event 1";
207  break;
208  case GPIO_TRIG:
209  wakeup_reason = "GPIO";
210  break;
211  case TIMER_EXPIRE:
212  wakeup_reason = "Wakeup Timer";
213  break;
214  case SDIO_TRIG:
215  wakeup_reason = "SDIO";
216  break;
217  case MAC_TRIG:
218  wakeup_reason = "MAC";
219  break;
220  case UART0_TRIG:
221  wakeup_reason = "UART0";
222  break;
223  case UART1_TRIG:
224  wakeup_reason = "UART1";
225  break;
226  case TOUCH_TRIG:
227  wakeup_reason = "Touch";
228  break;
229  case SAR_TRIG:
230  wakeup_reason = "SAR";
231  break;
232  case BT_TRIG:
233  wakeup_reason = "BT";
234  break;
235  default:
236  wakeup_reason = "Unknown";
237  }
238  ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason);
239  device_info += "|Wakeup: ";
240  device_info += wakeup_reason;
241 #endif
242 
243 #if defined(USE_ESP8266) && !defined(CLANG_TIDY)
244  ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId());
245  ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion());
246  ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str());
247  ESP_LOGD(TAG, "Boot Version=%u Mode=%u", ESP.getBootVersion(), ESP.getBootMode());
248  ESP_LOGD(TAG, "CPU Frequency: %u", ESP.getCpuFreqMHz());
249  ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
250  ESP_LOGD(TAG, "Reset Reason: %s", ESP.getResetReason().c_str());
251  ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
252 
253  device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
254  device_info += "|SDK: ";
255  device_info += ESP.getSdkVersion();
256  device_info += "|Core: ";
257  device_info += ESP.getCoreVersion().c_str();
258  device_info += "|Boot: ";
259  device_info += to_string(ESP.getBootVersion());
260  device_info += "|Mode: " + to_string(ESP.getBootMode());
261  device_info += "|CPU: " + to_string(ESP.getCpuFreqMHz());
262  device_info += "|Flash: 0x" + format_hex(ESP.getFlashChipId());
263  device_info += "|Reset: ";
264  device_info += ESP.getResetReason().c_str();
265  device_info += "|";
266  device_info += ESP.getResetInfo().c_str();
267 
268  reset_reason = ESP.getResetReason().c_str();
269 #endif
270 
271 #ifdef USE_TEXT_SENSOR
272  if (this->device_info_ != nullptr) {
273  if (device_info.length() > 255)
274  device_info.resize(255);
275  this->device_info_->publish_state(device_info);
276  }
277  if (this->reset_reason_ != nullptr) {
278  this->reset_reason_->publish_state(reset_reason);
279  }
280 #endif // USE_TEXT_SENSOR
281 }
282 
284  // log when free heap space has halved
285  uint32_t new_free_heap = get_free_heap();
286  if (new_free_heap < this->free_heap_ / 2) {
287  this->free_heap_ = new_free_heap;
288  ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_);
289  this->status_momentary_warning("heap", 1000);
290  }
291 
292 #ifdef USE_SENSOR
293  // calculate loop time - from last call to this one
294  if (this->loop_time_sensor_ != nullptr) {
295  uint32_t now = millis();
296  uint32_t loop_time = now - this->last_loop_timetag_;
297  this->max_loop_time_ = std::max(this->max_loop_time_, loop_time);
298  this->last_loop_timetag_ = now;
299  }
300 #endif // USE_SENSOR
301 }
302 
304 #ifdef USE_SENSOR
305  if (this->free_sensor_ != nullptr) {
306  this->free_sensor_->publish_state(get_free_heap());
307  }
308 
309  if (this->block_sensor_ != nullptr) {
310 #if defined(USE_ESP8266)
311  // NOLINTNEXTLINE(readability-static-accessed-through-instance)
312  this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize());
313 #elif defined(USE_ESP32)
314  this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
315 #endif
316  }
317 
318 #if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
319  if (this->fragmentation_sensor_ != nullptr) {
320  // NOLINTNEXTLINE(readability-static-accessed-through-instance)
321  this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation());
322  }
323 #endif
324 
325  if (this->loop_time_sensor_ != nullptr) {
327  this->max_loop_time_ = 0;
328  }
329 #endif // USE_SENSOR
330 }
331 
333 
334 } // namespace debug
335 } // 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:225
void status_momentary_warning(const std::string &name, uint32_t length=5000)
Definition: component.cpp:151
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:12
sensor::Sensor * loop_time_sensor_
float get_setup_priority() const override
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:26
sensor::Sensor * fragmentation_sensor_
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:72
text_sensor::TextSensor * reset_reason_
std::string to_string(int value)
Definition: helpers.cpp:42
Definition: a4988.cpp:4
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
Definition: helpers.cpp:447