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() {
132 ESP_LOGCONFIG(TAG,
"espnow:");
136 if (this->state_ != ESPNOW_STATE_ENABLED) {
138 ESP_LOGCONFIG(TAG,
" %s", this->is_disabled() ? LOG_STR_LITERAL(
"Disabled") : LOG_STR_LITERAL(
"Not enabled"));
143 char own_addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
147 " Version: v%" PRIu32
"\n"
148 " Wi-Fi channel: %d",
149 own_addr_buf, version, this->wifi_channel_);
151 ESP_LOGCONFIG(TAG,
" Wi-Fi enabled: %s", YESNO(this->is_wifi_enabled()));
155bool ESPNowComponent::is_wifi_enabled() {
163void ESPNowComponent::setup() {
164#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
169 if (this->enable_on_boot_) {
172 this->state_ = ESPNOW_STATE_DISABLED;
176#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
177void ESPNowComponent::on_wifi_connect_state(StringRef ssid, std::span<const uint8_t, 6> bssid) {
181 uint8_t old_channel = this->wifi_channel_;
182 this->get_wifi_channel();
183 if (this->wifi_channel_ != old_channel) {
184 ESP_LOGI(TAG,
"WiFi channel changed from %d to %d", old_channel, this->wifi_channel_);
189void ESPNowComponent::enable() {
190 if (this->state_ == ESPNOW_STATE_ENABLED)
193 ESP_LOGD(TAG,
"Enabling");
194 this->state_ = ESPNOW_STATE_OFF;
199void ESPNowComponent::enable_() {
200 if (!this->is_wifi_enabled()) {
201 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
203 ESP_ERROR_CHECK(esp_wifi_init(&cfg));
204 ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
205 ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
206 ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
207 ESP_ERROR_CHECK(esp_wifi_start());
208 ESP_ERROR_CHECK(esp_wifi_disconnect());
210 this->apply_wifi_channel();
212 this->get_wifi_channel();
216 ESP_LOGE(TAG,
"esp_now_init failed: %s", esp_err_to_name(err));
223 ESP_LOGE(TAG,
"esp_now_register_recv_cb failed: %s", esp_err_to_name(err));
230 ESP_LOGE(TAG,
"esp_now_register_recv_cb failed: %s", esp_err_to_name(err));
235 esp_wifi_get_mac(WIFI_IF_STA, this->own_address_);
237 this->state_ = ESPNOW_STATE_ENABLED;
239 for (
auto peer : this->peers_) {
240 this->add_peer(peer.address);
244void ESPNowComponent::disable() {
245 if (this->state_ == ESPNOW_STATE_DISABLED)
248 ESP_LOGD(TAG,
"Disabling");
249 this->state_ = ESPNOW_STATE_DISABLED;
256 ESP_LOGE(TAG,
"esp_now_deinit failed! 0x%x", err);
260void ESPNowComponent::apply_wifi_channel() {
261 if (this->state_ == ESPNOW_STATE_DISABLED) {
262 ESP_LOGE(TAG,
"Cannot set channel when ESPNOW disabled");
267 if (this->is_wifi_enabled()) {
268 ESP_LOGE(TAG,
"Cannot set channel when Wi-Fi enabled");
273 ESP_LOGI(TAG,
"Channel set to %d.", this->wifi_channel_);
274 esp_wifi_set_promiscuous(
true);
275 esp_wifi_set_channel(this->wifi_channel_, WIFI_SECOND_CHAN_NONE);
276 esp_wifi_set_promiscuous(
false);
279void ESPNowComponent::loop() {
281 ESPNowPacket *packet = this->receive_packet_queue_.pop();
282 while (packet !=
nullptr) {
283 switch (packet->type_) {
285 const ESPNowRecvInfo info = packet->get_receive_info();
287 bool handled =
false;
288 for (
auto *handler : this->unknown_peer_handlers_) {
289 if (handler->on_unknown_peer(info, packet->packet_.receive.data, packet->packet_.receive.size)) {
294 if (!handled && this->auto_add_peer_) {
295 this->add_peer(info.src_addr);
300#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
301 char src_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
302 char dst_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
308 ESP_LOGV(TAG,
"<<< [%s -> %s] %s", src_buf, dst_buf,
310 std::min<uint16_t>(packet->packet_.receive.size, ESP_NOW_MAX_DATA_LEN)));
312 if (memcmp(info.des_addr, ESPNOW_BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0) {
313 for (
auto *handler : this->broadcast_handlers_) {
314 if (handler->on_broadcast(info, packet->packet_.receive.data, packet->packet_.receive.size))
318 for (
auto *handler : this->receive_handlers_) {
319 if (handler->on_receive(info, packet->packet_.receive.data, packet->packet_.receive.size))
327#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
328 char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
330 ESP_LOGV(TAG,
">>> [%s] %s", addr_buf, LOG_STR_ARG(espnow_error_to_str(packet->packet_.sent.status)));
332 if (this->current_send_packet_ !=
nullptr) {
333 if (this->current_send_packet_->callback_ !=
nullptr) {
334 this->current_send_packet_->callback_(packet->packet_.sent.status);
336 this->send_packet_pool_.release(this->current_send_packet_);
337 this->current_send_packet_ =
nullptr;
345 this->receive_packet_pool_.release(packet);
346 packet = this->receive_packet_queue_.pop();
350 if (this->current_send_packet_ ==
nullptr) {
355 uint16_t received_dropped = this->receive_packet_queue_.get_and_reset_dropped_count();
356 if (received_dropped > 0) {
357 ESP_LOGW(TAG,
"Dropped %u received packets (queue full or oversized frame)", received_dropped);
361 uint16_t send_dropped = this->send_packet_queue_.get_and_reset_dropped_count();
362 if (send_dropped > 0) {
363 ESP_LOGW(TAG,
"Dropped %u send packets (queue full)", send_dropped);
370 if (this->receive_packet_queue_.empty() &&
371 (this->current_send_packet_ !=
nullptr || this->send_packet_queue_.empty())) {
372 this->disable_loop();
376uint8_t ESPNowComponent::get_wifi_channel() {
377 wifi_second_chan_t dummy;
378 esp_wifi_get_channel(&this->wifi_channel_, &dummy);
379 return this->wifi_channel_;
382esp_err_t ESPNowComponent::send(
const uint8_t *peer_address,
const uint8_t *payload,
size_t size,
384 if (this->state_ != ESPNOW_STATE_ENABLED) {
385 return ESP_ERR_ESPNOW_NOT_INIT;
386 }
else if (this->is_failed()) {
387 return ESP_ERR_ESPNOW_FAILED;
388 }
else if (peer_address == 0ULL) {
389 return ESP_ERR_ESPNOW_PEER_NOT_SET;
390 }
else if (memcmp(peer_address, this->own_address_, ESP_NOW_ETH_ALEN) == 0) {
391 return ESP_ERR_ESPNOW_OWN_ADDRESS;
392 }
else if (
size > ESPNOW_MAX_DATA_LEN) {
393 return ESP_ERR_ESPNOW_DATA_SIZE;
395 if (memcmp(peer_address, ESPNOW_BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0 || this->auto_add_peer_) {
396 esp_err_t err = this->add_peer(peer_address);
401 return ESP_ERR_ESPNOW_PEER_NOT_PAIRED;
405 ESPNowSendPacket *packet = this->send_packet_pool_.allocate();
406 if (packet ==
nullptr) {
407 this->send_packet_queue_.increment_dropped_count();
408 ESP_LOGE(TAG,
"Failed to allocate send packet from pool");
409 this->status_momentary_warning(
"send-packet-pool-full");
410 return ESP_ERR_ESPNOW_NO_MEM;
413 packet->load_data(peer_address, payload,
size, callback);
415 this->send_packet_queue_.push(packet);
422void ESPNowComponent::send_() {
423 ESPNowSendPacket *packet = this->send_packet_queue_.pop();
424 if (packet ==
nullptr) {
428 this->current_send_packet_ = packet;
429 esp_err_t err =
esp_now_send(packet->address_, packet->data_, packet->size_);
431 char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
433 ESP_LOGE(TAG,
"Failed to send packet to %s - %s", addr_buf, LOG_STR_ARG(espnow_error_to_str(err)));
434 if (packet->callback_ !=
nullptr) {
435 packet->callback_(err);
437 this->status_momentary_warning(
"send-failed");
438 this->send_packet_pool_.release(packet);
439 this->current_send_packet_ =
nullptr;
444esp_err_t ESPNowComponent::add_peer(
const uint8_t *peer) {
445 if (this->state_ != ESPNOW_STATE_ENABLED || this->is_failed()) {
446 return ESP_ERR_ESPNOW_NOT_INIT;
449 if (memcmp(peer, this->own_address_, ESP_NOW_ETH_ALEN) == 0) {
450 this->status_momentary_warning(
"peer-add-failed");
451 return ESP_ERR_INVALID_MAC;
455 esp_now_peer_info_t peer_info = {};
456 memset(&peer_info, 0,
sizeof(esp_now_peer_info_t));
457 peer_info.ifidx = WIFI_IF_STA;
458 memcpy(peer_info.peer_addr, peer, ESP_NOW_ETH_ALEN);
462 char peer_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
464 ESP_LOGE(TAG,
"Failed to add peer %s - %s", peer_buf, LOG_STR_ARG(espnow_error_to_str(err)));
465 this->status_momentary_warning(
"peer-add-failed");
470 for (
auto &it : this->peers_) {
478 memcpy(new_peer.address, peer, ESP_NOW_ETH_ALEN);
479 this->peers_.push_back(new_peer);
485esp_err_t ESPNowComponent::del_peer(
const uint8_t *peer) {
486 if (this->state_ != ESPNOW_STATE_ENABLED || this->is_failed()) {
487 return ESP_ERR_ESPNOW_NOT_INIT;
492 char peer_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
494 ESP_LOGE(TAG,
"Failed to delete peer %s - %s", peer_buf, LOG_STR_ARG(espnow_error_to_str(err)));
495 this->status_momentary_warning(
"peer-del-failed");
499 for (
auto it = this->peers_.begin(); it != this->peers_.end(); ++it) {
501 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.
esp_err_t esp_now_del_peer(const uint8_t *peer_addr)
esp_err_t esp_now_get_version(uint32_t *version)
esp_err_t esp_now_unregister_recv_cb(void)
esp_err_t esp_now_unregister_send_cb(void)
esp_err_t esp_now_register_send_cb(esp_now_send_cb_t cb)
esp_err_t esp_now_deinit(void)
esp_err_t esp_now_add_peer(const esp_now_peer_info_t *peer)
esp_err_t esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len)
bool esp_now_is_peer_exist(const uint8_t *peer_addr)
esp_err_t esp_now_register_recv_cb(esp_now_recv_cb_t cb)
esp_err_t esp_now_init(void)
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)