[usb_uart] Return flush result, expose timeout via config (#14616)

This commit is contained in:
Keith Burzinski
2026-03-08 00:04:14 -06:00
committed by GitHub
parent 5e842a8b20
commit 04cff1c916
3 changed files with 17 additions and 10 deletions
+5 -1
View File
@@ -1,7 +1,7 @@
import esphome.codegen as cg
from esphome.components import socket
from esphome.components.const import CONF_DATA_BITS, CONF_PARITY, CONF_STOP_BITS
from esphome.components.uart import CONF_DEBUG_PREFIX, UARTComponent
from esphome.components.uart import CONF_DEBUG_PREFIX, CONF_FLUSH_TIMEOUT, UARTComponent
from esphome.components.usb_host import register_usb_client, usb_device_schema
import esphome.config_validation as cv
from esphome.const import (
@@ -91,6 +91,9 @@ def channel_schema(channels, baud_rate_required):
cv.Optional(CONF_DUMMY_RECEIVER, default=False): cv.boolean,
cv.Optional(CONF_DEBUG, default=False): cv.boolean,
cv.Optional(CONF_DEBUG_PREFIX, default=""): cv.string,
cv.Optional(
CONF_FLUSH_TIMEOUT, default="100ms"
): cv.positive_time_period_milliseconds,
}
)
),
@@ -129,6 +132,7 @@ async def to_code(config):
cg.add(chvar.set_parity(channel[CONF_PARITY]))
cg.add(chvar.set_baud_rate(channel[CONF_BAUD_RATE]))
cg.add(chvar.set_dummy_receiver(channel[CONF_DUMMY_RECEIVER]))
cg.add(chvar.set_flush_timeout(channel[CONF_FLUSH_TIMEOUT]))
cg.add(chvar.set_debug(channel[CONF_DEBUG]))
if channel[CONF_DEBUG_PREFIX]:
cg.add(chvar.set_debug_prefix(channel[CONF_DEBUG_PREFIX]))
+6 -4
View File
@@ -169,10 +169,10 @@ void USBUartChannel::write_array(const uint8_t *data, size_t len) {
uart::FlushResult USBUartChannel::flush() {
// Spin until the output queue is drained and the last USB transfer completes.
// Safe to call from the main loop only.
// The 100 ms timeout guards against a device that stops responding mid-flush;
// The flush_timeout_ms_ timeout guards against a device that stops responding mid-flush;
// in that case the main loop is blocked for the full duration.
uint32_t start = millis(); // 100 ms safety timeout
while ((!this->output_queue_.empty() || this->output_started_.load()) && millis() - start < 100) {
uint32_t start = millis();
while ((!this->output_queue_.empty() || this->output_started_.load()) && millis() - start < this->flush_timeout_ms_) {
// Kick start_output() in case data arrived but no transfer is in flight yet.
this->parent_->start_output(this);
yield();
@@ -260,10 +260,12 @@ void USBUartComponent::dump_config() {
" Data Bits: %u\n"
" Parity: %s\n"
" Stop bits: %s\n"
" Flush Timeout: %" PRIu32 " ms\n"
" Debug: %s\n"
" Dummy receiver: %s",
channel->index_, channel->baud_rate_, channel->data_bits_, PARITY_NAMES[channel->parity_],
STOP_BITS_NAMES[channel->stop_bits_], YESNO(channel->debug_), YESNO(channel->dummy_receiver_));
STOP_BITS_NAMES[channel->stop_bits_], channel->flush_timeout_ms_, YESNO(channel->debug_),
YESNO(channel->dummy_receiver_));
}
}
void USBUartComponent::start_input(USBUartChannel *channel) {
+6 -5
View File
@@ -116,6 +116,7 @@ class USBUartChannel : public uart::UARTComponent, public Parented<USBUartCompon
void set_debug(bool debug) { this->debug_ = debug; }
void set_dummy_receiver(bool dummy_receiver) { this->dummy_receiver_ = dummy_receiver; }
void set_debug_prefix(const char *prefix) { this->debug_prefix_ = StringRef(prefix); }
void set_flush_timeout(uint32_t flush_timeout_ms) override { this->flush_timeout_ms_ = flush_timeout_ms; }
/// Register a callback invoked immediately after data is pushed to the input ring buffer.
/// Called from USBUartComponent::loop() in the main loop context.
@@ -124,23 +125,23 @@ class USBUartChannel : public uart::UARTComponent, public Parented<USBUartCompon
void set_rx_callback(std::function<void()> cb) { this->rx_callback_ = std::move(cb); }
protected:
// Larger structures first for better alignment
// Larger structures first (8+ bytes)
RingBuffer input_buffer_;
LockFreeQueue<UsbOutputChunk, USB_OUTPUT_CHUNK_COUNT> output_queue_;
EventPool<UsbOutputChunk, USB_OUTPUT_CHUNK_COUNT> output_pool_;
std::function<void()> rx_callback_{};
CdcEps cdc_dev_{};
// Enum (likely 4 bytes)
StringRef debug_prefix_{};
// 4-byte fields
UARTParityOptions parity_{UART_CONFIG_PARITY_NONE};
// Group atomics together (each 1 byte)
uint32_t flush_timeout_ms_{100};
// 1-byte fields (no padding between groups)
std::atomic<bool> input_started_{true};
std::atomic<bool> output_started_{true};
std::atomic<bool> initialised_{false};
// Group regular bytes together to minimize padding
const uint8_t index_;
bool debug_{};
bool dummy_receiver_{};
StringRef debug_prefix_{};
};
class USBUartComponent : public usb_host::USBClient {