ESPHome 2025.11.0b4
Loading...
Searching...
No Matches
uart_component_esp_idf.cpp
Go to the documentation of this file.
1#ifdef USE_ESP32
2
4#include <cinttypes>
8#include "esphome/core/log.h"
9#include "esphome/core/gpio.h"
10#include "driver/gpio.h"
11#include "soc/gpio_num.h"
12
13#ifdef USE_LOGGER
15#endif
16
17namespace esphome {
18namespace uart {
19static const char *const TAG = "uart.idf";
20
22 uart_parity_t parity = UART_PARITY_DISABLE;
23 if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
24 parity = UART_PARITY_EVEN;
25 } else if (this->parity_ == UART_CONFIG_PARITY_ODD) {
26 parity = UART_PARITY_ODD;
27 }
28
29 uart_word_length_t data_bits;
30 switch (this->data_bits_) {
31 case 5:
32 data_bits = UART_DATA_5_BITS;
33 break;
34 case 6:
35 data_bits = UART_DATA_6_BITS;
36 break;
37 case 7:
38 data_bits = UART_DATA_7_BITS;
39 break;
40 case 8:
41 data_bits = UART_DATA_8_BITS;
42 break;
43 default:
44 data_bits = UART_DATA_BITS_MAX;
45 break;
46 }
47
48 uart_config_t uart_config{};
49 uart_config.baud_rate = this->baud_rate_;
50 uart_config.data_bits = data_bits;
51 uart_config.parity = parity;
52 uart_config.stop_bits = this->stop_bits_ == 1 ? UART_STOP_BITS_1 : UART_STOP_BITS_2;
53 uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
54 uart_config.source_clk = UART_SCLK_DEFAULT;
55 uart_config.rx_flow_ctrl_thresh = 122;
56
57 return uart_config;
58}
59
61 static uint8_t next_uart_num = 0;
62
63#ifdef USE_LOGGER
64 bool logger_uses_hardware_uart = true;
65
66#ifdef USE_LOGGER_USB_CDC
68 // this is not a hardware UART, ignore it
69 logger_uses_hardware_uart = false;
70 }
71#endif // USE_LOGGER_USB_CDC
72
73#ifdef USE_LOGGER_USB_SERIAL_JTAG
75 // this is not a hardware UART, ignore it
76 logger_uses_hardware_uart = false;
77 }
78#endif // USE_LOGGER_USB_SERIAL_JTAG
79
80 if (logger_uses_hardware_uart && logger::global_logger->get_baud_rate() > 0 &&
81 logger::global_logger->get_uart_num() == next_uart_num) {
82 next_uart_num++;
83 }
84#endif // USE_LOGGER
85
86 if (next_uart_num >= SOC_UART_NUM) {
87 ESP_LOGW(TAG, "Maximum number of UART components created already");
88 this->mark_failed();
89 return;
90 }
91 this->uart_num_ = static_cast<uart_port_t>(next_uart_num++);
92 this->lock_ = xSemaphoreCreateMutex();
93
94#if (SOC_UART_LP_NUM >= 1)
95 size_t fifo_len = ((this->uart_num_ < SOC_UART_HP_NUM) ? SOC_UART_FIFO_LEN : SOC_LP_UART_FIFO_LEN);
96#else
97 size_t fifo_len = SOC_UART_FIFO_LEN;
98#endif
99 if (this->rx_buffer_size_ <= fifo_len) {
100 ESP_LOGW(TAG, "rx_buffer_size is too small, must be greater than %zu", fifo_len);
101 this->rx_buffer_size_ = fifo_len * 2;
102 }
103
104 xSemaphoreTake(this->lock_, portMAX_DELAY);
105
106 this->load_settings(false);
107
108 xSemaphoreGive(this->lock_);
109}
110
111void IDFUARTComponent::load_settings(bool dump_config) {
112 esp_err_t err;
113
114 if (uart_is_driver_installed(this->uart_num_)) {
115 err = uart_driver_delete(this->uart_num_);
116 if (err != ESP_OK) {
117 ESP_LOGW(TAG, "uart_driver_delete failed: %s", esp_err_to_name(err));
118 this->mark_failed();
119 return;
120 }
121 }
122 err = uart_driver_install(this->uart_num_, // UART number
123 this->rx_buffer_size_, // RX ring buffer size
124 0, // TX ring buffer size. If zero, driver will not use a TX buffer and TX function will
125 // block task until all data has been sent out
126 20, // event queue size/depth
127 &this->uart_event_queue_, // event queue
128 0 // Flags used to allocate the interrupt
129 );
130 if (err != ESP_OK) {
131 ESP_LOGW(TAG, "uart_driver_install failed: %s", esp_err_to_name(err));
132 this->mark_failed();
133 return;
134 }
135
136 auto setup_pin_if_needed = [](InternalGPIOPin *pin) {
137 if (!pin) {
138 return;
139 }
141 if ((pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) {
142 pin->setup();
143 }
144 };
145
146 setup_pin_if_needed(this->rx_pin_);
147 if (this->rx_pin_ != this->tx_pin_) {
148 setup_pin_if_needed(this->tx_pin_);
149 }
150
151 int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1;
152 int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1;
153 int8_t flow_control = this->flow_control_pin_ != nullptr ? this->flow_control_pin_->get_pin() : -1;
154
155 uint32_t invert = 0;
156 if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted()) {
157 invert |= UART_SIGNAL_TXD_INV;
158 }
159 if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted()) {
160 invert |= UART_SIGNAL_RXD_INV;
161 }
162
163 err = uart_set_line_inverse(this->uart_num_, invert);
164 if (err != ESP_OK) {
165 ESP_LOGW(TAG, "uart_set_line_inverse failed: %s", esp_err_to_name(err));
166 this->mark_failed();
167 return;
168 }
169
170 err = uart_set_pin(this->uart_num_, tx, rx, flow_control, UART_PIN_NO_CHANGE);
171 if (err != ESP_OK) {
172 ESP_LOGW(TAG, "uart_set_pin failed: %s", esp_err_to_name(err));
173 this->mark_failed();
174 return;
175 }
176
177 err = uart_set_rx_full_threshold(this->uart_num_, this->rx_full_threshold_);
178 if (err != ESP_OK) {
179 ESP_LOGW(TAG, "uart_set_rx_full_threshold failed: %s", esp_err_to_name(err));
180 this->mark_failed();
181 return;
182 }
183
184 err = uart_set_rx_timeout(this->uart_num_, this->rx_timeout_);
185 if (err != ESP_OK) {
186 ESP_LOGW(TAG, "uart_set_rx_timeout failed: %s", esp_err_to_name(err));
187 this->mark_failed();
188 return;
189 }
190
191 auto mode = this->flow_control_pin_ != nullptr ? UART_MODE_RS485_HALF_DUPLEX : UART_MODE_UART;
192 err = uart_set_mode(this->uart_num_, mode); // per docs, must be called only after uart_driver_install()
193 if (err != ESP_OK) {
194 ESP_LOGW(TAG, "uart_set_mode failed: %s", esp_err_to_name(err));
195 this->mark_failed();
196 return;
197 }
198
199 uart_config_t uart_config = this->get_config_();
200 err = uart_param_config(this->uart_num_, &uart_config);
201 if (err != ESP_OK) {
202 ESP_LOGW(TAG, "uart_param_config failed: %s", esp_err_to_name(err));
203 this->mark_failed();
204 return;
205 }
206
207 if (dump_config) {
208 ESP_LOGCONFIG(TAG, "Reloaded UART %u", this->uart_num_);
209 this->dump_config();
210 }
211}
212
214 ESP_LOGCONFIG(TAG, "UART Bus %u:", this->uart_num_);
215 LOG_PIN(" TX Pin: ", this->tx_pin_);
216 LOG_PIN(" RX Pin: ", this->rx_pin_);
217 LOG_PIN(" Flow Control Pin: ", this->flow_control_pin_);
218 if (this->rx_pin_ != nullptr) {
219 ESP_LOGCONFIG(TAG,
220 " RX Buffer Size: %u\n"
221 " RX Full Threshold: %u\n"
222 " RX Timeout: %u",
224 }
225 ESP_LOGCONFIG(TAG,
226 " Baud Rate: %" PRIu32 " baud\n"
227 " Data Bits: %u\n"
228 " Parity: %s\n"
229 " Stop bits: %u",
230 this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_);
231 this->check_logger_conflict();
232}
233
234void IDFUARTComponent::set_rx_full_threshold(size_t rx_full_threshold) {
235 if (this->is_ready()) {
236 esp_err_t err = uart_set_rx_full_threshold(this->uart_num_, rx_full_threshold);
237 if (err != ESP_OK) {
238 ESP_LOGW(TAG, "uart_set_rx_full_threshold failed: %s", esp_err_to_name(err));
239 return;
240 }
241 }
242 this->rx_full_threshold_ = rx_full_threshold;
243}
244
245void IDFUARTComponent::set_rx_timeout(size_t rx_timeout) {
246 if (this->is_ready()) {
247 esp_err_t err = uart_set_rx_timeout(this->uart_num_, rx_timeout);
248 if (err != ESP_OK) {
249 ESP_LOGW(TAG, "uart_set_rx_timeout failed: %s", esp_err_to_name(err));
250 return;
251 }
252 }
253 this->rx_timeout_ = rx_timeout;
254}
255
256void IDFUARTComponent::write_array(const uint8_t *data, size_t len) {
257 xSemaphoreTake(this->lock_, portMAX_DELAY);
258 int32_t write_len = uart_write_bytes(this->uart_num_, data, len);
259 xSemaphoreGive(this->lock_);
260 if (write_len != (int32_t) len) {
261 ESP_LOGW(TAG, "uart_write_bytes failed: %d != %zu", write_len, len);
262 this->mark_failed();
263 }
264#ifdef USE_UART_DEBUGGER
265 for (size_t i = 0; i < len; i++) {
266 this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
267 }
268#endif
269}
270
271bool IDFUARTComponent::peek_byte(uint8_t *data) {
272 if (!this->check_read_timeout_())
273 return false;
274 xSemaphoreTake(this->lock_, portMAX_DELAY);
275 if (this->has_peek_) {
276 *data = this->peek_byte_;
277 } else {
278 int len = uart_read_bytes(this->uart_num_, data, 1, 20 / portTICK_PERIOD_MS);
279 if (len == 0) {
280 *data = 0;
281 } else {
282 this->has_peek_ = true;
283 this->peek_byte_ = *data;
284 }
285 }
286 xSemaphoreGive(this->lock_);
287 return true;
288}
289
290bool IDFUARTComponent::read_array(uint8_t *data, size_t len) {
291 size_t length_to_read = len;
292 int32_t read_len = 0;
293 if (!this->check_read_timeout_(len))
294 return false;
295 xSemaphoreTake(this->lock_, portMAX_DELAY);
296 if (this->has_peek_) {
297 length_to_read--;
298 *data = this->peek_byte_;
299 data++;
300 this->has_peek_ = false;
301 }
302 if (length_to_read > 0)
303 read_len = uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_PERIOD_MS);
304 xSemaphoreGive(this->lock_);
305#ifdef USE_UART_DEBUGGER
306 for (size_t i = 0; i < len; i++) {
307 this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
308 }
309#endif
310 return read_len == (int32_t) length_to_read;
311}
312
314 size_t available = 0;
315 esp_err_t err;
316
317 xSemaphoreTake(this->lock_, portMAX_DELAY);
318 err = uart_get_buffered_data_len(this->uart_num_, &available);
319 xSemaphoreGive(this->lock_);
320
321 if (err != ESP_OK) {
322 ESP_LOGW(TAG, "uart_get_buffered_data_len failed: %s", esp_err_to_name(err));
323 this->mark_failed();
324 }
325 if (this->has_peek_) {
326 available++;
327 }
328 return available;
329}
330
332 ESP_LOGVV(TAG, " Flushing");
333 xSemaphoreTake(this->lock_, portMAX_DELAY);
334 uart_wait_tx_done(this->uart_num_, portMAX_DELAY);
335 xSemaphoreGive(this->lock_);
336}
337
339
340} // namespace uart
341} // namespace esphome
342
343#endif // USE_ESP32
BedjetMode mode
BedJet operating mode.
virtual void mark_failed()
Mark this component as failed.
bool is_ready() const
virtual uint8_t get_pin() const =0
virtual bool is_inverted() const =0
void set_rx_timeout(size_t rx_timeout) override
bool peek_byte(uint8_t *data) override
void write_array(const uint8_t *data, size_t len) override
void set_rx_full_threshold(size_t rx_full_threshold) override
bool read_array(uint8_t *data, size_t len) override
bool check_read_timeout_(size_t len=1)
InternalGPIOPin * flow_control_pin_
CallbackManager< void(UARTDirection, uint8_t)> debug_callback_
@ FLAG_OPEN_DRAIN
Definition gpio.h:20
@ FLAG_NONE
Definition gpio.h:17
@ FLAG_PULLUP
Definition gpio.h:21
@ FLAG_PULLDOWN
Definition gpio.h:22
@ UART_SELECTION_USB_SERIAL_JTAG
Definition logger.h:94
@ UART_SELECTION_USB_CDC
Definition logger.h:91
Logger * global_logger
Definition logger.cpp:294
const char *const TAG
Definition spi.cpp:8
const LogString * parity_to_str(UARTParityOptions parity)
Definition uart.cpp:33
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:483