From 195b606259f5fc226b44d240c35b1ad00ceab396 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Jan 2026 08:30:13 -1000 Subject: [PATCH] explain --- esphome/components/api/api_connection.cpp | 15 ++++++++++++--- esphome/components/api/api_frame_helper.h | 15 +++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index b624cace2c..12766ffe65 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1826,9 +1826,18 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) { return false; } - // Toggle NODELAY based on message type: - // - Log messages: Enable Nagle (NODELAY=false) so they coalesce into fewer packets - // - All other messages: Disable Nagle (NODELAY=true) for immediate delivery + // Toggle Nagle's algorithm based on message type to prevent log messages from + // filling the TCP send buffer and crowding out important state updates. + // + // - Log messages: Enable Nagle (NODELAY=false) so small log packets coalesce + // into fewer, larger packets. They flush naturally via TCP delayed ACK timer + // (~200ms), buffer filling, or when a state update triggers a flush. + // + // - All other messages (state updates, responses): Disable Nagle (NODELAY=true) + // for immediate delivery. These are time-sensitive and should not be delayed. + // + // This must be done proactively BEFORE the buffer fills up - checking buffer + // state here would be too late since we'd already be in a degraded state. this->helper_->set_nodelay(!is_log_message); APIError err = this->helper_->write_protobuf_packet(message_type, buffer); diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 6fdda64c3e..1c4c045a86 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -111,7 +111,15 @@ class APIFrameHelper { } return APIError::OK; } - /// Set TCP_NODELAY option. Only calls setsockopt when state changes. + /// Toggle TCP_NODELAY socket option to control Nagle's algorithm. + /// + /// This is used to allow log messages to coalesce (Nagle enabled) while keeping + /// state updates low-latency (NODELAY enabled). Without this, many small log + /// packets fill the TCP send buffer, crowding out important state updates. + /// + /// State is tracked to minimize setsockopt() overhead - on lwip_raw (ESP8266/RP2040) + /// this is just a boolean assignment; on other platforms it's a lightweight syscall. + /// /// @param enable true to enable NODELAY (disable Nagle), false to enable Nagle /// @return true if successful or already in desired state bool set_nodelay(bool enable) { @@ -211,7 +219,10 @@ class APIFrameHelper { uint8_t tx_buf_head_{0}; uint8_t tx_buf_tail_{0}; uint8_t tx_buf_count_{0}; - bool nodelay_enabled_{true}; // Tracks current TCP_NODELAY state + // Tracks TCP_NODELAY state to minimize setsockopt() calls. Initialized to true + // since init_common_() enables NODELAY. Used by set_nodelay() to allow log + // messages to coalesce while keeping state updates low-latency. + bool nodelay_enabled_{true}; // Common initialization for both plaintext and noise protocols APIError init_common_();