ESPHome 2026.4.3
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);
33}
34
35void USBCDCACMInstance::queue_line_coding_event(uint32_t bit_rate, uint8_t stop_bits, uint8_t parity,
36 uint8_t data_bits) {
37 // Allocate event from pool
38 CDCEvent *event = this->event_pool_.allocate();
39 if (event == nullptr) {
40 ESP_LOGW(TAG, "Event pool exhausted, line coding event dropped (itf=%d)", this->itf_);
41 return;
42 }
43
45 event->data.line_coding.bit_rate = bit_rate;
46 event->data.line_coding.stop_bits = stop_bits;
47 event->data.line_coding.parity = parity;
48 event->data.line_coding.data_bits = data_bits;
49
50 // Push always succeeds: pool is sized to queue capacity (SIZE-1), so if
51 // allocate() returned non-null, the queue cannot be full.
52 this->event_queue_.push(event);
54}
55
57 // Process all pending events from the queue
58 CDCEvent *event;
59 while ((event = this->event_queue_.pop()) != nullptr) {
60 switch (event->type) {
62 bool dtr = event->data.line_state.dtr;
63 bool rts = event->data.line_state.rts;
64
65 // Invoke user callback in main loop context
66 if (this->line_state_callback_ != nullptr) {
67 this->line_state_callback_(dtr, rts);
68 }
69 break;
70 }
72 uint32_t bit_rate = event->data.line_coding.bit_rate;
73 uint8_t stop_bits = event->data.line_coding.stop_bits;
74 uint8_t parity = event->data.line_coding.parity;
75 uint8_t data_bits = event->data.line_coding.data_bits;
76
77 // Update UART configuration based on CDC line coding
78 this->baud_rate_ = bit_rate;
79 this->data_bits_ = data_bits;
80
81 // Convert CDC stop bits to UART stop bits format
82 // CDC: 0=1 stop bit, 1=1.5 stop bits, 2=2 stop bits
83 this->stop_bits_ = (stop_bits == 0) ? 1 : (stop_bits == 1) ? 1 : 2;
84
85 // Convert CDC parity to UART parity format
86 // CDC: 0=None, 1=Odd, 2=Even, 3=Mark, 4=Space
87 switch (parity) {
88 case 0:
90 break;
91 case 1:
93 break;
94 case 2:
96 break;
97 default:
98 // Mark and Space parity are not commonly supported, default to None
100 break;
101 }
102
103 // Invoke user callback in main loop context
104 if (this->line_coding_callback_ != nullptr) {
105 this->line_coding_callback_(bit_rate, stop_bits, parity, data_bits);
106 }
107 break;
108 }
109 }
110 // Return event to pool for reuse
111 this->event_pool_.release(event);
112 }
113}
114
115//==============================================================================
116// USBCDCACMComponent Implementation
117//==============================================================================
118
120
122 // Setup all registered interfaces
123 for (auto *interface : this->interfaces_) {
124 if (interface != nullptr) {
125 interface->setup();
126 }
127 }
128}
129
131 // Call loop() on all registered interfaces to process events
132 for (auto *interface : this->interfaces_) {
133 if (interface != nullptr) {
134 interface->loop();
135 }
136 }
137}
138
140 ESP_LOGCONFIG(TAG,
141 "USB CDC-ACM:\n"
142 " Number of Interfaces: %d",
143 ESPHOME_MAX_USB_CDC_INSTANCES);
144 for (uint8_t i = 0; i < ESPHOME_MAX_USB_CDC_INSTANCES; ++i) {
145 if (this->interfaces_[i] != nullptr) {
146 this->interfaces_[i]->dump_config();
147 } else {
148 ESP_LOGCONFIG(TAG, " Interface %u is disabled", i);
149 }
150 }
151}
152
154 uint8_t itf_num = static_cast<uint8_t>(interface->get_itf());
155 if (itf_num < ESPHOME_MAX_USB_CDC_INSTANCES) {
156 this->interfaces_[itf_num] = interface;
157 } else {
158 ESP_LOGE(TAG, "Interface number must be less than %u", ESPHOME_MAX_USB_CDC_INSTANCES);
159 }
160}
161
163 for (auto *interface : this->interfaces_) {
164 if ((interface != nullptr) && (interface->get_itf() == itf)) {
165 return interface;
166 }
167 }
168 return nullptr;
169}
170
171} // namespace esphome::usb_cdc_acm
172#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: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