ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
logger_zephyr.cpp
Go to the documentation of this file.
1#ifdef USE_ZEPHYR
2
4#include "esphome/core/log.h"
5#include "logger.h"
6#ifdef USE_CRASH_HANDLER
8#endif
9
10#include <zephyr/device.h>
11#include <zephyr/drivers/uart.h>
12#include <zephyr/sys/printk.h>
13#include <zephyr/usb/usb_device.h>
14#ifdef USE_LOGGER_EARLY_MESSAGE
16#endif
17
18namespace esphome::logger {
19
20// Zephyr 3.7 renamed z_arch_esf_t to struct arch_esf; the old name was later removed.
21#if KERNEL_VERSION_NUMBER >= 0x030700
22using FatalErrorEsf = ::arch_esf;
23#else
24using FatalErrorEsf = z_arch_esf_t;
25#endif
26
27__attribute__((section(".noinit"))) struct {
28 uint32_t magic;
29 uint32_t reason;
30 uint32_t pc;
31 uint32_t lr;
32#if defined(CONFIG_THREAD_NAME)
33 char thread[CONFIG_THREAD_MAX_NAME_LEN];
34#endif
35} crash_buf; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
36
37ESPHOME_LOG_TAG(TAG, "logger");
38
39#ifdef USE_LOGGER_UART_SELECTION_USB_CDC
41 if (this->uart_ != UART_SELECTION_USB_CDC || this->uart_dev_ == nullptr) {
42 return;
43 }
44 static bool opened = false;
45 uint32_t dtr = 0;
46 uart_line_ctrl_get(this->uart_dev_, UART_LINE_CTRL_DTR, &dtr);
47
48 /* Poll if the DTR flag was set, optional */
49 if (opened == dtr) {
50 return;
51 }
52
53 if (!opened) {
55 }
56#ifdef USE_CRASH_HANDLER
57 // The new reader also gets a coredump stored by the last crash; a closed port keeps it for the next one
58 if (!opened) {
60 } else {
62 }
63#endif
64 opened = !opened;
65}
66#endif
67
68void Logger::pre_setup() {
69#ifdef CONFIG_SERIAL
70 if (this->baud_rate_ > 0) {
71 static const struct device *uart_dev = nullptr;
72 switch (this->uart_) {
73 case UART_SELECTION_UART0: // NOLINT(bugprone-branch-clone)
74 uart_dev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(uart0));
75 break;
77 uart_dev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(uart1));
78 break;
79#ifdef USE_LOGGER_USB_CDC
81#ifdef CONFIG_USB_DEVICE_STACK
82 uart_dev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(cdc_acm_uart0));
83 if (device_is_ready(uart_dev)) {
84 usb_enable(nullptr);
85 }
86#endif
87 break;
88#endif
89 }
90 if (device_is_ready(uart_dev)) {
91 this->uart_dev_ = uart_dev;
92#if defined(USE_LOGGER_WAIT_FOR_CDC) && defined(USE_LOGGER_UART_SELECTION_USB_CDC)
93 uint32_t dtr = 0;
94 int32_t count = (10 * 100); // wait 10 sec for USB CDC to have early logs
95 while (dtr == 0 && count-- > 0) {
96 uart_line_ctrl_get(this->uart_dev_, UART_LINE_CTRL_DTR, &dtr);
97 delay(10);
99 }
100#endif
101 }
102 }
103#endif
104 global_logger = this;
105 ESP_LOGI(TAG, "Log initialized");
106#ifdef USE_LOGGER_EARLY_MESSAGE
107 char reason_buffer[zephyr::RESET_REASON_BUFFER_SIZE];
108 const char *reset_reason = zephyr::get_reset_reason(std::span<char, zephyr::RESET_REASON_BUFFER_SIZE>(reason_buffer));
109 ESP_LOGI(TAG, "Reset reason: %s", reset_reason);
110 dump_crash_();
111#endif
112#ifdef USE_CRASH_HANDLER
113#ifdef USE_LOGGER_UART_SELECTION_USB_CDC
114 // Nobody reads a USB port this early; cdc_loop_() reports the dump once the port is opened
115 if (this->uart_ == UART_SELECTION_USB_CDC)
116 return;
117#endif
118 // Without a UART (baud_rate: 0, or one that did not come up) nothing reaches the console
119 if (this->uart_dev_ != nullptr)
121#endif
122}
123
124void HOT Logger::write_msg_(const char *msg, uint16_t len) {
125 // Single write with newline already in buffer (added by caller)
126#ifdef CONFIG_PRINTK
127 // Requires the debug component and an active SWD connection.
128 // It is used for pyocd rtt -t nrf52840
129 printk("%.*s", static_cast<int>(len), msg);
130#endif
131 if (this->uart_dev_ == nullptr) {
132 return;
133 }
134 for (uint16_t i = 0; i < len; ++i) {
135 uart_poll_out(this->uart_dev_, msg[i]);
136 }
137}
138
139const LogString *Logger::get_uart_selection_() {
140 switch (this->uart_) {
142 return LOG_STR("UART0");
144 return LOG_STR("UART1");
145#ifdef USE_LOGGER_USB_CDC
147 return LOG_STR("USB_CDC");
148#endif
149 default:
150 return LOG_STR("UNKNOWN");
151 }
152}
153
154static const uint8_t REASON_BUF_SIZE = 32;
155
156static const char *reason_to_str(unsigned int reason, char *buf) {
157 switch (reason) {
158 case K_ERR_CPU_EXCEPTION:
159 return "CPU exception";
160 case K_ERR_SPURIOUS_IRQ:
161 return "Unhandled interrupt";
162 case K_ERR_STACK_CHK_FAIL:
163 return "Stack overflow";
164 case K_ERR_KERNEL_OOPS:
165 return "Kernel oops";
166 case K_ERR_KERNEL_PANIC:
167 return "Kernel panic";
168 default:
169 snprintf(buf, REASON_BUF_SIZE, "Unknown error (%u)", reason);
170 return buf;
171 }
172}
173
175 ESP_LOGD(TAG, "Crash buffer address %p", &crash_buf);
176 if (crash_buf.magic == App.get_config_hash()) {
177 char reason_buf[REASON_BUF_SIZE];
178 ESP_LOGE(TAG, "Last crash:");
179 ESP_LOGE(TAG, "Reason=%s PC=0x%08x LR=0x%08x", reason_to_str(crash_buf.reason, reason_buf), crash_buf.pc,
180 crash_buf.lr);
181#if defined(CONFIG_THREAD_NAME)
182 ESP_LOGE(TAG, "Thread: %s", crash_buf.thread);
183#endif
184 int32_t count = (2 * 100); // wait 2 sec to give a chance to print crash
185 while (count-- > 0) {
186 delay(10);
188 }
189 }
190}
191
192void k_sys_fatal_error_handler(unsigned int reason, const FatalErrorEsf *esf) {
193 crash_buf.magic = App.get_config_hash();
194 crash_buf.reason = reason;
195 if (esf) {
196 crash_buf.pc = esf->basic.pc;
197 crash_buf.lr = esf->basic.lr;
198 }
199#if defined(CONFIG_THREAD_NAME)
200 auto thread = k_current_get();
201 const char *name = k_thread_name_get(thread);
202 if (name) {
203 strncpy(crash_buf.thread, name, sizeof(crash_buf.thread) - 1);
204 crash_buf.thread[sizeof(crash_buf.thread) - 1] = '\0';
205 } else {
206 crash_buf.thread[0] = '\0';
207 }
208#endif
209 arch_restart();
210}
211
212#ifdef USE_CRASH_HANDLER
214void log_crash_to_client() {
215 // A level that drops the lines would erase the dump unseen; keep it for a client that can see it
216 if (global_logger->get_log_level(zephyr_coredump::CRASH_LOG_TAG) < ESPHOME_LOG_LEVEL_ERROR)
217 return;
219}
220#endif
221
222} // namespace esphome::logger
223
224extern "C" {
225
229}
230
231#endif
uint32_t get_config_hash()
Get the config hash as a 32-bit integer.
UARTSelection uart_
Definition logger.h:362
void write_msg_(const char *msg, uint16_t len)
Definition logger_esp32.h:9
const LogString * get_uart_selection_()
uint8_t get_log_level()
Definition logger.h:180
void pre_setup()
Set up this component.
const device * uart_dev_
Definition logger.h:328
struct @66::@67 __attribute__
Wake the main loop task from an ISR. ISR-safe.
Definition main_task.h:45
unsigned count
Definition gif.cpp:51
void k_sys_fatal_error_handler(unsigned int reason, const esphome::logger::FatalErrorEsf *esf)
::arch_esf FatalErrorEsf
bool crash_log_pending()
Whether a crash record from before this boot is waiting to be reported.
void k_sys_fatal_error_handler(unsigned int reason, const FatalErrorEsf *esf)
@ UART_SELECTION_USB_CDC
Definition logger.h:121
@ UART_SELECTION_UART0
Definition logger.h:112
@ UART_SELECTION_UART1
Definition logger.h:116
Logger * global_logger
Definition logger.cpp:276
void log_crash_to_client()
Log the previous crash for a client that just subscribed to the logs.
ESPHOME_LOG_TAG(TAG, "logger")
bool coredump_pending()
Whether a dump from before this boot is waiting to be printed.
Definition coredump.cpp:35
void print_coredump()
A log reader is present: serial at boot, USB CDC when the port opens, an API or MQTT client subscribi...
Definition coredump.cpp:37
constexpr const char * CRASH_LOG_TAG
The tag the dump is logged under, so a client can check the level in force for it.
Definition coredump.h:7
void stop_coredump()
The reader went away (the USB port was closed); the print stops and the dump stays for the next one.
Definition coredump.cpp:43
const char * get_reset_reason(std::span< char, RESET_REASON_BUFFER_SIZE > buffer)
const void size_t len
Definition hal.h:68
void arch_feed_wdt()
Definition hal.cpp:58
void HOT delay(uint32_t ms)
Definition hal.cpp:85
void arch_restart()
Definition hal.cpp:36
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t