From 9f44c65d99d75edd54d7dc8755ee7aff9a3afdeb Mon Sep 17 00:00:00 2001 From: clydebarrow <2366188+clydebarrow@users.noreply.github.com> Date: Sun, 18 Oct 2026 18:55:11 +1100 Subject: [PATCH] [udp] Keep the copy buffer in static memory Code generation declares the UDP link's buffer for packets split across buffers as a static array of max_packet_size bytes, so it is not allocated at run time. Co-Authored-By: Claude Opus 5.5 --- esphome/components/udp/packet_interface/__init__.py | 9 ++++++++- .../udp/packet_interface/udp_packet_interface.cpp | 5 ++--- .../udp/packet_interface/udp_packet_interface.h | 7 +++---- tests/components/udp/udp_packet_interface.cpp | 4 +++- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/esphome/components/udp/packet_interface/__init__.py b/esphome/components/udp/packet_interface/__init__.py index 971002aeffb..8527615e35f 100644 --- a/esphome/components/udp/packet_interface/__init__.py +++ b/esphome/components/udp/packet_interface/__init__.py @@ -1,9 +1,11 @@ import esphome.codegen as cg from esphome.components import packet_interface from esphome.components.packet_interface import ( + CONF_MAX_PACKET_SIZE, new_packet_interface, packet_interface_schema, ) +from esphome.const import CONF_ID from esphome.types import ConfigType from .. import CONF_UDP_ID, UDP_SCHEMA, udp_ns @@ -30,4 +32,9 @@ async def to_code(config: ConfigType) -> None: cg.add(udp_var.set_should_listen()) # The udp component sends to its addresses through its broadcast socket cg.add(udp_var.set_should_broadcast()) - await new_packet_interface(config, udp_var) + # The copy buffer's size is known here, so it is a static array rather than allocated at run time + tx_buffer = f"{config[CONF_ID].id}__tx_buffer" + cg.add_global( + cg.RawStatement(f"static uint8_t {tx_buffer}[{config[CONF_MAX_PACKET_SIZE]}];") + ) + await new_packet_interface(config, udp_var, cg.RawExpression(tx_buffer)) diff --git a/esphome/components/udp/packet_interface/udp_packet_interface.cpp b/esphome/components/udp/packet_interface/udp_packet_interface.cpp index 6305e02c87d..d2f5354ffb2 100644 --- a/esphome/components/udp/packet_interface/udp_packet_interface.cpp +++ b/esphome/components/udp/packet_interface/udp_packet_interface.cpp @@ -11,7 +11,6 @@ using packet_interface::PacketMetaData; ESPHOME_LOG_TAG(TAG, "udp.packet_interface"); void UdpPacketInterface::setup() { - this->tx_buffer_ = std::make_unique(this->max_packet_size_); this->parent_->add_listener([this](std::span data) { this->receive_(data); }); } @@ -36,8 +35,8 @@ bool UdpPacketInterface::send_to_interface(const PacketBuffer &data, const Packe auto span = data.as_span(); return this->parent_->send_packet(span.data(), span.size()); } - data.copy_to(this->tx_buffer_.get(), size); - return this->parent_->send_packet(this->tx_buffer_.get(), size); + data.copy_to(this->tx_buffer_, size); + return this->parent_->send_packet(this->tx_buffer_, size); } } // namespace esphome::udp diff --git a/esphome/components/udp/packet_interface/udp_packet_interface.h b/esphome/components/udp/packet_interface/udp_packet_interface.h index e967d80355a..aa02e67dc46 100644 --- a/esphome/components/udp/packet_interface/udp_packet_interface.h +++ b/esphome/components/udp/packet_interface/udp_packet_interface.h @@ -4,7 +4,6 @@ // udp_component.h declares nothing without USE_NETWORK #ifdef USE_NETWORK -#include #include #include "esphome/components/packet_interface/packet_interface.h" @@ -20,7 +19,8 @@ namespace esphome::udp { */ class UdpPacketInterface : public packet_interface::PacketInterface { public: - explicit UdpPacketInterface(UDPComponent *udp_component) : parent_(udp_component) {} + /// @param tx_buffer Holds max_packet_size bytes, for packets split across buffers; must outlive the link + UdpPacketInterface(UDPComponent *udp_component, uint8_t *tx_buffer) : parent_(udp_component), tx_buffer_(tx_buffer) {} void setup() override; bool send_to_interface(const packet_interface::PacketBuffer &data, @@ -30,8 +30,7 @@ class UdpPacketInterface : public packet_interface::PacketInterface { void receive_(std::span data); UDPComponent *parent_; - // For packets split across buffers, allocated in setup() - std::unique_ptr tx_buffer_; + uint8_t *const tx_buffer_; }; } // namespace esphome::udp #endif diff --git a/tests/components/udp/udp_packet_interface.cpp b/tests/components/udp/udp_packet_interface.cpp index 756f3a204f2..532eee848cc 100644 --- a/tests/components/udp/udp_packet_interface.cpp +++ b/tests/components/udp/udp_packet_interface.cpp @@ -22,13 +22,15 @@ using Packet = std::vector; class TestUdp : public UdpPacketInterface { public: - explicit TestUdp(UDPComponent *udp) : UdpPacketInterface(udp) { + explicit TestUdp(UDPComponent *udp) : UdpPacketInterface(udp, this->tx_buffer_storage) { this->set_max_packet_size(8); this->add_packet_interface_listener( [this](const PacketBuffer &data, const PacketMetaData &) { this->received.push_back(to_vector(data)); }); } using UdpPacketInterface::receive_; + // The base class only keeps the pointer, so the storage can be a member set up after it + uint8_t tx_buffer_storage[8]{}; std::vector received; };