ESPHome 2026.2.3
Loading...
Searching...
No Matches
debug_zephyr.cpp
Go to the documentation of this file.
1#include "debug_component.h"
2#ifdef USE_ZEPHYR
3#include <climits>
4#include "esphome/core/log.h"
5#include <zephyr/drivers/hwinfo.h>
6#include <hal/nrf_power.h>
7#include <cstdint>
8#include <zephyr/storage/flash_map.h>
9
10#define BOOTLOADER_VERSION_REGISTER NRF_TIMER2->CC[0]
11
12namespace esphome::debug {
13
14static const char *const TAG = "debug";
15constexpr std::uintptr_t MBR_PARAM_PAGE_ADDR = 0xFFC;
16constexpr std::uintptr_t MBR_BOOTLOADER_ADDR = 0xFF8;
17
18static size_t append_reset_reason(char *buf, size_t size, size_t pos, bool set, const char *reason) {
19 if (!set) {
20 return pos;
21 }
22 if (pos > 0) {
23 pos = buf_append_printf(buf, size, pos, ", ");
24 }
25 return buf_append_printf(buf, size, pos, "%s", reason);
26}
27
28static inline uint32_t read_mem_u32(uintptr_t addr) {
29 return *reinterpret_cast<volatile uint32_t *>(addr); // NOLINT(performance-no-int-to-ptr)
30}
31
32static inline uint8_t read_mem_u8(uintptr_t addr) {
33 return *reinterpret_cast<volatile uint8_t *>(addr); // NOLINT(performance-no-int-to-ptr)
34}
35
36// defines from https://github.com/adafruit/Adafruit_nRF52_Bootloader which prints those information
37constexpr uint32_t SD_MAGIC_NUMBER = 0x51B1E5DB;
38constexpr uintptr_t MBR_SIZE = 0x1000;
39constexpr uintptr_t SOFTDEVICE_INFO_STRUCT_OFFSET = 0x2000;
40constexpr uintptr_t SD_ID_OFFSET = SOFTDEVICE_INFO_STRUCT_OFFSET + 0x10;
42
43static inline bool is_sd_present() {
44 return read_mem_u32(SOFTDEVICE_INFO_STRUCT_OFFSET + MBR_SIZE + 4) == SD_MAGIC_NUMBER;
45}
46static inline uint32_t sd_id_get() {
48 return read_mem_u32(MBR_SIZE + SD_ID_OFFSET);
49 }
50 return 0;
51}
52static inline uint32_t sd_version_get() {
54 return read_mem_u32(MBR_SIZE + SD_VERSION_OFFSET);
55 }
56 return 0;
57}
58
59const char *DebugComponent::get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
60 char *buf = buffer.data();
61 const size_t size = RESET_REASON_BUFFER_SIZE;
62
63 uint32_t cause;
64 auto ret = hwinfo_get_reset_cause(&cause);
65 if (ret) {
66 ESP_LOGE(TAG, "Unable to get reset cause: %d", ret);
67 buf[0] = '\0';
68 return buf;
69 }
70 size_t pos = 0;
71
72 pos = append_reset_reason(buf, size, pos, cause & RESET_PIN, "External pin");
73 pos = append_reset_reason(buf, size, pos, cause & RESET_SOFTWARE, "Software reset");
74 pos = append_reset_reason(buf, size, pos, cause & RESET_BROWNOUT, "Brownout (drop in voltage)");
75 pos = append_reset_reason(buf, size, pos, cause & RESET_POR, "Power-on reset (POR)");
76 pos = append_reset_reason(buf, size, pos, cause & RESET_WATCHDOG, "Watchdog timer expiration");
77 pos = append_reset_reason(buf, size, pos, cause & RESET_DEBUG, "Debug event");
78 pos = append_reset_reason(buf, size, pos, cause & RESET_SECURITY, "Security violation");
79 pos = append_reset_reason(buf, size, pos, cause & RESET_LOW_POWER_WAKE, "Waking up from low power mode");
80 pos = append_reset_reason(buf, size, pos, cause & RESET_CPU_LOCKUP, "CPU lock-up detected");
81 pos = append_reset_reason(buf, size, pos, cause & RESET_PARITY, "Parity error");
82 pos = append_reset_reason(buf, size, pos, cause & RESET_PLL, "PLL error");
83 pos = append_reset_reason(buf, size, pos, cause & RESET_CLOCK, "Clock error");
84 pos = append_reset_reason(buf, size, pos, cause & RESET_HARDWARE, "Hardware reset");
85 pos = append_reset_reason(buf, size, pos, cause & RESET_USER, "User reset");
86 pos = append_reset_reason(buf, size, pos, cause & RESET_TEMPERATURE, "Temperature reset");
87
88 // Ensure null termination if nothing was written
89 if (pos == 0) {
90 buf[0] = '\0';
91 }
92
93 ESP_LOGD(TAG, "Reset Reason: %s", buf);
94 return buf;
95}
96
97const char *DebugComponent::get_wakeup_cause_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
98 // Zephyr doesn't have detailed wakeup cause like ESP32
99 return "";
100}
101
102uint32_t DebugComponent::get_free_heap_() { return INT_MAX; }
103
104static void fa_cb(const struct flash_area *fa, void *user_data) {
105#if CONFIG_FLASH_MAP_LABELS
106 const char *fa_label = flash_area_label(fa);
107
108 if (fa_label == nullptr) {
109 fa_label = "-";
110 }
111 ESP_LOGCONFIG(TAG, "%2d 0x%0*" PRIxPTR " %-26s %-24.24s 0x%-10x 0x%-12x", (int) fa->fa_id,
112 sizeof(uintptr_t) * 2, (uintptr_t) fa->fa_dev, fa->fa_dev->name, fa_label, (uint32_t) fa->fa_off,
113 fa->fa_size);
114#else
115 ESP_LOGCONFIG(TAG, "%2d 0x%0*" PRIxPTR " %-26s 0x%-10x 0x%-12x", (int) fa->fa_id, sizeof(uintptr_t) * 2,
116 (uintptr_t) fa->fa_dev, fa->fa_dev->name, (uint32_t) fa->fa_off, fa->fa_size);
117#endif
118}
119
121#if CONFIG_FLASH_MAP_LABELS
122 ESP_LOGCONFIG(TAG, "ID | Device | Device Name "
123 "| Label | Offset | Size\n"
124 "--------------------------------------------"
125 "-----------------------------------------------");
126#else
127 ESP_LOGCONFIG(TAG, "ID | Device | Device Name "
128 "| Offset | Size\n"
129 "-----------------------------------------"
130 "------------------------------");
131#endif
132 flash_area_foreach(fa_cb, nullptr);
133}
134
135static const char *regout0_to_str(uint32_t value) {
136 switch (value) {
137 case (UICR_REGOUT0_VOUT_DEFAULT):
138 return "1.8V (default)";
139 case (UICR_REGOUT0_VOUT_1V8):
140 return "1.8V";
141 case (UICR_REGOUT0_VOUT_2V1):
142 return "2.1V";
143 case (UICR_REGOUT0_VOUT_2V4):
144 return "2.4V";
145 case (UICR_REGOUT0_VOUT_2V7):
146 return "2.7V";
147 case (UICR_REGOUT0_VOUT_3V0):
148 return "3.0V";
149 case (UICR_REGOUT0_VOUT_3V3):
150 return "3.3V";
151 }
152 return "???V";
153}
154
155size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE> buffer, size_t pos) {
156 constexpr size_t size = DEVICE_INFO_BUFFER_SIZE;
157 char *buf = buffer.data();
158
159 // Main supply status
160 const char *supply_status =
161 (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_NORMAL) ? "Normal voltage." : "High voltage.";
162 ESP_LOGD(TAG, "Main supply status: %s", supply_status);
163 pos = buf_append_printf(buf, size, pos, "|Main supply status: %s", supply_status);
164
165 // Regulator stage 0
166 if (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_HIGH) {
167 const char *reg0_type = nrf_power_dcdcen_vddh_get(NRF_POWER) ? "DC/DC" : "LDO";
168 const char *reg0_voltage = regout0_to_str((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) >> UICR_REGOUT0_VOUT_Pos);
169 ESP_LOGD(TAG, "Regulator stage 0: %s, %s", reg0_type, reg0_voltage);
170 pos = buf_append_printf(buf, size, pos, "|Regulator stage 0: %s, %s", reg0_type, reg0_voltage);
171#ifdef USE_NRF52_REG0_VOUT
172 if ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) >> UICR_REGOUT0_VOUT_Pos != USE_NRF52_REG0_VOUT) {
173 ESP_LOGE(TAG, "Regulator stage 0: expected %s", regout0_to_str(USE_NRF52_REG0_VOUT));
174 }
175#endif
176 } else {
177 ESP_LOGD(TAG, "Regulator stage 0: disabled");
178 pos = buf_append_printf(buf, size, pos, "|Regulator stage 0: disabled");
179 }
180
181 // Regulator stage 1
182 const char *reg1_type = nrf_power_dcdcen_get(NRF_POWER) ? "DC/DC" : "LDO";
183 ESP_LOGD(TAG, "Regulator stage 1: %s", reg1_type);
184 pos = buf_append_printf(buf, size, pos, "|Regulator stage 1: %s", reg1_type);
185
186 // USB power state
187 const char *usb_state;
188 if (nrf_power_usbregstatus_vbusdet_get(NRF_POWER)) {
189 if (nrf_power_usbregstatus_outrdy_get(NRF_POWER)) {
190 usb_state = "ready";
191 } else {
192 usb_state = "connected (regulator is not ready)";
193 }
194 } else {
195 usb_state = "disconnected";
196 }
197 ESP_LOGD(TAG, "USB power state: %s", usb_state);
198 pos = buf_append_printf(buf, size, pos, "|USB power state: %s", usb_state);
199
200 // Power-fail comparator
201 bool enabled;
202 nrf_power_pof_thr_t pof_thr = nrf_power_pofcon_get(NRF_POWER, &enabled);
203 if (enabled) {
204 const char *pof_voltage;
205 switch (pof_thr) {
206 case POWER_POFCON_THRESHOLD_V17:
207 pof_voltage = "1.7V";
208 break;
209 case POWER_POFCON_THRESHOLD_V18:
210 pof_voltage = "1.8V";
211 break;
212 case POWER_POFCON_THRESHOLD_V19:
213 pof_voltage = "1.9V";
214 break;
215 case POWER_POFCON_THRESHOLD_V20:
216 pof_voltage = "2.0V";
217 break;
218 case POWER_POFCON_THRESHOLD_V21:
219 pof_voltage = "2.1V";
220 break;
221 case POWER_POFCON_THRESHOLD_V22:
222 pof_voltage = "2.2V";
223 break;
224 case POWER_POFCON_THRESHOLD_V23:
225 pof_voltage = "2.3V";
226 break;
227 case POWER_POFCON_THRESHOLD_V24:
228 pof_voltage = "2.4V";
229 break;
230 case POWER_POFCON_THRESHOLD_V25:
231 pof_voltage = "2.5V";
232 break;
233 case POWER_POFCON_THRESHOLD_V26:
234 pof_voltage = "2.6V";
235 break;
236 case POWER_POFCON_THRESHOLD_V27:
237 pof_voltage = "2.7V";
238 break;
239 case POWER_POFCON_THRESHOLD_V28:
240 pof_voltage = "2.8V";
241 break;
242 default:
243 pof_voltage = "???V";
244 break;
245 }
246
247 if (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_HIGH) {
248 const char *vddh_voltage;
249 switch (nrf_power_pofcon_vddh_get(NRF_POWER)) {
250 case NRF_POWER_POFTHRVDDH_V27:
251 vddh_voltage = "2.7V";
252 break;
253 case NRF_POWER_POFTHRVDDH_V28:
254 vddh_voltage = "2.8V";
255 break;
256 case NRF_POWER_POFTHRVDDH_V29:
257 vddh_voltage = "2.9V";
258 break;
259 case NRF_POWER_POFTHRVDDH_V30:
260 vddh_voltage = "3.0V";
261 break;
262 case NRF_POWER_POFTHRVDDH_V31:
263 vddh_voltage = "3.1V";
264 break;
265 case NRF_POWER_POFTHRVDDH_V32:
266 vddh_voltage = "3.2V";
267 break;
268 case NRF_POWER_POFTHRVDDH_V33:
269 vddh_voltage = "3.3V";
270 break;
271 case NRF_POWER_POFTHRVDDH_V34:
272 vddh_voltage = "3.4V";
273 break;
274 case NRF_POWER_POFTHRVDDH_V35:
275 vddh_voltage = "3.5V";
276 break;
277 case NRF_POWER_POFTHRVDDH_V36:
278 vddh_voltage = "3.6V";
279 break;
280 case NRF_POWER_POFTHRVDDH_V37:
281 vddh_voltage = "3.7V";
282 break;
283 case NRF_POWER_POFTHRVDDH_V38:
284 vddh_voltage = "3.8V";
285 break;
286 case NRF_POWER_POFTHRVDDH_V39:
287 vddh_voltage = "3.9V";
288 break;
289 case NRF_POWER_POFTHRVDDH_V40:
290 vddh_voltage = "4.0V";
291 break;
292 case NRF_POWER_POFTHRVDDH_V41:
293 vddh_voltage = "4.1V";
294 break;
295 case NRF_POWER_POFTHRVDDH_V42:
296 vddh_voltage = "4.2V";
297 break;
298 default:
299 vddh_voltage = "???V";
300 break;
301 }
302 ESP_LOGD(TAG, "Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage);
303 pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage);
304 } else {
305 ESP_LOGD(TAG, "Power-fail comparator: %s", pof_voltage);
306 pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: %s", pof_voltage);
307 }
308 } else {
309 ESP_LOGD(TAG, "Power-fail comparator: disabled");
310 pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: disabled");
311 }
312
313 auto package = [](uint32_t value) {
314 switch (value) {
315 case 0x2004:
316 return "QIxx - 7x7 73-pin aQFN";
317 case 0x2000:
318 return "QFxx - 6x6 48-pin QFN";
319 case 0x2005:
320 return "CKxx - 3.544 x 3.607 WLCSP";
321 }
322 return "Unspecified";
323 };
324
325 char mac_pretty[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
327 ESP_LOGD(TAG,
328 "Code page size: %u, code size: %u, device id: 0x%08x%08x\n"
329 "Encryption root: 0x%08x%08x%08x%08x, Identity Root: 0x%08x%08x%08x%08x\n"
330 "Device address type: %s, address: %s\n"
331 "Part code: nRF%x, version: %c%c%c%c, package: %s\n"
332 "RAM: %ukB, Flash: %ukB, production test: %sdone",
333 NRF_FICR->CODEPAGESIZE, NRF_FICR->CODESIZE, NRF_FICR->DEVICEID[1], NRF_FICR->DEVICEID[0], NRF_FICR->ER[0],
334 NRF_FICR->ER[1], NRF_FICR->ER[2], NRF_FICR->ER[3], NRF_FICR->IR[0], NRF_FICR->IR[1], NRF_FICR->IR[2],
335 NRF_FICR->IR[3], (NRF_FICR->DEVICEADDRTYPE & 0x1 ? "Random" : "Public"), mac_pretty, NRF_FICR->INFO.PART,
336 NRF_FICR->INFO.VARIANT >> 24 & 0xFF, NRF_FICR->INFO.VARIANT >> 16 & 0xFF, NRF_FICR->INFO.VARIANT >> 8 & 0xFF,
337 NRF_FICR->INFO.VARIANT & 0xFF, package(NRF_FICR->INFO.PACKAGE), NRF_FICR->INFO.RAM, NRF_FICR->INFO.FLASH,
338 (NRF_FICR->PRODTEST[0] == 0xBB42319F ? "" : "not "));
339 bool n_reset_enabled = NRF_UICR->PSELRESET[0] == NRF_UICR->PSELRESET[1] &&
340 (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) == UICR_PSELRESET_CONNECT_Connected
341 << UICR_PSELRESET_CONNECT_Pos;
342 ESP_LOGD(
343 TAG, "GPIO as NFC pins: %s, GPIO as nRESET pin: %s",
344 YESNO((NRF_UICR->NFCPINS & UICR_NFCPINS_PROTECT_Msk) == (UICR_NFCPINS_PROTECT_NFC << UICR_NFCPINS_PROTECT_Pos)),
345 YESNO(n_reset_enabled));
346 if (n_reset_enabled) {
347 uint8_t port = (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_PORT_Msk) >> UICR_PSELRESET_PORT_Pos;
348 uint8_t pin = (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_PIN_Msk) >> UICR_PSELRESET_PIN_Pos;
349 ESP_LOGD(TAG, "nRESET port P%u.%02u", port, pin);
350 }
351#ifdef USE_BOOTLOADER_MCUBOOT
352 ESP_LOGD(TAG, "bootloader: mcuboot");
353#else
354 ESP_LOGD(TAG, "bootloader: Adafruit, version %u.%u.%u", (BOOTLOADER_VERSION_REGISTER >> 16) & 0xFF,
355 (BOOTLOADER_VERSION_REGISTER >> 8) & 0xFF, BOOTLOADER_VERSION_REGISTER & 0xFF);
356 ESP_LOGD(TAG,
357 "MBR bootloader addr 0x%08x, UICR bootloader addr 0x%08x\n"
358 "MBR param page addr 0x%08x, UICR param page addr 0x%08x",
359 read_mem_u32(MBR_BOOTLOADER_ADDR), NRF_UICR->NRFFW[0], read_mem_u32(MBR_PARAM_PAGE_ADDR),
360 NRF_UICR->NRFFW[1]);
361 if (is_sd_present()) {
362 uint32_t const sd_id = sd_id_get();
363 uint32_t const sd_version = sd_version_get();
364
365 uint32_t ver[3];
366 ver[0] = sd_version / 1000000;
367 ver[1] = (sd_version - ver[0] * 1000000) / 1000;
368 ver[2] = (sd_version - ver[0] * 1000000 - ver[1] * 1000);
369
370 ESP_LOGD(TAG, "SoftDevice: S%u %u.%u.%u", sd_id, ver[0], ver[1], ver[2]);
371#ifdef USE_SOFTDEVICE_ID
372#ifdef USE_SOFTDEVICE_VERSION
373 if (USE_SOFTDEVICE_ID != sd_id || USE_SOFTDEVICE_VERSION != ver[0]) {
374 ESP_LOGE(TAG, "Built for SoftDevice S%u %u.x.y. It may crash due to mismatch of bootloader version.",
375 USE_SOFTDEVICE_ID, USE_SOFTDEVICE_VERSION);
376 }
377#else
378 if (USE_SOFTDEVICE_ID != sd_id) {
379 ESP_LOGE(TAG, "Built for SoftDevice S%u. It may crash due to mismatch of bootloader version.", USE_SOFTDEVICE_ID);
380 }
381#endif
382#endif
383 }
384#endif
385 auto uicr = [](volatile uint32_t *data, uint8_t size) {
386 std::string res;
387 char buf[sizeof(uint32_t) * 2 + 1];
388 for (size_t i = 0; i < size; i++) {
389 if (i > 0) {
390 res += ' ';
391 }
392 res += format_hex_pretty<uint32_t>(data[i], '\0', false);
393 }
394 return res;
395 };
396 ESP_LOGD(TAG,
397 "NRFFW %s\n"
398 "NRFHW %s",
399 uicr(NRF_UICR->NRFFW, 13).c_str(), uicr(NRF_UICR->NRFHW, 12).c_str());
400
401 return pos;
402}
403
404void DebugComponent::update_platform_() {}
405
406} // namespace esphome::debug
407#endif
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)
const char * get_wakeup_cause_(std::span< char, RESET_REASON_BUFFER_SIZE > buffer)
const char * get_reset_reason_(std::span< char, RESET_REASON_BUFFER_SIZE > buffer)
constexpr std::uintptr_t MBR_PARAM_PAGE_ADDR
constexpr uintptr_t SD_ID_OFFSET
constexpr std::uintptr_t MBR_BOOTLOADER_ADDR
constexpr uintptr_t MBR_SIZE
constexpr uintptr_t SOFTDEVICE_INFO_STRUCT_OFFSET
constexpr uintptr_t SD_VERSION_OFFSET
constexpr uint32_t SD_MAGIC_NUMBER
size_t size
Definition helpers.h:729
size_t size_t pos
Definition helpers.h:729
const char * get_mac_address_pretty_into_buffer(std::span< char, MAC_ADDRESS_PRETTY_BUFFER_SIZE > buf)
Get the device MAC address into the given buffer, in colon-separated uppercase hex notation.
Definition helpers.cpp:819
std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length)
Format a byte array in pretty-printed, human-readable hex format.
Definition helpers.cpp:401