3#if defined(USE_ESP32) || defined(USE_ESP8266)
27static const LogString *espnow_error_to_str(
esp_err_t error) {
29 case ESP_ERR_ESPNOW_FAILED:
30 return LOG_STR(
"ESPNow is in fail mode");
31 case ESP_ERR_ESPNOW_OWN_ADDRESS:
32 return LOG_STR(
"Message to your self");
33 case ESP_ERR_ESPNOW_DATA_SIZE:
34 return LOG_STR(
"Data size to large");
35 case ESP_ERR_ESPNOW_PEER_NOT_SET:
36 return LOG_STR(
"Peer address not set");
37 case ESP_ERR_ESPNOW_PEER_NOT_PAIRED:
38 return LOG_STR(
"Peer address not paired");
39 case ESP_ERR_ESPNOW_NOT_INIT:
40 return LOG_STR(
"Not init");
41 case ESP_ERR_ESPNOW_ARG:
42 return LOG_STR(
"Invalid argument");
43 case ESP_ERR_ESPNOW_INTERNAL:
44 return LOG_STR(
"Internal Error");
45 case ESP_ERR_ESPNOW_NO_MEM:
46 return LOG_STR(
"Out of memory");
47 case ESP_ERR_ESPNOW_NOT_FOUND:
48 return LOG_STR(
"Peer not found");
49 case ESP_ERR_ESPNOW_IF:
50 return LOG_STR(
"Interface does not match");
55 return LOG_STR(
"Failed");
57 return LOG_STR(
"Unknown Error");
67 ESPNowPacket *packet = this->receive_packet_pool_.allocate();
68 if (packet ==
nullptr) {
70 this->receive_packet_queue_.increment_dropped_count();
71 this->enable_loop_soon_any_context();
75 packet->load_sent_data(mac_addr, status);
79 this->receive_packet_queue_.push(packet);
82 this->enable_loop_soon_any_context();
85void ESPNowComponent::packet_received(
const uint8_t *src_addr,
const uint8_t *des_addr,
const uint8_t *data,
int size,
91 if (
size < 0 ||
static_cast<size_t>(
size) > ESPNOW_MAX_DATA_LEN) {
92 this->receive_packet_queue_.increment_dropped_count();
93 this->enable_loop_soon_any_context();
98 ESPNowPacket *packet = this->receive_packet_pool_.allocate();
99 if (packet ==
nullptr) {
101 this->receive_packet_queue_.increment_dropped_count();
102 this->enable_loop_soon_any_context();
106 packet->load_received_data(src_addr, des_addr, data,
size, rssi,
timestamp);
110 this->receive_packet_queue_.push(packet);
113 this->enable_loop_soon_any_context();
119void ESPNowComponent::dump_config() {
120 ESP_LOGCONFIG(TAG,
"espnow:");
124 if (this->state_ != ESPNOW_STATE_ENABLED) {
126 ESP_LOGCONFIG(TAG,
" %s", this->
is_disabled() ? LOG_STR_LITERAL(
"Disabled") : LOG_STR_LITERAL(
"Not enabled"));
129 char own_addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
133 " Wi-Fi channel: %d",
134 own_addr_buf, this->wifi_channel_);
137 ESP_LOGCONFIG(TAG,
" Version: v%" PRIu32, version);
140 ESP_LOGCONFIG(TAG,
" Wi-Fi enabled: %s", YESNO(this->is_wifi_enabled()));
144bool ESPNowComponent::is_wifi_enabled() {
152void ESPNowComponent::setup() {
153#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
158 if (this->enable_on_boot_) {
161 this->state_ = ESPNOW_STATE_DISABLED;
165#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
166void ESPNowComponent::on_wifi_connect_state(StringRef ssid, std::span<const uint8_t, 6> bssid) {
170 uint8_t old_channel = this->wifi_channel_;
171 this->get_wifi_channel();
172 if (this->wifi_channel_ != old_channel) {
173 ESP_LOGI(TAG,
"WiFi channel changed from %d to %d", old_channel, this->wifi_channel_);
176 if (this->state_ == ESPNOW_STATE_ENABLED) {
182void ESPNowComponent::enable() {
183 if (this->state_ == ESPNOW_STATE_ENABLED)
186 ESP_LOGD(TAG,
"Enabling");
187 this->state_ = ESPNOW_STATE_OFF;
192void ESPNowComponent::enable_() {
193 if (!this->is_wifi_enabled()) {
195 this->apply_wifi_channel();
197 this->get_wifi_channel();
208 this->state_ = ESPNOW_STATE_ENABLED;
210 for (
auto peer : this->peers_) {
215void ESPNowComponent::disable() {
216 if (this->state_ == ESPNOW_STATE_DISABLED)
219 ESP_LOGD(TAG,
"Disabling");
220 this->state_ = ESPNOW_STATE_DISABLED;
228void ESPNowComponent::apply_wifi_channel() {
229 if (this->state_ == ESPNOW_STATE_DISABLED) {
230 ESP_LOGE(TAG,
"Cannot set channel when ESPNOW disabled");
235 if (this->is_wifi_enabled()) {
236 ESP_LOGE(TAG,
"Cannot set channel when Wi-Fi enabled");
241 ESP_LOGI(TAG,
"Channel set to %d.", this->wifi_channel_);
243 if (this->state_ == ESPNOW_STATE_ENABLED) {
248void ESPNowComponent::loop() {
250 ESPNowPacket *packet = this->receive_packet_queue_.pop();
251 while (packet !=
nullptr) {
252 switch (packet->type_) {
254 const ESPNowRecvInfo info = packet->get_receive_info();
256 bool handled =
false;
257 for (
auto *handler : this->unknown_peer_handlers_) {
258 if (handler->on_unknown_peer(info, packet->packet_.receive.data, packet->packet_.receive.size)) {
263 if (!handled && this->auto_add_peer_) {
269#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
270 char src_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
271 char dst_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
277 ESP_LOGV(TAG,
"<<< [%s -> %s] %s", src_buf, dst_buf,
279 std::min<uint16_t>(packet->packet_.receive.size, ESP_NOW_MAX_DATA_LEN)));
282 if (ESPNOW_REPORTS_DESTINATION && memcmp(info.des_addr, ESPNOW_BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0) {
283 for (
auto *handler : this->broadcast_handlers_) {
284 if (handler->on_broadcast(info, packet->packet_.receive.data, packet->packet_.receive.size))
288 for (
auto *handler : this->receive_handlers_) {
289 if (handler->on_receive(info, packet->packet_.receive.data, packet->packet_.receive.size))
297#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
298 char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
300 ESP_LOGV(TAG,
">>> [%s] %s", addr_buf, LOG_STR_ARG(espnow_error_to_str(packet->packet_.sent.status)));
302 if (this->current_send_packet_ !=
nullptr) {
303 if (this->current_send_packet_->callback_ !=
nullptr) {
304 this->current_send_packet_->callback_(packet->packet_.sent.status);
306 this->send_packet_pool_.release(this->current_send_packet_);
307 this->current_send_packet_ =
nullptr;
315 this->receive_packet_pool_.release(packet);
316 packet = this->receive_packet_queue_.pop();
320 if (this->current_send_packet_ ==
nullptr) {
325 uint16_t received_dropped = this->receive_packet_queue_.get_and_reset_dropped_count();
326 if (received_dropped > 0) {
327 ESP_LOGW(TAG,
"Dropped %u received packets (queue full or oversized frame)", received_dropped);
331 uint16_t send_dropped = this->send_packet_queue_.get_and_reset_dropped_count();
332 if (send_dropped > 0) {
333 ESP_LOGW(TAG,
"Dropped %u send packets (queue full)", send_dropped);
340 if (this->receive_packet_queue_.empty() &&
341 (this->current_send_packet_ !=
nullptr || this->send_packet_queue_.empty())) {
342 this->disable_loop();
346uint8_t ESPNowComponent::get_wifi_channel() {
348 return this->wifi_channel_;
353esp_err_t ESPNowComponent::send(
const uint8_t *peer_address,
const uint8_t *payload,
size_t size,
355 if (this->state_ != ESPNOW_STATE_ENABLED) {
356 return ESP_ERR_ESPNOW_NOT_INIT;
357 }
else if (this->is_failed()) {
358 return ESP_ERR_ESPNOW_FAILED;
359 }
else if (peer_address ==
nullptr) {
360 return ESP_ERR_ESPNOW_PEER_NOT_SET;
361 }
else if (memcmp(peer_address, this->own_address_, ESP_NOW_ETH_ALEN) == 0) {
362 return ESP_ERR_ESPNOW_OWN_ADDRESS;
363 }
else if (
size > ESPNOW_MAX_DATA_LEN) {
364 return ESP_ERR_ESPNOW_DATA_SIZE;
366 if (memcmp(peer_address, ESPNOW_BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0 || this->auto_add_peer_) {
372 return ESP_ERR_ESPNOW_PEER_NOT_PAIRED;
376 ESPNowSendPacket *packet = this->send_packet_pool_.allocate();
377 if (packet ==
nullptr) {
378 this->send_packet_queue_.increment_dropped_count();
379 ESP_LOGE(TAG,
"Failed to allocate send packet from pool");
380 this->status_momentary_warning();
381 return ESP_ERR_ESPNOW_NO_MEM;
384 packet->load_data(peer_address, payload,
size, std::move(callback));
386 this->send_packet_queue_.push(packet);
389 this->enable_loop_soon_any_context();
394void ESPNowComponent::send_() {
395 ESPNowSendPacket *packet = this->send_packet_queue_.pop();
396 if (packet ==
nullptr) {
400 this->current_send_packet_ = packet;
403 char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
405 ESP_LOGE(TAG,
"Failed to send packet to %s - %s", addr_buf, LOG_STR_ARG(espnow_error_to_str(err)));
406 if (packet->callback_ !=
nullptr) {
407 packet->callback_(err);
409 this->status_momentary_warning();
410 this->send_packet_pool_.release(packet);
411 this->current_send_packet_ =
nullptr;
416esp_err_t ESPNowComponent::add_peer(
const uint8_t *peer) {
417 if (this->state_ != ESPNOW_STATE_ENABLED || this->is_failed()) {
418 return ESP_ERR_ESPNOW_NOT_INIT;
421 if (memcmp(peer, this->own_address_, ESP_NOW_ETH_ALEN) == 0) {
422 this->status_momentary_warning();
423 return ESP_ERR_INVALID_MAC;
429 char peer_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
431 ESP_LOGE(TAG,
"Failed to add peer %s - %s", peer_buf, LOG_STR_ARG(espnow_error_to_str(err)));
432 this->status_momentary_warning();
437 for (
auto &it : this->peers_) {
445 memcpy(new_peer.address, peer, ESP_NOW_ETH_ALEN);
446 this->peers_.push_back(new_peer);
452esp_err_t ESPNowComponent::del_peer(
const uint8_t *peer) {
453 if (this->state_ != ESPNOW_STATE_ENABLED || this->is_failed()) {
454 return ESP_ERR_ESPNOW_NOT_INIT;
459 char peer_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
461 ESP_LOGE(TAG,
"Failed to delete peer %s - %s", peer_buf, LOG_STR_ARG(espnow_error_to_str(err)));
462 this->status_momentary_warning();
466 for (
auto it = this->peers_.begin(); it != this->peers_.end(); ++it) {
468 this->peers_.erase(it);
void add_connect_state_listener(WiFiConnectStateListener *listener)
Add a listener for WiFi connection state changes.
ESPNowComponent * global_esp_now
const char * esp_err_to_name(esp_err_t err)
ESPHOME_LOG_TAG(TAG, "espnow")
std::function< void(esp_err_t)> send_callback_t
bool is_disabled()
Return whether the network is disabled: every configured interface with a disable() lifecycle (modem,...
WiFiComponent * global_wifi_component
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
char * format_mac_addr_upper(const uint8_t *mac, char *output)
Format MAC address as XX:XX:XX:XX:XX:XX (uppercase, colon separators)