arduino-esp32/libraries/ESP_NOW/src/ESP32_NOW.cpp
Lucas Saavedra Vaz 6754b1962c
feat(esp_now): Add support for ESP NOW V2 (#11524)
* feat(esp_now): Add support for ESP NOW V2

* fix(esp_now): Return -1 on error
2025-06-30 12:02:12 +03:00

464 lines
12 KiB
C++

#include "sdkconfig.h"
#if CONFIG_ESP_WIFI_REMOTE_ENABLED
#warning "ESP-NOW is only supported in SoCs with native Wi-Fi support"
#else
#include "ESP32_NOW.h"
#include <string.h>
#include "esp_system.h"
#include "esp32-hal.h"
#include "esp_wifi.h"
static void (*new_cb)(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg) = nullptr;
static void *new_arg = nullptr; // * tx_arg = nullptr, * rx_arg = nullptr,
static bool _esp_now_has_begun = false;
static ESP_NOW_Peer *_esp_now_peers[ESP_NOW_MAX_TOTAL_PEER_NUM];
static esp_err_t _esp_now_add_peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk, ESP_NOW_Peer *_peer = nullptr) {
log_v(MACSTR, MAC2STR(mac_addr));
if (esp_now_is_peer_exist(mac_addr)) {
log_e("Peer Already Exists");
return ESP_ERR_ESPNOW_EXIST;
}
esp_now_peer_info_t peer;
memset(&peer, 0, sizeof(esp_now_peer_info_t));
memcpy(peer.peer_addr, mac_addr, ESP_NOW_ETH_ALEN);
peer.channel = channel;
peer.ifidx = iface;
peer.encrypt = lmk != nullptr;
if (lmk) {
memcpy(peer.lmk, lmk, ESP_NOW_KEY_LEN);
}
esp_err_t result = esp_now_add_peer(&peer);
if (result == ESP_OK) {
if (_peer != nullptr) {
for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) {
if (_esp_now_peers[i] == nullptr) {
_esp_now_peers[i] = _peer;
return ESP_OK;
}
}
return ESP_FAIL;
}
} else if (result == ESP_ERR_ESPNOW_NOT_INIT) {
log_e("ESPNOW Not Init");
} else if (result == ESP_ERR_ESPNOW_ARG) {
log_e("Invalid Argument");
} else if (result == ESP_ERR_ESPNOW_FULL) {
log_e("Peer list full");
} else if (result == ESP_ERR_ESPNOW_NO_MEM) {
log_e("Out of memory");
} else if (result == ESP_ERR_ESPNOW_EXIST) {
log_e("Peer already exists");
}
return result;
}
static esp_err_t _esp_now_del_peer(const uint8_t *mac_addr) {
esp_err_t result = esp_now_del_peer(mac_addr);
if (result == ESP_ERR_ESPNOW_NOT_INIT) {
log_e("ESPNOW Not Init");
} else if (result == ESP_ERR_ESPNOW_ARG) {
log_e("Invalid Argument");
} else if (result == ESP_ERR_ESPNOW_NOT_FOUND) {
log_e("Peer Not Found");
}
for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) {
if (_esp_now_peers[i] != nullptr && memcmp(mac_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0) {
_esp_now_peers[i] = nullptr;
break;
}
}
return result;
}
static esp_err_t _esp_now_modify_peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk) {
log_v(MACSTR, MAC2STR(mac_addr));
if (!esp_now_is_peer_exist(mac_addr)) {
log_e("Peer not found");
return ESP_ERR_ESPNOW_NOT_FOUND;
}
esp_now_peer_info_t peer;
memset(&peer, 0, sizeof(esp_now_peer_info_t));
memcpy(peer.peer_addr, mac_addr, ESP_NOW_ETH_ALEN);
peer.channel = channel;
peer.ifidx = iface;
peer.encrypt = lmk != nullptr;
if (lmk) {
memcpy(peer.lmk, lmk, ESP_NOW_KEY_LEN);
}
esp_err_t result = esp_now_mod_peer(&peer);
if (result == ESP_OK) {
return result;
} else if (result == ESP_ERR_ESPNOW_NOT_INIT) {
log_e("ESPNOW Not Init");
} else if (result == ESP_ERR_ESPNOW_ARG) {
log_e("Invalid Argument");
} else if (result == ESP_ERR_ESPNOW_FULL) {
log_e("Peer list full");
} else if (result == ESP_ERR_ESPNOW_NO_MEM) {
log_e("Out of memory");
}
return result;
}
static void _esp_now_rx_cb(const esp_now_recv_info_t *info, const uint8_t *data, int len) {
bool broadcast = memcmp(info->des_addr, ESP_NOW.BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0;
log_v("%s from " MACSTR ", data length : %u", broadcast ? "Broadcast" : "Unicast", MAC2STR(info->src_addr), len);
log_buf_v(data, len);
if (!esp_now_is_peer_exist(info->src_addr) && new_cb != nullptr) {
log_v("Calling new_cb, peer not found.");
new_cb(info, data, len, new_arg);
return;
}
//find the peer and call it's callback
for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) {
if (_esp_now_peers[i] != nullptr) {
log_v("Checking peer " MACSTR, MAC2STR(_esp_now_peers[i]->addr()));
}
if (_esp_now_peers[i] != nullptr && memcmp(info->src_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0) {
log_v("Calling onReceive");
_esp_now_peers[i]->onReceive(data, len, broadcast);
return;
}
}
}
static void _esp_now_tx_cb(const uint8_t *mac_addr, esp_now_send_status_t status) {
log_v(MACSTR " : %s", MAC2STR(mac_addr), (status == ESP_NOW_SEND_SUCCESS) ? "SUCCESS" : "FAILED");
//find the peer and call it's callback
for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) {
if (_esp_now_peers[i] != nullptr && memcmp(mac_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0) {
_esp_now_peers[i]->onSent(status == ESP_NOW_SEND_SUCCESS);
return;
}
}
}
ESP_NOW_Class::ESP_NOW_Class() {
max_data_len = 0;
version = 0;
}
ESP_NOW_Class::~ESP_NOW_Class() {}
bool ESP_NOW_Class::begin(const uint8_t *pmk) {
if (_esp_now_has_begun) {
return true;
}
esp_err_t err = esp_wifi_start();
if (err != ESP_OK) {
log_e("WiFi not started! 0x%x)", err);
return false;
}
// Unfortunately we can't get the ESP-NOW version before initializing the Wi-Fi
uint32_t esp_now_version;
err = esp_now_get_version(&esp_now_version);
if (err != ESP_OK) {
log_w("esp_now_get_version failed! Assuming ESP-NOW v1.0");
esp_now_version = 1;
}
if (esp_now_version == 1) {
max_data_len = ESP_NOW_MAX_DATA_LEN;
} else {
max_data_len = ESP_NOW_MAX_DATA_LEN_V2;
}
version = esp_now_version;
log_i("ESP-NOW version: %lu, max_data_len: %lu", version, max_data_len);
_esp_now_has_begun = true;
memset(_esp_now_peers, 0, sizeof(ESP_NOW_Peer *) * ESP_NOW_MAX_TOTAL_PEER_NUM);
err = esp_now_init();
if (err != ESP_OK) {
log_e("esp_now_init failed! 0x%x", err);
_esp_now_has_begun = false;
return false;
}
if (pmk) {
err = esp_now_set_pmk(pmk);
if (err != ESP_OK) {
log_e("esp_now_set_pmk failed! 0x%x", err);
_esp_now_has_begun = false;
return false;
}
}
err = esp_now_register_recv_cb(_esp_now_rx_cb);
if (err != ESP_OK) {
log_e("esp_now_register_recv_cb failed! 0x%x", err);
_esp_now_has_begun = false;
return false;
}
err = esp_now_register_send_cb(_esp_now_tx_cb);
if (err != ESP_OK) {
log_e("esp_now_register_send_cb failed! 0x%x", err);
_esp_now_has_begun = false;
return false;
}
return true;
}
bool ESP_NOW_Class::end() {
if (!_esp_now_has_begun) {
return true;
}
//remove all peers
for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) {
if (_esp_now_peers[i] != nullptr) {
removePeer(*_esp_now_peers[i]);
}
}
esp_err_t err = esp_now_deinit();
if (err != ESP_OK) {
log_e("esp_now_deinit failed! 0x%x", err);
return false;
}
_esp_now_has_begun = false;
//clear the peer list
memset(_esp_now_peers, 0, sizeof(ESP_NOW_Peer *) * ESP_NOW_MAX_TOTAL_PEER_NUM);
return true;
}
int ESP_NOW_Class::getTotalPeerCount() const {
if (!_esp_now_has_begun) {
return -1;
}
esp_now_peer_num_t num;
esp_err_t err = esp_now_get_peer_num(&num);
if (err != ESP_OK) {
log_e("esp_now_get_peer_num failed! 0x%x", err);
return -1;
}
return num.total_num;
}
int ESP_NOW_Class::getEncryptedPeerCount() const {
if (!_esp_now_has_begun) {
return -1;
}
esp_now_peer_num_t num;
esp_err_t err = esp_now_get_peer_num(&num);
if (err != ESP_OK) {
log_e("esp_now_get_peer_num failed! 0x%x", err);
return -1;
}
return num.encrypt_num;
}
int ESP_NOW_Class::getMaxDataLen() const {
if (max_data_len == 0) {
log_e("ESP-NOW not initialized. Please call begin() first to get the max data length.");
return -1;
}
return max_data_len;
}
int ESP_NOW_Class::getVersion() const {
if (version == 0) {
log_e("ESP-NOW not initialized. Please call begin() first to get the version.");
return -1;
}
return version;
}
int ESP_NOW_Class::availableForWrite() {
int available = getMaxDataLen();
if (available < 0) {
return 0;
}
return available;
}
size_t ESP_NOW_Class::write(const uint8_t *data, size_t len) {
if (!_esp_now_has_begun) {
return 0;
}
if (len > max_data_len) {
len = max_data_len;
}
esp_err_t result = esp_now_send(nullptr, data, len);
if (result == ESP_OK) {
return len;
} else if (result == ESP_ERR_ESPNOW_NOT_INIT) {
log_e("ESPNOW not init.");
} else if (result == ESP_ERR_ESPNOW_ARG) {
log_e("Invalid argument");
} else if (result == ESP_ERR_ESPNOW_INTERNAL) {
log_e("Internal Error");
} else if (result == ESP_ERR_ESPNOW_NO_MEM) {
log_e("Our of memory");
} else if (result == ESP_ERR_ESPNOW_NOT_FOUND) {
log_e("Peer not found.");
} else if (result == ESP_ERR_ESPNOW_IF) {
log_e("Interface does not match.");
}
return 0;
}
void ESP_NOW_Class::onNewPeer(void (*cb)(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg), void *arg) {
new_cb = cb;
new_arg = arg;
}
bool ESP_NOW_Class::removePeer(ESP_NOW_Peer &peer) {
return peer.remove();
}
ESP_NOW_Class ESP_NOW;
/*
*
* Inheritable Peer Class
*
*/
ESP_NOW_Peer::ESP_NOW_Peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk) {
added = false;
if (mac_addr) {
memcpy(mac, mac_addr, 6);
}
chan = channel;
ifc = iface;
encrypt = lmk != nullptr;
if (encrypt) {
memcpy(key, lmk, 16);
}
}
bool ESP_NOW_Peer::add() {
if (!_esp_now_has_begun) {
return false;
}
if (added) {
return true;
}
if (_esp_now_add_peer(mac, chan, ifc, encrypt ? key : nullptr, this) != ESP_OK) {
return false;
}
log_v("Peer added - " MACSTR, MAC2STR(mac));
added = true;
return true;
}
bool ESP_NOW_Peer::remove() {
if (!_esp_now_has_begun) {
return false;
}
if (!added) {
return true;
}
log_v("Peer removed - " MACSTR, MAC2STR(mac));
esp_err_t err = _esp_now_del_peer(mac);
if (err == ESP_OK) {
added = false;
return true;
}
return false;
}
const uint8_t *ESP_NOW_Peer::addr() const {
return mac;
}
bool ESP_NOW_Peer::addr(const uint8_t *mac_addr) {
if (!_esp_now_has_begun || !added) {
memcpy(mac, mac_addr, 6);
return true;
}
return false;
}
uint8_t ESP_NOW_Peer::getChannel() const {
return chan;
}
bool ESP_NOW_Peer::setChannel(uint8_t channel) {
chan = channel;
if (!_esp_now_has_begun || !added) {
return true;
}
return _esp_now_modify_peer(mac, chan, ifc, encrypt ? key : nullptr) == ESP_OK;
}
wifi_interface_t ESP_NOW_Peer::getInterface() const {
return ifc;
}
bool ESP_NOW_Peer::setInterface(wifi_interface_t iface) {
ifc = iface;
if (!_esp_now_has_begun || !added) {
return true;
}
return _esp_now_modify_peer(mac, chan, ifc, encrypt ? key : nullptr) == ESP_OK;
}
bool ESP_NOW_Peer::isEncrypted() const {
return encrypt;
}
bool ESP_NOW_Peer::setKey(const uint8_t *lmk) {
encrypt = lmk != nullptr;
if (encrypt) {
memcpy(key, lmk, 16);
}
if (!_esp_now_has_begun || !added) {
return true;
}
return _esp_now_modify_peer(mac, chan, ifc, encrypt ? key : nullptr) == ESP_OK;
}
size_t ESP_NOW_Peer::send(const uint8_t *data, int len) {
log_v(MACSTR ", data length %d", MAC2STR(mac), len);
if (!_esp_now_has_begun || !added) {
log_e("Peer not added.");
return 0;
}
int max_data_len = ESP_NOW.getMaxDataLen();
if (max_data_len < 0) {
log_e("Error getting max data length.");
return 0;
}
if (len > max_data_len) {
len = max_data_len;
}
esp_err_t result = esp_now_send(mac, data, len);
if (result == ESP_OK) {
return len;
} else if (result == ESP_ERR_ESPNOW_NOT_INIT) {
log_e("ESPNOW not init.");
} else if (result == ESP_ERR_ESPNOW_ARG) {
log_e("Invalid argument");
} else if (result == ESP_ERR_ESPNOW_INTERNAL) {
log_e("Internal Error");
} else if (result == ESP_ERR_ESPNOW_NO_MEM) {
log_e("Our of memory");
} else if (result == ESP_ERR_ESPNOW_NOT_FOUND) {
log_e("Peer not found.");
} else if (result == ESP_ERR_ESPNOW_IF) {
log_e("Interface does not match.");
}
return 0;
}
ESP_NOW_Peer::operator bool() const {
return added;
}
#endif