ESPHome 2025.5.0
Loading...
Searching...
No Matches
udp_component.cpp
Go to the documentation of this file.
2#ifdef USE_NETWORK
3#include "esphome/core/log.h"
6#include "udp_component.h"
7
8namespace esphome {
9namespace udp {
10
11static const char *const TAG = "udp";
12
14#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
15 for (const auto &address : this->addresses_) {
16 struct sockaddr saddr {};
17 socket::set_sockaddr(&saddr, sizeof(saddr), address, this->broadcast_port_);
18 this->sockaddrs_.push_back(saddr);
19 }
20 // set up broadcast socket
21 if (this->should_broadcast_) {
22 this->broadcast_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
23 if (this->broadcast_socket_ == nullptr) {
24 this->mark_failed();
25 this->status_set_error("Could not create socket");
26 return;
27 }
28 int enable = 1;
29 auto err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
30 if (err != 0) {
31 this->status_set_warning("Socket unable to set reuseaddr");
32 // we can still continue
33 }
34 err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int));
35 if (err != 0) {
36 this->status_set_warning("Socket unable to set broadcast");
37 }
38 }
39 // create listening socket if we either want to subscribe to providers, or need to listen
40 // for ping key broadcasts.
41 if (this->should_listen_) {
42 this->listen_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
43 if (this->listen_socket_ == nullptr) {
44 this->mark_failed();
45 this->status_set_error("Could not create socket");
46 return;
47 }
48 auto err = this->listen_socket_->setblocking(false);
49 if (err < 0) {
50 ESP_LOGE(TAG, "Unable to set nonblocking: errno %d", errno);
51 this->mark_failed();
52 this->status_set_error("Unable to set nonblocking");
53 return;
54 }
55 int enable = 1;
56 err = this->listen_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
57 if (err != 0) {
58 this->status_set_warning("Socket unable to set reuseaddr");
59 // we can still continue
60 }
61 struct sockaddr_in server {};
62
63 server.sin_family = AF_INET;
64 server.sin_addr.s_addr = ESPHOME_INADDR_ANY;
65 server.sin_port = htons(this->listen_port_);
66
67 if (this->listen_address_.has_value()) {
68 struct ip_mreq imreq = {};
69 imreq.imr_interface.s_addr = ESPHOME_INADDR_ANY;
70 inet_aton(this->listen_address_.value().str().c_str(), &imreq.imr_multiaddr);
71 server.sin_addr.s_addr = imreq.imr_multiaddr.s_addr;
72 ESP_LOGD(TAG, "Join multicast %s", this->listen_address_.value().str().c_str());
73 err = this->listen_socket_->setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(imreq));
74 if (err < 0) {
75 ESP_LOGE(TAG, "Failed to set IP_ADD_MEMBERSHIP. Error %d", errno);
76 this->mark_failed();
77 this->status_set_error("Failed to set IP_ADD_MEMBERSHIP");
78 return;
79 }
80 }
81
82 err = this->listen_socket_->bind((struct sockaddr *) &server, sizeof(server));
83 if (err != 0) {
84 ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
85 this->mark_failed();
86 this->status_set_error("Unable to bind socket");
87 return;
88 }
89 }
90#endif
91#ifdef USE_SOCKET_IMPL_LWIP_TCP
92 // 8266 and RP2040 `Duino
93 for (const auto &address : this->addresses_) {
94 auto ipaddr = IPAddress();
95 ipaddr.fromString(address.c_str());
96 this->ipaddrs_.push_back(ipaddr);
97 }
98 if (this->should_listen_)
99 this->udp_client_.begin(this->listen_port_);
100#endif
101}
102
104 auto buf = std::vector<uint8_t>(MAX_PACKET_SIZE);
105 if (this->should_listen_) {
106 for (;;) {
107#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
108 auto len = this->listen_socket_->read(buf.data(), buf.size());
109#endif
110#ifdef USE_SOCKET_IMPL_LWIP_TCP
111 auto len = this->udp_client_.parsePacket();
112 if (len > 0)
113 len = this->udp_client_.read(buf.data(), buf.size());
114#endif
115 if (len <= 0)
116 break;
117 buf.resize(len);
118 ESP_LOGV(TAG, "Received packet of length %zu", len);
119 this->packet_listeners_.call(buf);
120 }
121 }
122}
123
125 ESP_LOGCONFIG(TAG, "UDP:");
126 ESP_LOGCONFIG(TAG, " Listen Port: %u", this->listen_port_);
127 ESP_LOGCONFIG(TAG, " Broadcast Port: %u", this->broadcast_port_);
128 for (const auto &address : this->addresses_)
129 ESP_LOGCONFIG(TAG, " Address: %s", address.c_str());
130 if (this->listen_address_.has_value()) {
131 ESP_LOGCONFIG(TAG, " Listen address: %s", this->listen_address_.value().str().c_str());
132 }
133 ESP_LOGCONFIG(TAG, " Broadcasting: %s", YESNO(this->should_broadcast_));
134 ESP_LOGCONFIG(TAG, " Listening: %s", YESNO(this->should_listen_));
135}
136
137void UDPComponent::send_packet(const uint8_t *data, size_t size) {
138#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
139 for (const auto &saddr : this->sockaddrs_) {
140 auto result = this->broadcast_socket_->sendto(data, size, 0, &saddr, sizeof(saddr));
141 if (result < 0)
142 ESP_LOGW(TAG, "sendto() error %d", errno);
143 }
144#endif
145#ifdef USE_SOCKET_IMPL_LWIP_TCP
146 auto iface = IPAddress(0, 0, 0, 0);
147 for (const auto &saddr : this->ipaddrs_) {
148 if (this->udp_client_.beginPacketMulticast(saddr, this->broadcast_port_, iface, 128) != 0) {
149 this->udp_client_.write(data, size);
150 auto result = this->udp_client_.endPacket();
151 if (result == 0)
152 ESP_LOGW(TAG, "udp.write() error");
153 }
154 }
155#endif
156}
157} // namespace udp
158} // namespace esphome
159
160#endif
uint8_t address
Definition bl0906.h:4
virtual void mark_failed()
Mark this component as failed.
void status_set_warning(const char *message="unspecified")
void status_set_error(const char *message="unspecified")
std::vector< std::string > addresses_
CallbackManager< void(std::vector< uint8_t > &)> packet_listeners_
std::unique_ptr< socket::Socket > listen_socket_
std::unique_ptr< socket::Socket > broadcast_socket_
void send_packet(const uint8_t *data, size_t size)
std::vector< IPAddress > ipaddrs_
optional< network::IPAddress > listen_address_
std::vector< struct sockaddr > sockaddrs_
std::unique_ptr< Socket > socket(int domain, int type, int protocol)
Create a socket of the given domain, type and protocol.
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port)
Set a sockaddr to the specified address and port for the IP version used by socket_ip().
Definition socket.cpp:21
const char *const TAG
Definition spi.cpp:8
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:301
struct in_addr sin_addr
Definition headers.h:65
sa_family_t sin_family
Definition headers.h:63
in_port_t sin_port
Definition headers.h:64