[modbus] Fix timeout for non-hardware UARTs (e.g., USB UART)

The rx_full_threshold is only meaningful for ESP32 native UARTs where
it controls the hardware FIFO interrupt threshold. On other platforms
(USB UART, Arduino, etc.) it was left at the default of 1, causing
the long_rx_buffer_delay_ms calculation to produce a tiny value (~1-2ms).

This caused false timeouts on partial responses when data arrives in
USB packets with inherent USB-level latency, leading to cascading
CRC failures as the remaining bytes were parsed as garbage.

Change the default rx_full_threshold to 0 (unset sentinel) and use
50ms when unset, matching the previous hardcoded timeout behavior.
This commit is contained in:
J. Nick Koston
2026-03-07 17:37:57 -10:00
parent ea7cfffdda
commit d13e87b5b5
2 changed files with 12 additions and 3 deletions
+8 -2
View File
@@ -21,8 +21,14 @@ void Modbus::setup() {
// 3.5 characters * 11 bits per character * 1000ms/sec / (bits/sec) (Standard modbus frame delay)
(uint16_t) (3.5 * 11 * 1000 / this->parent_->get_baud_rate()) + 1);
this->long_rx_buffer_delay_ms_ =
(this->parent_->get_rx_full_threshold() * 11 * 1000 / this->parent_->get_baud_rate()) + 1;
// When rx_full_threshold is configured (non-zero), the UART has a hardware FIFO with a
// meaningful threshold (e.g., ESP32 native UART), so we can calculate a precise delay.
// Otherwise (e.g., USB UART), use 50ms to handle data arriving in chunks.
static constexpr uint16_t DEFAULT_LONG_RX_BUFFER_DELAY_MS = 50;
size_t rx_threshold = this->parent_->get_rx_full_threshold();
this->long_rx_buffer_delay_ms_ = rx_threshold != uart::UARTComponent::RX_FULL_THRESHOLD_UNSET
? (rx_threshold * 11 * 1000 / this->parent_->get_baud_rate()) + 1
: DEFAULT_LONG_RX_BUFFER_DELAY_MS;
}
void Modbus::loop() {
+4 -1
View File
@@ -187,7 +187,10 @@ class UARTComponent {
InternalGPIOPin *rx_pin_{};
InternalGPIOPin *flow_control_pin_{};
size_t rx_buffer_size_{};
size_t rx_full_threshold_{1};
static constexpr size_t RX_FULL_THRESHOLD_UNSET = 0;
// ESP-IDF always sets this at codegen time via set_rx_full_threshold().
// Other platforms (USB UART, Arduino, etc.) leave it unset.
size_t rx_full_threshold_{RX_FULL_THRESHOLD_UNSET};
size_t rx_timeout_{0};
uint32_t baud_rate_{0};
uint8_t stop_bits_{0};