ESPHome 2026.10.2
Loading...
Searching...
No Matches
espnow_component.h
Go to the documentation of this file.
1#pragma once
2
6
7#ifdef USE_ESP32
8
11#include "espnow_packet.h"
12
13#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
15#endif
16
17#include <esp_idf_version.h>
18
19#include <esp_mac.h>
20#include <esp_now.h>
21
22#include <array>
23#include <map>
24#include <memory>
25#include <string>
26#include <vector>
27
28namespace esphome::espnow {
29
30// Maximum size of the ESPNow event queue - must be power of 2 for lock-free queue
31static constexpr size_t MAX_ESP_NOW_SEND_QUEUE_SIZE = 16;
32static constexpr size_t MAX_ESP_NOW_RECEIVE_QUEUE_SIZE = 16;
33
34using peer_address_t = std::array<uint8_t, ESP_NOW_ETH_ALEN>;
35
36enum class ESPNowTriggers : uint8_t {
37 TRIGGER_NONE = 0,
38 ON_NEW_PEER = 1,
39 ON_RECEIVE = 2,
40 ON_BROADCAST = 3,
41 ON_SUCCEED = 10,
42 ON_FAILED = 11,
43};
44
45enum ESPNowState : uint8_t {
47 ESPNOW_STATE_OFF = 0,
49 ESPNOW_STATE_DISABLED,
51 ESPNOW_STATE_ENABLED,
52};
53
54struct ESPNowPeer {
55 uint8_t address[ESP_NOW_ETH_ALEN]; // MAC address of the peer
56
57 bool operator==(const ESPNowPeer &other) const { return memcmp(this->address, other.address, ESP_NOW_ETH_ALEN) == 0; }
58 bool operator==(const uint8_t *other) const { return memcmp(this->address, other, ESP_NOW_ETH_ALEN) == 0; }
59};
60
63class ESPNowUnknownPeerHandler {
64 public:
70 virtual bool on_unknown_peer(const ESPNowRecvInfo &info, const uint8_t *data, uint16_t size) = 0;
71};
72
75class ESPNowReceivedPacketHandler {
76 public:
82 virtual bool on_receive(const ESPNowRecvInfo &info, const uint8_t *data, uint16_t size) = 0;
83};
86class ESPNowBroadcastHandler {
87 public:
93 virtual bool on_broadcast(const ESPNowRecvInfo &info, const uint8_t *data, uint16_t size) = 0;
94};
95
96#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
97class ESPNowComponent final : public Component, public wifi::WiFiConnectStateListener {
98#else
99class ESPNowComponent final : public Component {
100#endif
101 public:
102 ESPNowComponent();
103 void setup() override;
104 void loop() override;
105 void dump_config() override;
106
107 float get_setup_priority() const override { return setup_priority::LATE; }
108
109 // Add a peer to the internal list of peers
110 void add_peer(peer_address_t address) {
111 ESPNowPeer peer;
112 memcpy(peer.address, address.data(), ESP_NOW_ETH_ALEN);
113 this->peers_.push_back(peer);
114 }
115 // Add a peer with the esp_now api and add to the internal list if doesnt exist already
116 esp_err_t add_peer(const uint8_t *peer);
117 // Remove a peer with the esp_now api and remove from the internal list if exists
118 esp_err_t del_peer(const uint8_t *peer);
119 // Action entry points; distinct names because add_peer(peer_address_t) only fills the boot-time list
120 esp_err_t add_peer_from_action(const peer_address_t &address) { return this->add_peer(address.data()); }
121 esp_err_t del_peer_from_action(const peer_address_t &address) { return this->del_peer(address.data()); }
122
123 void set_wifi_channel(uint8_t channel) { this->wifi_channel_ = channel; }
124 void apply_wifi_channel();
125 void set_channel_from_action(uint8_t channel) {
126 if (this->is_wifi_enabled())
127 return;
128 this->set_wifi_channel(channel);
129 this->apply_wifi_channel();
130 }
131 uint8_t get_wifi_channel();
132
133 void set_auto_add_peer(bool value) { this->auto_add_peer_ = value; }
134
135#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
136 // WiFiConnectStateListener interface: refresh the cached channel after each (re)connect
137 void on_wifi_connect_state(StringRef ssid, std::span<const uint8_t, 6> bssid) override;
138#endif
139
140 void enable();
141 void disable();
142 bool is_disabled() const { return this->state_ == ESPNOW_STATE_DISABLED; };
143 void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
144 bool is_wifi_enabled();
145
155 esp_err_t send(const uint8_t *peer_address, const std::vector<uint8_t> &payload,
156 const send_callback_t &callback = nullptr) {
157 return this->send(peer_address, payload.data(), payload.size(), callback);
158 }
159 esp_err_t send(const uint8_t *peer_address, const uint8_t *payload, size_t size,
160 const send_callback_t &callback = nullptr);
161
162 void register_receive_handler(ESPNowReceivedPacketHandler *handler) { this->receive_handlers_.push_back(handler); }
163 void register_unknown_peer_handler(ESPNowUnknownPeerHandler *handler) {
164 this->unknown_peer_handlers_.push_back(handler);
165 }
166 void register_broadcast_handler(ESPNowBroadcastHandler *handler) { this->broadcast_handlers_.push_back(handler); }
167
168 protected:
169 friend void on_data_received(const esp_now_recv_info_t *info, const uint8_t *data, int size);
170#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
171 friend void on_send_report(const esp_now_send_info_t *info, esp_now_send_status_t status);
172#else
173 friend void on_send_report(const uint8_t *mac_addr, esp_now_send_status_t status);
174#endif
175
176 void enable_();
177 void send_();
178
179 std::vector<ESPNowUnknownPeerHandler *> unknown_peer_handlers_;
180 std::vector<ESPNowReceivedPacketHandler *> receive_handlers_;
181 std::vector<ESPNowBroadcastHandler *> broadcast_handlers_;
182
183 std::vector<ESPNowPeer> peers_{};
184
185 uint8_t own_address_[ESP_NOW_ETH_ALEN]{0};
186 LockFreeQueue<ESPNowPacket, MAX_ESP_NOW_RECEIVE_QUEUE_SIZE> receive_packet_queue_{};
187 // Pool sized to queue capacity (SIZE-1) because LockFreeQueue<T,N> is a ring
188 // buffer that holds N-1 elements. This guarantees allocate() returns nullptr
189 // before push() can fail, preventing a pool slot leak.
190 EventPool<ESPNowPacket, MAX_ESP_NOW_RECEIVE_QUEUE_SIZE - 1> receive_packet_pool_{};
191
192 LockFreeQueue<ESPNowSendPacket, MAX_ESP_NOW_SEND_QUEUE_SIZE> send_packet_queue_{};
193 // Pool sized to queue capacity (SIZE-1) — see receive_packet_pool_ comment.
194 EventPool<ESPNowSendPacket, MAX_ESP_NOW_SEND_QUEUE_SIZE - 1> send_packet_pool_{};
195 ESPNowSendPacket *current_send_packet_{nullptr}; // Currently sending packet, nullptr if none
196
197 uint8_t wifi_channel_{0};
198 ESPNowState state_{ESPNOW_STATE_OFF};
199
200 bool auto_add_peer_{false};
201 bool enable_on_boot_{true};
202};
203
204extern ESPNowComponent *global_esp_now; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
205
206} // namespace esphome::espnow
207
208#endif // USE_ESP32
uint8_t address
Definition bl0906.h:4
uint8_t status
Definition bl0942.h:8
void setup()
void loop()
void on_send_report(const esp_now_send_info_t *info, esp_now_send_status_t status) void on_send_report(const uint8_t *mac_addr
void on_data_received(const esp_now_recv_info_t *info, const uint8_t *data, int size)
ESPNowComponent * global_esp_now
bool operator==(const GainTimePair &lhs, const GainTimePair &rhs)
Definition ltr501.cpp:21
bool is_disabled()
Return whether the network is disabled: every configured interface with a disable() lifecycle (modem,...
Definition util.cpp:12