From f19bb2cd0afd9cc04e17e86cf8be2ef469540b54 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Feb 2026 23:59:38 +0100 Subject: [PATCH 1/3] [modbus] Batch UART reads to reduce loop overhead --- esphome/components/modbus/modbus.cpp | 31 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index 5e9387b843..a2bfcf3292 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -19,16 +19,27 @@ void Modbus::setup() { void Modbus::loop() { const uint32_t now = App.get_loop_component_start_time(); - while (this->available()) { - uint8_t byte; - this->read_byte(&byte); - if (this->parse_modbus_byte_(byte)) { - this->last_modbus_byte_ = now; - } else { - size_t at = this->rx_buffer_.size(); - if (at > 0) { - ESP_LOGV(TAG, "Clearing buffer of %d bytes - parse failed", at); - this->rx_buffer_.clear(); + int avail = this->available(); + if (avail > 0) { + // Read all available bytes in batches to reduce UART call overhead. + uint8_t buf[64]; + while (avail > 0) { + size_t to_read = std::min(static_cast(avail), sizeof(buf)); + if (!this->read_array(buf, to_read)) { + break; + } + avail -= to_read; + + for (size_t i = 0; i < to_read; i++) { + if (this->parse_modbus_byte_(buf[i])) { + this->last_modbus_byte_ = now; + } else { + size_t at = this->rx_buffer_.size(); + if (at > 0) { + ESP_LOGV(TAG, "Clearing buffer of %d bytes - parse failed", at); + this->rx_buffer_.clear(); + } + } } } } From 44e9346e9c7224ca962d608b1cb10fb9c936c7d5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 9 Feb 2026 09:30:43 -0600 Subject: [PATCH 2/3] Add comment explaining early guard --- esphome/components/modbus/modbus.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index a2bfcf3292..ccfa589b90 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -19,6 +19,8 @@ void Modbus::setup() { void Modbus::loop() { const uint32_t now = App.get_loop_component_start_time(); + // Early return avoids stack adjustment for the batch buffer below. + // loop() runs ~7000/min so most calls have nothing to read. int avail = this->available(); if (avail > 0) { // Read all available bytes in batches to reduce UART call overhead. From e07144ef74dc2a2f1fad6ce4f1472e5eeaea8e40 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 9 Feb 2026 09:40:26 -0600 Subject: [PATCH 3/3] Remove unnecessary early guard --- esphome/components/modbus/modbus.cpp | 36 +++++++++++++--------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index ccfa589b90..a80ac546ef 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -19,28 +19,24 @@ void Modbus::setup() { void Modbus::loop() { const uint32_t now = App.get_loop_component_start_time(); - // Early return avoids stack adjustment for the batch buffer below. - // loop() runs ~7000/min so most calls have nothing to read. + // Read all available bytes in batches to reduce UART call overhead. int avail = this->available(); - if (avail > 0) { - // Read all available bytes in batches to reduce UART call overhead. - uint8_t buf[64]; - while (avail > 0) { - size_t to_read = std::min(static_cast(avail), sizeof(buf)); - if (!this->read_array(buf, to_read)) { - break; - } - avail -= to_read; + uint8_t buf[64]; + while (avail > 0) { + size_t to_read = std::min(static_cast(avail), sizeof(buf)); + if (!this->read_array(buf, to_read)) { + break; + } + avail -= to_read; - for (size_t i = 0; i < to_read; i++) { - if (this->parse_modbus_byte_(buf[i])) { - this->last_modbus_byte_ = now; - } else { - size_t at = this->rx_buffer_.size(); - if (at > 0) { - ESP_LOGV(TAG, "Clearing buffer of %d bytes - parse failed", at); - this->rx_buffer_.clear(); - } + for (size_t i = 0; i < to_read; i++) { + if (this->parse_modbus_byte_(buf[i])) { + this->last_modbus_byte_ = now; + } else { + size_t at = this->rx_buffer_.size(); + if (at > 0) { + ESP_LOGV(TAG, "Clearing buffer of %d bytes - parse failed", at); + this->rx_buffer_.clear(); } } }