add peer class

add broadcast action
This commit is contained in:
NP v/d Spek 2024-11-11 22:43:17 +01:00
parent 5e526e2a37
commit de5519bcaa
4 changed files with 176 additions and 77 deletions

View file

@ -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

View file

@ -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);

View file

@ -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 <esp_now.h>
#include <array>
@ -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<ESPNowComponent> {
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<typename... Ts> class SendAction : public Action<Ts...>, public Parented<ESPNowComponent> {
public:
template<typename V> void set_mac(V mac) { this->mac_ = mac; }
template<typename V> void set_peer(V peer) { this->peer_ = peer; }
template<typename V> void set_command(V command) { this->command_ = command; }
void set_data_template(std::function<std::vector<uint8_t>(Ts...)> func) {
@ -265,7 +336,7 @@ template<typename... Ts> class SendAction : public Action<Ts...>, public Parente
void set_data_static(const std::vector<uint8_t> &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<typename... Ts> class SendAction : public Action<Ts...>, 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<uint8_t, Ts...> command_{};
TemplatableValue<uint64_t, Ts...> mac_{};
TemplatableValue<Peer, Ts...> peer_{};
bool dynamic_{false};
std::function<std::vector<uint8_t>(Ts...)> data_func_{};
std::vector<uint8_t> data_static_{};
@ -287,26 +358,26 @@ template<typename... Ts> class SendAction : public Action<Ts...>, public Parente
template<typename... Ts> class NewPeerAction : public Action<Ts...>, public Parented<ESPNowComponent> {
public:
template<typename V> void set_mac(V mac) { this->mac_ = mac; }
template<typename V> 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<uint64_t, Ts...> mac_{};
TemplatableValue<Peer, Ts...> mac_{};
};
template<typename... Ts> class DelPeerAction : public Action<Ts...>, public Parented<ESPNowComponent> {
public:
template<typename V> void set_mac(V mac) { this->mac_ = mac; }
template<typename V> 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<uint64_t, Ts...> mac_{};
TemplatableValue<Peer, Ts...> peer_{};
};
class ESPNowSentTrigger : public Trigger<const ESPNowPacket, bool> {

View file

@ -42,4 +42,4 @@ espnow:
interval:
- interval: 10sec
then:
- espnow.send: "hallo everyone"
- espnow.broatcast: "hallo everyone"