diff --git a/esphome/components/espnow/__init__.py b/esphome/components/espnow/__init__.py index a11e09c312..4676328788 100644 --- a/esphome/components/espnow/__init__.py +++ b/esphome/components/espnow/__init__.py @@ -258,6 +258,20 @@ async def register_peer(var, config, args): cv.maybe_simple_value( { cv.GenerateID(): cv.use_id(ESPNowComponent), + cv.Optional(CONF_MAC_ADDRESS, default=0xFFFFFFFFFFFF): cv.uint64_t, + cv.Required(CONF_PAYLOAD): cv.templatable(validate_raw_data), + cv.Optional(CONF_COMMAND): cv.templatable(validate_command), + }, + key=CONF_PAYLOAD, + ), +) +@automation.register_action( + "espnow.multicast", + SendAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(ESPNowComponent), + cv.Optional(CONF_MAC_ADDRESS, default=0xFFFFFFFFFFFE): cv.uint64_t, cv.Required(CONF_PAYLOAD): cv.templatable(validate_raw_data), cv.Optional(CONF_COMMAND): cv.templatable(validate_command), }, diff --git a/esphome/components/espnow/espnow.cpp b/esphome/components/espnow/espnow.cpp index 17cb4d5aa0..b444c98a38 100644 --- a/esphome/components/espnow/espnow.cpp +++ b/esphome/components/espnow/espnow.cpp @@ -41,8 +41,8 @@ std::string peer_str(uint64_t peer) { return "[Not Set]"; if (peer == ESPNOW_BROADCAST_ADDR) return "[Broadcast]"; - if (peer == ESPNOW_MASS_SEND_ADDR) - return "[Mass Send]"; + if (peer == ESPNOW_MULTICAST_ADDR) + return "[Multicast]"; uint8_t *ppeer = (uint8_t *) &peer; snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X", ppeer[0], ppeer[1], ppeer[2], ppeer[3], ppeer[4], ppeer[5]); @@ -199,7 +199,7 @@ void ESPNowComponent::loop() { bool ESPNowComponent::can_proceed() { #ifdef USE_WIFI if (wifi::global_wifi_component != nullptr) - return wifi::global_wifi_component->is_ready(); + return wifi::global_wifi_component->is_connected(); return false; #else return true; @@ -225,14 +225,13 @@ void ESPNowComponent::espnow_task(void *param) { } if (xQueueReceive(this_espnow->send_queue_, (void *) &packet, (TickType_t) 1) == pdTRUE) { if (packet.attempts > this_espnow->retries_) { - ESP_LOGE(TAG, "Dropped '%s' (%d.%d). To many retries.", packet.get_peer_code().c_str(), packet.get_sequents(), + ESP_LOGE(TAG, "Dropped %s (%d.%d). To many retries.", packet.get_peer_code().c_str(), packet.get_sequents(), packet.attempts); this_espnow->unlock(); continue; } else if (this_espnow->is_locked()) { if (packet.timestamp + this_espnow->conformation_timeout_ < millis()) { - ESP_LOGW(TAG, "TimeOut '%s' (%d.%d).", packet.get_peer_code().c_str(), packet.get_sequents(), - packet.attempts); + ESP_LOGW(TAG, "TimeOut %s (%d.%d).", packet.get_peer_code().c_str(), packet.get_sequents(), packet.attempts); this_espnow->unlock(); } } else { @@ -241,17 +240,18 @@ void ESPNowComponent::espnow_task(void *param) { packet.timestamp = millis(); esp_err_t err; - if (packet.peer == ESPNOW_MASS_SEND_ADDR) { + if (packet.peer == ESPNOW_MULTICAST_ADDR) { + this_espnow->start_multi_cast_(); err = esp_now_send(nullptr, packet.get_content(), packet.content_size()); } else { err = esp_now_send(packet.get_peer(), packet.get_content(), packet.content_size()); } if (err == ESP_OK) { - ESP_LOGD(TAG, "Sended '%s' (%d.%d) from buffer. Wait for conformation.", packet.get_peer_code().c_str(), + ESP_LOGD(TAG, "Sended %s (%d.%d) from buffer. Wait for conformation.", packet.get_peer_code().c_str(), packet.get_sequents(), packet.attempts); } else { - ESP_LOGE(TAG, "Sending '%s' (%d.%d) FAILED. B: %d.", packet.get_peer_code().c_str(), packet.get_sequents(), + ESP_LOGE(TAG, "Sending %s (%d.%d) FAILED. B: %d.", packet.get_peer_code().c_str(), packet.get_sequents(), packet.attempts, this_espnow->send_queue_used()); this_espnow->unlock(); } @@ -263,7 +263,7 @@ void ESPNowComponent::espnow_task(void *param) { void ESPNowComponent::set_wifi_channel(uint8_t channel) { if (this->is_ready() && (this->wifi_channel_ != channel)) { - ESPNowPacket packet(ESPNOW_MASS_SEND_ADDR, &channel, 1, ESPNOW_MAIN_PROTOCOL_ID, 251); + ESPNowPacket packet(ESPNOW_MULTICAST_ADDR, &channel, 1, ESPNOW_MAIN_PROTOCOL_ID, 251); this->send(packet); ESP_LOGD(TAG, "Wifi Channel is changed from %d to %d.", this->wifi_channel_, channel); } @@ -380,7 +380,7 @@ void ESPNowComponent::call_on_del_peer_(uint64_t peer) { bool ESPNowComponent::is_paired(uint64_t peer) { bool result = false; - if (peer == ESPNOW_MASS_SEND_ADDR) { + if (peer == ESPNOW_MULTICAST_ADDR) { return true; } else if (peer == ESPNOW_BROADCAST_ADDR) { this->add_peer(ESPNOW_BROADCAST_ADDR); @@ -456,6 +456,11 @@ void ESPNowComponent::handle_internal_commands(ESPNowPacket packet) { } } +void ESPNowComponent::handle_internal_sent(ESPNowPacket packet, bool status) { + ESP_LOGW(TAG, "Internal packet sent to %s (%d.%d) %sreceived.", packet.get_peer_code().c_str(), packet.get_sequents(), + packet.attempts, status ? "" : "NOT "); +} + bool ESPNowComponent::send(ESPNowPacket packet) { if (packet.peer == this->own_peer_address_) { ESP_LOGE(TAG, "Tried to peer your self."); @@ -477,7 +482,7 @@ bool ESPNowComponent::send(ESPNowPacket packet) { return true; } else { esp_err_t err; - if (packet.peer == ESPNOW_MASS_SEND_ADDR) { + if (packet.peer == ESPNOW_MULTICAST_ADDR) { err = esp_now_send(nullptr, packet.get_content(), packet.content_size()); } else { err = esp_now_send(packet.get_peer(), packet.get_content(), packet.content_size()); @@ -493,11 +498,19 @@ bool ESPNowComponent::send(ESPNowPacket packet) { } void ESPNowComponent::on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) { + ESPNowComponent *that = ESPNowComponent::static_; + bool unlock = true; ESPNowPacket packet; // NOLINT uint64_t peer = 0; memcpy((void *) &peer, mac_addr, 6); - if (xQueuePeek(ESPNowComponent::static_->send_queue_, (void *) &packet, 10 / portTICK_PERIOD_MS) == pdTRUE) { - if (packet.peer != peer) { + if (xQueuePeek(that->send_queue_, (void *) &packet, 10 / portTICK_PERIOD_MS) == pdTRUE) { + if (packet.peer == ESPNOW_MULTICAST_ADDR) { + packet.peer = peer; + auto it = std::find(that->multicast_.begin(), that->multicast_.end(), peer); + that->multicast_.erase(it); + unlock = that->multicast_.empty(); + + } else if (packet.peer != peer) { ESP_LOGE(TAG, " Invalid mac address. Expected: %s (%d.%d); got: %s", packet.get_peer_code().c_str(), packet.get_sequents(), packet.attempts, peer_str(peer).c_str()); return; @@ -507,10 +520,32 @@ void ESPNowComponent::on_data_sent(const uint8_t *mac_addr, esp_now_send_status_ } else { ESP_LOGV(TAG, "Confirm packet sent %s (%d.%d)", packet.get_peer_code().c_str(), packet.get_sequents(), packet.attempts); - xQueueReceive(ESPNowComponent::static_->send_queue_, (void *) &packet, 10 / portTICK_PERIOD_MS); } - ESPNowComponent::static_->call_on_sent_(packet, status == ESP_OK); - ESPNowComponent::static_->unlock(); + if (packet.get_protocol() == ESPNOW_MAIN_PROTOCOL_ID && packet.get_command() > 250) { + that->defer([that, packet, status]() { that->handle_internal_sent(packet, status == ESP_OK); }); + } else if (status == ESP_OK) { + that->call_on_sent_(packet, true); + } + if (unlock) { + if (status == ESP_OK) { + xQueueReceive(that->send_queue_, (void *) &packet, 10 / portTICK_PERIOD_MS); + } + that->unlock(); + } + } +} + +uint8_t ESPNowComponent::get_channel_for_(uint64_t peer) { return 0; } +void ESPNowComponent::update_channel_scan_(ESPNowPacket &packet) {} + +void ESPNowComponent::start_multi_cast_() { + esp_now_peer_info_t peer; + memset(&peer, 0, sizeof(esp_now_peer_info_t)); + multicast_.clear(); + for (esp_err_t e = esp_now_fetch_peer(true, &peer); e == ESP_OK; e = esp_now_fetch_peer(false, &peer)) { + uint64_t mac = 0; + memcpy((void *) &mac, peer.peer_addr, 6); + multicast_.push_back(mac); } } diff --git a/esphome/components/espnow/espnow.h b/esphome/components/espnow/espnow.h index 1a9f6ccf93..9ce413f2d0 100644 --- a/esphome/components/espnow/espnow.h +++ b/esphome/components/espnow/espnow.h @@ -23,7 +23,7 @@ namespace esphome { namespace espnow { static const uint64_t ESPNOW_BROADCAST_ADDR = 0xFFFFFFFFFFFF; -static const uint64_t ESPNOW_MASS_SEND_ADDR = 0xFFFFFFFFFFFE; +static const uint64_t ESPNOW_MULTICAST_ADDR = 0xFFFFFFFFFFFE; static const uint8_t MAX_ESPNOW_DATA_SIZE = 240; @@ -55,6 +55,8 @@ struct ESPNowPacket { uint8_t rssi{0}; int8_t attempts{0}; bool is_broadcast{false}; + bool is_multicast{false}; + uint8_t scan_start{0}; uint32_t timestamp{0}; uint8_t size{0}; struct { @@ -232,6 +234,8 @@ class ESPNowComponent : public Component { void set_wifi_channel(uint8_t channel); void set_auto_add_peer(bool value) { this->auto_add_peer_ = value; } void set_use_sent_check(bool value) { this->use_sent_check_ = value; } + void set_auto_channel_scan(bool value) { this->auto_channel_scan_ = value; } + void set_conformation_timeout(uint32_t timeout) { this->conformation_timeout_ = timeout; } void set_retries(uint8_t value) { this->retries_ = value; } void set_pairing_protocol(ESPNowProtocol *pairing_protocol) { this->pairing_protocol_ = pairing_protocol; } @@ -269,11 +273,14 @@ class ESPNowComponent : public Component { static void espnow_task(void *params); void handle_internal_commands(ESPNowPacket packet); + void handle_internal_sent(ESPNowPacket packet, bool status); protected: bool validate_channel_(uint8_t channel); + ESPNowProtocol *get_protocol_(uint32_t protocol); ESPNowProtocol *pairing_protocol_{nullptr}; + uint64_t own_peer_address_{0}; uint8_t wifi_channel_{0}; uint32_t conformation_timeout_{5000}; @@ -281,6 +288,7 @@ class ESPNowComponent : public Component { bool auto_add_peer_{false}; bool use_sent_check_{true}; + bool auto_channel_scan_{false}; bool lock_{false}; @@ -292,11 +300,16 @@ class ESPNowComponent : public Component { void call_on_add_peer_(uint64_t peer); void call_on_del_peer_(uint64_t peer); + uint8_t get_channel_for_(uint64_t peer); + void update_channel_scan_(ESPNowPacket &packet); + void start_multi_cast_(); + QueueHandle_t receive_queue_{}; QueueHandle_t send_queue_{}; std::map protocols_{}; std::map peers_{}; + std::vector multicast_{}; bool task_running_{false}; static ESPNowComponent *static_; // NOLINT diff --git a/esphome/components/espnow/test1.yaml b/esphome/components/espnow/test1.yaml index 026699aba2..35385a9e26 100644 --- a/esphome/components/espnow/test1.yaml +++ b/esphome/components/espnow/test1.yaml @@ -17,11 +17,18 @@ esphome: logger: level: debug +#wifi: +# fast_connect: true +# networks: +# - ssid: !secret wifi_ssid +# password: !secret wifi_password + espnow: auto_add_peer: true wifi_channel: 1 predefined_peers: - mac_address: E8:6B:EA:23:CD:98 + wifi_channel: 2 on_receive: - logger.log: format: "Received from: %s = '%s' cmd: %d RSSI: %d" @@ -34,7 +41,7 @@ espnow: ] interval: - - interval: 10sec + - interval: 30sec startup_delay: 20sec then: - espnow.send: diff --git a/esphome/components/espnow/test2.yaml b/esphome/components/espnow/test2.yaml index 2139eadabf..d62741694a 100644 --- a/esphome/components/espnow/test2.yaml +++ b/esphome/components/espnow/test2.yaml @@ -26,19 +26,26 @@ esphome: # To be able to get logs from the device via serial and api. logger: - level: debug + level: verbose globals: - id: hub_address type: uint64_t initial_value: "0x0" restore_value: yes + - id: channel + type: uint8_t + initial_value: "3" + restore_value: false espnow: auto_add_peer: true - wifi_channel: 1 + wifi_channel: 2 predefined_peers: - mac_address: e8:6b:ea:24:22:04 + wifi_channel: 1 + - mac_address: e8:6b:ea:24:22:03 + - mac_address: e8:6b:ea:24:22:02 on_receive: - logger.log: format: "Received from: %s = '%s' cmd: %d RSSI: %d" @@ -52,13 +59,10 @@ espnow: interval: - interval: 30sec - startup_delay: 10sec then: - - espnow.channel.set: 5 - - interval: 5sec - startup_delay: 10sec - then: - - espnow.send: - mac_address: e8:6b:ea:24:22:04 - payload: "Test 2." - command: 123 + - espnow.channel.set: !lambda return id(channel); + - lambda: |- + id(channel) = id(channel)+1; + if (id(channel) > 10) { + id(channel) = 1; + }