mirror of
https://github.com/esphome/esphome.git
synced 2026-09-17 18:18:43 +00:00
Merge remote-tracking branch 'upstream/proto-byte-buffer' into integration
This commit is contained in:
@@ -100,16 +100,17 @@ size_t BLENUS::available() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void BLENUS::flush() {
|
||||
uart::FlushResult BLENUS::flush() {
|
||||
constexpr uint32_t timeout_5sec = 5000;
|
||||
uint32_t start = millis();
|
||||
while (atomic_get(&this->tx_status_) != TX_DISABLED && !ring_buf_is_empty(&global_ble_tx_ring_buf)) {
|
||||
if (millis() - start > timeout_5sec) {
|
||||
ESP_LOGW(TAG, "Flush timeout");
|
||||
return;
|
||||
return uart::FlushResult::TIMEOUT;
|
||||
}
|
||||
delay(1);
|
||||
}
|
||||
return uart::FlushResult::SUCCESS;
|
||||
}
|
||||
|
||||
void BLENUS::connected(bt_conn *conn, uint8_t err) {
|
||||
|
||||
@@ -26,7 +26,7 @@ class BLENUS : public uart::UARTComponent, public Component {
|
||||
bool peek_byte(uint8_t *data) override;
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
size_t available() override;
|
||||
void flush() override;
|
||||
uart::FlushResult flush() override;
|
||||
void check_logger_conflict() override {}
|
||||
void set_expose_log(bool expose_log) { this->expose_log_ = expose_log; }
|
||||
#ifdef USE_LOGGER
|
||||
|
||||
@@ -183,6 +183,7 @@ UART_PARITY_OPTIONS = {
|
||||
"ODD": UARTParityOptions.UART_CONFIG_PARITY_ODD,
|
||||
}
|
||||
|
||||
CONF_FLUSH_TIMEOUT = "flush_timeout"
|
||||
CONF_RX_FULL_THRESHOLD = "rx_full_threshold"
|
||||
CONF_RX_TIMEOUT = "rx_timeout"
|
||||
|
||||
@@ -266,6 +267,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.SplitDefault(CONF_RX_TIMEOUT, esp32=2): cv.All(
|
||||
cv.only_on_esp32, cv.validate_bytes, cv.int_range(min=0, max=92)
|
||||
),
|
||||
cv.Optional(CONF_FLUSH_TIMEOUT): cv.All(
|
||||
cv.only_on_esp32, cv.positive_time_period_milliseconds
|
||||
),
|
||||
cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True),
|
||||
cv.Optional(CONF_DATA_BITS, default=8): cv.int_range(min=5, max=8),
|
||||
cv.Optional(CONF_PARITY, default="NONE"): cv.enum(
|
||||
@@ -345,6 +349,8 @@ async def to_code(config):
|
||||
)
|
||||
cg.add(var.set_rx_full_threshold(config[CONF_RX_FULL_THRESHOLD]))
|
||||
cg.add(var.set_rx_timeout(config[CONF_RX_TIMEOUT]))
|
||||
if CONF_FLUSH_TIMEOUT in config:
|
||||
cg.add(var.set_flush_timeout(config[CONF_FLUSH_TIMEOUT]))
|
||||
cg.add(var.set_stop_bits(config[CONF_STOP_BITS]))
|
||||
cg.add(var.set_data_bits(config[CONF_DATA_BITS]))
|
||||
cg.add(var.set_parity(config[CONF_PARITY]))
|
||||
|
||||
@@ -45,7 +45,7 @@ class UARTDevice {
|
||||
|
||||
size_t available() { return this->parent_->available(); }
|
||||
|
||||
void flush() { this->parent_->flush(); }
|
||||
FlushResult flush() { return this->parent_->flush(); }
|
||||
|
||||
// Compat APIs
|
||||
int read() {
|
||||
|
||||
@@ -29,6 +29,14 @@ enum UARTDirection {
|
||||
|
||||
const LogString *parity_to_str(UARTParityOptions parity);
|
||||
|
||||
/// Result of a flush() call.
|
||||
enum class FlushResult {
|
||||
SUCCESS, ///< Confirmed: all bytes left the TX FIFO.
|
||||
TIMEOUT, ///< Confirmed: timed out before TX completed.
|
||||
FAILED, ///< Confirmed: driver or hardware error.
|
||||
ASSUMED_SUCCESS, ///< Platform cannot report result; success is assumed.
|
||||
};
|
||||
|
||||
class UARTComponent {
|
||||
public:
|
||||
static constexpr size_t RX_FULL_THRESHOLD_UNSET = 0;
|
||||
@@ -74,7 +82,13 @@ class UARTComponent {
|
||||
virtual size_t available() = 0;
|
||||
|
||||
// Pure virtual method to block until all bytes have been written to the UART bus.
|
||||
virtual void flush() = 0;
|
||||
// @return FlushResult indicating whether the flush was confirmed, timed out, failed, or assumed successful.
|
||||
virtual FlushResult flush() = 0;
|
||||
|
||||
// Sets the maximum time to wait for TX to drain during flush().
|
||||
// Only meaningful on ESP32 (IDF). Other platforms ignore this value.
|
||||
// @param flush_timeout_ms Timeout in milliseconds; 0 means wait indefinitely.
|
||||
virtual void set_flush_timeout(uint32_t flush_timeout_ms) {}
|
||||
|
||||
// Sets the TX (transmit) pin for the UART bus.
|
||||
// @param tx_pin Pointer to the internal GPIO pin used for transmission.
|
||||
|
||||
@@ -213,13 +213,14 @@ size_t ESP8266UartComponent::available() {
|
||||
return this->sw_serial_->available();
|
||||
}
|
||||
}
|
||||
void ESP8266UartComponent::flush() {
|
||||
FlushResult ESP8266UartComponent::flush() {
|
||||
ESP_LOGVV(TAG, " Flushing");
|
||||
if (this->hw_serial_ != nullptr) {
|
||||
this->hw_serial_->flush();
|
||||
} else {
|
||||
this->sw_serial_->flush();
|
||||
}
|
||||
return FlushResult::ASSUMED_SUCCESS;
|
||||
}
|
||||
void ESP8266SoftwareSerial::setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate,
|
||||
uint8_t stop_bits, uint32_t data_bits, UARTParityOptions parity,
|
||||
|
||||
@@ -58,7 +58,7 @@ class ESP8266UartComponent : public UARTComponent, public Component {
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
|
||||
size_t available() override;
|
||||
void flush() override;
|
||||
FlushResult flush() override;
|
||||
|
||||
uint32_t get_config();
|
||||
|
||||
|
||||
@@ -230,6 +230,9 @@ void IDFUARTComponent::dump_config() {
|
||||
" RX Timeout: %u",
|
||||
this->rx_buffer_size_, this->rx_full_threshold_, this->rx_timeout_);
|
||||
}
|
||||
if (this->flush_timeout_ms_ > 0) {
|
||||
ESP_LOGCONFIG(TAG, " Flush Timeout: %" PRIu32 " ms", this->flush_timeout_ms_);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Baud Rate: %" PRIu32 " baud\n"
|
||||
" Data Bits: %u\n"
|
||||
@@ -332,9 +335,15 @@ size_t IDFUARTComponent::available() {
|
||||
return available;
|
||||
}
|
||||
|
||||
void IDFUARTComponent::flush() {
|
||||
FlushResult IDFUARTComponent::flush() {
|
||||
ESP_LOGVV(TAG, " Flushing");
|
||||
uart_wait_tx_done(this->uart_num_, portMAX_DELAY);
|
||||
TickType_t ticks = this->flush_timeout_ms_ == 0 ? portMAX_DELAY : pdMS_TO_TICKS(this->flush_timeout_ms_);
|
||||
esp_err_t err = uart_wait_tx_done(this->uart_num_, ticks);
|
||||
if (err == ESP_OK)
|
||||
return FlushResult::SUCCESS;
|
||||
if (err == ESP_ERR_TIMEOUT)
|
||||
return FlushResult::TIMEOUT;
|
||||
return FlushResult::FAILED;
|
||||
}
|
||||
|
||||
void IDFUARTComponent::check_logger_conflict() {}
|
||||
|
||||
@@ -31,7 +31,9 @@ class IDFUARTComponent : public UARTComponent, public Component {
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
|
||||
size_t available() override;
|
||||
void flush() override;
|
||||
FlushResult flush() override;
|
||||
|
||||
void set_flush_timeout(uint32_t flush_timeout_ms) override { this->flush_timeout_ms_ = flush_timeout_ms; }
|
||||
|
||||
uint8_t get_hw_serial_number() { return this->uart_num_; }
|
||||
|
||||
@@ -57,6 +59,7 @@ class IDFUARTComponent : public UARTComponent, public Component {
|
||||
|
||||
bool has_peek_{false};
|
||||
uint8_t peek_byte_;
|
||||
uint32_t flush_timeout_ms_{0}; ///< 0 means wait indefinitely (portMAX_DELAY).
|
||||
|
||||
#ifdef USE_UART_WAKE_LOOP_ON_RX
|
||||
// ISR callback for UART RX data notification — wakes the main loop directly.
|
||||
|
||||
@@ -274,12 +274,13 @@ size_t HostUartComponent::available() {
|
||||
return result;
|
||||
};
|
||||
|
||||
void HostUartComponent::flush() {
|
||||
FlushResult HostUartComponent::flush() {
|
||||
if (this->file_descriptor_ == -1) {
|
||||
return;
|
||||
return FlushResult::ASSUMED_SUCCESS;
|
||||
}
|
||||
tcflush(this->file_descriptor_, TCIOFLUSH);
|
||||
ESP_LOGV(TAG, " Flushing");
|
||||
return FlushResult::ASSUMED_SUCCESS;
|
||||
}
|
||||
|
||||
void HostUartComponent::update_error_(const std::string &error) {
|
||||
|
||||
@@ -18,7 +18,7 @@ class HostUartComponent : public UARTComponent, public Component {
|
||||
bool peek_byte(uint8_t *data) override;
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
size_t available() override;
|
||||
void flush() override;
|
||||
FlushResult flush() override;
|
||||
void set_name(std::string port_name) { port_name_ = port_name; };
|
||||
|
||||
protected:
|
||||
|
||||
@@ -170,9 +170,10 @@ bool LibreTinyUARTComponent::read_array(uint8_t *data, size_t len) {
|
||||
}
|
||||
|
||||
size_t LibreTinyUARTComponent::available() { return this->serial_->available(); }
|
||||
void LibreTinyUARTComponent::flush() {
|
||||
FlushResult LibreTinyUARTComponent::flush() {
|
||||
ESP_LOGVV(TAG, " Flushing");
|
||||
this->serial_->flush();
|
||||
return FlushResult::ASSUMED_SUCCESS;
|
||||
}
|
||||
|
||||
void LibreTinyUARTComponent::check_logger_conflict() {
|
||||
|
||||
@@ -22,7 +22,7 @@ class LibreTinyUARTComponent : public UARTComponent, public Component {
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
|
||||
size_t available() override;
|
||||
void flush() override;
|
||||
FlushResult flush() override;
|
||||
|
||||
uint16_t get_config();
|
||||
|
||||
|
||||
@@ -187,9 +187,10 @@ bool RP2040UartComponent::read_array(uint8_t *data, size_t len) {
|
||||
return true;
|
||||
}
|
||||
size_t RP2040UartComponent::available() { return this->serial_->available(); }
|
||||
void RP2040UartComponent::flush() {
|
||||
FlushResult RP2040UartComponent::flush() {
|
||||
ESP_LOGVV(TAG, " Flushing");
|
||||
this->serial_->flush();
|
||||
return FlushResult::ASSUMED_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -25,7 +25,7 @@ class RP2040UartComponent : public UARTComponent, public Component {
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
|
||||
size_t available() override;
|
||||
void flush() override;
|
||||
FlushResult flush() override;
|
||||
|
||||
uint16_t get_config();
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class USBCDCACMInstance : public uart::UARTComponent, public Parented<USBCDCACMC
|
||||
bool peek_byte(uint8_t *data) override;
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
size_t available() override;
|
||||
void flush() override;
|
||||
uart::FlushResult flush() override;
|
||||
|
||||
protected:
|
||||
void check_logger_conflict() override;
|
||||
|
||||
@@ -326,10 +326,10 @@ size_t USBCDCACMInstance::available() {
|
||||
return waiting + (this->has_peek_ ? 1 : 0);
|
||||
}
|
||||
|
||||
void USBCDCACMInstance::flush() {
|
||||
uart::FlushResult USBCDCACMInstance::flush() {
|
||||
// Wait for TX ring buffer to be empty
|
||||
if (this->usb_tx_ringbuf_ == nullptr) {
|
||||
return;
|
||||
return uart::FlushResult::ASSUMED_SUCCESS;
|
||||
}
|
||||
|
||||
UBaseType_t waiting = 1;
|
||||
@@ -341,7 +341,12 @@ void USBCDCACMInstance::flush() {
|
||||
}
|
||||
|
||||
// Also wait for USB to finish transmitting
|
||||
tinyusb_cdcacm_write_flush(static_cast<tinyusb_cdcacm_itf_t>(this->itf_), pdMS_TO_TICKS(100));
|
||||
esp_err_t err = tinyusb_cdcacm_write_flush(static_cast<tinyusb_cdcacm_itf_t>(this->itf_), pdMS_TO_TICKS(100));
|
||||
if (err == ESP_OK)
|
||||
return uart::FlushResult::SUCCESS;
|
||||
if (err == ESP_ERR_TIMEOUT)
|
||||
return uart::FlushResult::TIMEOUT;
|
||||
return uart::FlushResult::FAILED;
|
||||
}
|
||||
|
||||
void USBCDCACMInstance::check_logger_conflict() {}
|
||||
|
||||
@@ -166,7 +166,7 @@ void USBUartChannel::write_array(const uint8_t *data, size_t len) {
|
||||
this->parent_->start_output(this);
|
||||
}
|
||||
|
||||
void USBUartChannel::flush() {
|
||||
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;
|
||||
@@ -177,6 +177,9 @@ void USBUartChannel::flush() {
|
||||
this->parent_->start_output(this);
|
||||
yield();
|
||||
}
|
||||
if (!this->output_queue_.empty() || this->output_started_.load())
|
||||
return uart::FlushResult::TIMEOUT;
|
||||
return uart::FlushResult::SUCCESS;
|
||||
}
|
||||
|
||||
bool USBUartChannel::peek_byte(uint8_t *data) {
|
||||
|
||||
@@ -110,7 +110,7 @@ class USBUartChannel : public uart::UARTComponent, public Parented<USBUartCompon
|
||||
bool peek_byte(uint8_t *data) override;
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
size_t available() override { return this->input_buffer_.get_available(); }
|
||||
void flush() override;
|
||||
uart::FlushResult flush() override;
|
||||
void check_logger_conflict() override {}
|
||||
void set_parity(UARTParityOptions parity) { this->parity_ = parity; }
|
||||
void set_debug(bool debug) { this->debug_ = debug; }
|
||||
|
||||
@@ -433,15 +433,16 @@ void WeikaiChannel::write_array(const uint8_t *buffer, size_t length) {
|
||||
this->reg(0).write_fifo(const_cast<uint8_t *>(buffer), length);
|
||||
}
|
||||
|
||||
void WeikaiChannel::flush() {
|
||||
uart::FlushResult WeikaiChannel::flush() {
|
||||
uint32_t const start_time = millis();
|
||||
while (this->tx_fifo_is_not_empty_()) { // wait until buffer empty
|
||||
if (millis() - start_time > 200) {
|
||||
ESP_LOGW(TAG, "WARNING flush timeout - still %d bytes not sent after 200 ms", this->tx_in_fifo_());
|
||||
return;
|
||||
return uart::FlushResult::TIMEOUT;
|
||||
}
|
||||
yield(); // reschedule our thread to avoid blocking
|
||||
}
|
||||
return uart::FlushResult::SUCCESS;
|
||||
}
|
||||
|
||||
size_t WeikaiChannel::xfer_fifo_to_buffer_() {
|
||||
|
||||
@@ -380,7 +380,7 @@ class WeikaiChannel : public uart::UARTComponent {
|
||||
/// @details If we refer to Serial.flush() in Arduino it says: ** Waits for the transmission of outgoing serial data
|
||||
/// to complete. (Prior to Arduino 1.0, this the method was removing any buffered incoming serial data.). ** Therefore
|
||||
/// we wait until all bytes are gone with a timeout of 100 ms
|
||||
void flush() override;
|
||||
uart::FlushResult flush() override;
|
||||
|
||||
protected:
|
||||
friend class WeikaiComponent;
|
||||
|
||||
@@ -16,7 +16,7 @@ class MockUARTComponent : public uart::UARTComponent {
|
||||
MOCK_METHOD(bool, read_array, (uint8_t * data, size_t len), (override));
|
||||
MOCK_METHOD(bool, peek_byte, (uint8_t * data), (override));
|
||||
MOCK_METHOD(size_t, available, (), (override));
|
||||
MOCK_METHOD(void, flush, (), (override));
|
||||
MOCK_METHOD(uart::FlushResult, flush, (), (override));
|
||||
MOCK_METHOD(void, check_logger_conflict, (), (override));
|
||||
};
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class MockUARTComponent : public UARTComponent {
|
||||
MOCK_METHOD(bool, read_array, (uint8_t * data, size_t len), (override));
|
||||
MOCK_METHOD(bool, peek_byte, (uint8_t * data), (override));
|
||||
MOCK_METHOD(size_t, available, (), (override));
|
||||
MOCK_METHOD(void, flush, (), (override));
|
||||
MOCK_METHOD(FlushResult, flush, (), (override));
|
||||
MOCK_METHOD(void, check_logger_conflict, (), (override));
|
||||
};
|
||||
|
||||
|
||||
@@ -153,8 +153,9 @@ bool MockUartComponent::read_array(uint8_t *data, size_t len) {
|
||||
|
||||
size_t MockUartComponent::available() { return this->rx_buffer_.size(); }
|
||||
|
||||
void MockUartComponent::flush() {
|
||||
uart::FlushResult MockUartComponent::flush() {
|
||||
// Nothing to flush in mock
|
||||
return uart::FlushResult::ASSUMED_SUCCESS;
|
||||
}
|
||||
|
||||
void MockUartComponent::set_rx_full_threshold(size_t rx_full_threshold) {
|
||||
|
||||
@@ -28,7 +28,7 @@ class MockUartComponent : public uart::UARTComponent, public Component {
|
||||
bool peek_byte(uint8_t *data) override;
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
size_t available() override;
|
||||
void flush() override;
|
||||
uart::FlushResult flush() override;
|
||||
void set_rx_full_threshold(size_t rx_full_threshold) override;
|
||||
void set_rx_timeout(size_t rx_timeout) override;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user