ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
usb_uart.h
Go to the documentation of this file.
1#pragma once
2
3#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
10#include <atomic>
11#include <functional>
12
13namespace esphome::usb_uart {
14
15class USBUartTypeCdcAcm;
16class USBUartComponent;
17class USBUartChannel;
18
19static const char *const TAG = "usb_uart";
20
21static constexpr uint8_t USB_CDC_SUBCLASS_ACM = 0x02;
22static constexpr uint8_t USB_SUBCLASS_COMMON = 0x02;
23static constexpr uint8_t USB_SUBCLASS_NULL = 0x00;
24static constexpr uint8_t USB_PROTOCOL_NULL = 0x00;
25static constexpr uint8_t USB_DEVICE_PROTOCOL_IAD = 0x01;
26static constexpr uint8_t USB_VENDOR_IFC = usb_host::USB_TYPE_VENDOR | usb_host::USB_RECIP_INTERFACE;
27static constexpr uint8_t USB_VENDOR_DEV = usb_host::USB_TYPE_VENDOR | usb_host::USB_RECIP_DEVICE;
28
29struct CdcEps {
30 const usb_ep_desc_t *notify_ep;
31 const usb_ep_desc_t *in_ep;
32 const usb_ep_desc_t *out_ep;
36};
37
45
51
52static const char *const PARITY_NAMES[] = {"NONE", "ODD", "EVEN", "MARK", "SPACE"};
53static const char *const STOP_BITS_NAMES[] = {"1", "1.5", "2"};
54
56 public:
57 RingBuffer(uint16_t buffer_size) : buffer_size_(buffer_size), buffer_(new uint8_t[buffer_size]) {}
58 bool is_empty() const { return this->read_pos_ == this->insert_pos_; }
59 size_t get_available() const {
60 return (this->insert_pos_ + this->buffer_size_ - this->read_pos_) % this->buffer_size_;
61 };
62 size_t get_free_space() const { return this->buffer_size_ - 1 - this->get_available(); }
63 uint8_t peek() const { return this->buffer_[this->read_pos_]; }
64 void push(uint8_t item);
65 void push(const uint8_t *data, size_t len);
66 uint8_t pop();
67 size_t pop(uint8_t *data, size_t len);
68 void clear() { this->read_pos_ = this->insert_pos_ = 0; }
69
70 protected:
71 uint16_t insert_pos_ = 0;
72 uint16_t read_pos_ = 0;
73 uint16_t buffer_size_;
74 uint8_t *buffer_;
75};
76
77// Structure for queuing received USB data chunks
79 static constexpr size_t MAX_CHUNK_SIZE = 64; // USB packet size
81 uint8_t length; // Max 64 bytes, so uint8_t is sufficient
83
84 // Required for EventPool - no cleanup needed for POD types
85 void release() {}
86};
87
88// Structure for queuing outgoing USB data chunks (one per USB FS packet)
90 static constexpr size_t MAX_CHUNK_SIZE = 64; // USB FS MPS
92 uint8_t length;
93
94 // Required for EventPool - no cleanup needed for POD types
95 void release() {}
96};
97
98class USBUartChannel : public uart::UARTComponent, public Parented<USBUartComponent> {
99 friend class USBUartComponent;
100 friend class USBUartTypeCdcAcm;
101 friend class USBUartTypeCP210X;
102 friend class USBUartTypeCH34X;
103
104 public:
105 // Number of output chunk slots per channel (8 × 64 bytes = 512 bytes peak, lazily allocated)
106 static constexpr uint8_t USB_OUTPUT_CHUNK_COUNT = 8;
107
108 USBUartChannel(uint8_t index, uint16_t buffer_size) : index_(index), input_buffer_(RingBuffer(buffer_size)) {}
109 void write_array(const uint8_t *data, size_t len) override;
110 bool peek_byte(uint8_t *data) override;
111 bool read_array(uint8_t *data, size_t len) override;
112 size_t available() override { return this->input_buffer_.get_available(); }
113 void flush() override;
114 void check_logger_conflict() override {}
115 void set_parity(UARTParityOptions parity) { this->parity_ = parity; }
116 void set_debug(bool debug) { this->debug_ = debug; }
117 void set_dummy_receiver(bool dummy_receiver) { this->dummy_receiver_ = dummy_receiver; }
118
123 void set_rx_callback(std::function<void()> cb) { this->rx_callback_ = std::move(cb); }
124
125 protected:
126 // Larger structures first for better alignment
130 std::function<void()> rx_callback_{};
132 // Enum (likely 4 bytes)
134 // Group atomics together (each 1 byte)
135 std::atomic<bool> input_started_{true};
136 std::atomic<bool> output_started_{true};
137 std::atomic<bool> initialised_{false};
138 // Group regular bytes together to minimize padding
139 const uint8_t index_;
140 bool debug_{};
142};
143
145 public:
146 USBUartComponent(uint16_t vid, uint16_t pid) : usb_host::USBClient(vid, pid) {}
147 void setup() override;
148 void loop() override;
149 void dump_config() override;
150 std::vector<USBUartChannel *> get_channels() { return this->channels_; }
151
152 void add_channel(USBUartChannel *channel) { this->channels_.push_back(channel); }
153
154 void start_input(USBUartChannel *channel);
155 void start_output(USBUartChannel *channel);
156
157 // Lock-free data transfer from USB task to main loop
158 static constexpr int USB_DATA_QUEUE_SIZE = 32;
161
162 protected:
163 std::vector<USBUartChannel *> channels_{};
164};
165
167 public:
168 USBUartTypeCdcAcm(uint16_t vid, uint16_t pid) : USBUartComponent(vid, pid) {}
169
170 protected:
171 virtual std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl);
172 void on_connected() override;
173 void on_disconnected() override;
174 virtual void enable_channels();
178 void start_channels();
179};
180
182 public:
183 USBUartTypeCP210X(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {}
184
185 protected:
186 std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl) override;
187 void enable_channels() override;
188};
190 public:
191 USBUartTypeCH34X(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {}
192
193 protected:
194 void enable_channels() override;
195};
196
197} // namespace esphome::usb_uart
198
199#endif // USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
Helper class to easily give an object a parent of type T.
Definition helpers.h:1618
USBClient(uint16_t vid, uint16_t pid)
Definition usb_host.h:129
void push(uint8_t item)
Definition usb_uart.cpp:107
RingBuffer(uint16_t buffer_size)
Definition usb_uart.h:57
size_t get_free_space() const
Definition usb_uart.h:62
size_t get_available() const
Definition usb_uart.h:59
void set_dummy_receiver(bool dummy_receiver)
Definition usb_uart.h:117
std::atomic< bool > input_started_
Definition usb_uart.h:135
std::atomic< bool > initialised_
Definition usb_uart.h:137
LockFreeQueue< UsbOutputChunk, USB_OUTPUT_CHUNK_COUNT > output_queue_
Definition usb_uart.h:128
bool peek_byte(uint8_t *data) override
Definition usb_uart.cpp:181
std::function< void()> rx_callback_
Definition usb_uart.h:130
void set_parity(UARTParityOptions parity)
Definition usb_uart.h:115
void write_array(const uint8_t *data, size_t len) override
Definition usb_uart.cpp:131
void check_logger_conflict() override
Definition usb_uart.h:114
void set_rx_callback(std::function< void()> cb)
Register a callback invoked immediately after data is pushed to the input ring buffer.
Definition usb_uart.h:123
static constexpr uint8_t USB_OUTPUT_CHUNK_COUNT
Definition usb_uart.h:106
EventPool< UsbOutputChunk, USB_OUTPUT_CHUNK_COUNT > output_pool_
Definition usb_uart.h:129
bool read_array(uint8_t *data, size_t len) override
Definition usb_uart.cpp:188
USBUartChannel(uint8_t index, uint16_t buffer_size)
Definition usb_uart.h:108
std::atomic< bool > output_started_
Definition usb_uart.h:136
void add_channel(USBUartChannel *channel)
Definition usb_uart.h:152
EventPool< UsbDataChunk, USB_DATA_QUEUE_SIZE > chunk_pool_
Definition usb_uart.h:160
USBUartComponent(uint16_t vid, uint16_t pid)
Definition usb_uart.h:146
std::vector< USBUartChannel * > channels_
Definition usb_uart.h:163
LockFreeQueue< UsbDataChunk, USB_DATA_QUEUE_SIZE > usb_data_queue_
Definition usb_uart.h:159
void start_output(USBUartChannel *channel)
Definition usb_uart.cpp:335
void start_input(USBUartChannel *channel)
Definition usb_uart.cpp:265
std::vector< USBUartChannel * > get_channels()
Definition usb_uart.h:150
static constexpr int USB_DATA_QUEUE_SIZE
Definition usb_uart.h:158
void enable_channels() override
CH34x.
Definition ch34x.cpp:15
USBUartTypeCH34X(uint16_t vid, uint16_t pid)
Definition usb_uart.h:191
USBUartTypeCP210X(uint16_t vid, uint16_t pid)
Definition usb_uart.h:183
std::vector< CdcEps > parse_descriptors(usb_device_handle_t dev_hdl) override
Definition cp210x.cpp:45
void start_channels()
Resets per-channel transfer flags and posts the first bulk IN transfer.
Definition usb_uart.cpp:523
USBUartTypeCdcAcm(uint16_t vid, uint16_t pid)
Definition usb_uart.h:168
virtual std::vector< CdcEps > parse_descriptors(usb_device_handle_t dev_hdl)
Definition usb_uart.cpp:61
const char *const TAG
Definition spi.cpp:7
@ UART_CONFIG_STOP_BITS_1_5
Definition usb_uart.h:48
std::string size_t len
Definition helpers.h:817
const usb_ep_desc_t * out_ep
Definition usb_uart.h:32
const usb_ep_desc_t * notify_ep
Definition usb_uart.h:30
const usb_ep_desc_t * in_ep
Definition usb_uart.h:31
uint8_t interrupt_interface_number
Definition usb_uart.h:34
uint8_t data[MAX_CHUNK_SIZE]
Definition usb_uart.h:80
static constexpr size_t MAX_CHUNK_SIZE
Definition usb_uart.h:79
uint8_t data[MAX_CHUNK_SIZE]
Definition usb_uart.h:91
static constexpr size_t MAX_CHUNK_SIZE
Definition usb_uart.h:90