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