ESPHome 2026.3.0
Loading...
Searching...
No Matches
usb_cdc_acm.h
Go to the documentation of this file.
1#pragma once
2#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
3
8
9#include <functional>
10#include "freertos/ringbuf.h"
11#include "tusb_cdc_acm.h"
12
13namespace esphome::usb_cdc_acm {
14
15static const uint8_t EVENT_QUEUE_SIZE = 12;
16
17// Callback types for line coding and line state changes
18using LineCodingCallback = std::function<void(uint32_t bit_rate, uint8_t stop_bits, uint8_t parity, uint8_t data_bits)>;
19using LineStateCallback = std::function<void(bool dtr, bool rts)>;
20
21// Event types
26
27// Event structure for the queue
28struct CDCEvent {
30 union {
31 struct {
32 bool dtr;
33 bool rts;
35 struct {
37 uint8_t stop_bits;
38 uint8_t parity;
39 uint8_t data_bits;
42
43 // Required by EventPool - called before returning to pool
44 void release() {
45 // No dynamic memory to clean up, data is stored inline
46 }
47};
48
49// Forward declaration
50class USBCDCACMComponent;
51
53class USBCDCACMInstance : public uart::UARTComponent, public Parented<USBCDCACMComponent> {
54 public:
55 void setup();
56 void loop();
57 void dump_config();
58
59 void set_interface_number(uint8_t itf) { this->itf_ = itf; }
60 // Get the CDC port number for this instance
61 uint8_t get_itf() const { return this->itf_; }
62 // Ring buffer accessors for bridge components
63 RingbufHandle_t get_tx_ringbuf() const { return this->usb_tx_ringbuf_; }
64 RingbufHandle_t get_rx_ringbuf() const { return this->usb_rx_ringbuf_; }
65
66 // Task handle accessor for notifying TX task
67 TaskHandle_t get_tx_task_handle() const { return this->usb_tx_task_handle_; }
68
69 // Callback registration for line coding and line state changes
70 void set_line_coding_callback(LineCodingCallback callback) { this->line_coding_callback_ = std::move(callback); }
71 void set_line_state_callback(LineStateCallback callback) { this->line_state_callback_ = std::move(callback); }
72
73 // Called from USB core task context queues event for processing in main loop
74 void queue_line_coding_event(uint32_t bit_rate, uint8_t stop_bits, uint8_t parity, uint8_t data_bits);
75 void queue_line_state_event(bool dtr, bool rts);
76
77 static void usb_tx_task_fn(void *arg);
78 void usb_tx_task();
79
80 // UARTComponent interface implementation
81 void write_array(const uint8_t *data, size_t len) override;
82 bool peek_byte(uint8_t *data) override;
83 bool read_array(uint8_t *data, size_t len) override;
84 size_t available() override;
85 uart::FlushResult flush() override;
86
87 protected:
88 void check_logger_conflict() override;
89
90 // Process queued events and invoke callbacks (called from main loop)
91 void process_events_();
92 TaskHandle_t usb_tx_task_handle_{nullptr};
93
94 RingbufHandle_t usb_tx_ringbuf_{nullptr};
95 RingbufHandle_t usb_rx_ringbuf_{nullptr};
96 // RX buffer for peek functionality
97 uint8_t peek_buffer_{0};
98 bool has_peek_{false};
99 uint8_t itf_{0};
100 // User-registered callbacks (called from main loop)
103
104 // Lock-free queue and event pool for cross-task event passing
105 // Pool sized to queue capacity (SIZE-1) because LockFreeQueue<T,N> is a ring
106 // buffer that holds N-1 elements. This guarantees allocate() returns nullptr
107 // before push() can fail, preventing both a pool slot leak and an SPSC
108 // violation on the pool's internal free list.
109 EventPool<CDCEvent, EVENT_QUEUE_SIZE - 1> event_pool_;
111};
112
115 public:
117
118 void setup() override;
119 void loop() override;
120 void dump_config() override;
121 float get_setup_priority() const override { return setup_priority::IO; }
122
123 // Interface management
124 void add_interface(USBCDCACMInstance *interface);
126
127 protected:
128 std::array<USBCDCACMInstance *, ESPHOME_MAX_USB_CDC_INSTANCES> interfaces_{};
129};
130
131extern USBCDCACMComponent *global_usb_cdc_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
132
133} // namespace esphome::usb_cdc_acm
134#endif
Helper class to easily give an object a parent of type T.
Definition helpers.h:1715
Main USB CDC ACM component that manages the USB device and all CDC interfaces.
void add_interface(USBCDCACMInstance *interface)
std::array< USBCDCACMInstance *, ESPHOME_MAX_USB_CDC_INSTANCES > interfaces_
USBCDCACMInstance * get_interface_by_number(uint8_t itf)
float get_setup_priority() const override
Represents a single CDC ACM interface instance.
Definition usb_cdc_acm.h:53
RingbufHandle_t get_rx_ringbuf() const
Definition usb_cdc_acm.h:64
EventPool< CDCEvent, EVENT_QUEUE_SIZE - 1 > event_pool_
bool read_array(uint8_t *data, size_t len) override
RingbufHandle_t get_tx_ringbuf() const
Definition usb_cdc_acm.h:63
void set_line_state_callback(LineStateCallback callback)
Definition usb_cdc_acm.h:71
LockFreeQueue< CDCEvent, EVENT_QUEUE_SIZE > event_queue_
void queue_line_coding_event(uint32_t bit_rate, uint8_t stop_bits, uint8_t parity, uint8_t data_bits)
TaskHandle_t get_tx_task_handle() const
Definition usb_cdc_acm.h:67
void set_line_coding_callback(LineCodingCallback callback)
Definition usb_cdc_acm.h:70
void write_array(const uint8_t *data, size_t len) override
void queue_line_state_event(bool dtr, bool rts)
constexpr float IO
For components that represent GPIO pins like PCF8573.
Definition component.h:27
FlushResult
Result of a flush() call.
std::function< void(uint32_t bit_rate, uint8_t stop_bits, uint8_t parity, uint8_t data_bits)> LineCodingCallback
Definition usb_cdc_acm.h:18
USBCDCACMComponent * global_usb_cdc_component
std::function< void(bool dtr, bool rts)> LineStateCallback
Definition usb_cdc_acm.h:19
std::string size_t len
Definition helpers.h:892
static void uint32_t
union esphome::usb_cdc_acm::CDCEvent::@160 data
struct esphome::usb_cdc_acm::CDCEvent::@160::@162 line_coding
struct esphome::usb_cdc_acm::CDCEvent::@160::@161 line_state