22#if defined(CONFIG_IDF_TARGET_ESP32P4)
26#include "freertos/FreeRTOS.h"
27#include "freertos/semphr.h"
29#include "esp_idf_version.h"
34#include <esp_wifi_types.h>
41#include "esp_hosted_misc.h"
48const char *
const TAG =
"esp_now_hosted";
55SemaphoreHandle_t g_req_mutex =
nullptr;
56SemaphoreHandle_t g_resp_sem =
nullptr;
57bool g_setup_done =
false;
59volatile uint8_t g_expect_seq = 0;
60volatile int32_t g_resp_status = 0;
61uint8_t g_resp_ret[16];
62volatile uint16_t g_resp_ret_len = 0;
68volatile esp_now_recv_cb_t g_recv_cb =
nullptr;
69volatile esp_now_send_cb_t g_send_cb =
nullptr;
84constexpr size_t ESP_NOW_HOSTED_MAX_PEERS = 20;
85uint8_t g_peer_cache[ESP_NOW_HOSTED_MAX_PEERS][6];
86size_t g_peer_count = 0;
87portMUX_TYPE g_peer_lock = portMUX_INITIALIZER_UNLOCKED;
90int peer_cache_find_locked(
const uint8_t *mac) {
91 for (
size_t i = 0; i < g_peer_count; i++) {
92 if (memcmp(g_peer_cache[i], mac, 6) == 0)
93 return static_cast<int>(i);
98bool peer_cache_contains(
const uint8_t *mac) {
99 portENTER_CRITICAL(&g_peer_lock);
100 const bool found = peer_cache_find_locked(mac) >= 0;
101 portEXIT_CRITICAL(&g_peer_lock);
105void peer_cache_add(
const uint8_t *mac) {
106 portENTER_CRITICAL(&g_peer_lock);
107 if (peer_cache_find_locked(mac) < 0 && g_peer_count < ESP_NOW_HOSTED_MAX_PEERS)
108 memcpy(g_peer_cache[g_peer_count++], mac, 6);
109 portEXIT_CRITICAL(&g_peer_lock);
112void peer_cache_remove(
const uint8_t *mac) {
113 portENTER_CRITICAL(&g_peer_lock);
114 const int idx = peer_cache_find_locked(mac);
117 if (
static_cast<size_t>(idx) != g_peer_count)
118 memcpy(g_peer_cache[idx], g_peer_cache[g_peer_count], 6);
120 portEXIT_CRITICAL(&g_peer_lock);
123void peer_cache_clear() {
124 portENTER_CRITICAL(&g_peer_lock);
126 portEXIT_CRITICAL(&g_peer_lock);
134void on_resp(
uint32_t ,
const uint8_t *data,
size_t len,
void * ) {
135 if (
len <
sizeof(esp_now_hosted_resp_t)) {
136 ESP_LOGW(TAG,
"RESP too short: %u bytes",
static_cast<unsigned>(
len));
139 const auto *r =
reinterpret_cast<const esp_now_hosted_resp_t *
>(
data);
140 if (r->seq != g_expect_seq) {
141 ESP_LOGV(TAG,
"dropping stale RESP seq %u (want %u)", r->seq, g_expect_seq);
144 g_resp_status = r->status;
145 uint16_t rl = r->ret_len;
146 if (rl >
sizeof(g_resp_ret)) {
148 ESP_LOGW(TAG,
"RESP ret_len %u exceeds buffer, clamping (wire drift?)", rl);
149 rl =
sizeof(g_resp_ret);
151 if (
len >=
sizeof(esp_now_hosted_resp_t) + rl) {
152 memcpy(g_resp_ret, r->ret, rl);
157 ESP_LOGW(TAG,
"RESP truncated: claims %u ret bytes, frame too short", rl);
159 g_resp_status = ESP_ERR_INVALID_RESPONSE;
162 xSemaphoreGive(g_resp_sem);
165void on_recv(
uint32_t ,
const uint8_t *data,
size_t len,
void * ) {
169 const esp_now_recv_cb_t
cb = g_recv_cb;
172 if (
len <
sizeof(esp_now_hosted_recv_evt_t)) {
173 ESP_LOGW(TAG,
"RECV too short: %u bytes",
static_cast<unsigned>(
len));
176 const auto *e =
reinterpret_cast<const esp_now_hosted_recv_evt_t *
>(
data);
177 if (
len <
sizeof(esp_now_hosted_recv_evt_t) + e->data_len) {
178 ESP_LOGW(TAG,
"RECV data_len %u exceeds frame", e->data_len);
183 wifi_pkt_rx_ctrl_t rx_ctrl;
184 memset(&rx_ctrl, 0,
sizeof(rx_ctrl));
185 rx_ctrl.rssi = e->rssi;
186 rx_ctrl.channel = e->channel;
189 esp_now_recv_info_t info;
190 info.src_addr =
const_cast<uint8_t *
>(e->src_addr);
191 info.des_addr =
const_cast<uint8_t *
>(e->des_addr);
192 info.rx_ctrl = &rx_ctrl;
193 cb(&info, e->data,
static_cast<int>(e->data_len));
196void on_send(
uint32_t ,
const uint8_t *data,
size_t len,
void * ) {
199 const esp_now_send_cb_t
cb = g_send_cb;
202 if (
len <
sizeof(esp_now_hosted_send_evt_t)) {
203 ESP_LOGW(TAG,
"SEND evt too short: %u bytes",
static_cast<unsigned>(
len));
206 const auto *e =
reinterpret_cast<const esp_now_hosted_send_evt_t *
>(
data);
207#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
212 esp_now_send_info_t si;
213 memset(&si, 0,
sizeof(si));
214 si.des_addr =
const_cast<uint8_t *
>(e->des_addr);
215 cb(&si,
static_cast<esp_now_send_status_t
>(e->status));
217 cb(e->des_addr,
static_cast<esp_now_send_status_t
>(e->status));
221esp_err_t ensure_setup() {
228 if (g_req_mutex ==
nullptr)
229 g_req_mutex = xSemaphoreCreateMutex();
230 if (g_resp_sem ==
nullptr)
231 g_resp_sem = xSemaphoreCreateBinary();
232 if (g_req_mutex ==
nullptr || g_resp_sem ==
nullptr)
233 return ESP_ERR_NO_MEM;
235 if ((err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_RESP, on_resp,
nullptr)) != ESP_OK)
237 if ((err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_RECV, on_recv,
nullptr)) != ESP_OK)
239 if ((err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_SEND, on_send,
nullptr)) != ESP_OK)
254esp_err_t request(uint8_t opcode,
const void *payload, uint16_t plen,
void *
ret, uint16_t ret_cap, uint16_t *ret_len,
255 bool wait =
true,
const void *tail =
nullptr, uint16_t tail_len = 0) {
256 esp_err_t err = ensure_setup();
259 if (plen > ESP_NOW_HOSTED_MAX_PAYLOAD || tail_len > ESP_NOW_HOSTED_MAX_PAYLOAD - plen)
260 return ESP_ERR_INVALID_SIZE;
261 const uint16_t total_len =
static_cast<uint16_t
>(plen + tail_len);
263 if (xSemaphoreTake(g_req_mutex, portMAX_DELAY) != pdTRUE)
266 static uint8_t buf[
sizeof(esp_now_hosted_req_t) + ESP_NOW_HOSTED_MAX_PAYLOAD];
267 auto *req =
reinterpret_cast<esp_now_hosted_req_t *
>(buf);
268 req->opcode = opcode;
270 req->payload_len = total_len;
272 memcpy(req->payload, payload, plen);
274 memcpy(req->payload + plen, tail, tail_len);
275 g_expect_seq = req->seq;
277 xSemaphoreTake(g_resp_sem, 0);
278 err = esp_hosted_send_custom_data(ESP_NOW_HOSTED_MSG_REQ, buf,
sizeof(esp_now_hosted_req_t) + total_len);
280 xSemaphoreGive(g_req_mutex);
289 xSemaphoreGive(g_req_mutex);
292 if (xSemaphoreTake(g_resp_sem, pdMS_TO_TICKS(ESP_NOW_HOSTED_TIMEOUT_MS)) != pdTRUE) {
293 ESP_LOGW(TAG,
"opcode %u timed out", opcode);
294 xSemaphoreGive(g_req_mutex);
295 return ESP_ERR_TIMEOUT;
298 const int32_t
status = g_resp_status;
299 if (
ret !=
nullptr && ret_cap != 0) {
300 uint16_t n = g_resp_ret_len < ret_cap ? g_resp_ret_len : ret_cap;
301 memcpy(
ret,
const_cast<const uint8_t *
>(g_resp_ret), n);
302 if (ret_len !=
nullptr)
305 xSemaphoreGive(g_req_mutex);
306 return static_cast<esp_err_t
>(
status);
327 if (version !=
nullptr)
335 esp_err_t err = ensure_setup();
346 esp_err_t err = ensure_setup();
357static esp_err_t add_or_mod_peer(uint8_t opcode,
const esp_now_peer_info_t *peer,
bool wait) {
359 return ESP_ERR_ESPNOW_ARG;
360 esp_now_hosted_peer_t p;
361 memset(&p, 0,
sizeof(p));
362 memcpy(p.peer_addr, peer->peer_addr, 6);
363 memcpy(p.lmk, peer->lmk, 16);
364 p.channel = peer->channel;
365 p.ifidx =
static_cast<uint8_t
>(peer->ifidx);
366 p.encrypt = peer->encrypt ? 1 : 0;
367 return request(opcode, &p,
sizeof(p),
nullptr, 0,
nullptr, wait);
381 peer_cache_add(peer->peer_addr);
392 if (peer_addr ==
nullptr)
393 return ESP_ERR_ESPNOW_ARG;
398 peer_cache_remove(peer_addr);
403 if (peer_addr ==
nullptr)
408 return peer_cache_contains(peer_addr);
412 if (
len > ESP_NOW_HOSTED_MAX_FRAME)
413 return ESP_ERR_ESPNOW_ARG;
414 if (data ==
nullptr &&
len != 0)
415 return ESP_ERR_ESPNOW_ARG;
422 uint8_t hdr[
sizeof(esp_now_hosted_send_req_t)];
423 auto *s =
reinterpret_cast<esp_now_hosted_send_req_t *
>(hdr);
424 s->has_addr = peer_addr !=
nullptr ? 1 : 0;
425 if (peer_addr !=
nullptr)
426 memcpy(s->peer_addr, peer_addr, 6);
428 memset(s->peer_addr, 0, 6);
429 s->data_len =
static_cast<uint16_t
>(
len);
437 static_cast<uint16_t
>(
len));
442 return ESP_ERR_ESPNOW_ARG;
451 return ESP_ERR_NOT_SUPPORTED;
456 return ESP_ERR_NOT_SUPPORTED;
459 return ESP_ERR_NOT_SUPPORTED;
462 return ESP_ERR_NOT_SUPPORTED;
esp_err_t esp_now_del_peer(const uint8_t *peer_addr)
esp_err_t esp_now_get_version(uint32_t *version)
esp_err_t esp_now_unregister_recv_cb(void)
esp_err_t esp_wifi_config_espnow_rate(wifi_interface_t, wifi_phy_rate_t)
esp_err_t esp_now_unregister_send_cb(void)
esp_err_t esp_now_register_send_cb(esp_now_send_cb_t cb)
esp_err_t esp_now_deinit(void)
esp_err_t esp_now_get_peer_num(esp_now_peer_num_t *)
esp_err_t esp_now_add_peer(const esp_now_peer_info_t *peer)
esp_err_t esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len)
esp_err_t esp_now_set_pmk(const uint8_t *pmk)
esp_err_t esp_now_set_peer_rate_config(const uint8_t *, esp_now_rate_config_t *)
bool esp_now_is_peer_exist(const uint8_t *peer_addr)
esp_err_t esp_now_register_recv_cb(esp_now_recv_cb_t cb)
esp_err_t esp_now_mod_peer(const esp_now_peer_info_t *peer)
esp_err_t esp_now_get_peer(const uint8_t *, esp_now_peer_info_t *)
esp_err_t esp_now_set_wake_window(uint16_t)
esp_err_t esp_now_init(void)
esp_err_t esp_now_fetch_peer(bool, esp_now_peer_info_t *)
@ ESP_NOW_HOSTED_OP_SET_PMK
@ ESP_NOW_HOSTED_OP_DEL_PEER
@ ESP_NOW_HOSTED_OP_GET_VERSION
@ ESP_NOW_HOSTED_OP_DEINIT
@ ESP_NOW_HOSTED_OP_ADD_PEER
@ ESP_NOW_HOSTED_OP_MOD_PEER
std::span< const uint8_t > data
int64_t esp_timer_get_time(void)