-add option to set a global keeper to be used withe the espnow.send action. peer= keeper

- the keeper is templatable.
- introduce protocol modes Keeper, drudge and universal
- revert back event call's
- add get)iwb_peer_address()
- prepare for pairing protocol
This commit is contained in:
NP v/d Spek 2024-11-20 10:36:48 +01:00
parent 342a20a48e
commit 535c8dd70f
5 changed files with 356 additions and 126 deletions

View file

@ -8,6 +8,8 @@ CODEOWNERS = ["@nielsnl68", "@jesserockz"]
espnow_ns = cg.esphome_ns.namespace("espnow") espnow_ns = cg.esphome_ns.namespace("espnow")
ESPNowComponent = espnow_ns.class_("ESPNowComponent", cg.Component) ESPNowComponent = espnow_ns.class_("ESPNowComponent", cg.Component)
ESPNowProtocol = espnow_ns.class_("ESPNowProtocol")
ESPNowListener = espnow_ns.class_("ESPNowListener") ESPNowListener = espnow_ns.class_("ESPNowListener")
ESPNowPacket = espnow_ns.class_("ESPNowPacket") ESPNowPacket = espnow_ns.class_("ESPNowPacket")
@ -46,10 +48,20 @@ CONF_PEER = "peer"
CONF_PEERS = "peers" CONF_PEERS = "peers"
CONF_USE_SENT_CHECK = "use_sent_check" CONF_USE_SENT_CHECK = "use_sent_check"
CONF_WIFI_CHANNEL = "wifi_channel" CONF_WIFI_CHANNEL = "wifi_channel"
CONF_PROTOCOL_MODE = "protocol_mode"
CONF_KEEPER = "keeper"
CONF_MAC_CHARS = "0123456789-AbCdEfGhIjKlMnOpQrStUvWxYz+aBcDeFgHiJkLmNoPqRsTuVwXyZ" CONF_MAC_CHARS = "0123456789-AbCdEfGhIjKlMnOpQrStUvWxYz+aBcDeFgHiJkLmNoPqRsTuVwXyZ"
validate_command = cv.Range(min=1, max=250)
ESPNowProtocol_mode = espnow_ns.enum("ESPNowProtocol_mode")
ENUM_MODE = {
"universal": ESPNowProtocol_mode.universal,
"keeper": ESPNowProtocol_mode.keeper,
"drudge": ESPNowProtocol_mode.drudge,
}
def validate_raw_data(value): def validate_raw_data(value):
if isinstance(value, str): if isinstance(value, str):
@ -83,15 +95,18 @@ def validate_peer(value):
if isinstance(value, (int)): if isinstance(value, (int)):
return value return value
value = cv.string_strict(value)
if value.lower() == CONF_KEEPER:
return 0x0
if value.find(":") != -1: if value.find(":") != -1:
return convert_mac_address(value) return convert_mac_address(value)
if len(value) == 8: if len(value) == 8:
value = cv.string_strict(value)
mac = 0 mac = 0
for x in value: for x in range(8, 0, -1):
n = CONF_MAC_CHARS.find(x) n = CONF_MAC_CHARS.find(value[x - 1])
if n == -1: if n == -1:
raise cv.Invalid(f"peer code is invalid. ({value}|{x})") raise cv.Invalid(f"peer code is invalid. ({value}|{x})")
mac = (mac << 6) + n mac = (mac << 6) + n
@ -112,28 +127,29 @@ CONFIG_SCHEMA = cv.Schema(
CONF_CONFORMATION_TIMEOUT, default="5000ms" CONF_CONFORMATION_TIMEOUT, default="5000ms"
): cv.positive_time_period_milliseconds, ): cv.positive_time_period_milliseconds,
cv.Optional(CONF_RETRIES, default=5): cv.int_range(min=1, max=10), cv.Optional(CONF_RETRIES, default=5): cv.int_range(min=1, max=10),
cv.Optional(CONF_KEEPER): cv.templatable(validate_peer),
cv.Optional(CONF_ON_RECEIVE): automation.validate_automation( cv.Optional(CONF_ON_RECEIVE): automation.validate_automation(
{ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowReceiveTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowReceiveTrigger),
cv.Optional(CONF_COMMAND): cv.Range(min=16, max=255), cv.Optional(CONF_COMMAND): validate_command,
} }
), ),
cv.Optional(CONF_ON_BROADCAST): automation.validate_automation( cv.Optional(CONF_ON_BROADCAST): automation.validate_automation(
{ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowBroadcaseTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowBroadcaseTrigger),
cv.Optional(CONF_COMMAND): cv.Range(min=0, max=255), cv.Optional(CONF_COMMAND): validate_command,
} }
), ),
cv.Optional(CONF_ON_SENT): automation.validate_automation( cv.Optional(CONF_ON_SENT): automation.validate_automation(
{ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowSentTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowSentTrigger),
cv.Optional(CONF_COMMAND): cv.Range(min=0, max=255), cv.Optional(CONF_COMMAND): validate_command,
} }
), ),
cv.Optional(CONF_ON_NEW_PEER): automation.validate_automation( cv.Optional(CONF_ON_NEW_PEER): automation.validate_automation(
{ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowNewPeerTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowNewPeerTrigger),
cv.Optional(CONF_COMMAND): cv.Range(min=16, max=255), cv.Optional(CONF_COMMAND): validate_command,
} }
), ),
cv.Optional(CONF_PEERS): cv.ensure_list(validate_peer), cv.Optional(CONF_PEERS): cv.ensure_list(validate_peer),
@ -157,6 +173,10 @@ async def to_code(config):
cg.add(var.set_conformation_timeout(config[CONF_CONFORMATION_TIMEOUT])) cg.add(var.set_conformation_timeout(config[CONF_CONFORMATION_TIMEOUT]))
cg.add(var.set_retries(config[CONF_RETRIES])) cg.add(var.set_retries(config[CONF_RETRIES]))
if CONF_KEEPER in config:
template_ = await cg.templatable(config[CONF_KEEPER], [], cg.uint64)
cg.add(var.set_keeper(template_))
for conf in config.get(CONF_PEERS, []): for conf in config.get(CONF_PEERS, []):
cg.add(var.add_peer(conf)) cg.add(var.add_peer(conf))
@ -198,24 +218,27 @@ async def to_code(config):
PROTOCOL_SCHEMA = cv.Schema( PROTOCOL_SCHEMA = cv.Schema(
{ {
cv.GenerateID(CONF_ESPNOW): cv.use_id(ESPNowComponent), cv.GenerateID(CONF_ESPNOW): cv.use_id(ESPNowComponent),
cv.Optional(CONF_PROTOCOL_MODE): cv.enum(ENUM_MODE, string=True),
}, },
cv.only_on_esp32, cv.only_on_esp32,
).extend(cv.COMPONENT_SCHEMA) )
async def register_protocol(var, config): async def register_protocol(var, config):
now = await cg.get_variable(config[CONF_ESPNOW]) now = await cg.get_variable(config[CONF_ESPNOW])
cg.add(now.register_protocol(var)) cg.add(now.register_protocol(var))
if config[CONF_PROTOCOL_MODE]:
cg.add(var.set_protocol_mode(config[CONF_PROTOCOL_MODE]))
@automation.register_action( @automation.register_action(
"espnow.broatcast", "espnow.broadcast",
SendAction, SendAction,
cv.maybe_simple_value( cv.maybe_simple_value(
{ {
cv.GenerateID(): cv.use_id(ESPNowComponent), cv.GenerateID(): cv.use_id(ESPNowComponent),
cv.Required(CONF_PAYLOAD): cv.templatable(validate_raw_data), cv.Required(CONF_PAYLOAD): cv.templatable(validate_raw_data),
cv.Optional(CONF_COMMAND): cv.templatable(cv.Range(min=16, max=255)), cv.Optional(CONF_COMMAND): cv.templatable(validate_command),
}, },
key=CONF_PAYLOAD, key=CONF_PAYLOAD,
), ),
@ -228,9 +251,7 @@ async def register_protocol(var, config):
cv.GenerateID(): cv.use_id(ESPNowComponent), cv.GenerateID(): cv.use_id(ESPNowComponent),
cv.Required(CONF_PEER): cv.templatable(validate_peer), cv.Required(CONF_PEER): cv.templatable(validate_peer),
cv.Required(CONF_PAYLOAD): cv.templatable(validate_raw_data), cv.Required(CONF_PAYLOAD): cv.templatable(validate_raw_data),
cv.Optional(CONF_COMMAND, default=0): cv.templatable( cv.Optional(CONF_COMMAND): cv.templatable(validate_command),
cv.Range(min=0, max=255)
),
} }
), ),
) )

