1#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
8#include "freertos/FreeRTOS.h"
9#include "freertos/task.h"
10#include "freertos/ringbuf.h"
11#include "driver/uart.h"
12#include "soc/soc_caps.h"
18static constexpr size_t UART_TASK_STACK_SIZE = 4096;
19static constexpr size_t RINGBUF_RETRY_CHUNK_SIZE = 64;
20static constexpr uint32_t LOG_THROTTLE_MS = 1000;
21static constexpr uint32_t UART_RELOAD_SETTLE_MS = 20;
23static constexpr UBaseType_t TASK_PRIORITY = 4;
27 if ((now - *last_ms) >= interval_ms) {
34static bool ringbuf_send_with_retry(RingbufHandle_t ringbuf,
const uint8_t *data,
size_t len,
uint32_t *log_ms) {
39 if (xRingbufferSend(ringbuf, data,
len, pdMS_TO_TICKS(1)) == pdTRUE) {
44 while (offset <
len) {
45 size_t chunk = std::min(RINGBUF_RETRY_CHUNK_SIZE,
len - offset);
46 if (xRingbufferSend(ringbuf, data + offset, chunk, pdMS_TO_TICKS(1)) != pdTRUE) {
47 if (should_log_now(log_ms, LOG_THROTTLE_MS)) {
48 ESP_LOGW(TAG,
"USB TX buffer full; some data is lost");
73 ESP_LOGE(TAG,
"UART parent failed; aborting");
87 ESP_LOGE(TAG,
"USB CDC ACM failed; aborting");
94 char tx_task_name[] =
"cdc_uart_tx_0";
95 char rx_task_name[] =
"cdc_uart_rx_0";
97 tx_task_name[
sizeof(tx_task_name) - 2] = itf_char;
98 rx_task_name[
sizeof(rx_task_name) - 2] = itf_char;
102 ESP_LOGE(TAG,
"Failed to create UART TX task");
109 ESP_LOGE(TAG,
"Failed to create UART RX task");
139 "CDC-ACM UART Bridge:\n"
141 " USB CDC Interface: %u",
143 LOG_PIN(
" DTR Pin: ", this->
dtr_pin_);
144 LOG_PIN(
" RTS Pin: ", this->
rts_pin_);
206 bool changed =
false;
212 if (baud == 0 || baud > SOC_UART_BITRATE_MAX) {
213 ESP_LOGW(TAG,
"Ignoring unsupported baud rate %" PRIu32
" from host; keeping %" PRIu32, baud,
234 if (data_bits < 5 || data_bits > 8) {
235 ESP_LOGW(TAG,
"Ignoring unsupported data bits %u from host; keeping %u", data_bits,
243 ESP_LOGV(TAG,
"Line coding: baud=%" PRIu32
", data_bits=%u, stop_bits=%u, parity=%u",
245 this->uart_parent_->get_stop_bits(),
static_cast<uint8_t
>(this->uart_parent_->get_parity()));
304 return this->
tx_busy_ == 0 && uart_wait_tx_done(uart_num, 0) == ESP_OK;
318 ESP_LOGV(TAG,
"Line state: DTR=%d, RTS=%d", dtr, rts);
359 ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
366 ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
372 int total_rx_size = uart_read_bytes(uart_num, data, 1, pdMS_TO_TICKS(
UART_RX_WAIT_MS));
373 if (total_rx_size < 0) {
374 if (should_log_now(&err_log_ms, LOG_THROTTLE_MS)) {
375 ESP_LOGE(TAG,
"UART read failed: %d", total_rx_size);
377 vTaskDelay(pdMS_TO_TICKS(10));
380 if (total_rx_size == 0) {
390 int rx_data_size = uart_read_bytes(uart_num, data + total_rx_size, buf_size - total_rx_size, 0);
391 if (rx_data_size < 0) {
392 if (should_log_now(&err_log_ms, LOG_THROTTLE_MS)) {
393 ESP_LOGE(TAG,
"UART read failed: %d", rx_data_size);
397 if (rx_data_size == 0) {
400 ESP_LOGV(TAG,
"UART RX: %d bytes", rx_data_size);
401 total_rx_size += rx_data_size;
402 if (total_rx_size >= (
int) buf_size) {
407 ringbuf_send_with_retry(usb_tx_ringbuf, data, total_rx_size, &tx_full_log_ms);
409 ESP_LOGV(TAG,
"UART RX: waking up USB TX task");
410 xTaskNotifyGive(usb_tx_handle);
425 ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
428 ESP_LOGV(TAG,
"Waiting for data to send to UART");
432 if (should_log_now(&err_log_ms, LOG_THROTTLE_MS)) {
433 ESP_LOGE(TAG,
"USB RX RingBuf read failed");
437 vTaskDelay(pdMS_TO_TICKS(10));
446 if (should_log_now(&drop_log_ms, LOG_THROTTLE_MS)) {
447 ESP_LOGW(TAG,
"Paused; dropping %zu bytes from host", rx_size);
452 ESP_LOGV(TAG,
"Sending %zu bytes to UART", rx_size);
454 int xfer_size = uart_write_bytes(uart_num, data_to_uart, rx_size);
458 if (should_log_now(&err_log_ms, LOG_THROTTLE_MS)) {
459 ESP_LOGE(TAG,
"UART write failed: %d", xfer_size);
461 }
else if (
static_cast<size_t>(xfer_size) != rx_size) {
462 ESP_LOGW(TAG,
"UART write incomplete (%d/%zu bytes)", xfer_size, rx_size);
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void mark_failed()
Mark this component as failed.
void enable_loop()
Enable this component's loop.
void disable_loop()
Disable this component's loop.
virtual void digital_write(bool value)=0
T * get_parent() const
Get the parent of this object.
std::array< uint8_t, CONFIG_TINYUSB_CDC_RX_BUFSIZE > uart_tx_buffer_
TaskHandle_t uart_rx_task_handle_
uint8_t configured_data_bits_
std::atomic< uint8_t > paused_
bool sync_host_framing_()
uart::IDFUARTComponent * uart_parent_
void resume()
Re-apply the host's line coding and line state, then resume forwarding.
static void uart_rx_task_fn(void *arg)
void set_line_state(bool dtr, bool rts)
void restore_configured_framing_()
usb_cdc_acm::USBCDCACMInstance * usb_cdc_parent_
std::atomic< uint8_t > rx_parked_
static void uart_tx_task_fn(void *arg)
uart::UARTParityOptions configured_parity_
void on_shutdown() override
static constexpr uint32_t UART_RX_WAIT_MS
uint32_t reload_requested_at_
std::atomic< uint8_t > tx_busy_
void dump_config() override
@ MAIN_STATE_RELOAD_PENDING
uint32_t configured_baud_rate_
TaskHandle_t uart_tx_task_handle_
void pause()
Stop forwarding in both directions and hand the UART back to its configured framing,...
uint8_t configured_stop_bits_
std::array< uint8_t, CONFIG_TINYUSB_CDC_TX_BUFSIZE > uart_rx_buffer_
uint8_t get_hw_serial_number()
esp_err_t apply_settings_live()
Apply the current framing (baud rate, parity, data/stop bits) to the installed driver in place,...
UARTParityOptions get_parity() const
void set_baud_rate(uint32_t baud_rate)
uint8_t get_data_bits() const
void set_parity(UARTParityOptions parity)
uint8_t get_stop_bits() const
void set_stop_bits(uint8_t stop_bits)
uint32_t get_baud_rate() const
void set_data_bits(uint8_t data_bits)
RingbufHandle_t get_rx_ringbuf() const
RingbufHandle_t get_tx_ringbuf() const
void set_line_state_callback(LineStateCallback callback)
TaskHandle_t get_tx_task_handle() const
void set_line_coding_callback(LineCodingCallback callback)
ESPHOME_LOG_TAG(TAG, "cdc_acm_uart")
esp_err_t ringbuf_read_bytes(RingbufHandle_t ring_buf, uint8_t *out_buf, size_t out_buf_sz, size_t *rx_data_size, TickType_t x_ticks_to_wait)
ESPHOME_ALWAYS_INLINE char format_hex_char(uint8_t v, char base)
Convert a nibble (0-15) to hex char with specified base ('a' for lowercase, 'A' for uppercase)
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.