mirror of
https://github.com/esphome/esphome.git
synced 2026-05-21 10:21:35 +08:00
[usb_uart] Return flush result, expose timeout via config (#14616)
This commit is contained in:
@@ -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]))
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user