mirror of
https://github.com/esphome/esphome.git
synced 2026-10-07 11:26:39 +00:00
[udp] Keep addresses in a shared flash table (#20085)
This commit is contained in:
@@ -124,7 +124,15 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_broadcast_port(conf_port[CONF_BROADCAST_PORT]))
|
||||
if (listen_address := str(config[CONF_LISTEN_ADDRESS])) != "255.255.255.255":
|
||||
cg.add(var.set_listen_address(listen_address))
|
||||
cg.add(var.set_addresses([str(addr) for addr in config[CONF_ADDRESSES]]))
|
||||
# Shared flash table ended by nullptr, so the component stores only a pointer.
|
||||
if addresses := [str(addr) for addr in config[CONF_ADDRESSES]]:
|
||||
cg.add(
|
||||
var.set_addresses(
|
||||
cg.shared_progmem_array(
|
||||
"udp_addresses", cg.const_char_ptr, [*addresses, cg.nullptr]
|
||||
)
|
||||
)
|
||||
)
|
||||
for conf in config.get(CONF_ON_RECEIVE, []):
|
||||
trigger_id = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||
trigger = await automation.build_automation(trigger_id, trigger_argtype, conf)
|
||||
|
||||
@@ -11,7 +11,8 @@ static const char *const TAG = "udp";
|
||||
|
||||
void UDPComponent::setup() {
|
||||
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||
for (const auto &address : this->addresses_) {
|
||||
for (const char *const *it = this->addresses_; it != nullptr && *it != nullptr; it++) {
|
||||
const char *address = *it;
|
||||
struct sockaddr saddr {};
|
||||
if (socket::set_sockaddr(&saddr, sizeof(saddr), address, this->broadcast_port_) == 0) {
|
||||
ESP_LOGW(TAG, "Invalid address %s", address);
|
||||
@@ -97,7 +98,8 @@ void UDPComponent::setup() {
|
||||
#endif
|
||||
#ifdef USE_SOCKET_IMPL_LWIP_TCP
|
||||
// 8266 and RP2040 `Duino
|
||||
for (const auto &address : this->addresses_) {
|
||||
for (const char *const *it = this->addresses_; it != nullptr && *it != nullptr; it++) {
|
||||
const char *address = *it;
|
||||
auto ipaddr = IPAddress();
|
||||
if (!ipaddr.fromString(address)) {
|
||||
ESP_LOGW(TAG, "Invalid address %s", address);
|
||||
@@ -138,7 +140,8 @@ void UDPComponent::dump_config() {
|
||||
" Listen Port: %u\n"
|
||||
" Broadcast Port: %u",
|
||||
this->listen_port_, this->broadcast_port_);
|
||||
for (const char *address : this->addresses_) {
|
||||
for (const char *const *it = this->addresses_; it != nullptr && *it != nullptr; it++) {
|
||||
const char *address = *it;
|
||||
ESP_LOGCONFIG(TAG, " Address: %s", address);
|
||||
}
|
||||
if (this->listen_address_.has_value()) {
|
||||
|
||||
@@ -20,9 +20,8 @@ namespace esphome::udp {
|
||||
static const size_t MAX_PACKET_SIZE = 508;
|
||||
class UDPComponent final : public Component {
|
||||
public:
|
||||
void set_addresses(std::initializer_list<const char *> addresses) { this->addresses_ = addresses; }
|
||||
/// Prevent accidental use of std::string which would dangle
|
||||
void set_addresses(std::initializer_list<std::string> addresses) = delete;
|
||||
/// nullptr-terminated table of addresses; must outlive the component.
|
||||
void set_addresses(const char *const *addresses) { this->addresses_ = addresses; }
|
||||
void set_listen_address(const char *listen_addr) { this->listen_address_ = network::IPAddress(listen_addr); }
|
||||
void set_listen_port(uint16_t port) { this->listen_port_ = port; }
|
||||
void set_broadcast_port(uint16_t port) { this->broadcast_port_ = port; }
|
||||
@@ -52,7 +51,7 @@ class UDPComponent final : public Component {
|
||||
std::vector<IPAddress> ipaddrs_{};
|
||||
WiFiUDP udp_client_{};
|
||||
#endif
|
||||
FixedVector<const char *> addresses_{};
|
||||
const char *const *addresses_{nullptr};
|
||||
|
||||
optional<network::IPAddress> listen_address_{};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp8266:
|
||||
board: d1_mini
|
||||
|
||||
wifi:
|
||||
ssid: test
|
||||
password: password1
|
||||
|
||||
udp:
|
||||
addresses:
|
||||
- 10.0.0.1
|
||||
- 10.0.0.2
|
||||
@@ -0,0 +1,12 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp8266:
|
||||
board: d1_mini
|
||||
|
||||
wifi:
|
||||
ssid: test
|
||||
password: password1
|
||||
|
||||
udp:
|
||||
addresses: []
|
||||
@@ -0,0 +1,29 @@
|
||||
"""Tests for the udp component codegen."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_addresses_are_a_progmem_table(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
"""Addresses live in a nullptr-terminated flash table instead of a heap vector."""
|
||||
main_cpp = generate_main(component_config_path("udp_addresses.yaml"))
|
||||
|
||||
assert (
|
||||
"static constexpr const char * udp_addresses[] PROGMEM = "
|
||||
'{"10.0.0.1", "10.0.0.2", nullptr};' in main_cpp
|
||||
)
|
||||
assert "->set_addresses(udp_addresses);" in main_cpp
|
||||
|
||||
|
||||
def test_empty_addresses_emit_no_table(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
"""An empty address list leaves the pointer null instead of emitting a table."""
|
||||
main_cpp = generate_main(component_config_path("udp_no_addresses.yaml"))
|
||||
|
||||
assert "udp_addresses" not in main_cpp
|
||||
assert "set_addresses(" not in main_cpp
|
||||
Reference in New Issue
Block a user