ESPHome 2026.6.2
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)
11#include <atomic>
12#include <functional>
13
14namespace esphome::usb_uart {
15
16class USBUartTypeCdcAcm;
17class USBUartComponent;
18class USBUartChannel;
19class USBUartTypePL2303;
20
21static const char *const TAG = "usb_uart";
22
23static constexpr uint8_t USB_CDC_SUBCLASS_ACM = 0x02;
24static constexpr uint8_t USB_SUBCLASS_COMMON = 0x02;
25static constexpr uint8_t USB_SUBCLASS_NULL = 0x00;
26static constexpr uint8_t USB_PROTOCOL_NULL = 0x00;
27static constexpr uint8_t USB_DEVICE_PROTOCOL_IAD = 0x01;
28static constexpr uint8_t USB_VENDOR_IFC = usb_host::USB_TYPE_VENDOR | usb_host::USB_RECIP_INTERFACE;
29static constexpr uint8_t USB_VENDOR_DEV = usb_host::USB_TYPE_VENDOR | usb_host::USB_RECIP_DEVICE;
30
31struct CdcEps {
32 const usb_ep_desc_t *notify_ep;
33 const usb_ep_desc_t *in_ep;
34 const usb_ep_desc_t *out_ep;
37};
38
68
76
82
83static const char *const PARITY_NAMES[] = {"NONE", "ODD", "EVEN", "MARK", "SPACE"};
84static const char *const STOP_BITS_NAMES[] = {"1", "1.5", "2"};
85
87 public:
88 RingBuffer(uint16_t buffer_size) : buffer_size_(buffer_size), buffer_(new uint8_t[buffer_size]) {}
89 bool is_empty() const { return this->read_pos_ == this->insert_pos_; }
90 size_t get_available() const {
91 return (this->insert_pos_ + this->buffer_size_ - this->read_pos_) % this->buffer_size_;
92 };
93 size_t get_free_space() const { return this->buffer_size_ - 1 - this->get_available(); }
94 uint8_t peek() const { return this->buffer_[this->read_pos_]; }
95 void push(uint8_t item);
96 void push(const uint8_t *data, size_t len);
97 uint8_t pop();
98 size_t pop(uint8_t *data, size_t len);
99 void clear() { this->read_pos_ = this->insert_pos_ = 0; }
100
101 protected:
102 uint16_t insert_pos_ = 0;
103 uint16_t read_pos_ = 0;
104 uint16_t buffer_size_;
105 uint8_t *buffer_;
106};
107
108// Structure for queuing received USB data chunks
110 uint8_t data[usb_host::USB_MAX_PACKET_SIZE];
111 uint16_t length;
113
114 // Required for EventPool - no cleanup needed for POD types
115 void release() {}
116};
117
118// Structure for queuing outgoing USB data chunks (one per USB packet)
120 static constexpr size_t MAX_CHUNK_SIZE = usb_host::USB_MAX_PACKET_SIZE;
122 uint16_t length;
123
124 // Required for EventPool - no cleanup needed for POD types
125 void release() {}
126};
127
128class USBUartChannel : public uart::UARTComponent, public Parented<USBUartComponent> {
129 friend class USBUartComponent;
130 friend class USBUartTypeCdcAcm;
131 friend class USBUartTypeCP210X;
132 friend class USBUartTypeCH34X;
133 friend class USBUartTypeFT23XX;
134 friend class USBUartTypePL2303;
135
136 public:
137 // Number of output chunk slots per channel, derived from buffer_size config.
138 // Computed as ceil(buffer_size / 64) + 1 in Python codegen; defaults to 5 (256 / 64 + 1).
139 static constexpr uint8_t USB_OUTPUT_CHUNK_COUNT = USB_UART_OUTPUT_CHUNK_COUNT;
140
141 USBUartChannel(uint8_t index, uint16_t buffer_size) : input_buffer_(RingBuffer(buffer_size)), index_(index) {}
142 void write_array(const uint8_t *data, size_t len) override;
143 bool peek_byte(uint8_t *data) override;
144 bool read_array(uint8_t *data, size_t len) override;
145 size_t available() override { return this->input_buffer_.get_available(); }
146 bool is_connected() override { return this->initialised_.load(); }
147 uart::UARTFlushResult flush() override;
148 void check_logger_conflict() override {}
149 void set_parity(UARTParityOptions parity) { this->parity_ = parity; }
150 void set_debug(bool debug) { this->debug_ = debug; }
151 void set_dummy_receiver(bool dummy_receiver) { this->dummy_receiver_ = dummy_receiver; }
152 void set_debug_prefix(const char *prefix) { this->debug_prefix_ = StringRef(prefix); }
153 void set_flush_timeout(uint32_t flush_timeout_ms) override { this->flush_timeout_ms_ = flush_timeout_ms; }
154
159 void set_rx_callback(std::function<void()> cb) { this->rx_callback_ = std::move(cb); }
160
161 protected:
162 // Larger structures first (8+ bytes)
165 // Pool sized to queue capacity (SIZE-1) because LockFreeQueue<T,N> is a ring
166 // buffer that holds N-1 elements. This guarantees allocate() returns nullptr
167 // before push() can fail, preventing a pool slot leak.
169 std::function<void()> rx_callback_{};
172 // 4-byte fields
175 // 1-byte fields (no padding between groups)
176 std::atomic<bool> input_started_{true};
177 std::atomic<bool> output_started_{true};
178 std::atomic<bool> initialised_{false};
179 const uint8_t index_;
180 bool debug_{};
182};
183
185 public:
186 USBUartComponent(uint16_t vid, uint16_t pid) : usb_host::USBClient(vid, pid) {}
187 void setup() override;
188 void loop() override;
189 void dump_config() override;
190 std::vector<USBUartChannel *> get_channels() { return this->channels_; }
191
192 void add_channel(USBUartChannel *channel) { this->channels_.push_back(channel); }
193
194 void start_input(USBUartChannel *channel);
195 void start_output(USBUartChannel *channel);
196
197 // Lock-free data transfer from USB task to main loop
198 static constexpr int USB_DATA_QUEUE_SIZE = 32;
200 // Pool sized to queue capacity (SIZE-1) — see USBUartChannel::output_pool_ comment.
202
203 protected:
204 std::vector<USBUartChannel *> channels_{};
205};
206
208 public:
209 USBUartTypeCdcAcm(uint16_t vid, uint16_t pid) : USBUartComponent(vid, pid) {}
210
211 protected:
212 virtual std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl);
213 void on_connected() override;
214 void on_disconnected() override;
215 virtual void enable_channels();
219 void start_channels_();
220};
221
223 public:
224 USBUartTypeCP210X(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {}
225
226 protected:
227 std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl) override;
228 void enable_channels() override;
229};
231 public:
232 USBUartTypeCH34X(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {}
233 void dump_config() override;
234
235 protected:
236 void enable_channels() override;
237 std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl) override;
238
239 private:
240 void apply_line_settings_();
241 CH34xChipType chiptype_{CHIP_UNKNOWN};
242 const char *chip_name_{"unknown"};
243 uint8_t num_ports_{1};
244};
245
247 public:
248 USBUartTypeFT23XX(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {}
249
250 void start_input(USBUartChannel *channel);
251
252 protected:
253 std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl) override;
254 void enable_channels() override;
255
256 int reset_(USBUartChannel *channel);
257 int set_baudrate_(USBUartChannel *channel, uint32_t baudrate = 0);
259 int set_dtr_rts_(USBUartChannel *channel);
260
261 uint8_t chip_type_{255};
262};
263
264enum Pl2303ChipType : uint8_t {
265 PL2303_TYPE_H = 0, // Legacy, max 1.2Mbaud
266 PL2303_TYPE_HX, // max 6Mbaud, divisor encoding
267 PL2303_TYPE_TA, // max 6Mbaud, alt divisor encoding
268 PL2303_TYPE_TB, // max 12Mbaud, alt divisor encoding
269 PL2303_TYPE_HXD, // max 12Mbaud, divisor encoding
270 PL2303_TYPE_HXN, // G-series, max 12Mbaud, direct encoding only
272};
273
275 friend class USBUartChannel;
276
277 public:
278 USBUartTypePL2303(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {}
279
280 protected:
281 std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl) override;
282 void enable_channels() override;
283
285};
286
287} // namespace esphome::usb_uart
288
289#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:1861
StringRef is a reference to a string owned by something else.
Definition string_ref.h:26
USBClient(uint16_t vid, uint16_t pid)
Definition usb_host.h:131
void push(uint8_t item)
Definition usb_uart.cpp:108
RingBuffer(uint16_t buffer_size)
Definition usb_uart.h:88
size_t get_free_space() const
Definition usb_uart.h:93
size_t get_available() const
Definition usb_uart.h:90
void set_dummy_receiver(bool dummy_receiver)
Definition usb_uart.h:151
EventPool< UsbOutputChunk, USB_OUTPUT_CHUNK_COUNT - 1 > output_pool_
Definition usb_uart.h:168
std::atomic< bool > input_started_
Definition usb_uart.h:176
std::atomic< bool > initialised_
Definition usb_uart.h:178
LockFreeQueue< UsbOutputChunk, USB_OUTPUT_CHUNK_COUNT > output_queue_
Definition usb_uart.h:164
bool peek_byte(uint8_t *data) override
Definition usb_uart.cpp:187
void set_flush_timeout(uint32_t flush_timeout_ms) override
Definition usb_uart.h:153
std::function< void()> rx_callback_
Definition usb_uart.h:169
void set_parity(UARTParityOptions parity)
Definition usb_uart.h:149
void write_array(const uint8_t *data, size_t len) override
Definition usb_uart.cpp:137
void check_logger_conflict() override
Definition usb_uart.h:148
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:159
uart::UARTFlushResult flush() override
Definition usb_uart.cpp:171
static constexpr uint8_t USB_OUTPUT_CHUNK_COUNT
Definition usb_uart.h:139
bool read_array(uint8_t *data, size_t len) override
Definition usb_uart.cpp:194
void set_debug_prefix(const char *prefix)
Definition usb_uart.h:152
USBUartChannel(uint8_t index, uint16_t buffer_size)
Definition usb_uart.h:141
std::atomic< bool > output_started_
Definition usb_uart.h:177
void add_channel(USBUartChannel *channel)
Definition usb_uart.h:192
USBUartComponent(uint16_t vid, uint16_t pid)
Definition usb_uart.h:186
std::vector< USBUartChannel * > channels_
Definition usb_uart.h:204
LockFreeQueue< UsbDataChunk, USB_DATA_QUEUE_SIZE > usb_data_queue_
Definition usb_uart.h:199
void start_output(USBUartChannel *channel)
Definition usb_uart.cpp:341
void start_input(USBUartChannel *channel)
Definition usb_uart.cpp:272
std::vector< USBUartChannel * > get_channels()
Definition usb_uart.h:190
static constexpr int USB_DATA_QUEUE_SIZE
Definition usb_uart.h:198
EventPool< UsbDataChunk, USB_DATA_QUEUE_SIZE - 1 > chunk_pool_
Definition usb_uart.h:201
std::vector< CdcEps > parse_descriptors(usb_device_handle_t dev_hdl) override
Definition ch34x.cpp:163
USBUartTypeCH34X(uint16_t vid, uint16_t pid)
Definition usb_uart.h:232
USBUartTypeCP210X(uint16_t vid, uint16_t pid)
Definition usb_uart.h:224
std::vector< CdcEps > parse_descriptors(usb_device_handle_t dev_hdl) override
Definition cp210x.cpp:45
USBUartTypeCdcAcm(uint16_t vid, uint16_t pid)
Definition usb_uart.h:209
virtual std::vector< CdcEps > parse_descriptors(usb_device_handle_t dev_hdl)
Definition usb_uart.cpp:62
void start_channels_()
Resets per-channel transfer flags and posts the first bulk IN transfer.
Definition usb_uart.cpp:532
USBUartTypeFT23XX(uint16_t vid, uint16_t pid)
Definition usb_uart.h:248
void start_input(USBUartChannel *channel)
Definition ft23xx.cpp:396
int set_baudrate_(USBUartChannel *channel, uint32_t baudrate=0)
Definition ft23xx.cpp:284
int reset_(USBUartChannel *channel)
Definition ft23xx.cpp:264
std::vector< CdcEps > parse_descriptors(usb_device_handle_t dev_hdl) override
Definition ft23xx.cpp:214
int set_dtr_rts_(USBUartChannel *channel)
Definition ft23xx.cpp:365
int set_line_properties_(USBUartChannel *channel)
Definition ft23xx.cpp:310
USBUartTypePL2303(uint16_t vid, uint16_t pid)
Definition usb_uart.h:278
std::vector< CdcEps > parse_descriptors(usb_device_handle_t dev_hdl) override
Definition pl2303.cpp:115
const char *const TAG
Definition spi.cpp:7
UARTFlushResult
Result of a flush() call.
@ UART_CONFIG_STOP_BITS_1_5
Definition usb_uart.h:79
const void size_t len
Definition hal.h:64
static void uint32_t
const usb_ep_desc_t * out_ep
Definition usb_uart.h:34
const usb_ep_desc_t * notify_ep
Definition usb_uart.h:32
const usb_ep_desc_t * in_ep
Definition usb_uart.h:33
uint8_t interrupt_interface_number
Definition usb_uart.h:36
uint8_t data[usb_host::USB_MAX_PACKET_SIZE]
Definition usb_uart.h:110
uint8_t data[MAX_CHUNK_SIZE]
Definition usb_uart.h:121
static constexpr size_t MAX_CHUNK_SIZE
Definition usb_uart.h:120