18#include <esp_random.h>
29static constexpr const char *TAG =
"espnow";
33static const LogString *espnow_error_to_str(esp_err_t error) {
35 case ESP_ERR_ESPNOW_FAILED:
36 return LOG_STR(
"ESPNow is in fail mode");
37 case ESP_ERR_ESPNOW_OWN_ADDRESS:
38 return LOG_STR(
"Message to your self");
39 case ESP_ERR_ESPNOW_DATA_SIZE:
40 return LOG_STR(
"Data size to large");
41 case ESP_ERR_ESPNOW_PEER_NOT_SET:
42 return LOG_STR(
"Peer address not set");
43 case ESP_ERR_ESPNOW_PEER_NOT_PAIRED:
44 return LOG_STR(
"Peer address not paired");
45 case ESP_ERR_ESPNOW_NOT_INIT:
46 return LOG_STR(
"Not init");
47 case ESP_ERR_ESPNOW_ARG:
48 return LOG_STR(
"Invalid argument");
49 case ESP_ERR_ESPNOW_INTERNAL:
50 return LOG_STR(
"Internal Error");
51 case ESP_ERR_ESPNOW_NO_MEM:
52 return LOG_STR(
"Our of memory");
53 case ESP_ERR_ESPNOW_NOT_FOUND:
54 return LOG_STR(
"Peer not found");
55 case ESP_ERR_ESPNOW_IF:
56 return LOG_STR(
"Interface does not match");
59 case ESP_NOW_SEND_FAIL:
60 return LOG_STR(
"Failed");
62 return LOG_STR(
"Unknown Error");
66#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
74 if (packet ==
nullptr) {
82#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
110 if (packet ==
nullptr) {
131void ESPNowComponent::dump_config() {
133 esp_now_get_version(&version);
135 ESP_LOGCONFIG(TAG,
"espnow:");
136 if (this->is_disabled()) {
137 ESP_LOGCONFIG(TAG,
" Disabled");
140 char own_addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
144 " Version: v%" PRIu32
"\n"
145 " Wi-Fi channel: %d",
146 own_addr_buf, version, this->wifi_channel_);
148 ESP_LOGCONFIG(TAG,
" Wi-Fi enabled: %s", YESNO(this->is_wifi_enabled()));
152bool ESPNowComponent::is_wifi_enabled() {
160void ESPNowComponent::setup() {
161#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
166 if (this->enable_on_boot_) {
169 this->state_ = ESPNOW_STATE_DISABLED;
173#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
174void ESPNowComponent::on_wifi_connect_state(StringRef ssid, std::span<const uint8_t, 6> bssid) {
178 uint8_t old_channel = this->wifi_channel_;
179 this->get_wifi_channel();
180 if (this->wifi_channel_ != old_channel) {
181 ESP_LOGI(TAG,
"WiFi channel changed from %d to %d", old_channel, this->wifi_channel_);
186void ESPNowComponent::enable() {
187 if (this->state_ == ESPNOW_STATE_ENABLED)
190 ESP_LOGD(TAG,
"Enabling");
191 this->state_ = ESPNOW_STATE_OFF;
196void ESPNowComponent::enable_() {
197 if (!this->is_wifi_enabled()) {
198 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
200 ESP_ERROR_CHECK(esp_wifi_init(&cfg));
201 ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
202 ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
203 ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
204 ESP_ERROR_CHECK(esp_wifi_start());
205 ESP_ERROR_CHECK(esp_wifi_disconnect());
207 this->apply_wifi_channel();
209 this->get_wifi_channel();
211 esp_err_t err = esp_now_init();
213 ESP_LOGE(TAG,
"esp_now_init failed: %s", esp_err_to_name(err));
220 ESP_LOGE(TAG,
"esp_now_register_recv_cb failed: %s", esp_err_to_name(err));
227 ESP_LOGE(TAG,
"esp_now_register_recv_cb failed: %s", esp_err_to_name(err));
232 esp_wifi_get_mac(WIFI_IF_STA, this->own_address_);
234 this->state_ = ESPNOW_STATE_ENABLED;
236 for (
auto peer : this->peers_) {
237 this->add_peer(peer.address);
241void ESPNowComponent::disable() {
242 if (this->state_ == ESPNOW_STATE_DISABLED)
245 ESP_LOGD(TAG,
"Disabling");
246 this->state_ = ESPNOW_STATE_DISABLED;
248 esp_now_unregister_recv_cb();
249 esp_now_unregister_send_cb();
251 esp_err_t err = esp_now_deinit();
253 ESP_LOGE(TAG,
"esp_now_deinit failed! 0x%x", err);
257void ESPNowComponent::apply_wifi_channel() {
258 if (this->state_ == ESPNOW_STATE_DISABLED) {
259 ESP_LOGE(TAG,
"Cannot set channel when ESPNOW disabled");
264 if (this->is_wifi_enabled()) {
265 ESP_LOGE(TAG,
"Cannot set channel when Wi-Fi enabled");
270 ESP_LOGI(TAG,
"Channel set to %d.", this->wifi_channel_);
271 esp_wifi_set_promiscuous(
true);
272 esp_wifi_set_channel(this->wifi_channel_, WIFI_SECOND_CHAN_NONE);
273 esp_wifi_set_promiscuous(
false);
276void ESPNowComponent::loop() {
278 ESPNowPacket *packet = this->receive_packet_queue_.pop();
279 while (packet !=
nullptr) {
280 switch (packet->type_) {
282 const ESPNowRecvInfo info = packet->get_receive_info();
283 if (!esp_now_is_peer_exist(info.src_addr)) {
284 bool handled =
false;
285 for (
auto *handler : this->unknown_peer_handlers_) {
286 if (handler->on_unknown_peer(info, packet->packet_.receive.data, packet->packet_.receive.size)) {
291 if (!handled && this->auto_add_peer_) {
292 this->add_peer(info.src_addr);
296 if (esp_now_is_peer_exist(info.src_addr)) {
297#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
298 char src_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
299 char dst_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
305 ESP_LOGV(TAG,
"<<< [%s -> %s] %s", src_buf, dst_buf,
307 std::min<uint16_t>(packet->packet_.receive.size, ESP_NOW_MAX_DATA_LEN)));
309 if (memcmp(info.des_addr, ESPNOW_BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0) {
310 for (
auto *handler : this->broadcast_handlers_) {
311 if (handler->on_broadcast(info, packet->packet_.receive.data, packet->packet_.receive.size))
315 for (
auto *handler : this->receive_handlers_) {
316 if (handler->on_receive(info, packet->packet_.receive.data, packet->packet_.receive.size))
324#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
325 char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
327 ESP_LOGV(TAG,
">>> [%s] %s", addr_buf, LOG_STR_ARG(espnow_error_to_str(packet->packet_.sent.status)));
329 if (this->current_send_packet_ !=
nullptr) {
330 if (this->current_send_packet_->callback_ !=
nullptr) {
331 this->current_send_packet_->callback_(packet->packet_.sent.status);
333 this->send_packet_pool_.release(this->current_send_packet_);
334 this->current_send_packet_ =
nullptr;
342 this->receive_packet_pool_.release(packet);
343 packet = this->receive_packet_queue_.pop();
347 if (this->current_send_packet_ ==
nullptr) {
352 uint16_t received_dropped = this->receive_packet_queue_.get_and_reset_dropped_count();
353 if (received_dropped > 0) {
354 ESP_LOGW(TAG,
"Dropped %u received packets (queue full or oversized frame)", received_dropped);
358 uint16_t send_dropped = this->send_packet_queue_.get_and_reset_dropped_count();
359 if (send_dropped > 0) {
360 ESP_LOGW(TAG,
"Dropped %u send packets (queue full)", send_dropped);
367 if (this->receive_packet_queue_.empty() &&
368 (this->current_send_packet_ !=
nullptr || this->send_packet_queue_.empty())) {
369 this->disable_loop();
373uint8_t ESPNowComponent::get_wifi_channel() {
374 wifi_second_chan_t dummy;
375 esp_wifi_get_channel(&this->wifi_channel_, &dummy);
376 return this->wifi_channel_;
379esp_err_t ESPNowComponent::send(
const uint8_t *peer_address,
const uint8_t *payload,
size_t size,
381 if (this->state_ != ESPNOW_STATE_ENABLED) {
382 return ESP_ERR_ESPNOW_NOT_INIT;
383 }
else if (this->is_failed()) {
384 return ESP_ERR_ESPNOW_FAILED;
385 }
else if (peer_address == 0ULL) {
386 return ESP_ERR_ESPNOW_PEER_NOT_SET;
387 }
else if (memcmp(peer_address, this->own_address_, ESP_NOW_ETH_ALEN) == 0) {
388 return ESP_ERR_ESPNOW_OWN_ADDRESS;
389 }
else if (
size > ESPNOW_MAX_DATA_LEN) {
390 return ESP_ERR_ESPNOW_DATA_SIZE;
391 }
else if (!esp_now_is_peer_exist(peer_address)) {
392 if (memcmp(peer_address, ESPNOW_BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0 || this->auto_add_peer_) {
393 esp_err_t err = this->add_peer(peer_address);
398 return ESP_ERR_ESPNOW_PEER_NOT_PAIRED;
402 ESPNowSendPacket *packet = this->send_packet_pool_.allocate();
403 if (packet ==
nullptr) {
404 this->send_packet_queue_.increment_dropped_count();
405 ESP_LOGE(TAG,
"Failed to allocate send packet from pool");
406 this->status_momentary_warning(
"send-packet-pool-full");
407 return ESP_ERR_ESPNOW_NO_MEM;
410 packet->load_data(peer_address, payload,
size, callback);
412 this->send_packet_queue_.push(packet);
419void ESPNowComponent::send_() {
420 ESPNowSendPacket *packet = this->send_packet_queue_.pop();
421 if (packet ==
nullptr) {
425 this->current_send_packet_ = packet;
426 esp_err_t err = esp_now_send(packet->address_, packet->data_, packet->size_);
428 char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
430 ESP_LOGE(TAG,
"Failed to send packet to %s - %s", addr_buf, LOG_STR_ARG(espnow_error_to_str(err)));
431 if (packet->callback_ !=
nullptr) {
432 packet->callback_(err);
434 this->status_momentary_warning(
"send-failed");
435 this->send_packet_pool_.release(packet);
436 this->current_send_packet_ =
nullptr;
441esp_err_t ESPNowComponent::add_peer(
const uint8_t *peer) {
442 if (this->state_ != ESPNOW_STATE_ENABLED || this->is_failed()) {
443 return ESP_ERR_ESPNOW_NOT_INIT;
446 if (memcmp(peer, this->own_address_, ESP_NOW_ETH_ALEN) == 0) {
447 this->status_momentary_warning(
"peer-add-failed");
448 return ESP_ERR_INVALID_MAC;
451 if (!esp_now_is_peer_exist(peer)) {
452 esp_now_peer_info_t peer_info = {};
453 memset(&peer_info, 0,
sizeof(esp_now_peer_info_t));
454 peer_info.ifidx = WIFI_IF_STA;
455 memcpy(peer_info.peer_addr, peer, ESP_NOW_ETH_ALEN);
456 esp_err_t err = esp_now_add_peer(&peer_info);
459 char peer_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
461 ESP_LOGE(TAG,
"Failed to add peer %s - %s", peer_buf, LOG_STR_ARG(espnow_error_to_str(err)));
462 this->status_momentary_warning(
"peer-add-failed");
467 for (
auto &it : this->peers_) {
475 memcpy(new_peer.address, peer, ESP_NOW_ETH_ALEN);
476 this->peers_.push_back(new_peer);
482esp_err_t ESPNowComponent::del_peer(
const uint8_t *peer) {
483 if (this->state_ != ESPNOW_STATE_ENABLED || this->is_failed()) {
484 return ESP_ERR_ESPNOW_NOT_INIT;
486 if (esp_now_is_peer_exist(peer)) {
487 esp_err_t err = esp_now_del_peer(peer);
489 char peer_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
491 ESP_LOGE(TAG,
"Failed to delete peer %s - %s", peer_buf, LOG_STR_ARG(espnow_error_to_str(err)));
492 this->status_momentary_warning(
"peer-del-failed");
496 for (
auto it = this->peers_.begin(); it != this->peers_.end(); ++it) {
498 this->peers_.erase(it);
void load_sent_data(const uint8_t *mac_addr, esp_now_send_status_t status)
void load_received_data(const esp_now_recv_info_t *info, const uint8_t *data, int size)
void add_connect_state_listener(WiFiConnectStateListener *listener)
Add a listener for WiFi connection state changes.
void on_send_report(const esp_now_send_info_t *info, esp_now_send_status_t status) void on_send_report(const uint8_t *mac_addr
void on_data_received(const esp_now_recv_info_t *info, const uint8_t *data, int size)
ESPNowComponent * global_esp_now
global_esp_now enable_loop_soon_any_context()
void esp_now_send_status_t status
std::function< void(esp_err_t)> send_callback_t
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)