ESPHome 2026.3.0-dev
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 if (!this->event_queue_.push(event)) {
30 ESP_LOGW(TAG, "Event queue full, line state event dropped (itf=%d)", this->itf_);
31 // Return event to pool since we couldn't queue it
32 this->event_pool_.release(event);
33 } else {
34 // Wake main loop immediately to process event
35#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
37#endif
38 }
39}
40
41void USBCDCACMInstance::queue_line_coding_event(uint32_t bit_rate, uint8_t stop_bits, uint8_t parity,
42 uint8_t data_bits) {
43 // Allocate event from pool
44 CDCEvent *event = this->event_pool_.allocate();
45 if (event == nullptr) {
46 ESP_LOGW(TAG, "Event pool exhausted, line coding event dropped (itf=%d)", this->itf_);
47 return;
48 }
49
51 event->data.line_coding.bit_rate = bit_rate;
52 event->data.line_coding.stop_bits = stop_bits;
53 event->data.line_coding.parity = parity;
54 event->data.line_coding.data_bits = data_bits;
55
56 if (!this->event_queue_.push(event)) {
57 ESP_LOGW(TAG, "Event queue full, line coding event dropped (itf=%d)", this->itf_);
58 // Return event to pool since we couldn't queue it
59 this->event_pool_.release(event);
60 } else {
61 // Wake main loop immediately to process event
62#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
64#endif
65 }
66}
67
69 // Process all pending events from the queue
70 CDCEvent *event;
71 while ((event = this->event_queue_.pop()) != nullptr) {
72 switch (event->type) {
74 bool dtr = event->data.line_state.dtr;
75 bool rts = event->data.line_state.rts;
76
77 // Invoke user callback in main loop context
78 if (this->line_state_callback_ != nullptr) {
79 this->line_state_callback_(dtr, rts);
80 }
81 break;
82 }
84 uint32_t bit_rate = event->data.line_coding.bit_rate;
85 uint8_t stop_bits = event->data.line_coding.stop_bits;
86 uint8_t parity = event->data.line_coding.parity;
87 uint8_t data_bits = event->data.line_coding.data_bits;
88
89 // Update UART configuration based on CDC line coding
90 this->baud_rate_ = bit_rate;
91 this->data_bits_ = data_bits;
92
93 // Convert CDC stop bits to UART stop bits format
94 // CDC: 0=1 stop bit, 1=1.5 stop bits, 2=2 stop bits
95 this->stop_bits_ = (stop_bits == 0) ? 1 : (stop_bits == 1) ? 1 : 2;
96
97 // Convert CDC parity to UART parity format
98 // CDC: 0=None, 1=Odd, 2=Even, 3=Mark, 4=Space
99 switch (parity) {
100 case 0:
102 break;
103 case 1:
105 break;
106 case 2:
108 break;
109 default:
110 // Mark and Space parity are not commonly supported, default to None
112 break;
113 }
114
115 // Invoke user callback in main loop context
116 if (this->line_coding_callback_ != nullptr) {
117 this->line_coding_callback_(bit_rate, stop_bits, parity, data_bits);
118 }
119 break;
120 }
121 }
122 // Return event to pool for reuse
123 this->event_pool_.release(event);
124 }
125}
126
127//==============================================================================
128// USBCDCACMComponent Implementation
129//==============================================================================
130
132
134 // Setup all registered interfaces
135 for (auto *interface : this->interfaces_) {
136 if (interface != nullptr) {
137 interface->setup();
138 }
139 }
140}
141
143 // Call loop() on all registered interfaces to process events
144 for (auto *interface : this->interfaces_) {
145 if (interface != nullptr) {
146 interface->loop();
147 }
148 }
149}
150
152 ESP_LOGCONFIG(TAG,
153 "USB CDC-ACM:\n"
154 " Number of Interfaces: %d",
155 ESPHOME_MAX_USB_CDC_INSTANCES);
156 for (uint8_t i = 0; i < ESPHOME_MAX_USB_CDC_INSTANCES; ++i) {
157 if (this->interfaces_[i] != nullptr) {
158 this->interfaces_[i]->dump_config();
159 } else {
160 ESP_LOGCONFIG(TAG, " Interface %u is disabled", i);
161 }
162 }
163}
164
166 uint8_t itf_num = static_cast<uint8_t>(interface->get_itf());
167 if (itf_num < ESPHOME_MAX_USB_CDC_INSTANCES) {
168 this->interfaces_[itf_num] = interface;
169 } else {
170 ESP_LOGE(TAG, "Interface number must be less than %u", ESPHOME_MAX_USB_CDC_INSTANCES);
171 }
172}
173
175 for (auto *interface : this->interfaces_) {
176 if ((interface != nullptr) && (interface->get_itf() == itf)) {
177 return interface;
178 }
179 }
180 return nullptr;
181}
182
183} // namespace esphome::usb_cdc_acm
184#endif
void wake_loop_threadsafe()
Wake the main event loop from a FreeRTOS task Thread-safe, can be called from task context to immedia...
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 > 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.