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