mirror of
https://github.com/esphome/esphome.git
synced 2024-12-18 19:44:53 +01:00
update espnow base component
This commit is contained in:
parent
95e0d0219d
commit
6bb720e071
4 changed files with 464 additions and 310 deletions
|
@ -10,14 +10,14 @@ espnow_ns = cg.esphome_ns.namespace("espnow")
|
||||||
ESPNowComponent = espnow_ns.class_("ESPNowComponent", cg.Component)
|
ESPNowComponent = espnow_ns.class_("ESPNowComponent", cg.Component)
|
||||||
ESPNowListener = espnow_ns.class_("ESPNowListener")
|
ESPNowListener = espnow_ns.class_("ESPNowListener")
|
||||||
|
|
||||||
ESPNowPackage = espnow_ns.class_("ESPNowPackage")
|
ESPNowPacket = espnow_ns.class_("ESPNowPacket")
|
||||||
ESPNowPackagePtrConst = ESPNowPackage.operator("ptr") # .operator("const")
|
ESPNowPacketPtrConst = ESPNowPacket.operator("ptr") # .operator("const")
|
||||||
|
|
||||||
ESPNowInterface = espnow_ns.class_(
|
ESPNowInterface = espnow_ns.class_(
|
||||||
"ESPNowInterface", cg.Component, cg.Parented.template(ESPNowComponent)
|
"ESPNowInterface", cg.Component, cg.Parented.template(ESPNowComponent)
|
||||||
)
|
)
|
||||||
|
|
||||||
ESPNowSendTrigger = espnow_ns.class_("ESPNowSendTrigger", automation.Trigger.template())
|
ESPNowSentTrigger = espnow_ns.class_("ESPNowSentTrigger", automation.Trigger.template())
|
||||||
ESPNowReceiveTrigger = espnow_ns.class_(
|
ESPNowReceiveTrigger = espnow_ns.class_(
|
||||||
"ESPNowReceiveTrigger", automation.Trigger.template()
|
"ESPNowReceiveTrigger", automation.Trigger.template()
|
||||||
)
|
)
|
||||||
|
@ -30,12 +30,13 @@ NewPeerAction = espnow_ns.class_("NewPeerAction", automation.Action)
|
||||||
DelPeerAction = espnow_ns.class_("DelPeerAction", automation.Action)
|
DelPeerAction = espnow_ns.class_("DelPeerAction", automation.Action)
|
||||||
|
|
||||||
CONF_ESPNOW = "espnow"
|
CONF_ESPNOW = "espnow"
|
||||||
CONF_ON_PACKAGE_RECEIVED = "on_package_received"
|
CONF_ON_RECEIVE = "on_receive"
|
||||||
CONF_ON_PACKAGE_SEND = "on_package_send"
|
CONF_ON_SENT = "on_sent"
|
||||||
CONF_ON_NEW_PEER = "on_new_peer"
|
CONF_ON_NEW_PEER = "on_new_peer"
|
||||||
CONF_WIFI_CHANNEL = "wifi_channel"
|
CONF_WIFI_CHANNEL = "wifi_channel"
|
||||||
CONF_PEERS = "peers"
|
CONF_PEERS = "peers"
|
||||||
CONF_AUTO_ADD_PEER = "auto_add_peer"
|
CONF_AUTO_ADD_PEER = "auto_add_peer"
|
||||||
|
CONF_USE_SENT_CHECK = "use_sent_check"
|
||||||
|
|
||||||
|
|
||||||
def validate_raw_data(value):
|
def validate_raw_data(value):
|
||||||
|
@ -53,14 +54,15 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
cv.GenerateID(): cv.declare_id(ESPNowComponent),
|
cv.GenerateID(): cv.declare_id(ESPNowComponent),
|
||||||
cv.Optional(CONF_WIFI_CHANNEL, default=0): cv.int_range(0, 14),
|
cv.Optional(CONF_WIFI_CHANNEL, default=0): cv.int_range(0, 14),
|
||||||
cv.Optional(CONF_AUTO_ADD_PEER, default=False): cv.boolean,
|
cv.Optional(CONF_AUTO_ADD_PEER, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_ON_PACKAGE_RECEIVED): automation.validate_automation(
|
cv.Optional(CONF_USE_SENT_CHECK, default=True): cv.boolean,
|
||||||
|
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_ON_PACKAGE_SEND): automation.validate_automation(
|
cv.Optional(CONF_ON_SENT): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowSendTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowSentTrigger),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_NEW_PEER): automation.validate_automation(
|
cv.Optional(CONF_ON_NEW_PEER): automation.validate_automation(
|
||||||
|
@ -78,40 +80,46 @@ async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
if CORE.is_esp8266:
|
if CORE.is_esp32 and CORE.using_arduino:
|
||||||
cg.add_library("ESP8266WiFi", None)
|
|
||||||
elif CORE.is_esp32 and CORE.using_arduino:
|
|
||||||
cg.add_library("WiFi", None)
|
|
||||||
elif CORE.is_rp2040:
|
|
||||||
cg.add_library("WiFi", None)
|
cg.add_library("WiFi", None)
|
||||||
|
|
||||||
cg.add_define("USE_ESPNOW")
|
cg.add_define("USE_ESPNOW")
|
||||||
|
|
||||||
cg.add(var.set_wifi_channel(config[CONF_WIFI_CHANNEL]))
|
cg.add(var.set_wifi_channel(config[CONF_WIFI_CHANNEL]))
|
||||||
cg.add(var.set_auto_add_peer(config[CONF_AUTO_ADD_PEER]))
|
cg.add(var.set_auto_add_peer(config[CONF_AUTO_ADD_PEER]))
|
||||||
|
cg.add(var.set_use_sent_check(config[CONF_USE_SENT_CHECK]))
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_PACKAGE_SEND, []):
|
for conf in config.get(CONF_ON_SENT, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(
|
await automation.build_automation(trigger, [(ESPNowPacket, "it")], conf)
|
||||||
trigger, [(ESPNowPackagePtrConst, "it")], conf
|
|
||||||
)
|
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_PACKAGE_RECEIVED, []):
|
for conf in config.get(CONF_ON_RECEIVE, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(
|
await automation.build_automation(trigger, [(ESPNowPacket, "it")], conf)
|
||||||
trigger, [(ESPNowPackagePtrConst, "it")], conf
|
|
||||||
)
|
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_NEW_PEER, []):
|
for conf in config.get(CONF_ON_NEW_PEER, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
trigger, [(ESPNowPackagePtrConst, "it")], conf
|
trigger, [(ESPNowPacket, "it"), (bool, "status")], conf
|
||||||
)
|
)
|
||||||
|
|
||||||
for conf in config.get(CONF_PEERS, []):
|
for conf in config.get(CONF_PEERS, []):
|
||||||
cg.add(var.add_peer(conf.as_hex))
|
cg.add(var.add_peer(conf.as_hex))
|
||||||
|
|
||||||
|
|
||||||
|
PROTOCOL_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_ESPNOW): cv.use_id(ESPNowComponent),
|
||||||
|
},
|
||||||
|
cv.only_on_esp32,
|
||||||
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
async def register_protocol(var, config):
|
||||||
|
now = await cg.get_variable(config[CONF_ESPNOW])
|
||||||
|
cg.add(now.register_protocol(var))
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
"espnow.send",
|
"espnow.send",
|
||||||
SendAction,
|
SendAction,
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#ifdef USE_WIFI
|
#ifdef USE_WIFI
|
||||||
#include "esphome/components/wifi/wifi_component.h"
|
#include "esphome/components/wifi/wifi_component.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/version.h"
|
#include "esphome/core/version.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
@ -22,6 +22,14 @@ namespace espnow {
|
||||||
|
|
||||||
static const char *const TAG = "espnow";
|
static const char *const TAG = "espnow";
|
||||||
|
|
||||||
|
static const size_t SEND_BUFFER_SIZE = 200;
|
||||||
|
|
||||||
|
static void application_task(void *param) {
|
||||||
|
// delegate onto the application
|
||||||
|
ESPNowComponent *application = (ESPNowComponent *) param;
|
||||||
|
application->runner();
|
||||||
|
}
|
||||||
|
|
||||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 1)
|
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 1)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t frame_head;
|
uint16_t frame_head;
|
||||||
|
@ -45,28 +53,36 @@ typedef struct {
|
||||||
} __attribute__((packed)) espnow_frame_format_t;
|
} __attribute__((packed)) espnow_frame_format_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ESPNowInterface::setup() { parent_->register_protocol(this); }
|
void ESPNowProtocol::setup() { parent_->register_protocol(this); }
|
||||||
|
|
||||||
ESPNowPackage::ESPNowPackage(const uint64_t mac_address, const std::vector<uint8_t> data) {
|
bool ESPNowProtocol::write(const uint64_t mac_address, const uint8_t *data, uint8_t len) {
|
||||||
this->mac_address_ = mac_address;
|
ESPNowPacket packet(mac_address, data, len, this->get_app_id());
|
||||||
this->data_ = data;
|
return this->parent_->write(packet);
|
||||||
}
|
}
|
||||||
|
bool ESPNowProtocol::write(const uint64_t mac_address, const std::vector<uint8_t> data) {
|
||||||
ESPNowPackage::ESPNowPackage(const uint64_t mac_address, const uint8_t *data, size_t len) {
|
ESPNowPacket packet(mac_address, (uint8_t *) data.data(), (uint8_t) data.size(), this->get_app_id());
|
||||||
this->mac_address_ = mac_address;
|
return this->parent_->write(packet);
|
||||||
this->data_.clear();
|
}
|
||||||
// this->data_.insert(this->data_.begin(), len, *data)
|
bool ESPNowProtocol::write(ESPNowPacket packet) {
|
||||||
// std::copy_n(data, len, this->data_.begin());
|
packet.app_id = this->get_app_id();
|
||||||
|
packet.ref_id = this->get_next_ref_id();
|
||||||
|
packet.recalc();
|
||||||
|
return this->parent_->write(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
ESPNowComponent::ESPNowComponent() { global_esp_now = this; }
|
ESPNowComponent::ESPNowComponent() { global_esp_now = this; }
|
||||||
|
|
||||||
void ESPNowComponent::log_error_(std::string msg, esp_err_t err) { ESP_LOGE(TAG, msg.c_str(), esp_err_to_name(err)); }
|
|
||||||
|
|
||||||
void ESPNowComponent::dump_config() {
|
void ESPNowComponent::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "esp_now:");
|
ESP_LOGCONFIG(TAG, "esp_now:");
|
||||||
ESP_LOGCONFIG(TAG, " MAC Address: " MACSTR, MAC2STR(ESPNOW_ADDR_SELF));
|
ESP_LOGCONFIG(TAG, " MAC Address: " MACSTR, MAC2STR(ESPNOW_ADDR_SELF));
|
||||||
// ESP_LOGCONFIG(TAG, " WiFi Channel: %n", WiFi.channel());
|
ESPNowPacket packet(0x112233445566, (uint8_t *) TAG, 5, 0x111111);
|
||||||
|
ESP_LOGI(TAG, "test: %s |H:%02x%02x%02x A:%02x%02x%02x %02x T:%02x C:%02x%02x S:%02d", packet.to_str().c_str(),
|
||||||
|
packet.content[0], packet.content[1], packet.content[2], packet.content[3], packet.content[4],
|
||||||
|
packet.content[5], packet.content[6], packet.content[7], packet.content[8], packet.content[9], packet.size);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "test: A:%06x R:%02x C:%04x S:%02d", packet.app_id, packet.ref_id, packet.crc16, packet.size);
|
||||||
|
ESP_LOGI(TAG, "test: is_valid: %s",
|
||||||
|
packet.is_valid() ? "Yes" : "No"); // ESP_LOGCONFIG(TAG, " WiFi Channel: %n", WiFi.channel());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ESPNowComponent::validate_channel_(uint8_t channel) {
|
bool ESPNowComponent::validate_channel_(uint8_t channel) {
|
||||||
|
@ -108,13 +124,6 @@ void ESPNowComponent::setup() {
|
||||||
esp_wifi_set_channel(this->wifi_channel_, WIFI_SECOND_CHAN_NONE);
|
esp_wifi_set_channel(this->wifi_channel_, WIFI_SECOND_CHAN_NONE);
|
||||||
esp_wifi_set_promiscuous(false);
|
esp_wifi_set_promiscuous(false);
|
||||||
|
|
||||||
this->send_lock_ = xSemaphoreCreateMutex();
|
|
||||||
if (!this->send_lock_) {
|
|
||||||
ESP_LOGE(TAG, "Create send semaphore mutex fail");
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t err = esp_now_init();
|
esp_err_t err = esp_now_init();
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "esp_now_init failed: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "esp_now_init failed: %s", esp_err_to_name(err));
|
||||||
|
@ -124,41 +133,44 @@ void ESPNowComponent::setup() {
|
||||||
|
|
||||||
err = esp_now_register_recv_cb(ESPNowComponent::on_data_received);
|
err = esp_now_register_recv_cb(ESPNowComponent::on_data_received);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
this->log_error_("esp_now_register_recv_cb failed: %s", err);
|
ESP_LOGE("esp_now_register_recv_cb failed: %s", esp_err_to_name(err));
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = esp_now_register_send_cb(ESPNowComponent::on_data_send);
|
err = esp_now_register_send_cb(ESPNowComponent::on_data_sent);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
this->log_error_("esp_now_register_send_cb failed: %s", err);
|
ESP_LOGE("esp_now_register_send_cb failed: %s", esp_err_to_name(err));
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_wifi_get_mac(WIFI_IF_STA, ESPNOW_ADDR_SELF);
|
esp_wifi_get_mac(WIFI_IF_STA, ESPNOW_ADDR_SELF);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "ESP-NOW add peers.");
|
|
||||||
for (auto &address : this->peers_) {
|
for (auto &address : this->peers_) {
|
||||||
ESP_LOGI(TAG, "Add peer 0x%s .", format_hex(address).c_str());
|
ESP_LOGI(TAG, "Add peer 0x%s .", format_hex(address).c_str());
|
||||||
add_peer(address);
|
add_peer(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->send_queue_ = xQueueCreate(SEND_BUFFER_SIZE, sizeof(ESPNowPacket));
|
||||||
|
if (this->send_queue_ == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create send queue");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->receive_queue_ = xQueueCreate(SEND_BUFFER_SIZE, sizeof(ESPNowPacket));
|
||||||
|
if (this->receive_queue_ == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create receive queue");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
xTaskCreate(application_task, "espnow_task", 8192, this, 2, &this->espnow_task_handle_);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "ESP-NOW setup complete");
|
ESP_LOGI(TAG, "ESP-NOW setup complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
ESPNowPackage *ESPNowComponent::send_package(ESPNowPackage *package) {
|
|
||||||
uint8_t mac[6];
|
|
||||||
package->mac_bytes((uint8_t *) &mac);
|
|
||||||
|
|
||||||
if (esp_now_is_peer_exist((uint8_t *) &mac)) {
|
|
||||||
this->send_queue_.push(std::move(package));
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "Peer does not exist cant send this package: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2],
|
|
||||||
mac[3], mac[4], mac[5]);
|
|
||||||
}
|
|
||||||
return package;
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t ESPNowComponent::add_peer(uint64_t addr) {
|
esp_err_t ESPNowComponent::add_peer(uint64_t addr) {
|
||||||
if (!this->is_ready()) {
|
if (!this->is_ready()) {
|
||||||
this->peers_.push_back(addr);
|
this->peers_.push_back(addr);
|
||||||
|
@ -167,13 +179,11 @@ esp_err_t ESPNowComponent::add_peer(uint64_t addr) {
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
this->del_peer(addr);
|
this->del_peer(addr);
|
||||||
|
|
||||||
uint64_to_addr(addr, (uint8_t *) &mac);
|
|
||||||
|
|
||||||
esp_now_peer_info_t peerInfo = {};
|
esp_now_peer_info_t peerInfo = {};
|
||||||
memset(&peerInfo, 0, sizeof(esp_now_peer_info_t));
|
memset(&peerInfo, 0, sizeof(esp_now_peer_info_t));
|
||||||
peerInfo.channel = this->wifi_channel_;
|
peerInfo.channel = this->wifi_channel_;
|
||||||
peerInfo.encrypt = false;
|
peerInfo.encrypt = false;
|
||||||
memcpy(peerInfo.peer_addr, mac, 6);
|
memcpy((void *) peerInfo.peer_addr, (void *) &addr, 6);
|
||||||
|
|
||||||
return esp_now_add_peer(&peerInfo);
|
return esp_now_add_peer(&peerInfo);
|
||||||
}
|
}
|
||||||
|
@ -181,97 +191,42 @@ esp_err_t ESPNowComponent::add_peer(uint64_t addr) {
|
||||||
|
|
||||||
esp_err_t ESPNowComponent::del_peer(uint64_t addr) {
|
esp_err_t ESPNowComponent::del_peer(uint64_t addr) {
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
uint64_to_addr(addr, (uint8_t *) &mac);
|
memcpy((void *) &mac, (void *) &addr, 6);
|
||||||
if (esp_now_is_peer_exist((uint8_t *) &mac))
|
if (esp_now_is_peer_exist((uint8_t *) &mac))
|
||||||
return esp_now_del_peer((uint8_t *) &mac);
|
return esp_now_del_peer((uint8_t *) &mac);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESPNowComponent::on_package_received(ESPNowPackage *package) {
|
ESPNowDefaultProtocol *ESPNowComponent::get_defaultProtocol_() {
|
||||||
for (auto *protocol : this->protocols_) {
|
if (this->protocols_[ESPNOW_DEFAULT_APP_ID] == nullptr) {
|
||||||
if (protocol->on_package_received(package)) {
|
ESPNowDefaultProtocol *tmp = new ESPNowDefaultProtocol();
|
||||||
return;
|
this->protocols_[ESPNOW_DEFAULT_APP_ID] = tmp;
|
||||||
|
this->register_protocol(tmp);
|
||||||
}
|
}
|
||||||
}
|
return (ESPNowDefaultProtocol *) this->protocols_[ESPNOW_DEFAULT_APP_ID];
|
||||||
this->on_package_receved_.call(package);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESPNowComponent::on_package_send(ESPNowPackage *package) {
|
void ESPNowComponent::on_receive_(ESPNowPacket packet) {
|
||||||
for (auto *protocol : this->protocols_) {
|
if (this->protocols_[packet.app_id] == nullptr) {
|
||||||
if (protocol->on_package_send(package)) {
|
ESP_LOGE(TAG, "Protocol for '%06x' is not registered", packet.app_id);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this->on_package_send_.call(package);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESPNowComponent::on_new_peer(ESPNowPackage *package) {
|
|
||||||
for (auto *protocol : this->protocols_) {
|
|
||||||
if (protocol->on_new_peer(package)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this->on_new_peer_.call(package);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESPNowComponent::unHold_send_(uint64_t mac) {
|
|
||||||
for (ESPNowPackage *package : this->send_queue_) {
|
|
||||||
if (package->is_holded() && package->mac_address() == mac) {
|
|
||||||
package->reset_counter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESPNowComponent::loop() {
|
|
||||||
if (!send_queue_.empty() && this->can_send_ && !this->status_has_warning()) {
|
|
||||||
ESPNowPackage *package = this->send_queue_.front();
|
|
||||||
ESPNowPackage *front = package;
|
|
||||||
while (package->is_holded()) {
|
|
||||||
this->send_queue_.pop();
|
|
||||||
this->send_queue_.push(package);
|
|
||||||
package = this->send_queue_.front();
|
|
||||||
if (front == package)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!package->is_holded()) {
|
|
||||||
if (package->get_counter() == 5) {
|
|
||||||
this->status_set_warning("to many send retries. Stopping sending until new package received.");
|
|
||||||
} else {
|
} else {
|
||||||
uint8_t mac_address[6];
|
this->protocols_[packet.app_id]->on_receive(packet);
|
||||||
package->mac_bytes((uint8_t *) &mac_address);
|
|
||||||
esp_err_t err = esp_now_send(mac_address, package->data().data(), package->data().size());
|
|
||||||
package->inc_counter();
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
this->log_error_("esp_now_init failed: %s", err);
|
|
||||||
this->send_queue_.pop();
|
|
||||||
this->send_queue_.push(package);
|
|
||||||
} else {
|
|
||||||
this->can_send_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!receive_queue_.empty()) {
|
void ESPNowComponent::on_sent_(ESPNowPacket packet, bool status) {
|
||||||
ESPNowPackage *package = std::move(this->receive_queue_.front());
|
if (this->protocols_[packet.app_id] == nullptr) {
|
||||||
this->receive_queue_.pop();
|
ESP_LOGE(TAG, "Protocol for '%06x' is not registered", packet.app_id);
|
||||||
uint8_t mac[6];
|
|
||||||
package->mac_bytes((uint8_t *) &mac);
|
|
||||||
if (!esp_now_is_peer_exist((uint8_t *) &mac)) {
|
|
||||||
if (this->auto_add_peer_) {
|
|
||||||
this->add_peer(addr_to_uint64((uint8_t *) &mac));
|
|
||||||
} else {
|
} else {
|
||||||
this->on_new_peer(package);
|
this->protocols_[packet.app_id]->on_sent(packet, status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (esp_now_is_peer_exist((uint8_t *) &mac)) {
|
|
||||||
this->unHold_send_(package->mac_address());
|
void ESPNowComponent::on_new_peer_(ESPNowPacket packet) {
|
||||||
this->on_package_received(package);
|
if (this->protocols_[packet.app_id] == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Protocol for '%06x' is not registered", packet.app_id);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "Peer does not exist can't handle this package: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1],
|
this->protocols_[packet.app_id]->on_new_peer(packet);
|
||||||
mac[2], mac[3], mac[4], mac[5]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,44 +237,151 @@ void ESPNowComponent::on_data_received(const esp_now_recv_info_t *recv_info, con
|
||||||
void ESPNowComponent::on_data_received(const uint8_t *addr, const uint8_t *data, int size)
|
void ESPNowComponent::on_data_received(const uint8_t *addr, const uint8_t *data, int size)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
ESPNowPacket packet;
|
||||||
wifi_pkt_rx_ctrl_t *rx_ctrl = NULL;
|
wifi_pkt_rx_ctrl_t *rx_ctrl = NULL;
|
||||||
|
|
||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 1)
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 1)
|
||||||
uint8_t *addr = recv_info->src_addr;
|
uint8_t *addr = recv_info->src_addr;
|
||||||
|
packet.broadcast = (*recv_info->des_addr == ESPNOW_BROADCAST_ADDR);
|
||||||
rx_ctrl = recv_info->rx_ctrl;
|
rx_ctrl = recv_info->rx_ctrl;
|
||||||
#else
|
#else
|
||||||
wifi_promiscuous_pkt_t *promiscuous_pkt =
|
wifi_promiscuous_pkt_t *promiscuous_pkt =
|
||||||
(wifi_promiscuous_pkt_t *) (data - sizeof(wifi_pkt_rx_ctrl_t) - sizeof(espnow_frame_format_t));
|
(wifi_promiscuous_pkt_t *) (data - sizeof(wifi_pkt_rx_ctrl_t) - sizeof(espnow_frame_format_t));
|
||||||
rx_ctrl = &promiscuous_pkt->rx_ctrl;
|
rx_ctrl = &promiscuous_pkt->rx_ctrl;
|
||||||
#endif
|
#endif
|
||||||
|
packet.set_mac((espnow_addr_t *) addr);
|
||||||
|
packet.rssi = rx_ctrl->rssi;
|
||||||
|
memcpy((void *) &packet.content, (void *) data, size);
|
||||||
|
packet.size = size - 10;
|
||||||
|
packet.timestamp = rx_ctrl->timestamp;
|
||||||
|
ESP_LOGVV(TAG, "Read: %s |H:%02x%02x%02x A:%02x%02x%02x %02x T:%02x C:%02x%02x S:%02d", packet.to_str().c_str(),
|
||||||
|
packet.content[0], packet.content[1], packet.content[2], packet.content[3], packet.content[4],
|
||||||
|
packet.content[5], packet.content[6], packet.content[7], packet.content[8], packet.content[9], packet.size);
|
||||||
|
|
||||||
ESPNowPackage *package = new ESPNowPackage(addr_to_uint64(addr), data, size);
|
if (packet.is_valid()) {
|
||||||
|
xQueueSendToBack(global_esp_now->receive_queue_, &packet, 10);
|
||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 1)
|
|
||||||
package->is_broadcast(addr_to_uint64(recv_info->des_addr) == ESPNOW_BROADCAST_ADDR);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
package->rssi(rx_ctrl->rssi);
|
|
||||||
package->timestamp(rx_ctrl->timestamp);
|
|
||||||
global_esp_now->push_receive_package_(package);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESPNowComponent::on_data_send(const uint8_t *mac_addr, esp_now_send_status_t status) {
|
|
||||||
ESPNowPackage *package = global_esp_now->send_queue_.front();
|
|
||||||
espnow_addr_t mac;
|
|
||||||
uint64_to_addr(package->mac_address(), mac);
|
|
||||||
if (status != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "on_data_send failed");
|
|
||||||
} else if (std::memcmp(mac, mac_addr, 6) != 0) {
|
|
||||||
ESP_LOGE(TAG, "on_data_send Invalid mac address.");
|
|
||||||
ESP_LOGW(TAG, "expected: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
||||||
ESP_LOGW(TAG, "returned: %02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
|
|
||||||
mac_addr[4], mac_addr[5]);
|
|
||||||
} else {
|
} else {
|
||||||
global_esp_now->on_package_send(package);
|
ESP_LOGE(TAG, "Invalid ESP-NOW packet received (CRC)");
|
||||||
global_esp_now->send_queue_.pop();
|
|
||||||
}
|
}
|
||||||
global_esp_now->can_send_ = true;
|
}
|
||||||
|
|
||||||
|
bool ESPNowComponent::write(ESPNowPacket packet) {
|
||||||
|
ESP_LOGVV(TAG, "Write: %s |H:%02x%02x%02x A:%02x%02x%02x %02x T:%02x C:%02x%02x S:%02d", packet.to_str().c_str(),
|
||||||
|
packet.content[0], packet.content[1], packet.content[2], packet.content[3], packet.content[4],
|
||||||
|
packet.content[5], packet.content[6], packet.content[7], packet.content[8], packet.content[9], packet.size);
|
||||||
|
espnow_addr_t mac;
|
||||||
|
packet.get_mac(&mac);
|
||||||
|
if (this->is_failed()) {
|
||||||
|
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((uint8_t *) &mac)) {
|
||||||
|
ESP_LOGW(TAG, "Peer not registered: 0x%s.", packet.to_str().c_str());
|
||||||
|
} else if (!packet.is_valid()) {
|
||||||
|
ESP_LOGW(TAG, "Packet is invalid. maybe you need to ::recalc(). the packat before writing.");
|
||||||
|
} else if (this->use_sent_check_) {
|
||||||
|
xQueueSendToBack(this->send_queue_, &packet, 10);
|
||||||
|
ESP_LOGVV(TAG, "Send (0x%04x.%d): 0x%s. Buffer Used: %d", packet.ref_id, packet.retrys,
|
||||||
|
format_hex(packet.mac64).c_str(), this->send_queue_used());
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
esp_err_t err = esp_now_send((uint8_t *) &mac, (uint8_t *) &packet.content, packet.size + 10);
|
||||||
|
ESP_LOGVV(TAG, "S: 0x%04x.%d B: %d%s.", packet.ref_id, packet.retrys, this->send_queue_used(),
|
||||||
|
(err == ESP_OK) ? "" : " FAILED");
|
||||||
|
this->defer([this, packet, err]() { this->on_sent_(packet, err == ESP_OK); });
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESPNowComponent::loop() {
|
||||||
|
// runner();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESPNowComponent::runner() {
|
||||||
|
ESPNowPacket packet;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (xQueueReceive(this->receive_queue_, &packet, (TickType_t) 1) == pdTRUE) {
|
||||||
|
espnow_addr_t mac;
|
||||||
|
packet.get_mac(&mac);
|
||||||
|
|
||||||
|
if (!esp_now_is_peer_exist((uint8_t *) &mac)) {
|
||||||
|
if (this->auto_add_peer_) {
|
||||||
|
this->add_peer(packet.mac64);
|
||||||
|
} else {
|
||||||
|
this->defer([this, packet]() { this->on_new_peer_(packet); });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (esp_now_is_peer_exist((uint8_t *) &mac)) {
|
||||||
|
this->defer([this, packet]() { this->on_receive_(packet); });
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Peer not registered: %s", format_hex(packet.mac64).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (xQueueReceive(this->send_queue_, &packet, (TickType_t) 1) == pdTRUE) {
|
||||||
|
if (this->is_locked()) {
|
||||||
|
if (millis() - packet.timestamp > 1000) {
|
||||||
|
if (packet.retrys == 6) {
|
||||||
|
ESP_LOGW(TAG, "To many send retries. Packet dropped. 0x%04x", packet.ref_id);
|
||||||
|
this->unlock();
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "TimeOut (0x%04x.%d).", packet.ref_id, packet.retrys);
|
||||||
|
packet.retry();
|
||||||
|
}
|
||||||
|
this->unlock();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
packet.retry();
|
||||||
|
if (packet.retrys == 6) {
|
||||||
|
ESP_LOGW(TAG, "To many send retries. Packet dropped. 0x%04x", packet.ref_id);
|
||||||
|
// continue;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
packet.timestamp = millis();
|
||||||
|
this->lock();
|
||||||
|
espnow_addr_t mac;
|
||||||
|
packet.get_mac(&mac);
|
||||||
|
|
||||||
|
esp_err_t err = esp_now_send((uint8_t *) &mac, packet.data, packet.size + 10);
|
||||||
|
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
ESP_LOGV(TAG, "S: 0x%04x.%d. Wait for conformation. M: %s", packet.ref_id, packet.retrys,
|
||||||
|
packet.to_str().c_str());
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "S: 0x%04x.%d B: %d.", packet.ref_id, packet.retrys, this->send_queue_used());
|
||||||
|
this->unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xQueueSendToFront(this->send_queue_, &packet, 10 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESPNowComponent::on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) {
|
||||||
|
ESPNowPacket packet;
|
||||||
|
if (!global_esp_now->use_sent_check_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint64_t mac64 = packet.to_mac64((espnow_addr_t *) mac_addr);
|
||||||
|
if (xQueueReceive(global_esp_now->send_queue_, &packet, 10 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||||
|
if (status != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "sent packet failed (0x%04x.%d)", packet.ref_id, packet.retrys);
|
||||||
|
} else if (packet.mac64 != mac64) {
|
||||||
|
ESP_LOGE(TAG, " Invalid mac address. (0x%04x.%d) expected: %s got %s", packet.ref_id, packet.retrys,
|
||||||
|
packet.to_str().c_str(), packet.to_str(mac64));
|
||||||
|
} else {
|
||||||
|
ESP_LOGV(TAG, "Confirm sent (0x%04x.%d)", packet.ref_id, packet.retrys);
|
||||||
|
global_esp_now->unlock();
|
||||||
|
global_esp_now->defer([packet]() { global_esp_now->on_sent_(packet, true); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
global_esp_now->defer([packet]() { global_esp_now->on_sent_(packet, false); });
|
||||||
|
xQueueSendToFront(global_esp_now->send_queue_, &packet, 10 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
global_esp_now->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
ESPNowComponent *global_esp_now = nullptr;
|
ESPNowComponent *global_esp_now = nullptr;
|
||||||
|
|
|
@ -1,119 +1,79 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// #if defined(USE_ESP32)
|
#if defined(USE_ESP32)
|
||||||
|
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "espnow_packet.h"
|
||||||
#include <esp_now.h>
|
#include <esp_now.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace espnow {
|
namespace espnow {
|
||||||
|
|
||||||
typedef uint8_t espnow_addr_t[6];
|
|
||||||
|
|
||||||
static const uint64_t ESPNOW_BROADCAST_ADDR = 0xFFFFFFFFFFFF;
|
|
||||||
static espnow_addr_t ESPNOW_ADDR_SELF = {0};
|
|
||||||
|
|
||||||
static void uint64_to_addr(uint64_t address, uint8_t *bd_addr) {
|
|
||||||
*(bd_addr + 0) = (address >> 40) & 0xff;
|
|
||||||
*(bd_addr + 1) = (address >> 32) & 0xff;
|
|
||||||
*(bd_addr + 2) = (address >> 24) & 0xff;
|
|
||||||
*(bd_addr + 3) = (address >> 16) & 0xff;
|
|
||||||
*(bd_addr + 4) = (address >> 8) & 0xff;
|
|
||||||
*(bd_addr + 5) = (address >> 0) & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t addr_to_uint64(const uint8_t *address) {
|
|
||||||
uint64_t u = 0;
|
|
||||||
u |= uint64_t(*(address + 0) & 0xFF) << 40;
|
|
||||||
u |= uint64_t(*(address + 1) & 0xFF) << 32;
|
|
||||||
u |= uint64_t(*(address + 2) & 0xFF) << 24;
|
|
||||||
u |= uint64_t(*(address + 3) & 0xFF) << 16;
|
|
||||||
u |= uint64_t(*(address + 4) & 0xFF) << 8;
|
|
||||||
u |= uint64_t(*(address + 5) & 0xFF) << 0;
|
|
||||||
return u;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ESPNowComponent;
|
class ESPNowComponent;
|
||||||
|
|
||||||
template<typename T, typename Container = std::deque<T> > class iterable_queue : public std::queue<T, Container> {
|
static const uint32_t ESPNOW_DEFAULT_APP_ID = 0x11CFAF;
|
||||||
|
|
||||||
|
class ESPNowProtocol : public Component, public Parented<ESPNowComponent> {
|
||||||
public:
|
public:
|
||||||
typedef typename Container::iterator iterator;
|
ESPNowProtocol(){};
|
||||||
typedef typename Container::const_iterator const_iterator;
|
|
||||||
|
|
||||||
iterator begin() { return this->c.begin(); }
|
|
||||||
iterator end() { return this->c.end(); }
|
|
||||||
const_iterator begin() const { return this->c.begin(); }
|
|
||||||
const_iterator end() const { return this->c.end(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
class ESPNowPackage {
|
|
||||||
public:
|
|
||||||
ESPNowPackage(const uint64_t mac_address, const std::vector<uint8_t> data);
|
|
||||||
ESPNowPackage(const uint64_t mac_address, const uint8_t *data, size_t len);
|
|
||||||
|
|
||||||
uint64_t mac_address() { return this->mac_address_ == 0 ? ESPNOW_BROADCAST_ADDR : this->mac_address_; }
|
|
||||||
|
|
||||||
void mac_bytes(uint8_t *mac_addres) {
|
|
||||||
uint64_t mac = this->mac_address_ == 0 ? ESPNOW_BROADCAST_ADDR : this->mac_address_;
|
|
||||||
uint64_to_addr(mac, mac_addres);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> data() { return data_; }
|
|
||||||
|
|
||||||
uint8_t get_counter() { return send_count_; }
|
|
||||||
void inc_counter() {
|
|
||||||
send_count_ = send_count_ + 1;
|
|
||||||
if (send_count_ > 5 && !is_holded_) {
|
|
||||||
set_holding();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void reset_counter() {
|
|
||||||
send_count_ = 0;
|
|
||||||
del_holding();
|
|
||||||
}
|
|
||||||
|
|
||||||
void is_broadcast(bool value) { this->is_broadcast_ = value; }
|
|
||||||
bool is_broadcast() const { return this->is_broadcast_; }
|
|
||||||
|
|
||||||
void timestamp(uint32_t value) { this->timestamp_ = value; }
|
|
||||||
uint32_t timestamp() { return this->timestamp_; }
|
|
||||||
|
|
||||||
void rssi(int8_t rssi) { this->rssi_ = rssi; }
|
|
||||||
int8_t rssi() { return this->rssi_; }
|
|
||||||
|
|
||||||
bool is_holded() { return this->is_holded_; }
|
|
||||||
void set_holding() { this->is_holded_ = true; }
|
|
||||||
void del_holding() { this->is_holded_ = false; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
uint64_t mac_address_{0};
|
|
||||||
std::vector<uint8_t> data_;
|
|
||||||
|
|
||||||
uint8_t send_count_{0};
|
|
||||||
bool is_broadcast_{false};
|
|
||||||
uint32_t timestamp_{0};
|
|
||||||
uint8_t rssi_{0};
|
|
||||||
|
|
||||||
bool is_holded_{false};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ESPNowInterface : public Component, public Parented<ESPNowComponent> {
|
|
||||||
public:
|
|
||||||
ESPNowInterface(){};
|
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
|
||||||
virtual bool on_package_received(ESPNowPackage *package) { return false; };
|
virtual void on_receive(ESPNowPacket packet) { return; };
|
||||||
virtual bool on_package_send(ESPNowPackage *package) { return false; };
|
virtual void on_sent(ESPNowPacket packet, bool status) { return; };
|
||||||
virtual bool on_new_peer(ESPNowPackage *package) { return false; };
|
virtual void on_new_peer(ESPNowPacket packet) { return; };
|
||||||
|
|
||||||
|
virtual uint32_t get_app_id() = 0;
|
||||||
|
uint8_t get_next_ref_id() { return next_ref_id_++; }
|
||||||
|
|
||||||
|
bool write(const uint64_t mac_address, const uint8_t *data, uint8_t len);
|
||||||
|
bool write(const uint64_t mac_address, const std::vector<uint8_t> data);
|
||||||
|
bool write(ESPNowPacket packet);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t next_ref_id_{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
class ESPNowDefaultProtocol : public ESPNowProtocol {
|
||||||
|
public:
|
||||||
|
bool on_receive(ESPNowPacket packet) {
|
||||||
|
this->on_receive_.call(packet);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
bool on_sent(ESPNowPacket packet, bool status) {
|
||||||
|
this->on_sent_.call(packet, status);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
bool on_new_peer(ESPNowPacket packet) {
|
||||||
|
this->on_new_peer_.call(packet);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t get_app_id() override { return ESPNOW_DEFAULT_APP_ID; };
|
||||||
|
|
||||||
|
void add_on_sent_callback(std::function<void(ESPNowPacket, bool status)> &&callback) {
|
||||||
|
this->on_sent_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
void add_on_receive_callback(std::function<void(ESPNowPacket)> &&callback) {
|
||||||
|
this->on_receive_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
void add_on_peer_callback(std::function<void(ESPNowPacket)> &&callback) {
|
||||||
|
this->on_new_peer_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CallbackManager<void(ESPNowPacket, bool)> on_sent_;
|
||||||
|
CallbackManager<void(ESPNowPacket)> on_receive_;
|
||||||
|
CallbackManager<void(ESPNowPacket)> on_new_peer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ESPNowComponent : public Component {
|
class ESPNowComponent : public Component {
|
||||||
|
@ -126,77 +86,62 @@ class ESPNowComponent : public Component {
|
||||||
static void on_data_received(const uint8_t *addr, const uint8_t *data, int size);
|
static void on_data_received(const uint8_t *addr, const uint8_t *data, int size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void on_data_send(const uint8_t *mac_addr, esp_now_send_status_t status);
|
static void on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status);
|
||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
float get_setup_priority() const override { return -100; }
|
float get_setup_priority() const override { return -100; }
|
||||||
|
|
||||||
|
void set_wifi_channel(uint8_t channel) { this->wifi_channel_ = 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 setup() override;
|
void setup() override;
|
||||||
|
|
||||||
|
void runner();
|
||||||
void loop() override;
|
void loop() override;
|
||||||
void set_wifi_channel(uint8_t channel) { this->wifi_channel_ = channel; }
|
|
||||||
|
|
||||||
ESPNowPackage *send_package(const uint64_t mac_address, const uint8_t *data, int len) {
|
bool write(ESPNowPacket packet);
|
||||||
// ESPNowPackage * package = new ESPNowPackage(mac_address, data, len);
|
|
||||||
// return this->send_package(package);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESPNowPackage *send_package(const uint64_t mac_address, const std::vector<uint8_t> data) {
|
void register_protocol(ESPNowProtocol *protocol) {
|
||||||
ESPNowPackage *package = new ESPNowPackage(mac_address, data);
|
|
||||||
return this->send_package(package);
|
|
||||||
}
|
|
||||||
|
|
||||||
ESPNowPackage *send_package(ESPNowPackage *package);
|
|
||||||
|
|
||||||
void add_on_package_send_callback(std::function<void(ESPNowPackage *)> &&callback) {
|
|
||||||
this->on_package_send_.add(std::move(callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_on_package_receive_callback(std::function<void(ESPNowPackage *)> &&callback) {
|
|
||||||
this->on_package_receved_.add(std::move(callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_on_peer_callback(std::function<void(ESPNowPackage *)> &&callback) {
|
|
||||||
this->on_new_peer_.add(std::move(callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_protocol(ESPNowInterface *protocol) {
|
|
||||||
protocol->set_parent(this);
|
protocol->set_parent(this);
|
||||||
this->protocols_.push_back(protocol);
|
this->protocols_[protocol->get_app_id()] = std::move(protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t add_peer(uint64_t addr);
|
esp_err_t add_peer(uint64_t addr);
|
||||||
esp_err_t del_peer(uint64_t addr);
|
esp_err_t del_peer(uint64_t addr);
|
||||||
|
|
||||||
void set_auto_add_peer(bool value) { this->auto_add_peer_ = value; }
|
bool send_queue_empty() { return uxQueueMessagesWaiting(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_free() { return uxQueueSpacesAvailable(this->send_queue_); }
|
||||||
|
|
||||||
void on_package_received(ESPNowPackage *package);
|
void lock() { this->lock_ = true; }
|
||||||
void on_package_send(ESPNowPackage *package);
|
bool is_locked() { return this->lock_; }
|
||||||
void on_new_peer(ESPNowPackage *package);
|
void unlock() { this->lock_ = false; }
|
||||||
|
|
||||||
void log_error_(std::string msg, esp_err_t err);
|
ESPNowDefaultProtocol *get_defaultProtocol_();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void unHold_send_(uint64_t mac);
|
|
||||||
void push_receive_package_(ESPNowPackage *package) { this->receive_queue_.push(std::move(package)); }
|
|
||||||
bool validate_channel_(uint8_t channel);
|
bool validate_channel_(uint8_t channel);
|
||||||
|
|
||||||
uint8_t wifi_channel_{0};
|
uint8_t wifi_channel_{0};
|
||||||
|
|
||||||
bool auto_add_peer_{false};
|
bool auto_add_peer_{false};
|
||||||
|
bool use_sent_check_{true};
|
||||||
|
bool lock_{false};
|
||||||
|
|
||||||
CallbackManager<void(ESPNowPackage *)> on_package_send_;
|
void on_receive_(ESPNowPacket packet);
|
||||||
CallbackManager<void(ESPNowPackage *)> on_package_receved_;
|
void on_sent_(ESPNowPacket packet, bool status);
|
||||||
CallbackManager<void(ESPNowPackage *)> on_new_peer_;
|
void on_new_peer_(ESPNowPacket packet);
|
||||||
|
|
||||||
iterable_queue<ESPNowPackage *> receive_queue_{};
|
QueueHandle_t receive_queue_{};
|
||||||
iterable_queue<ESPNowPackage *> send_queue_{};
|
QueueHandle_t send_queue_{};
|
||||||
|
|
||||||
std::vector<ESPNowInterface *> protocols_{};
|
std::map<uint32_t, ESPNowProtocol *> protocols_{};
|
||||||
std::vector<uint64_t> peers_{};
|
std::vector<uint64_t> peers_{};
|
||||||
|
|
||||||
SemaphoreHandle_t send_lock_ = NULL;
|
TaskHandle_t espnow_task_handle_{nullptr};
|
||||||
|
|
||||||
bool can_send_{true};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class SendAction : public Action<Ts...>, public Parented<ESPNowComponent> {
|
template<typename... Ts> class SendAction : public Action<Ts...>, public Parented<ESPNowComponent> {
|
||||||
|
@ -215,10 +160,10 @@ template<typename... Ts> class SendAction : public Action<Ts...>, public Parente
|
||||||
auto mac = this->mac_.value(x...);
|
auto mac = this->mac_.value(x...);
|
||||||
|
|
||||||
if (this->static_) {
|
if (this->static_) {
|
||||||
this->parent_->send_package(mac, this->data_static_);
|
this->parent_->get_defaultProtocol_()->write(mac, this->data_static_);
|
||||||
} else {
|
} else {
|
||||||
auto val = this->data_func_(x...);
|
auto val = this->data_func_(x...);
|
||||||
this->parent_->send_package(mac, val);
|
this->parent_->get_defaultProtocol_()->write(mac, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,24 +198,25 @@ template<typename... Ts> class DelPeerAction : public Action<Ts...>, public Pare
|
||||||
TemplatableValue<uint64_t, Ts...> mac_{};
|
TemplatableValue<uint64_t, Ts...> mac_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class ESPNowSendTrigger : public Trigger<ESPNowPackage *> {
|
class ESPNowSentTrigger : public Trigger<ESPNowPacket, bool> {
|
||||||
public:
|
public:
|
||||||
explicit ESPNowSendTrigger(ESPNowComponent *parent) {
|
explicit ESPNowSentTrigger(ESPNowComponent *parent) {
|
||||||
parent->add_on_package_send_callback([this](ESPNowPackage *value) { this->trigger(value); });
|
parent->get_defaultProtocol_()->add_on_sent_callback(
|
||||||
|
[this](ESPNowPacket value, bool status) { this->trigger(value, status); });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ESPNowReceiveTrigger : public Trigger<ESPNowPackage *> {
|
class ESPNowReceiveTrigger : public Trigger<ESPNowPacket> {
|
||||||
public:
|
public:
|
||||||
explicit ESPNowReceiveTrigger(ESPNowComponent *parent) {
|
explicit ESPNowReceiveTrigger(ESPNowComponent *parent) {
|
||||||
parent->add_on_package_receive_callback([this](ESPNowPackage *value) { this->trigger(value); });
|
parent->get_defaultProtocol_()->add_on_receive_callback([this](ESPNowPacket value) { this->trigger(value); });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ESPNowNewPeerTrigger : public Trigger<ESPNowPackage *> {
|
class ESPNowNewPeerTrigger : public Trigger<ESPNowPacket> {
|
||||||
public:
|
public:
|
||||||
explicit ESPNowNewPeerTrigger(ESPNowComponent *parent) {
|
explicit ESPNowNewPeerTrigger(ESPNowComponent *parent) {
|
||||||
parent->add_on_peer_callback([this](ESPNowPackage *value) { this->trigger(value); });
|
parent->get_defaultProtocol_()->add_on_peer_callback([this](ESPNowPacket value) { this->trigger(value); });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -279,4 +225,4 @@ extern ESPNowComponent *global_esp_now;
|
||||||
} // namespace espnow
|
} // namespace espnow
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
// #endif
|
#endif
|
||||||
|
|
138
esphome/components/espnow/espnow_packet.h
Normal file
138
esphome/components/espnow/espnow_packet.h
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// #if defined(USE_ESP32)
|
||||||
|
|
||||||
|
#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 "esp_crc.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace espnow {
|
||||||
|
|
||||||
|
typedef uint8_t espnow_addr_t[6];
|
||||||
|
|
||||||
|
static const uint64_t ESPNOW_BROADCAST_ADDR = 0xFFFFFFFFFFFF;
|
||||||
|
static espnow_addr_t ESPNOW_ADDR_SELF = {0};
|
||||||
|
static const uint8_t MAX_ESPNOW_DATA_SIZE = 240;
|
||||||
|
#define TRANSPORT_HEADER_SIZE 3
|
||||||
|
|
||||||
|
static const uint8_t transport_header[TRANSPORT_HEADER_SIZE] = {0xC1, 0x99, 0x83};
|
||||||
|
|
||||||
|
template<typename... Args> std::string string_format(const std::string &format, Args... args) {
|
||||||
|
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
||||||
|
if (size_s <= 0) {
|
||||||
|
return ("Error during formatting.");
|
||||||
|
}
|
||||||
|
auto size = static_cast<size_t>(size_s);
|
||||||
|
std::unique_ptr<char[]> buf(new char[size]);
|
||||||
|
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||||
|
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t last_ref_id = 0;
|
||||||
|
|
||||||
|
struct ESPNowPacket {
|
||||||
|
uint64_t mac64 = 0;
|
||||||
|
uint8_t size = 0;
|
||||||
|
uint8_t rssi = 0;
|
||||||
|
uint8_t retrys : 4;
|
||||||
|
uint8_t is_broadcast : 1;
|
||||||
|
uint8_t dummy : 3;
|
||||||
|
uint32_t timestamp = 0;
|
||||||
|
|
||||||
|
union {
|
||||||
|
uint8_t content[MAX_ESPNOW_DATA_SIZE + 11];
|
||||||
|
struct {
|
||||||
|
uint8_t header[3] = {0xC1, 0x99, 0x83};
|
||||||
|
uint32_t app_id = 0xFFFFFF;
|
||||||
|
uint1_t ref_id = 0x99;
|
||||||
|
uint8_t crc16 = 0x1234;
|
||||||
|
uint8_t data[MAX_ESPNOW_DATA_SIZE];
|
||||||
|
uint8_t space = 0;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
inline ESPNowPacket() ESPHOME_ALWAYS_INLINE : retrys(0) {}
|
||||||
|
|
||||||
|
inline void info(std::string place) {
|
||||||
|
ESP_LOGVV("Packet", "%s: M:%s A:0x%06x R:0x%02x C:0x%04x S:%02x", place.c_str(), this->to_str().c_str(),
|
||||||
|
this->app_id, this->ref_id, this->random, this->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ESPNowPacket(const uint64_t mac64_, const uint8_t *data_, uint8_t size_,
|
||||||
|
uint32_t app_id_) ESPHOME_ALWAYS_INLINE : mac64(mac64_),
|
||||||
|
size(size_),
|
||||||
|
app_id(app_id_),
|
||||||
|
retrys(0) {
|
||||||
|
if (mac64_ == 0)
|
||||||
|
this->mac64 = ESPNOW_BROADCAST_ADDR;
|
||||||
|
this->is_broadcast = this->mac64 == ESPNOW_BROADCAST_ADDR;
|
||||||
|
|
||||||
|
this->ref_id = 0;
|
||||||
|
|
||||||
|
this->size = std::min(MAX_ESPNOW_DATA_SIZE, size_);
|
||||||
|
std::memcpy(&data, (uint8_t *) data_, this->size);
|
||||||
|
|
||||||
|
this->data[this->size + 1] = 0;
|
||||||
|
this->recalc();
|
||||||
|
this->info("create");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void get_mac(espnow_addr_t *mac_addres) { std::memcpy(mac_addres, &mac64, 6); }
|
||||||
|
inline void set_mac(espnow_addr_t *mac_addres) { this->mac64 = this->to_mac64(mac_addres); }
|
||||||
|
|
||||||
|
uint64_t to_mac64(espnow_addr_t *mac_addres) {
|
||||||
|
uint64_t result;
|
||||||
|
std::memcpy(&result, mac_addres, 6);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void retry() {
|
||||||
|
if (this->retrys < 7) {
|
||||||
|
retrys = retrys + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void recalc() {
|
||||||
|
random = 0;
|
||||||
|
random = esp_crc16_le(ref_id, (uint8_t *) &content, 10 + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_valid() {
|
||||||
|
uint16_t crc = random;
|
||||||
|
recalc();
|
||||||
|
bool valid = (std::memcmp(&header, &transport_header, 3) == 0);
|
||||||
|
valid &= (this->app_id != 0);
|
||||||
|
valid &= (this->random == crc);
|
||||||
|
if (!valid) {
|
||||||
|
ESP_LOGV("Packet", "Invalid H:%02x%02x%02x A:%06x R:%02x C:%04x ipv. %04x, %d&%d&%d=%d\n", this->header[0],
|
||||||
|
this->header[1], this->header[2], this->app_id, this->ref_id, crc, this->random,
|
||||||
|
std::memcmp(&header, &transport_header, 3) == 0, (this->app_id != 0), (this->random == crc), valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->random = crc;
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string to_str(uint64_t mac64_ = 0) {
|
||||||
|
espnow_addr_t mac;
|
||||||
|
if (mac64_ == 0)
|
||||||
|
mac64_ = this->mac64;
|
||||||
|
memcpy((void *) &mac, &mac64_, 6);
|
||||||
|
return string_format("{\"%02x:%02x:%02x:%02x:%02x:%02x\"}", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t *dataptr() { return (uint8_t *) &content; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace espnow
|
||||||
|
} // namespace esphome
|
Loading…
Reference in a new issue