ESPHome 2026.3.0
Loading...
Searching...
No Matches
usb_cdc_acm.cpp
Go to the documentation of this file.
1#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
2#include "usb_cdc_acm.h"
5#include "esphome/core/log.h"
7
8static const char *const TAG = "usb_cdc_acm";
9
10// Global component instance for managing USB device
11USBCDCACMComponent *global_usb_cdc_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
12
13//==============================================================================
14// USBCDCACMInstance Implementation
15//==============================================================================
16
18 // Allocate event from pool
19 CDCEvent *event = this->event_pool_.allocate();
20 if (event == nullptr) {
21 ESP_LOGW(TAG, "Event pool exhausted, line state event dropped (itf=%d)", this->itf_);
22 return;
23 }
24
26 event->data.line_state.dtr = dtr;
27 event->data.line_state.rts = rts;
28
29 // Push always succeeds: pool is sized to queue capacity (SIZE-1), so if
30 // allocate() returned non-null, the queue cannot be full.
31 this->event_queue_.push(event);
32
33#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
35#endif
36}
37
38void USBCDCACMInstance::queue_line_coding_event(uint32_t bit_rate, uint8_t stop_bits, uint8_t parity,
39 uint8_t data_bits) {
40 // Allocate event from pool
41 CDCEvent *event = this->event_pool_.allocate();
42 if (event == nullptr) {
43 ESP_LOGW(TAG, "Event pool exhausted, line coding event dropped (itf=%d)", this->itf_);
44 return;
45 }
46
48 event->data.line_coding.bit_rate = bit_rate;
49 event->data.line_coding.stop_bits = stop_bits;
50 event->data.line_coding.parity = parity;
51 event->data.line_coding.data_bits = data_bits;
52
53 // Push always succeeds: pool is sized to queue capacity (SIZE-1), so if
54 // allocate() returned non-null, the queue cannot be full.
55 this->event_queue_.push(event);
56
57#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
59#endif
60}
61
63 // Process all pending events from the queue
64 CDCEvent *event;
65 while ((event = this->event_queue_.pop()) != nullptr) {
66 switch (event->type) {
68 bool dtr = event->data.line_state.dtr;
69 bool rts = event->data.line_state.rts;
70
71 // Invoke user callback in main loop context
72 if (this->line_state_callback_ != nullptr) {
73 this->line_state_callback_(dtr, rts);
74 }
75 break;
76 }
78 uint32_t bit_rate = event->data.line_coding.bit_rate;
79 uint8_t stop_bits = event->data.line_coding.stop_bits;
80 uint8_t parity = event->data.line_coding.parity;
81 uint8_t data_bits = event->data.line_coding.data_bits;
82
83 // Update UART configuration based on CDC line coding
84 this->baud_rate_ = bit_rate;
85 this->data_bits_ = data_bits;
86
87 // Convert CDC stop bits to UART stop bits format
88 // CDC: 0=1 stop bit, 1=1.5 stop bits, 2=2 stop bits
89 this->stop_bits_ = (stop_bits == 0) ? 1 : (stop_bits == 1) ? 1 : 2;
90
91 // Convert CDC parity to UART parity format
92 // CDC: 0=None, 1=Odd, 2=Even, 3=Mark, 4=Space
93 switch (parity) {
94 case 0:
96 break;
97 case 1:
99 break;
100 case 2:
102 break;
103 default:
104 // Mark and Space parity are not commonly supported, default to None
106 break;
107 }
108
109 // Invoke user callback in main loop context
110 if (this->line_coding_callback_ != nullptr) {
111 this->line_coding_callback_(bit_rate, stop_bits, parity, data_bits);
112 }
113 break;
114 }
115 }
116 // Return event to pool for reuse
117 this->event_pool_.release(event);
118 }
119}
120
121//==============================================================================
122// USBCDCACMComponent Implementation
123//==============================================================================
124
126
128 // Setup all registered interfaces
129 for (auto *interface : this->interfaces_) {
130 if (interface != nullptr) {
131 interface->setup();
132 }
133 }
134}
135
137 // Call loop() on all registered interfaces to process events
138 for (auto *interface : this->interfaces_) {
139 if (interface != nullptr) {
140 interface->loop();
141 }
142 }
143}
144
146 ESP_LOGCONFIG(TAG,
147 "USB CDC-ACM:\n"
148 " Number of Interfaces: %d",
149 ESPHOME_MAX_USB_CDC_INSTANCES);
150 for (uint8_t i = 0; i < ESPHOME_MAX_USB_CDC_INSTANCES; ++i) {
151 if (this->interfaces_[i] != nullptr) {
152 this->interfaces_[i]->dump_config();
153 } else {
154 ESP_LOGCONFIG(TAG, " Interface %u is disabled", i);
155 }
156 }
157}
158
160 uint8_t itf_num = static_cast<uint8_t>(interface->get_itf());
161 if (itf_num < ESPHOME_MAX_USB_CDC_INSTANCES) {
162 this->interfaces_[itf_num] = interface;
163 } else {
164 ESP_LOGE(TAG, "Interface number must be less than %u", ESPHOME_MAX_USB_CDC_INSTANCES);
165 }
166}
167
169 for (auto *interface : this->interfaces_) {
170 if ((interface != nullptr) && (interface->get_itf() == itf)) {
171 return interface;
172 }
173 }
174 return nullptr;
175}
176
177} // namespace esphome::usb_cdc_acm
178#endif
void wake_loop_threadsafe()
Wake the main event loop from another FreeRTOS task.
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)
Represents a single CDC ACM interface instance.
Definition usb_cdc_acm.h:53
EventPool< CDCEvent, EVENT_QUEUE_SIZE - 1 > event_pool_
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)
void queue_line_state_event(bool dtr, bool rts)
USBCDCACMComponent * global_usb_cdc_component
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t