1
0
mirror of https://github.com/esphome/esphome.git synced 2025-04-14 23:00:29 +01:00
2024-07-27 10:55:30 +02:00

327 lines
9.6 KiB
C++

#include "espnow.h"
#include <string.h>
#include "esp_mac.h"
#include "esp_random.h"
#include "esp_wifi.h"
#include "esp_crc.h"
#include "esp_now.h"
#include "esp_event.h"
#ifdef USE_WIFI
#include "esphome/components/wifi/wifi_component.h"
#endif
#include "esphome/core/version.h"
#include "esphome/core/log.h"
namespace esphome {
namespace espnow {
static const char *const TAG = "espnow";
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 1)
typedef struct {
uint16_t frame_head;
uint16_t duration;
uint8_t destination_address[6];
uint8_t source_address[6];
uint8_t broadcast_address[6];
uint16_t sequence_control;
uint8_t category_code;
uint8_t organization_identifier[3]; // 0x18fe34
uint8_t random_values[4];
struct {
uint8_t element_id; // 0xdd
uint8_t lenght; //
uint8_t organization_identifier[3]; // 0x18fe34
uint8_t type; // 4
uint8_t version;
uint8_t body[0];
} vendor_specific_content;
} __attribute__((packed)) espnow_frame_format_t;
#endif
void ESPNowInterface::setup() { parent_->register_protocol(this); }
ESPNowPackage::ESPNowPackage(const uint64_t mac_address, const std::vector<uint8_t> data) {
this->mac_address_ = mac_address;
this->data_ = data;
}
ESPNowPackage::ESPNowPackage(const uint64_t mac_address, const uint8_t *data, size_t len) {
this->data_.resize(len);
std::copy_n(data, len, this->data_.begin());
}
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() {
ESP_LOGCONFIG(TAG, "esp_now:");
ESP_LOGCONFIG(TAG, " MAC Address: " MACSTR, MAC2STR(ESPNOW_ADDR_SELF));
// ESP_LOGCONFIG(TAG, " WiFi Channel: %n", WiFi.channel());
}
bool ESPNowComponent::validate_channel_(uint8_t channel) {
wifi_country_t g_self_country;
esp_wifi_get_country(&g_self_country);
if (channel >= g_self_country.schan + g_self_country.nchan) {
ESP_LOGE(TAG, "Can't set channel %d, not allowed in country %c%c%c.", channel, g_self_country.cc[0],
g_self_country.cc[1], g_self_country.cc[2]);
return false;
}
return true;
}
void ESPNowComponent::setup() {
ESP_LOGI(TAG, "Setting up ESP-NOW...");
#ifdef USE_WIFI
global_wifi_component.disable();
#else // Set device as a Wi-Fi Station
esp_event_loop_create_default();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_ERROR_CHECK(esp_wifi_disconnect());
#endif
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
#ifdef CONFIG_ESPNOW_ENABLE_LONG_RANGE
esp_wifi_get_protocol(ESP_IF_WIFI_STA, WIFI_PROTOCOL_LR);
#endif
esp_wifi_set_promiscuous(true);
esp_wifi_set_channel(this->wifi_channel_, WIFI_SECOND_CHAN_NONE);
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();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_now_init failed: %s", esp_err_to_name(err));
this->mark_failed();
return;
}
err = esp_now_register_recv_cb(ESPNowComponent::on_data_received);
if (err != ESP_OK) {
this->log_error_("esp_now_register_recv_cb failed: %s", err);
this->mark_failed();
return;
}
err = esp_now_register_send_cb(ESPNowComponent::on_data_send);
if (err != ESP_OK) {
this->log_error_("esp_now_register_send_cb failed: %s", err);
this->mark_failed();
return;
}
esp_wifi_get_mac(WIFI_IF_STA, ESPNOW_ADDR_SELF);
ESP_LOGI(TAG, "ESP-NOW add peers.");
for (auto &address : this->peers_) {
ESP_LOGI(TAG, "Add peer 0x%s .", format_hex(address).c_str());
add_peer(address);
}
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) {
if (!this->is_ready()) {
this->peers_.push_back(addr);
return ESP_OK;
} else {
uint8_t mac[6];
this->del_peer(addr);
uint64_to_addr(addr, (uint8_t *) &mac);
esp_now_peer_info_t peerInfo = {};
memset(&peerInfo, 0, sizeof(esp_now_peer_info_t));
peerInfo.channel = this->wifi_channel_;
peerInfo.encrypt = false;
memcpy(peerInfo.peer_addr, mac, 6);
return esp_now_add_peer(&peerInfo);
}
}
esp_err_t ESPNowComponent::del_peer(uint64_t addr) {
uint8_t mac[6];
uint64_to_addr(addr, (uint8_t *) &mac);
if (esp_now_is_peer_exist((uint8_t *) &mac))
return esp_now_del_peer((uint8_t *) &mac);
return ESP_OK;
}
void ESPNowComponent::on_package_received(ESPNowPackage *package) {
for (auto *protocol : this->protocols_) {
if (protocol->on_package_received(package)) {
return;
}
}
this->on_package_receved_.call(package);
}
void ESPNowComponent::on_package_send(ESPNowPackage *package) {
for (auto *protocol : this->protocols_) {
if (protocol->on_package_send(package)) {
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 {
uint8_t mac_address[6];
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()) {
ESPNowPackage *package = std::move(this->receive_queue_.front());
this->receive_queue_.pop();
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 {
this->on_new_peer(package);
}
}
if (esp_now_is_peer_exist((uint8_t *) &mac)) {
this->unHold_send_(package->mac_address());
this->on_package_received(package);
} else {
ESP_LOGW(TAG, "Peer does not exist can't handle this package: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1],
mac[2], mac[3], mac[4], mac[5]);
}
}
}
/**< callback function of receiving ESPNOW data */
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 1)
void ESPNowComponent::on_data_received(const esp_now_recv_info_t *recv_info, const uint8_t *data, int size)
#else
void ESPNowComponent::on_data_received(const uint8_t *addr, const uint8_t *data, int size)
#endif
{
wifi_pkt_rx_ctrl_t *rx_ctrl = NULL;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 1)
uint8_t *addr = recv_info->src_addr;
rx_ctrl = recv_info->rx_ctrl;
#else
wifi_promiscuous_pkt_t *promiscuous_pkt =
(wifi_promiscuous_pkt_t *) (data - sizeof(wifi_pkt_rx_ctrl_t) - sizeof(espnow_frame_format_t));
rx_ctrl = &promiscuous_pkt->rx_ctrl;
#endif
ESPNowPackage *package = new ESPNowPackage(addr_to_uint64(addr), data, size);
#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 {
global_esp_now->on_package_send(package);
global_esp_now->send_queue_.pop();
}
global_esp_now->can_send_ = true;
}
ESPNowComponent *global_esp_now = nullptr;
} // namespace espnow
} // namespace esphome