View file

@ -30,11 +30,13 @@ static const size_t SEND_BUFFER_SIZE = 200;
ESPNowComponent *ESPNowComponent::static_{nullptr}; // NOLINT ESPNowComponent *ESPNowComponent::static_{nullptr}; // NOLINT
std::string espnow_encode_peer(uint64_t peer) { std::string espnow_encode_peer(uint64_t peer) {
std::string str1 = "";
if (peer == FAILED) { if (peer == FAILED) {
return "[Not Set]"; return "[Not Set]";
} else if (peer == ESPNOW_BROADCAST_ADDR) } else if (peer == ESPNOW_BROADCAST_ADDR)
return "[BroadCast]"; return "[BroadCast]";
std::string str1 = "";
str1.reserve(8);
do { do {
str1.push_back(chars[peer & 63]); // Add on the left str1.push_back(chars[peer & 63]); // Add on the left
peer = peer >> 6; peer = peer >> 6;
@ -63,10 +65,10 @@ ESPNowComponent::ESPNowComponent() { ESPNowComponent::static_ = this; } // NOLI
void ESPNowComponent::dump_config() { void ESPNowComponent::dump_config() {
ESP_LOGCONFIG(TAG, "esp_now:"); ESP_LOGCONFIG(TAG, "esp_now:");
ESP_LOGCONFIG(TAG, " Own Peer Address: %s.", espnow_encode_peer(this->own_peer_address_).c_str()); ESP_LOGCONFIG(TAG, " Own Peer code: %s.", espnow_encode_peer(this->own_peer_address_).c_str());
ESP_LOGCONFIG(TAG, " Keeper Peer code: %s.", espnow_encode_peer(this->get_keeper()).c_str());
ESP_LOGCONFIG(TAG, " Wifi channel: %d.", this->wifi_channel_); ESP_LOGCONFIG(TAG, " Wifi channel: %d.", this->wifi_channel_);
ESP_LOGCONFIG(TAG, " Auto add new peers: %s.", this->auto_add_peer_ ? "Yes" : "No"); ESP_LOGCONFIG(TAG, " Auto add new peers: %s.", this->auto_add_peer_ ? "Yes" : "No");
ESP_LOGCONFIG(TAG, " Use sent status: %s.", this->use_sent_check_ ? "Yes" : "No"); ESP_LOGCONFIG(TAG, " Use sent status: %s.", this->use_sent_check_ ? "Yes" : "No");
ESP_LOGCONFIG(TAG, " Convermation timeout: %" PRIx32 "ms.", this->conformation_timeout_); ESP_LOGCONFIG(TAG, " Convermation timeout: %" PRIx32 "ms.", this->conformation_timeout_);
ESP_LOGCONFIG(TAG, " Send retries: %d.", this->retries_); ESP_LOGCONFIG(TAG, " Send retries: %d.", this->retries_);
@ -142,7 +144,10 @@ void ESPNowComponent::setup() {
esp_wifi_get_mac(WIFI_IF_STA, (uint8_t *) &this->own_peer_address_); esp_wifi_get_mac(WIFI_IF_STA, (uint8_t *) &this->own_peer_address_);
for (auto id : this->peers_) { for (auto id : this->peers_) {
add_peer(id); this->add_peer(id);
}
if (this->get_keeper() != 0) {
this->add_peer(this->get_keeper());
} }
this->send_queue_ = xQueueCreate(SEND_BUFFER_SIZE, sizeof(ESPNowPacket)); this->send_queue_ = xQueueCreate(SEND_BUFFER_SIZE, sizeof(ESPNowPacket));
@ -172,16 +177,16 @@ void ESPNowComponent::espnow_task(void *param) {
ESPNowPacket packet; // NOLINT ESPNowPacket packet; // NOLINT
for (;;) { for (;;) {
if (xQueueReceive(this_espnow->receive_queue_, (void *) &packet, (TickType_t) 1) == pdTRUE) { if (xQueueReceive(this_espnow->receive_queue_, (void *) &packet, (TickType_t) 1) == pdTRUE) {
uint8_t *mac = packet.get_peer(); if (packet.is_broadcast) {
if (!packet.is_broadcast || !this_espnow->call_on_broadcast_(packet)) { this_espnow->call_on_broadcast_(packet);
if (!esp_now_is_peer_exist(mac) && !this_espnow->call_on_new_peer_(packet)) { continue;
if (this_espnow->auto_add_peer_) { } else if (!this_espnow->is_paired(packet.peer)) {
this_espnow->add_peer(packet.peer); this_espnow->call_on_new_peer_(packet);
} }
} if (this_espnow->is_paired(packet.peer)) {
if (esp_now_is_peer_exist(mac)) {
this_espnow->call_on_receive_(packet); this_espnow->call_on_receive_(packet);
} } else {
ESP_LOGI(TAG, "message skipt, not paired.");
} }
} }
if (xQueueReceive(this_espnow->send_queue_, (void *) &packet, (TickType_t) 1) == pdTRUE) { if (xQueueReceive(this_espnow->send_queue_, (void *) &packet, (TickType_t) 1) == pdTRUE) {
@ -218,26 +223,39 @@ void ESPNowComponent::espnow_task(void *param) {
} }
esp_err_t ESPNowComponent::add_peer(uint64_t peer) { esp_err_t ESPNowComponent::add_peer(uint64_t peer) {
esp_err_t result = ESP_OK;
if (!this->is_ready()) { if (!this->is_ready()) {
this->peers_.push_back(peer); this->peers_.push_back(peer);
return ESP_OK; return result;
} else { } else {
this->del_peer(peer); if (esp_now_is_peer_exist((uint8_t *) &peer)) {
result = esp_now_del_peer((uint8_t *) &peer);
if (result != ESP_OK)
return result;
}
esp_now_peer_info_t peer_info = {}; esp_now_peer_info_t peer_info = {};
memset(&peer_info, 0, sizeof(esp_now_peer_info_t)); memset(&peer_info, 0, sizeof(esp_now_peer_info_t));
peer_info.channel = this->wifi_channel_; peer_info.channel = this->wifi_channel_;
peer_info.encrypt = false; peer_info.encrypt = false;
memcpy((void *) peer_info.peer_addr, (void *) &peer, 6); memcpy((void *) peer_info.peer_addr, (void *) &peer, 6);
esp_err_t result = esp_now_add_peer(&peer_info);
return esp_now_add_peer(&peer_info); if (result == ESP_OK) {
this->call_on_add_peer_(peer);
}
return result;
} }
} }
esp_err_t ESPNowComponent::del_peer(uint64_t peer) { esp_err_t ESPNowComponent::del_peer(uint64_t peer) {
if (esp_now_is_peer_exist((uint8_t *) &peer)) esp_err_t result = ESP_OK;
return esp_now_del_peer((uint8_t *) &peer); if (esp_now_is_peer_exist((uint8_t *) &peer)) {
return ESP_OK; esp_err_t result = esp_now_del_peer((uint8_t *) &peer);
if (result == ESP_OK) {
this->call_on_del_peer_(peer);
}
}
return result;
} }
ESPNowDefaultProtocol *ESPNowComponent::get_default_protocol() { ESPNowDefaultProtocol *ESPNowComponent::get_default_protocol() {
@ -255,36 +273,61 @@ ESPNowProtocol *ESPNowComponent::get_protocol_(uint32_t protocol) {
return this->protocols_[protocol]; return this->protocols_[protocol];
} }
bool ESPNowComponent::call_on_receive_(ESPNowPacket &packet) { void ESPNowComponent::call_on_receive_(ESPNowPacket &packet) {
ESPNowProtocol *protocol = this->get_protocol_(packet.get_protocol()); ESPNowProtocol *protocol = this->get_protocol_(packet.get_protocol());
if (protocol != nullptr) { if (protocol != nullptr) {
return protocol->on_receive(packet); this->defer([protocol, packet]() { protocol->on_receive(packet); });
} }
return false;
} }
bool ESPNowComponent::call_on_broadcast_(ESPNowPacket &packet) { void ESPNowComponent::call_on_broadcast_(ESPNowPacket &packet) {
ESPNowProtocol *protocol = this->get_protocol_(packet.get_protocol()); ESPNowProtocol *protocol = this->get_protocol_(packet.get_protocol());
if (protocol != nullptr) { if (protocol != nullptr) {
return protocol->on_broadcast(packet); this->defer([protocol, packet]() { protocol->on_broadcast(packet); });
} }
return false;
} }
bool ESPNowComponent::call_on_sent_(ESPNowPacket &packet, bool status) { void ESPNowComponent::call_on_sent_(ESPNowPacket &packet, bool status) {
ESPNowProtocol *protocol = this->get_protocol_(packet.get_protocol()); ESPNowProtocol *protocol = this->get_protocol_(packet.get_protocol());
if (protocol != nullptr) { if (protocol != nullptr) {
return protocol->on_sent(packet, status); this->defer([protocol, packet, status]() { protocol->on_sent(packet, status); });
} }
return false;
} }
bool ESPNowComponent::call_on_new_peer_(ESPNowPacket &packet) { void ESPNowComponent::call_on_new_peer_(ESPNowPacket &packet) {
ESPNowProtocol *protocol = this->get_protocol_(packet.get_protocol()); ESPNowProtocol *protocol = this->get_protocol_(packet.get_protocol());
if (protocol != nullptr) { if (protocol != nullptr) {
return protocol->on_new_peer(packet); this->defer([protocol, packet]() { protocol->on_new_peer(packet); });
} }
return false; }
void ESPNowComponent::call_on_add_peer_(uint64_t peer) {
this->defer([this, peer]() {
for (const auto &kv : this->protocols_) {
kv.second->on_add_peer(peer);
}
});
}
void ESPNowComponent::call_on_del_peer_(uint64_t peer) {
this->defer([this, peer]() {
for (const auto &kv : this->protocols_) {
kv.second->on_del_peer(peer);
}
});
}
bool ESPNowComponent::is_paired(uint64_t peer) {
bool result = false;
if (this->pairing_protocol_ != nullptr) {
result = this->pairing_protocol_->is_paired(peer);
} else {
result = (esp_now_is_peer_exist((uint8_t *) &peer));
}
if (!result && this->auto_add_peer_) {
result = (this->add_peer(peer) == ESP_OK);
}
return result;
} }
/**< callback function of receiving ESPNOW data */ /**< callback function of receiving ESPNOW data */
@ -335,7 +378,7 @@ bool ESPNowComponent::send(ESPNowPacket packet) {
ESP_LOGE(TAG, "Cannot send espnow packet, espnow failed to setup"); ESP_LOGE(TAG, "Cannot send espnow packet, espnow failed to setup");
} else if (this->send_queue_full()) { } else if (this->send_queue_full()) {
ESP_LOGE(TAG, "Send Buffer Out of Memory."); ESP_LOGE(TAG, "Send Buffer Out of Memory.");
} else if (!esp_now_is_peer_exist(packet.get_peer())) { } else if (!this->is_paired(packet.peer)) {
ESP_LOGE(TAG, "Peer not registered: %s.", packet.get_peer_code().c_str()); ESP_LOGE(TAG, "Peer not registered: %s.", packet.get_peer_code().c_str());
} else if (!packet.is_valid()) { } else if (!packet.is_valid()) {
ESP_LOGE(TAG, "This Packet is invalid: %s (%d.%d)", packet.get_peer_code().c_str(), packet.get_sequents(), ESP_LOGE(TAG, "This Packet is invalid: %s (%d.%d)", packet.get_peer_code().c_str(), packet.get_sequents(),
@ -382,6 +425,10 @@ void ESPNowComponent::on_data_sent(const uint8_t *mac_addr, esp_now_send_status_
/* ESPNowProtocol ********************************************************************** */ /* ESPNowProtocol ********************************************************************** */
bool ESPNowProtocol::send(uint64_t peer, const uint8_t *data, uint8_t len, uint8_t command) { bool ESPNowProtocol::send(uint64_t peer, const uint8_t *data, uint8_t len, uint8_t command) {
if (peer == 0x0) {
peer = this->get_keeper();
}
ESPNowPacket packet(peer, data, len, this->get_protocol_id(), command); // NOLINT ESPNowPacket packet(peer, data, len, this->get_protocol_id(), command); // NOLINT
packet.set_sequents(this->get_next_sequents(packet.peer)); packet.set_sequents(this->get_next_sequents(packet.peer));
return this->parent_->send(packet); return this->parent_->send(packet);

View file

@ -15,13 +15,15 @@
#include <vector> #include <vector>
#include <mutex> #include <mutex>
#include <map> #include <map>
#include <random>
#include <string>
namespace esphome { namespace esphome {
namespace espnow { namespace espnow {
static const uint64_t ESPNOW_BROADCAST_ADDR = 0xFFFFFFFFFFFF; static const uint64_t ESPNOW_BROADCAST_ADDR = 0xFFFFFFFFFFFF;
static const uint8_t MAX_ESPNOW_DATA_SIZE = 241; static const uint8_t MAX_ESPNOW_DATA_SIZE = 240;
static const uint8_t TRANSPORT_HEADER[3] = {'N', '0', 'w'}; static const uint8_t TRANSPORT_HEADER[3] = {'N', '0', 'w'};
static const uint32_t ESPNOW_MAIN_PROTOCOL_ID = 0x447453; // = StD static const uint32_t ESPNOW_MAIN_PROTOCOL_ID = 0x447453; // = StD
@ -33,6 +35,10 @@ static const uint8_t ESPNOW_COMMAND_RESEND = 0x05;
static const char chars[] = "0123456789-AbCdEfGhIjKlMnOpQrStUvWxYz+aBcDeFgHiJkLmNoPqRsTuVwXyZ"; static const char chars[] = "0123456789-AbCdEfGhIjKlMnOpQrStUvWxYz+aBcDeFgHiJkLmNoPqRsTuVwXyZ";
static const uint64_t FAILED = 0; static const uint64_t FAILED = 0;
template<typename T> std::string espnow_i2h(T i) { return sprintf("%04x", i); }
std::string espnow_rdm(std::string::size_type length);
std::string espnow_encode_peer(uint64_t peer); std::string espnow_encode_peer(uint64_t peer);
uint64_t espnow_decode_peer(std::string peer); uint64_t espnow_decode_peer(std::string peer);
@ -96,9 +102,6 @@ struct ESPNowPacket {
uint8_t content_size() const { return (this->prefix_size() + this->size); } uint8_t content_size() const { return (this->prefix_size() + this->size); }
inline void set_peer(const uint8_t *peer) ESPHOME_ALWAYS_INLINE { 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); 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; } inline bool is_peer(const uint8_t *peer) const { return memcmp(peer, this->get_peer(), 6) == 0; }
@ -137,33 +140,45 @@ struct ESPNowPacket {
class ESPNowComponent; class ESPNowComponent;
enum ESPNowProtocol_mode { universal, keeper, drudge };
class ESPNowProtocol : public Parented<ESPNowComponent> { class ESPNowProtocol : public Parented<ESPNowComponent> {
public: public:
ESPNowProtocol(){}; void set_protocol_mode(ESPNowProtocol_mode mode) { this->protocol_mode_ = mode; }
ESPNowProtocol_mode get_protocol_mode() { return this->protocol_mode_; }
virtual bool on_receive(const ESPNowPacket &packet) { return false; }; protected:
virtual bool on_broadcast(const ESPNowPacket &packet) { return false; }; ESPNowProtocol_mode protocol_mode_{universal};
virtual bool on_sent(const ESPNowPacket &packet, bool status) { return false; };
virtual bool on_new_peer(const ESPNowPacket &packet) { return false; };
public:
virtual uint32_t get_protocol_id() = 0; virtual uint32_t get_protocol_id() = 0;
virtual std::string get_protocol_name() = 0; virtual std::string get_protocol_name() = 0;
virtual void init_protocol() {}
uint8_t get_next_sequents() { return this->get_next_sequents(0); } virtual void on_receive(const ESPNowPacket &packet){};
virtual uint8_t get_next_sequents(uint64_t peer) { virtual void on_broadcast(const ESPNowPacket &packet) { this->on_receive(packet); };
if (this->next_sequents_ == 255) {
this->next_sequents_ = 0; virtual void on_sent(const ESPNowPacket &packet, bool status){};
virtual void on_new_peer(const ESPNowPacket &packet){};
virtual void on_add_peer(uint64_t peer){};
virtual void on_del_peer(uint64_t peer){};
virtual bool is_paired(uint64_t to_peer) { return true; }
uint8_t get_next_sequents(uint64_t peer) {
if (this->next_sequents_[peer] == 255) {
this->next_sequents_[peer] = 0;
} else { } else {
this->next_sequents_++; this->next_sequents_[peer]++;
} }
return this->next_sequents_; return this->next_sequents_[peer];
} }
bool is_valid_squence(uint8_t received_sequence) { bool is_valid_squence(uint64_t peer, uint8_t received_sequence) {
bool valid = this->next_sequents_ + 1 == received_sequence; bool valid = this->next_sequents_[peer] + 1 == received_sequence;
if (valid) { if (valid) {
this->next_sequents_ = received_sequence; this->next_sequents_[peer] = received_sequence;
} }
return valid; return valid;
} }
@ -171,7 +186,17 @@ class ESPNowProtocol : public Parented<ESPNowComponent> {
bool send(uint64_t peer, const uint8_t *data, uint8_t len, uint8_t command = 0); bool send(uint64_t peer, const uint8_t *data, uint8_t len, uint8_t command = 0);
protected: protected:
uint8_t next_sequents_{255}; std::map<uint64_t, uint8_t> next_sequents_{};
std::string get_mode_name_() {
switch (this->protocol_mode_) {
case universal:
return "Universal";
case keeper:
return "Keeper";
case drudge:
return "Drudge";
}
}
}; };
class ESPNowDefaultProtocol : public ESPNowProtocol { class ESPNowDefaultProtocol : public ESPNowProtocol {
@ -182,35 +207,28 @@ class ESPNowDefaultProtocol : public ESPNowProtocol {
void add_on_receive_callback(std::function<void(const ESPNowPacket)> &&callback) { void add_on_receive_callback(std::function<void(const ESPNowPacket)> &&callback) {
this->on_receive_.add(std::move(callback)); this->on_receive_.add(std::move(callback));
} }
bool on_receive(const ESPNowPacket &packet) override { void on_receive(const ESPNowPacket &packet) override { this->on_receive_.call(packet); };
this->on_receive_.call(packet);
return this->on_receive_.size() > 0;
};
void add_on_broadcast_callback(std::function<void(const ESPNowPacket)> &&callback) { void add_on_broadcast_callback(std::function<void(const ESPNowPacket)> &&callback) {
this->on_broadcast_.add(std::move(callback)); this->on_broadcast_.add(std::move(callback));
} }
void on_broadcast(const ESPNowPacket &packet) override {
bool on_broadcast(const ESPNowPacket &packet) override { if (this->on_broadcast_.size() > 0) {
this->on_broadcast_.call(packet); this->on_broadcast_.call(packet);
return this->on_broadcast_.size() > 0; } else {
this->on_receive(packet);
}
}; };
void add_on_sent_callback(std::function<void(const ESPNowPacket, bool status)> &&callback) { void add_on_sent_callback(std::function<void(const ESPNowPacket, bool status)> &&callback) {
this->on_sent_.add(std::move(callback)); this->on_sent_.add(std::move(callback));
} }
bool on_sent(const ESPNowPacket &packet, bool status) override { void on_sent(const ESPNowPacket &packet, bool status) override { this->on_sent_.call(packet, status); };
this->on_sent_.call(packet, status);
return this->on_sent_.size() > 0;
};
void add_on_peer_callback(std::function<void(const ESPNowPacket)> &&callback) { void add_on_new_peer_callback(std::function<void(const ESPNowPacket)> &&callback) {
this->on_new_peer_.add(std::move(callback)); this->on_new_peer_.add(std::move(callback));
} }
bool on_new_peer(const ESPNowPacket &packet) override { void on_new_peer(const ESPNowPacket &packet) override { this->on_new_peer_.call(packet); };
this->on_new_peer_.call(packet);
return this->on_new_peer_.size() > 0;
};
protected: protected:
CallbackManager<void(const ESPNowPacket, bool)> on_sent_; CallbackManager<void(const ESPNowPacket, bool)> on_sent_;
@ -240,62 +258,73 @@ class ESPNowComponent : public Component {
void set_use_sent_check(bool value) { this->use_sent_check_ = value; } void set_use_sent_check(bool value) { this->use_sent_check_ = value; }
void set_conformation_timeout(uint32_t timeout) { this->conformation_timeout_ = timeout; } void set_conformation_timeout(uint32_t timeout) { this->conformation_timeout_ = timeout; }
void set_retries(uint8_t value) { this->retries_ = value; } void set_retries(uint8_t value) { this->retries_ = value; }
void set_pairing_protocol(ESPNowProtocol *pairing_protocol) { this->pairing_protocol_ = pairing_protocol; }
void set_keeper(uint64_t keeper) { this->keeper_ = keeper; }
} uint64_t get_keeper() {
return this->keeper_;
}
uint64_t get_own_peer_address() { return this->own_peer_address_; }
void setup() override; void setup() override;
void loop() override; void loop() override;
bool send(ESPNowPacket packet); bool is_paired(uint64_t to_peer);
void register_protocol(ESPNowProtocol *protocol) { bool send(ESPNowPacket packet);
void register_protocol(ESPNowProtocol *protocol) {
protocol->set_parent(this); protocol->set_parent(this);
this->protocols_[protocol->get_protocol_id()] = protocol; this->protocols_[protocol->get_protocol_id()] = protocol;
} protocol->init_protocol();
}
esp_err_t add_peer(uint64_t peer); esp_err_t add_peer(uint64_t peer);
esp_err_t del_peer(uint64_t peer); esp_err_t del_peer(uint64_t peer);
bool send_queue_empty() { return uxQueueMessagesWaiting(this->send_queue_) == 0; } bool send_queue_empty() { return uxQueueMessagesWaiting(this->send_queue_) == 0; }
bool send_queue_full() { return uxQueueSpacesAvailable(this->send_queue_) == 0; } bool send_queue_full() { return uxQueueSpacesAvailable(this->send_queue_) == 0; }
size_t send_queue_used() { return uxQueueMessagesWaiting(this->send_queue_); } size_t send_queue_used() { return uxQueueMessagesWaiting(this->send_queue_); }
size_t send_queue_free() { return uxQueueSpacesAvailable(this->send_queue_); } size_t send_queue_free() { return uxQueueSpacesAvailable(this->send_queue_); }
void lock() { this->lock_ = true; } void lock() { this->lock_ = true; }
bool is_locked() { return this->lock_; } bool is_locked() { return this->lock_; }
void unlock() { this->lock_ = false; } void unlock() { this->lock_ = false; }
ESPNowDefaultProtocol *get_default_protocol(); ESPNowDefaultProtocol *get_default_protocol();
void show_packet(const std::string &title, const ESPNowPacket &packet); void show_packet(const std::string &title, const ESPNowPacket &packet);
static void espnow_task(void *params); static void espnow_task(void *params);
protected: protected:
bool validate_channel_(uint8_t channel); bool validate_channel_(uint8_t channel);
ESPNowProtocol *get_protocol_(uint32_t protocol); 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};
uint8_t retries_{5};
uint64_t own_peer_address_{0}; bool auto_add_peer_{false};
uint8_t wifi_channel_{0}; bool use_sent_check_{true};
uint32_t conformation_timeout_{5000}; bool lock_{false};
uint8_t retries_{5};
bool auto_add_peer_{false}; void call_on_receive_(ESPNowPacket &packet);
bool use_sent_check_{true}; void call_on_broadcast_(ESPNowPacket &packet);
void call_on_sent_(ESPNowPacket &packet, bool status);
void call_on_new_peer_(ESPNowPacket &packet);
bool lock_{false}; void call_on_add_peer_(uint64_t peer);
void call_on_del_peer_(uint64_t peer);
bool call_on_receive_(ESPNowPacket &packet); QueueHandle_t receive_queue_{};
bool call_on_broadcast_(ESPNowPacket &packet); QueueHandle_t send_queue_{};
bool call_on_sent_(ESPNowPacket &packet, bool status);
bool call_on_new_peer_(ESPNowPacket &packet);
QueueHandle_t receive_queue_{}; std::map<uint32_t, ESPNowProtocol *> protocols_{};
QueueHandle_t send_queue_{}; std::vector<uint64_t> peers_{};
bool task_running_{false};
std::map<uint32_t, ESPNowProtocol *> protocols_{}; static ESPNowComponent *static_; // NOLINT
std::vector<uint64_t> peers_{};
bool task_running_{false};
static ESPNowComponent *static_; // NOLINT
}; };
template<typename... Ts> class SendAction : public Action<Ts...>, public Parented<ESPNowComponent> { template<typename... Ts> class SendAction : public Action<Ts...>, public Parented<ESPNowComponent> {
@ -380,7 +409,7 @@ class ESPNowBroadcaseTrigger : public Trigger<const ESPNowPacket> {
class ESPNowNewPeerTrigger : public Trigger<const ESPNowPacket> { class ESPNowNewPeerTrigger : public Trigger<const ESPNowPacket> {
public: public:
explicit ESPNowNewPeerTrigger(ESPNowComponent *parent) { explicit ESPNowNewPeerTrigger(ESPNowComponent *parent) {
parent->get_default_protocol()->add_on_peer_callback([this](const ESPNowPacket packet) { parent->get_default_protocol()->add_on_new_peer_callback([this](const ESPNowPacket packet) {
if ((this->command_ == 0) || this->command_ == packet.get_command()) { if ((this->command_ == 0) || this->command_ == packet.get_command()) {
this->trigger(packet); this->trigger(packet);
} }

View file

@ -0,0 +1,57 @@
# These substitutions allow the end user to override certain values
substitutions:
name: "lum-iot-test1"
friendly_name: "Project Template"
esp32:
board: esp32dev
framework:
type: esp-idf
# version: 5.1.5
esphome:
name: "${name}"
# Friendly names are used where appropriate in Home Assistant
friendly_name: "${friendly_name}"
# Automatically add the mac address to the name
# so you can use a single firmware for all devices
name_add_mac_suffix: false
# This will allow for (future) project identification,
# configuration and updates.
project:
name: LumenSoft.espnow-test
version: "1.0"
# To be able to get logs from the device via serial and api.
logger:
level: verbose
espnow:
auto_add_peer: false
peers:
- FF:FF:FF:FF:FF:FF
- flW1QA3k
on_receive:
- logger.log:
format: "Received: '%s' from '%s' command: %d RSSI: %d"
args:
[
packet.get_payload(),
packet.get_peer_code().c_str(),
packet.get_command(),
packet.rssi,
]
on_broadcast:
- command: 123
then:
- logger.log:
format: "Broadcast Received from: '%s' RSSI: %d: %s"
args:
[
packet.get_peer_code().c_str(),
packet.rssi,
packet.get_payload(),
]

View file

@ -0,0 +1,76 @@
# These substitutions allow the end user to override certain values
substitutions:
name: "lum-iot-test2"
friendly_name: "Project Template"
esp32:
board: esp32dev
framework:
type: esp-idf
# version: 5.1.5
esphome:
name: "${name}"
# Friendly names are used where appropriate in Home Assistant
friendly_name: "${friendly_name}"
# Automatically add the mac address to the name
# so you can use a single firmware for all devices
name_add_mac_suffix: false
# This will allow for (future) project identification,
# configuration and updates.
project:
name: LumenSoft.espnow-test
version: "1.0"
# To be able to get logs from the device via serial and api.
logger:
level: verbose
espnow:
auto_add_peer: true
peers:
- FF:FF:FF:FF:FF:FF
keeper: rmT7YF9o
on_receive:
- logger.log:
format: "Received: '%s' from '%s' command: %d RSSI: %d"
args:
[
packet.get_payload(),
packet.get_peer_code().c_str(),
packet.get_command(),
packet.rssi,
]
on_broadcast:
- command: 123
then:
- logger.log:
format: "Broadcast Received from: '%s' RSSI: %d: %s"
args:
[
packet.get_peer_code().c_str(),
packet.rssi,
packet.get_payload(),
]
interval:
- interval: 30sec
startup_delay: 20sec
then:
- espnow.broadcast:
payload: "Broadcast message"
command: 123
- interval: 5sec
then:
- espnow.send:
peer: keeper
payload: "tesing the test"
binary_sensor:
- platform: gpio
pin: GPIO39
name: Button
on_click:
- espnow.peer.del: rmT7YF9o