From de5519bcaa4c0babaf02f382f73e0f8bcd1ca4a9 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Mon, 11 Nov 2024 22:43:17 +0100 Subject: [PATCH] add peer class add broadcast action --- esphome/components/espnow/__init__.py | 35 ++++++- esphome/components/espnow/espnow.cpp | 73 +++++++------ esphome/components/espnow/espnow.h | 143 +++++++++++++++++++------- esphome/components/espnow/test.yaml | 2 +- 4 files changed, 176 insertions(+), 77 deletions(-) diff --git a/esphome/components/espnow/__init__.py b/esphome/components/espnow/__init__.py index d0631cfbb9..d08012128b 100644 --- a/esphome/components/espnow/__init__.py +++ b/esphome/components/espnow/__init__.py @@ -11,6 +11,8 @@ ESPNowComponent = espnow_ns.class_("ESPNowComponent", cg.Component) ESPNowListener = espnow_ns.class_("ESPNowListener") ESPNowPacket = espnow_ns.class_("ESPNowPacket") +ESPNowPeer = espnow_ns.class_("Peer") + ESPNowPacketConst = ESPNowPacket.operator("const") @@ -53,6 +55,20 @@ def validate_raw_data(value): ) +def validate_espnow_peer(): + return PEER_SCHEMA + + +PEER_SCHEMA = cv.Any( + { + cv.templatable(validate_espnow_peer), + cv.All(cv.string, cv.Length(min=8, max=8)), + cv.mac_address, + cv.uint64_t, + } +) + + CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(ESPNowComponent), @@ -145,13 +161,26 @@ async def register_protocol(var, config): cg.add(now.register_protocol(var)) +@automation.register_action( + "espnow.broatcast", + SendAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(ESPNowComponent), + cv.Optional(CONF_PEER, default=0xFFFFFFFFFFFF): cv.uint64_t, + cv.Required(CONF_DATA): cv.templatable(validate_raw_data), + cv.Optional(CONF_COMMAND): cv.templatable(cv.Range(min=16, max=255)), + }, + key=CONF_DATA, + ), +) @automation.register_action( "espnow.send", SendAction, cv.maybe_simple_value( { cv.GenerateID(): cv.use_id(ESPNowComponent), - cv.Optional(CONF_PEER): cv.templatable(cv.mac_address), + cv.Required(CONF_PEER): validate_espnow_peer, cv.Required(CONF_DATA): cv.templatable(validate_raw_data), cv.Optional(CONF_COMMAND): cv.templatable(cv.Range(min=16, max=255)), }, @@ -163,7 +192,7 @@ async def send_action(config, action_id, template_arg, args): await cg.register_parented(var, config[CONF_ID]) if CONF_PEER in config: template_ = await cg.templatable(config[CONF_PEER].as_hex, args, cg.uint64) - cg.add(var.set_mac(template_)) + cg.add(var.set_peer(template_)) if CONF_COMMAND in config: template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8) @@ -206,5 +235,5 @@ async def send_action(config, action_id, template_arg, args): async def del_peer_action(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) template_ = await cg.templatable(config[CONF_PEER].as_hex, args, cg.uint64) - cg.add(var.set_mac(template_)) + cg.add(var.set_peer(template_)) return var diff --git a/esphome/components/espnow/espnow.cpp b/esphome/components/espnow/espnow.cpp index 0aeeeec043..8bfdb83e0f 100644 --- a/esphome/components/espnow/espnow.cpp +++ b/esphome/components/espnow/espnow.cpp @@ -36,7 +36,7 @@ ESPNowComponent::ESPNowComponent() { ESPNowComponent::static_ = this; } // NOLI void ESPNowComponent::dump_config() { ESP_LOGCONFIG(TAG, "esp_now:"); - ESP_LOGCONFIG(TAG, " Own Peer Address: 0x%12llx.", this->own_peer_address_); + ESP_LOGCONFIG(TAG, " Own Peer Address: %s.", decode_peer(this->own_peer_address_)); ESP_LOGCONFIG(TAG, " Wifi channel: %d.", this->wifi_channel_); ESP_LOGCONFIG(TAG, " Auto add new peers: %s.", this->auto_add_peer_ ? "Yes" : "No"); @@ -46,10 +46,10 @@ void ESPNowComponent::dump_config() { } void ESPNowComponent::show_packet(const std::string &title, const ESPNowPacket &packet) { - ESP_LOGV(TAG, "%s packet. Peer: 0x%12llx, Header: %c%c%c, Protocol:%c%c%c-%02x, Sequents: %d.%d, Size: %d, Valid: %s", - title.c_str(), packet.peer, packet.get_byte_at(0), packet.get_byte_at(1), packet.get_byte_at(2), - packet.get_byte_at(3), packet.get_byte_at(4), packet.get_byte_at(5), packet.get_byte_at(6), - packet.get_byte_at(7), packet.attempts, packet.content_size(), packet.is_valid() ? "Yes" : "No"); + ESP_LOGV(TAG, "%s packet. Peer: %s, Header: %c%c%c, Protocol:%c%c%c-%02x, Sequents: %d.%d, Size: %d, Valid: %s", + title.c_str(), packet.peer.c_str(), packet.at(0), packet.at(1), packet.at(2), packet.at(3), packet.at(4), + packet.at(5), packet.at(6), packet.at(7), packet.attempts, packet.content_size(), + packet.is_valid() ? "Yes" : "No"); } bool ESPNowComponent::validate_channel_(uint8_t channel) { @@ -114,9 +114,9 @@ void ESPNowComponent::setup() { esp_wifi_get_mac(WIFI_IF_STA, (uint8_t *) &this->own_peer_address_); - for (auto &address : this->peers_) { - ESP_LOGV(TAG, "Add peer '%012llx'.", address); - add_peer(address); + for (auto &peer : this->peers_) { + ESP_LOGV(TAG, "Add peer '%s'.", peer.c_str()); + add_peer(peer); } this->send_queue_ = xQueueCreate(SEND_BUFFER_SIZE, sizeof(ESPNowPacket)); @@ -146,7 +146,7 @@ void ESPNowComponent::espnow_task(void *param) { ESPNowPacket packet; // NOLINT for (;;) { if (xQueueReceive(this_espnow->receive_queue_, (void *) &packet, (TickType_t) 1) == pdTRUE) { - uint8_t *mac = packet.get_peer(); + uint8_t *mac = packet.peer.mac(); if (esp_now_is_peer_exist(mac)) { this_espnow->call_on_receive_(packet); } else { @@ -158,13 +158,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 '%012llx' (%d.%d). To many retries.", packet.peer, packet.get_sequents(), + ESP_LOGE(TAG, "Dropped '%s' (%d.%d). To many retries.", packet.peer.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 '%012llx' (%d.%d).", packet.peer, packet.get_sequents(), packet.attempts); + ESP_LOGW(TAG, "TimeOut '%s' (%d.%d).", packet.peer.c_str(), packet.get_sequents(), packet.attempts); this_espnow->unlock(); } } else { @@ -172,14 +172,14 @@ void ESPNowComponent::espnow_task(void *param) { packet.retry(); packet.timestamp = millis(); - esp_err_t err = esp_now_send(packet.get_peer(), packet.get_content(), packet.content_size()); + esp_err_t err = esp_now_send(packet.peer.mac(), packet.get_content(), packet.content_size()); if (err == ESP_OK) { - ESP_LOGD(TAG, "Sended '%012llx' (%d.%d) from buffer. Wait for conformation.", packet.peer, + ESP_LOGD(TAG, "Sended '%s' (%d.%d) from buffer. Wait for conformation.", packet.peer.c_str(), packet.get_sequents(), packet.attempts); } else { - ESP_LOGE(TAG, "Sending '%012llx' (%d.%d) FAILED. B: %d.", packet.peer, packet.get_sequents(), packet.attempts, - this_espnow->send_queue_used()); + ESP_LOGE(TAG, "Sending '%s' (%d.%d) FAILED. B: %d.", packet.peer.c_str(), packet.get_sequents(), + packet.attempts, this_espnow->send_queue_used()); this_espnow->unlock(); } } @@ -188,26 +188,26 @@ void ESPNowComponent::espnow_task(void *param) { } } -esp_err_t ESPNowComponent::add_peer(uint64_t addr) { +esp_err_t ESPNowComponent::add_peer(Peer peer) { if (!this->is_ready()) { - this->peers_.push_back(addr); + this->peers_.push_back(peer); return ESP_OK; } else { - this->del_peer(addr); + this->del_peer(peer); esp_now_peer_info_t peer_info = {}; memset(&peer_info, 0, sizeof(esp_now_peer_info_t)); peer_info.channel = this->wifi_channel_; peer_info.encrypt = false; - memcpy((void *) peer_info.peer_addr, (void *) &addr, 6); + memcpy((void *) peer_info.peer_addr, (void *) peer.mac(), 6); return esp_now_add_peer(&peer_info); } } -esp_err_t ESPNowComponent::del_peer(uint64_t addr) { - if (esp_now_is_peer_exist((uint8_t *) &addr)) - return esp_now_del_peer((uint8_t *) &addr); +esp_err_t ESPNowComponent::del_peer(Peer peer) { + if (esp_now_is_peer_exist((uint8_t *) peer.mac())) + return esp_now_del_peer((uint8_t *) &peer.mac()); return ESP_OK; } @@ -292,18 +292,18 @@ bool ESPNowComponent::send(ESPNowPacket packet) { ESP_LOGE(TAG, "Cannot send espnow packet, espnow failed to setup"); } else if (this->send_queue_full()) { ESP_LOGE(TAG, "Send Buffer Out of Memory."); - } else if (!esp_now_is_peer_exist(packet.get_peer())) { - ESP_LOGE(TAG, "Peer not registered: 0x%12llx.", packet.peer); + } else if (!esp_now_is_peer_exist(packet.peer.mac())) { + ESP_LOGE(TAG, "Peer not registered: %s.", packet.peer.c_str()); } else if (!packet.is_valid()) { - ESP_LOGE(TAG, "This Packet is invalid: '%012llx' (%d.%d)", packet.peer, packet.get_sequents(), packet.attempts); - } else if (this->use_sent_check_) { - ESP_LOGV(TAG, "Placing '%012llx' (%d.%d) into send buffer. Used: %d of %d", packet.peer, packet.get_sequents(), + ESP_LOGE(TAG, "This Packet is invalid: %s (%d.%d)", packet.c_str(), packet.get_sequents(), packet.attempts); + } else if (this->use_sent_check_ && packet.peer != ESPNOW_BROADCAST_ADDR) { + ESP_LOGV(TAG, "Placing %s (%d.%d) into send buffer. Used: %d of %d", packet.peer.c_str(), packet.get_sequents(), packet.attempts, this->send_queue_used(), SEND_BUFFER_SIZE); xQueueSendToBack(this->send_queue_, (void *) &packet, 10); return true; } else { - esp_err_t err = esp_now_send(packet.get_peer(), packet.get_content(), packet.content_size()); - ESP_LOGV(TAG, "Sended '%012llx' (%d.%d) directly%s.", packet.peer, packet.get_sequents(), packet.attempts, + esp_err_t err = esp_now_send(packet.peer.mac(), packet.get_content(), packet.content_size()); + ESP_LOGV(TAG, "Sending to %s (%d.%d) directly%s.", packet.peer.c_str(), packet.get_sequents(), packet.attempts, (err == ESP_OK) ? "" : " FAILED"); this->call_on_sent_(packet, err == ESP_OK); @@ -315,17 +315,16 @@ bool ESPNowComponent::send(ESPNowPacket packet) { void ESPNowComponent::on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) { ESPNowPacket packet; // NOLINT - uint64_t mac64; - memcpy((void *) &mac64, mac_addr, 6); + Peer peer(mac_addr); if (xQueuePeek(ESPNowComponent::static_->send_queue_, (void *) &packet, 10 / portTICK_PERIOD_MS) == pdTRUE) { - if (!packet.is_peer(mac_addr)) { - ESP_LOGE(TAG, " Invalid mac address. Expected: '%012llx' (%d.%d) got: '%012llx'", packet.peer, - packet.get_sequents(), packet.attempts, mac64); + if (packet.peer != peer) { + ESP_LOGE(TAG, " Invalid mac address. Expected: %s (%d.%d); got: %s", packet.peer.c_str(), packet.get_sequents(), + packet.attempts, peer.c_str()); return; } else if (status != ESP_OK) { - ESP_LOGE(TAG, "Sent packet failed for '%012llx' (%d.%d)", packet.peer, packet.get_sequents(), packet.attempts); + ESP_LOGE(TAG, "Sent packet failed for %s (%d.%d)", packet.peer.c_str(), packet.get_sequents(), packet.attempts); } else { - ESP_LOGV(TAG, "Confirm packet sent '%012llx' (%d.%d)", packet.peer, packet.get_sequents(), packet.attempts); + ESP_LOGV(TAG, "Confirm packet sent %s (%d.%d)", packet.peer.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); @@ -335,7 +334,7 @@ void ESPNowComponent::on_data_sent(const uint8_t *mac_addr, esp_now_send_status_ /* ESPNowProtocol ********************************************************************** */ -bool ESPNowProtocol::send(uint64_t peer, const uint8_t *data, uint8_t len, uint8_t command) { +bool ESPNowProtocol::send(Peer peer, const uint8_t *data, uint8_t len, uint8_t command) { ESPNowPacket packet(peer, data, len, this->get_protocol_id()); // NOLINT packet.set_sequents(this->get_next_sequents(packet.peer)); packet.set_command(command); diff --git a/esphome/components/espnow/espnow.h b/esphome/components/espnow/espnow.h index 89de037c87..476473ef5f 100644 --- a/esphome/components/espnow/espnow.h +++ b/esphome/components/espnow/espnow.h @@ -5,7 +5,6 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include "esphome/core/log.h" #include #include @@ -29,8 +28,96 @@ static const uint8_t ESPNOW_COMMAND_ACK = 0x06; static const uint8_t ESPNOW_COMMAND_NAK = 0x15; static const uint8_t ESPNOW_COMMAND_RESEND = 0x05; +static const char chars[] = "0123456789-AbCdEfGhIjKlMnOpQrStUvWxYz+aBcDeFgHiJkLmNoPqRsTuVwXyZ"; +static const uint64_t FAILED = 0; + +std::string encode_peer(uint64_t peer) { + std::string str1 = ""; + if (peer == FAILED) { + return "[Not Set]" + } else if (peer == ESPNOW_BROADCAST_ADDR) + return "[BroadCast]"; + do { + str1.push_back(chars[peer & 63]); // Add on the left + peer = peer >> 6; + } while (peer != 0); + return str1; +} + +uint64_t decode_peer(std::string peer) { + uint64_t mac = 0; + if (peer.size() != 8) + return FAILED; + + for (int pos = peer.size(); pos > 0; pos--) { + char *p = strchr(chars, peer[pos - 1]); + if (p == nullptr) + return FAILED; + mac = (mac << 6) + (p - chars); + } + return mac; +} + +struct Peer { + uint64_t id{0}; + + inline Peer() ESPHOME_ALWAYS_INLINE{}; + + inline Peer(const Peer &peer) ESPHOME_ALWAYS_INLINE { this.is = peer.id; } + inline Peer(std::String peer) ESPHOME_ALWAYS_INLINE { this->id = decode_peer(peercode); } + inline Peer(uint64_t peer) ESPHOME_ALWAYS_INLINE { this->id = peerid; } + inline Peer(const uint8_t *peer) ESPHOME_ALWAYS_INLINE { this = peer; } + + inline uint8_t *mac() const { return (uint8_t *) &(this->id); } + inline std::string code() const { return encode_peer(this->id); } + + inline char *c_str() const { return encode_peer(this->id).c_str(); } + + inline bool operator=(const Peer &peer) { // NOLINT + return this->id = peer.id; + } + inline Color &operator=(uint8_t *peer) ESPHOME_ALWAYS_INLINE { + memcpy((void *) this->mac(), (const void *) peer, 6); + return *this; + } + inline Color &operator=(std::String peer) ESPHOME_ALWAYS_INLINE { + this->id = decode_peer(peercode); + return *this; + } + inline Color &operator=(uint64_t peer) ESPHOME_ALWAYS_INLINE { + this->id = peer; + return *this; + } + + inline bool operator==(const Peer &peer) { // NOLINT + return this->id == peer.id; + } + inline bool operator==(uint64_t peer) { // NOLINT + return this->id == peer; + } + inline bool operator==(std::String peer) { // NOLINT + return this->id == decode_peer(peer); + } + inline bool operator==(const uint8_t *peer) { // NOLINT + return memcmp(peer, this->mac(), 6) == 0; + } + + inline bool operator!=(const Peer &peer) { // NOLINT + return this->id != peer.id; + } + inline bool operator!=(uint64_t peer) { // NOLINT + return this->id != rhs.raw_32; + } + inline bool operator!=(std::String peer) { // NOLINT + return this->id != decode_peer(peer); + } + inline bool operator!=(const uint8_t *peer) { // NOLINT + return memcmp(peer, this->mac(), 6) != 0; + } +} + struct ESPNowPacket { - uint64_t peer{0}; + Peer &peerid{0}; uint8_t rssi{0}; int8_t attempts{0}; bool is_broadcast{false}; @@ -47,12 +134,11 @@ struct ESPNowPacket { inline ESPNowPacket() ESPHOME_ALWAYS_INLINE {} // Create packet to be send. - inline ESPNowPacket(uint64_t peer, const uint8_t *data, uint8_t size, uint32_t protocol, + + inline ESPNowPacket(Peer peer, const uint8_t *data, uint8_t size, uint32_t protocol, uint8_t command = 0) ESPHOME_ALWAYS_INLINE { assert(size <= MAX_ESPNOW_DATA_SIZE); - if (peer == 0) { - peer = ESPNOW_BROADCAST_ADDR; - } + assert(peer == 0); this->peer = peer; @@ -63,21 +149,6 @@ struct ESPNowPacket { this->size = size; std::memcpy(this->get_payload(), data, size); } - // Load received packet's. - ESPNowPacket(const uint8_t *peer, const uint8_t *data, uint8_t size) ESPHOME_ALWAYS_INLINE { - this->set_peer(peer); - std::memcpy(this->get_content(), data, size); - this->size = size - this->prefix_size(); - } - - inline uint8_t *get_peer() const { return (uint8_t *) &(this->peer); } - inline void set_peer(const uint8_t *peer) ESPHOME_ALWAYS_INLINE { - if (*peer == 0) { - peer = (uint8_t *) &ESPNOW_BROADCAST_ADDR; - } - memcpy((void *) this->get_peer(), (const void *) peer, 6); - }; - inline bool is_peer(const uint8_t *peer) const { return memcmp(peer, this->get_peer(), 6) == 0; } uint8_t prefix_size() const { return sizeof(this->content.prefix); } @@ -98,7 +169,7 @@ struct ESPNowPacket { inline uint8_t *get_content() const { return (uint8_t *) &(this->content); } inline uint8_t *get_payload() const { return (uint8_t *) &(this->content.payload); } - inline uint8_t get_byte_at(uint8_t pos) const { + inline uint8_t at(uint8_t pos) const { assert(pos < this->size); return *(((uint8_t *) &this->content) + pos); } @@ -143,7 +214,7 @@ class ESPNowProtocol : public Parented { return valid; } - bool send(uint64_t peer, const uint8_t *data, uint8_t len, uint8_t command = 0); + bool send(peer peer, const uint8_t *data, uint8_t len, uint8_t command = 0); protected: uint8_t next_sequents_{255}; @@ -207,8 +278,8 @@ class ESPNowComponent : public Component { this->protocols_[protocol->get_protocol_id()] = protocol; } - esp_err_t add_peer(uint64_t addr); - esp_err_t del_peer(uint64_t addr); + esp_err_t add_peer(Peer peer); + esp_err_t del_peer(Peer peer); bool send_queue_empty() { return uxQueueMessagesWaiting(this->send_queue_) == 0; } bool send_queue_full() { return uxQueueSpacesAvailable(this->send_queue_) == 0; } @@ -255,7 +326,7 @@ class ESPNowComponent : public Component { template class SendAction : public Action, public Parented { public: - template void set_mac(V mac) { this->mac_ = mac; } + template void set_peer(V peer) { this->peer_ = peer; } template void set_command(V command) { this->command_ = command; } void set_data_template(std::function(Ts...)> func) { @@ -265,7 +336,7 @@ template class SendAction : public Action, public Parente void set_data_static(const std::vector &data) { this->data_static_ = data; } void play(Ts... x) override { - uint64_t mac = this->mac_.value(x...); + Peer peer = this->peer_.value(x...); uint8_t command = 0; if (this->command_.has_value()) { command = this->mac_.value(x...); @@ -274,12 +345,12 @@ template class SendAction : public Action, public Parente if (this->dynamic_) { this->data_static_ = this->data_func_(x...); } - this->parent_->get_default_protocol()->send(mac, this->data_static_.data(), this->data_static_.size(), command); + this->parent_->get_default_protocol()->send(peer, this->data_static_.data(), this->data_static_.size(), command); } protected: TemplatableValue command_{}; - TemplatableValue mac_{}; + TemplatableValue peer_{}; bool dynamic_{false}; std::function(Ts...)> data_func_{}; std::vector data_static_{}; @@ -287,26 +358,26 @@ template class SendAction : public Action, public Parente template class NewPeerAction : public Action, public Parented { public: - template void set_mac(V mac) { this->mac_ = mac; } + template void set_peer(V peer) { this->peer_ = peer; } void play(Ts... x) override { - auto mac = this->mac_.value(x...); - parent_->add_peer(mac); + auto peer = this->peer_.value(x...); + parent_->add_peer(peer); } protected: - TemplatableValue mac_{}; + TemplatableValue mac_{}; }; template class DelPeerAction : public Action, public Parented { public: - template void set_mac(V mac) { this->mac_ = mac; } + template void set_peer(V mac) { this->peer_ = peer; } void play(Ts... x) override { - auto mac = this->mac_.value(x...); + auto peer = this->peer_.value(x...); parent_->del_peer(mac); } protected: - TemplatableValue mac_{}; + TemplatableValue peer_{}; }; class ESPNowSentTrigger : public Trigger { diff --git a/esphome/components/espnow/test.yaml b/esphome/components/espnow/test.yaml index 57b129cbaf..190a49a94e 100644 --- a/esphome/components/espnow/test.yaml +++ b/esphome/components/espnow/test.yaml @@ -42,4 +42,4 @@ espnow: interval: - interval: 10sec then: - - espnow.send: "hallo everyone" + - espnow.broatcast: "hallo everyone"