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"
58SemaphoreHandle_t g_req_mutex =
nullptr;
59SemaphoreHandle_t g_resp_sem =
nullptr;
60bool g_setup_done =
false;
62volatile uint8_t g_expect_seq = 0;
63volatile int32_t g_resp_status = 0;
64uint8_t g_resp_ret[16];
65volatile uint16_t g_resp_ret_len = 0;
71volatile esp_now_recv_cb_t g_recv_cb =
nullptr;
72volatile esp_now_send_cb_t g_send_cb =
nullptr;
87constexpr size_t ESP_NOW_HOSTED_MAX_PEERS = 20;
88uint8_t g_peer_cache[ESP_NOW_HOSTED_MAX_PEERS][6];
89size_t g_peer_count = 0;
90portMUX_TYPE g_peer_lock = portMUX_INITIALIZER_UNLOCKED;
94int peer_cache_find_locked(
const uint8_t *mac) {
95 for (
size_t i = 0; i < g_peer_count; i++) {
96 if (memcmp(g_peer_cache[i], mac, 6) == 0)
97 return static_cast<int>(i);
102bool peer_cache_contains(
const uint8_t *mac) {
103 portENTER_CRITICAL(&g_peer_lock);
104 const bool found = peer_cache_find_locked(mac) >= 0;
105 portEXIT_CRITICAL(&g_peer_lock);
109void peer_cache_add(
const uint8_t *mac) {
110 portENTER_CRITICAL(&g_peer_lock);
111 if (peer_cache_find_locked(mac) < 0 && g_peer_count < ESP_NOW_HOSTED_MAX_PEERS)
112 memcpy(g_peer_cache[g_peer_count++], mac, 6);
113 portEXIT_CRITICAL(&g_peer_lock);
116void peer_cache_remove(
const uint8_t *mac) {
117 portENTER_CRITICAL(&g_peer_lock);
118 const int idx = peer_cache_find_locked(mac);
121 if (
static_cast<size_t>(idx) != g_peer_count)
122 memcpy(g_peer_cache[idx], g_peer_cache[g_peer_count], 6);
124 portEXIT_CRITICAL(&g_peer_lock);
127void peer_cache_clear() {
128 portENTER_CRITICAL(&g_peer_lock);
130 portEXIT_CRITICAL(&g_peer_lock);
138void on_resp(
uint32_t ,
const uint8_t *data,
size_t len,
void * ) {
139 if (
len <
sizeof(esp_now_hosted_resp_t)) {
140 ESP_LOGW(TAG,
"RESP too short: %u bytes",
static_cast<unsigned>(
len));
143 const auto *r =
reinterpret_cast<const esp_now_hosted_resp_t *
>(
data);
144 if (r->seq != g_expect_seq) {
145 ESP_LOGV(TAG,
"dropping stale RESP seq %u (want %u)", r->seq, g_expect_seq);
148 g_resp_status = r->status;
149 uint16_t rl = r->ret_len;
150 if (rl >
sizeof(g_resp_ret)) {
152 ESP_LOGW(TAG,
"RESP ret_len %u exceeds buffer, clamping (wire drift?)", rl);
153 rl =
sizeof(g_resp_ret);
155 if (
len >=
sizeof(esp_now_hosted_resp_t) + rl) {
156 memcpy(g_resp_ret, r->ret, rl);
161 ESP_LOGW(TAG,
"RESP truncated: claims %u ret bytes, frame too short", rl);
163 g_resp_status = ESP_ERR_INVALID_RESPONSE;
166 xSemaphoreGive(g_resp_sem);
169void on_recv(
uint32_t ,
const uint8_t *data,
size_t len,
void * ) {
173 const esp_now_recv_cb_t
cb = g_recv_cb;
176 if (
len <
sizeof(esp_now_hosted_recv_evt_t)) {
177 ESP_LOGW(TAG,
"RECV too short: %u bytes",
static_cast<unsigned>(
len));
180 const auto *e =
reinterpret_cast<const esp_now_hosted_recv_evt_t *
>(
data);
181 if (
len <
sizeof(esp_now_hosted_recv_evt_t) + e->data_len) {
182 ESP_LOGW(TAG,
"RECV data_len %u exceeds frame", e->data_len);
187 wifi_pkt_rx_ctrl_t rx_ctrl;
188 memset(&rx_ctrl, 0,
sizeof(rx_ctrl));
189 rx_ctrl.rssi = e->rssi;
190 rx_ctrl.channel = e->channel;
193 esp_now_recv_info_t info;
194 info.src_addr =
const_cast<uint8_t *
>(e->src_addr);
195 info.des_addr =
const_cast<uint8_t *
>(e->des_addr);
196 info.rx_ctrl = &rx_ctrl;
197 cb(&info, e->data,
static_cast<int>(e->data_len));
200void on_send(
uint32_t ,
const uint8_t *data,
size_t len,
void * ) {
203 const esp_now_send_cb_t
cb = g_send_cb;
206 if (
len <
sizeof(esp_now_hosted_send_evt_t)) {
207 ESP_LOGW(TAG,
"SEND evt too short: %u bytes",
static_cast<unsigned>(
len));
210 const auto *e =
reinterpret_cast<const esp_now_hosted_send_evt_t *
>(
data);
211#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
216 esp_now_send_info_t si;
217 memset(&si, 0,
sizeof(si));
218 si.des_addr =
const_cast<uint8_t *
>(e->des_addr);
232 if (g_req_mutex ==
nullptr)
233 g_req_mutex = xSemaphoreCreateMutex();
234 if (g_resp_sem ==
nullptr)
235 g_resp_sem = xSemaphoreCreateBinary();
236 if (g_req_mutex ==
nullptr || g_resp_sem ==
nullptr)
237 return ESP_ERR_NO_MEM;
238 esp_err_t err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_RESP, on_resp,
nullptr);
241 err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_RECV, on_recv,
nullptr);
244 err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_SEND, on_send,
nullptr);
260esp_err_t request(uint8_t opcode,
const void *payload, uint16_t plen,
void *
ret, uint16_t ret_cap, uint16_t *ret_len,
261 bool wait =
true,
const void *tail =
nullptr, uint16_t tail_len = 0) {
265 if (plen > ESP_NOW_HOSTED_MAX_PAYLOAD || tail_len > ESP_NOW_HOSTED_MAX_PAYLOAD - plen)
266 return ESP_ERR_INVALID_SIZE;
267 const uint16_t total_len =
static_cast<uint16_t
>(plen + tail_len);
269 if (xSemaphoreTake(g_req_mutex, portMAX_DELAY) != pdTRUE)
272 static uint8_t buf[
sizeof(esp_now_hosted_req_t) + ESP_NOW_HOSTED_MAX_PAYLOAD];
273 auto *req =
reinterpret_cast<esp_now_hosted_req_t *
>(buf);
274 req->opcode = opcode;
276 req->payload_len = total_len;
278 memcpy(req->payload, payload, plen);
280 memcpy(req->payload + plen, tail, tail_len);
281 g_expect_seq = req->seq;
283 xSemaphoreTake(g_resp_sem, 0);
284 err = esp_hosted_send_custom_data(ESP_NOW_HOSTED_MSG_REQ, buf,
sizeof(esp_now_hosted_req_t) + total_len);
286 xSemaphoreGive(g_req_mutex);
295 xSemaphoreGive(g_req_mutex);
298 if (xSemaphoreTake(g_resp_sem, pdMS_TO_TICKS(ESP_NOW_HOSTED_TIMEOUT_MS)) != pdTRUE) {
299 ESP_LOGW(TAG,
"opcode %u timed out", opcode);
300 xSemaphoreGive(g_req_mutex);
301 return ESP_ERR_TIMEOUT;
304 const int32_t
status = g_resp_status;
305 if (
ret !=
nullptr && ret_cap != 0) {
306 uint16_t n = g_resp_ret_len < ret_cap ? g_resp_ret_len : ret_cap;
307 memcpy(
ret,
const_cast<const uint8_t *
>(g_resp_ret), n);
308 if (ret_len !=
nullptr)
311 xSemaphoreGive(g_req_mutex);
333 if (version !=
nullptr)
341 esp_err_t err = ensure_setup();
352 esp_err_t err = ensure_setup();
363static esp_err_t add_or_mod_peer(uint8_t opcode,
const esp_now_peer_info_t *peer,
bool wait) {
365 return ESP_ERR_ESPNOW_ARG;
366 esp_now_hosted_peer_t p;
367 memset(&p, 0,
sizeof(p));
368 memcpy(p.peer_addr, peer->peer_addr, 6);
369 memcpy(p.lmk, peer->lmk, 16);
370 p.channel = peer->channel;
371 p.ifidx =
static_cast<uint8_t
>(peer->ifidx);
372 p.encrypt = peer->encrypt ? 1 : 0;
373 return request(opcode, &p,
sizeof(p),
nullptr, 0,
nullptr, wait);
387 peer_cache_add(peer->peer_addr);
398 if (peer_addr ==
nullptr)
399 return ESP_ERR_ESPNOW_ARG;
404 peer_cache_remove(peer_addr);
409 if (peer_addr ==
nullptr)
414 return peer_cache_contains(peer_addr);
418 if (
len > ESP_NOW_HOSTED_MAX_FRAME)
419 return ESP_ERR_ESPNOW_ARG;
420 if (data ==
nullptr &&
len != 0)
421 return ESP_ERR_ESPNOW_ARG;
428 uint8_t hdr[
sizeof(esp_now_hosted_send_req_t)] = {};
429 auto *s =
reinterpret_cast<esp_now_hosted_send_req_t *
>(hdr);
430 s->has_addr = peer_addr !=
nullptr ? 1 : 0;
431 if (peer_addr !=
nullptr) {
432 memcpy(s->peer_addr, peer_addr, 6);
434 memset(s->peer_addr, 0, 6);
436 s->data_len =
static_cast<uint16_t
>(
len);
444 static_cast<uint16_t
>(
len));
449 return ESP_ERR_ESPNOW_ARG;
458 return ESP_ERR_NOT_SUPPORTED;
463 return ESP_ERR_NOT_SUPPORTED;
466 return ESP_ERR_NOT_SUPPORTED;
469 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
ESPHOME_LOG_TAG(TAG, "hw_timer_esp_idf")
std::span< const uint8_t > data
int64_t esp_timer_get_time(void)