ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
espnow_packet.h
Go to the documentation of this file.
1#pragma once
2
3#ifdef USE_ESP32
4
5#include "espnow_err.h"
6
7#include <cstdint>
8#include <cstring>
9#include <functional>
10#include <memory>
11#include <vector>
12
13#include <esp_err.h>
14#include <esp_idf_version.h>
15#include <esp_now.h>
16
17namespace esphome::espnow {
18
19static const uint8_t ESPNOW_BROADCAST_ADDR[ESP_NOW_ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
20static const uint8_t ESPNOW_MULTICAST_ADDR[ESP_NOW_ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE};
21
22// Maximum payload this component sends and receives, from the
23// ``max_payload_size`` option. The radio stack speaks ESP-NOW v2 regardless
24// (negotiated per peer); payloads beyond the v1 limit (250 bytes) are opt-in
25// because the packet pools are statically sized from this, so their RAM cost
26// is proportional (~8 KB at 250 bytes, ~44 KB at the v2 limit of 1470).
27#ifndef USE_ESPNOW_MAX_PAYLOAD_SIZE
28#define USE_ESPNOW_MAX_PAYLOAD_SIZE ESP_NOW_MAX_DATA_LEN
29#endif
30static constexpr uint16_t ESPNOW_MAX_DATA_LEN = USE_ESPNOW_MAX_PAYLOAD_SIZE;
31#ifdef ESP_NOW_MAX_DATA_LEN_V2
32static_assert(ESPNOW_MAX_DATA_LEN <= ESP_NOW_MAX_DATA_LEN_V2,
33 "espnow max_payload_size cannot exceed the ESP-NOW v2 frame limit");
34#else
35static_assert(ESPNOW_MAX_DATA_LEN <= ESP_NOW_MAX_DATA_LEN,
36 "espnow max_payload_size beyond 250 bytes requires an ESP-IDF with ESP-NOW v2 support (5.4+)");
37#endif
38
40 int8_t rssi; // Received Signal Strength Indicator (RSSI) of packet, unit: dBm
41 uint32_t timestamp; // Timestamp in microseconds when the packet was received, precise only if modem sleep or
42 // light sleep is not enabled
43};
44
46 uint8_t src_addr[ESP_NOW_ETH_ALEN];
47 uint8_t des_addr[ESP_NOW_ETH_ALEN];
48 wifi_pkt_rx_ctrl_t *rx_ctrl;
49};
50
51using send_callback_t = std::function<void(esp_err_t)>;
52
54 public:
55 // NOLINTNEXTLINE(readability-identifier-naming)
56 enum esp_now_packet_type_t : uint8_t {
59 };
60
61 // Constructor for received data
62 ESPNowPacket(const esp_now_recv_info_t *info, const uint8_t *data, int size) {
63 this->init_received_data_(info, data, size);
64 };
65
66#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
67 // Constructor for sent data
68 ESPNowPacket(const esp_now_send_info_t *info, esp_now_send_status_t status) {
69 this->init_sent_data_(info->src_addr, status);
70 }
71#else
72 // Constructor for sent data
73 ESPNowPacket(const uint8_t *mac_addr, esp_now_send_status_t status) { this->init_sent_data_(mac_addr, status); }
74#endif
75
76 // Default constructor for pre-allocation in pool
78
79 void release() {}
80
81 void load_received_data(const esp_now_recv_info_t *info, const uint8_t *data, int size) {
82 this->type_ = RECEIVED;
83 this->init_received_data_(info, data, size);
84 }
85
86 void load_sent_data(const uint8_t *mac_addr, esp_now_send_status_t status) {
87 this->type_ = SENT;
88 this->init_sent_data_(mac_addr, status);
89 }
90
91 // Disable copy to prevent double-delete
92 ESPNowPacket(const ESPNowPacket &) = delete;
94
95 union {
96 // NOLINTNEXTLINE(readability-identifier-naming)
97 struct received_data {
98 ESPNowRecvInfo info; // Information about the received packet
99 uint8_t data[ESPNOW_MAX_DATA_LEN]; // Data received in the packet
100 uint16_t size; // Size of the received data
101 WifiPacketRxControl rx_ctrl; // Status of the received packet
103
104 // NOLINTNEXTLINE(readability-identifier-naming)
105 struct sent_data {
106 uint8_t address[ESP_NOW_ETH_ALEN];
107 esp_now_send_status_t status;
110
112
113 esp_now_packet_type_t type() const { return this->type_; }
114 const ESPNowRecvInfo &get_receive_info() const { return this->packet_.receive.info; }
115
116 private:
117 void init_received_data_(const esp_now_recv_info_t *info, const uint8_t *data, int size) {
118 memcpy(this->packet_.receive.info.src_addr, info->src_addr, ESP_NOW_ETH_ALEN);
119 memcpy(this->packet_.receive.info.des_addr, info->des_addr, ESP_NOW_ETH_ALEN);
120 memcpy(this->packet_.receive.data, data, size);
121 this->packet_.receive.size = size;
122
123 this->packet_.receive.rx_ctrl.rssi = info->rx_ctrl->rssi;
124 this->packet_.receive.rx_ctrl.timestamp = info->rx_ctrl->timestamp;
125
126 this->packet_.receive.info.rx_ctrl = reinterpret_cast<wifi_pkt_rx_ctrl_t *>(&this->packet_.receive.rx_ctrl);
127 }
128
129 void init_sent_data_(const uint8_t *mac_addr, esp_now_send_status_t status) {
130 memcpy(this->packet_.sent.address, mac_addr, ESP_NOW_ETH_ALEN);
131 this->packet_.sent.status = status;
132 }
133};
134
136 public:
137 ESPNowSendPacket(const uint8_t *peer_address, const uint8_t *payload, size_t size, const send_callback_t &&callback)
138 : callback_(callback) {
139 this->init_data_(peer_address, payload, size);
140 }
141 ESPNowSendPacket(const uint8_t *peer_address, const uint8_t *payload, size_t size) {
142 this->init_data_(peer_address, payload, size);
143 }
144
145 // Default constructor for pre-allocation in pool
147
148 void release() {}
149
150 // Disable copy to prevent double-delete
153
154 void load_data(const uint8_t *peer_address, const uint8_t *payload, size_t size, const send_callback_t &callback) {
155 this->init_data_(peer_address, payload, size);
156 this->callback_ = callback;
157 }
158
159 void load_data(const uint8_t *peer_address, const uint8_t *payload, size_t size) {
160 this->init_data_(peer_address, payload, size);
161 this->callback_ = nullptr; // Reset callback
162 }
163
164 uint8_t address_[ESP_NOW_ETH_ALEN]{0}; // MAC address of the peer to send the packet to
165 uint8_t data_[ESPNOW_MAX_DATA_LEN]{0}; // Data to send
166 uint16_t size_{0}; // Size of the data to send, must be <= ESPNOW_MAX_DATA_LEN
167 send_callback_t callback_{nullptr}; // Callback to call when the send operation is complete
168
169 private:
170 void init_data_(const uint8_t *peer_address, const uint8_t *payload, size_t size) {
171 memcpy(this->address_, peer_address, ESP_NOW_ETH_ALEN);
172 if (size > ESPNOW_MAX_DATA_LEN) {
173 this->size_ = 0;
174 return;
175 }
176 this->size_ = size;
177 memcpy(this->data_, payload, this->size_);
178 }
179};
180
181} // namespace esphome::espnow
182
183#endif // USE_ESP32
uint8_t address
Definition bl0906.h:4
ESPNowPacket(const ESPNowPacket &)=delete
uint8_t data[ESPNOW_MAX_DATA_LEN]
esp_now_send_status_t status
void load_sent_data(const uint8_t *mac_addr, esp_now_send_status_t status)
ESPNowPacket & operator=(const ESPNowPacket &)=delete
struct esphome::espnow::ESPNowPacket::@85::received_data receive
void load_received_data(const esp_now_recv_info_t *info, const uint8_t *data, int size)
union esphome::espnow::ESPNowPacket::@85 packet_
esp_now_packet_type_t type_
esp_now_packet_type_t type() const
const ESPNowRecvInfo & get_receive_info() const
ESPNowPacket(const uint8_t *mac_addr, esp_now_send_status_t status)
WifiPacketRxControl rx_ctrl
ESPNowPacket(const esp_now_send_info_t *info, esp_now_send_status_t status)
ESPNowPacket(const esp_now_recv_info_t *info, const uint8_t *data, int size)
struct esphome::espnow::ESPNowPacket::@85::sent_data sent
void load_data(const uint8_t *peer_address, const uint8_t *payload, size_t size, const send_callback_t &callback)
uint8_t data_[ESPNOW_MAX_DATA_LEN]
ESPNowSendPacket & operator=(const ESPNowSendPacket &)=delete
void load_data(const uint8_t *peer_address, const uint8_t *payload, size_t size)
ESPNowSendPacket(const ESPNowSendPacket &)=delete
uint8_t address_[ESP_NOW_ETH_ALEN]
ESPNowSendPacket(const uint8_t *peer_address, const uint8_t *payload, size_t size, const send_callback_t &&callback)
ESPNowSendPacket(const uint8_t *peer_address, const uint8_t *payload, size_t size)
std::function< void(esp_err_t)> send_callback_t
uint16_t size
Definition helpers.cpp:25
static void uint32_t
wifi_pkt_rx_ctrl_t * rx_ctrl
Rx control info of ESPNOW packet.
uint8_t des_addr[ESP_NOW_ETH_ALEN]
Destination address of ESPNOW packet.
uint8_t src_addr[ESP_NOW_ETH_ALEN]
Source address of ESPNOW packet.