CDC-ACM UART Bridge
The CDC-ACM UART Bridge (the cdc_acm_uart platform of the Bridge component) enables
supported ESP32 microcontrollers to act as a USB-to-serial adapter, creating a bidirectional bridge between a UART
interface and a USB CDC-ACM (Communications Device Class - Abstract Control Model) interface. This allows the
microcontroller to appear as a virtual serial/COM port on a host computer while keeping the UART parameters in sync
with the host.
The following ESP32 microcontroller variants are currently supported:
- ESP32-P4
- ESP32-S2
- ESP32-S3
This component bridges an existing UART to an existing USB CDC-ACM interface, so your configuration must also include:
- TinyUSB - provides the underlying USB device functionality
- USB CDC-ACM - provides the USB virtual serial port to bridge
- UART - the hardware UART bus to bridge
# Example minimal configuration entrybridge: - platform: cdc_acm_uart uart_id: uart_bus usb_cdc_acm_id: cdc_interfaceConfiguration Variables
Section titled “Configuration Variables”- uart_id (Required, ID): The ID of the UART bus to bridge to the USB CDC-ACM interface.
- usb_cdc_acm_id (Required, ID): The ID of the USB CDC-ACM interface to use for the USB side of the bridge.
- dtr_pin (Optional, Pin): GPIO pin that mirrors the host’s DTR (Data Terminal
Ready) line state. This is typically asserted when the host opens the serial port and is commonly used to drive the
reset line of an attached device. The pin carries the logical line state (asserted/deasserted); for a conventional
active-low interface (
DTR#), setinverted: trueon the pin (see Control Signals). - rts_pin (Optional, Pin): GPIO pin that mirrors the host’s RTS (Request to
Send) line state. Combined with
dtr_pin, this is commonly used to drive the bootloader/reset sequence of an attached device. As withdtr_pin, setinverted: truefor a conventional active-low interface (RTS#). - id (Optional, ID): Manually specify the ID for this component.
NOTE
A bridge requires exclusive use of both its UART and its USB CDC-ACM interface. Configurations where another
component (or another bridge) also uses either of them are rejected during validation, since two readers on the same
interface would each receive only fragments of the data. For the same reason the bridged UART cannot use the UART
debug option: the bridge moves data below the UART component, so the debugger would see nothing and its
dummy_receiver would steal bytes.
How It Works
Section titled “How It Works”The CDC-ACM UART Bridge creates a transparent data bridge between the UART and USB interfaces.
Data Flow
Section titled “Data Flow”- UART to USB: Data received on the UART RX pin is automatically forwarded to the USB CDC-ACM interface, making it available to the host computer.
- USB to UART: Data sent from the host computer through the USB CDC-ACM interface is automatically forwarded to the UART TX pin.
Parameter Synchronization
Section titled “Parameter Synchronization”When the host computer changes serial port parameters (baud rate, data bits, stop bits, parity), those changes are automatically applied to the UART interface. This lets the bridge adapt to different device requirements without reconfiguration. Mark and space parity, and any data-bit count outside 5-8, are not supported by the UART and are ignored. Baud rate requests of zero or above 5 Mbit/s (the fastest these microcontrollers support) are ignored as well, with a warning in the log; anything in between is passed to the UART driver, which keeps the previous rate if the requested one is too low for its clock divider to reach.
NOTE
UART parameter changes are debounced with a 20ms minimum interval to coalesce the rapid updates a host often sends when opening a port. The new settings are applied to the running UART driver in place (without tearing it down), so the bridge keeps forwarding data across the change. The new framing takes effect immediately, so a byte that is mid-transmission when the host re-codes the line may be corrupted; this is expected and normally harmless, since hosts set the line coding when opening the port, before data flows.
Control Signals
Section titled “Control Signals”If dtr_pin and rts_pin are configured, they output the state of these signals as reported by the host computer:
- DTR: Typically asserted when the host opens the serial port and de-asserted when it closes.
- RTS: State depends on the host application. Together with DTR, this is most commonly used to trigger auto-reset/bootloader entry on an attached microcontroller.
The pins carry the logical line state — driven active (high) when the host asserts the signal and inactive (low)
when it de-asserts it — and they start de-asserted at boot, before any host has connected. A real serial interface
drives DTR/RTS active-low (the DTR#/RTS# pins idle high and go low when asserted), so to match conventional
wiring — and most auto-reset/bootloader circuits — set inverted: true on the pins:
bridge: - platform: cdc_acm_uart uart_id: uart_bus usb_cdc_acm_id: cdc_interface dtr_pin: number: GPIOXX inverted: true rts_pin: number: GPIOXX inverted: trueWith inverted: true, the pin idles high at boot (de-asserted) and is driven low while the host asserts the signal.
Buffer Sizing
Section titled “Buffer Sizing”The bridge has no buffer settings of its own. Data is buffered by the USB CDC-ACM interface, so use its
rx_buffer_size and tx_buffer_size (see USB CDC-ACM) to make room for higher baud rates
or bursty traffic. The defaults of 256 bytes suit most applications up to 115200 baud.
Pausing the Bridge
Section titled “Pausing the Bridge”The bridge can be paused and resumed at runtime through lambda calls. While paused, it stops forwarding in both
directions: data arriving on the UART stays in the UART’s buffer for other code on the device to read, and data sent by
the host is discarded. Pausing also restores the UART parameters from your configuration (baud rate, data bits, stop
bits and parity), and resuming re-applies the parameters last requested by the host. The dtr_pin and rts_pin
outputs hold their state while paused and follow the host again on resume, so a computer opening the port cannot reset
the attached device while other code is using it. This makes it possible to use the same UART from firmware running on
the device while no computer is connected, and as a USB serial port when one is.
The UART Mux component builds on this to switch a UART between the bridge and other
components with ordinary automations.
pause(): Stop forwarding and restore the configured UART parameters.resume(): Re-apply the host’s parameters (or the configured ones, if no computer has set any yet) and start forwarding again. If a write from the host is still draining, this happens once it has finished.is_paused(): Returnstrueonce the bridge has stopped forwarding and the UART is free to use.
bridge: - platform: cdc_acm_uart id: my_bridge uart_id: uart_bus usb_cdc_acm_id: cdc_interface
switch: - platform: template name: "Bridge paused" lambda: return id(my_bridge).is_paused(); turn_on_action: - lambda: id(my_bridge).pause(); turn_off_action: - lambda: id(my_bridge).resume();NOTE
The bridge normally stops within about 250 ms of pause(), but a write from the host that is already in progress
is allowed to finish first, which can take longer at low baud rates; any further host data not yet written to the
UART is discarded. Check is_paused() before using the UART rather than waiting a fixed time. Bytes that arrive on
the UART while the bridge is stopping are discarded too, so pause the bridge while both sides are idle. The
exclusive-use rule above still applies: another component cannot be configured on the same UART. To let other
components use the UART while the bridge is paused, put them on a UART Mux.