ndn-embeds 0.1.0
Lightweight NDN protocol stack for embedded systems
Loading...
Searching...
No Matches
espnow_face.cpp
Go to the documentation of this file.
1
6#include "espnow_face.hpp"
7
8#include <cstring>
9
10#include "esp_log.h"
11#include "esp_wifi.h"
12
13namespace {
14const char* TAG = "espnow_face";
15} // namespace
16
17namespace ndn {
18
19// Static instance
20EspNowFace* EspNowFace::instance_ = nullptr;
21
22EspNowFace::EspNowFace(FaceId faceId) : faceId_(faceId) {
23 // Initialize peer array
24 for (auto& peer : peers_) {
25 peer.inUse = false;
26 }
27
28 // Initialize receive queue
29 for (auto& pkt : rxQueue_) {
30 pkt.valid = false;
31 }
32}
33
37
39 if (running_) {
40 return Error::Success;
41 }
42
43 // Set static instance
44 instance_ = this;
45
46 // Initialize ESP-NOW
47 esp_err_t ret = esp_now_init();
48 if (ret != ESP_OK) {
49 ESP_LOGE(TAG, "esp_now_init failed: %s", esp_err_to_name(ret));
50 return Error::SendFailed;
51 }
52
53 // Register callbacks
54 ret = esp_now_register_recv_cb(onReceive);
55 if (ret != ESP_OK) {
56 ESP_LOGE(TAG, "esp_now_register_recv_cb failed: %s", esp_err_to_name(ret));
57 esp_now_deinit();
58 return Error::SendFailed;
59 }
60
61 ret = esp_now_register_send_cb(onSend);
62 if (ret != ESP_OK) {
63 ESP_LOGE(TAG, "esp_now_register_send_cb failed: %s", esp_err_to_name(ret));
64 esp_now_deinit();
65 return Error::SendFailed;
66 }
67
68 // Add broadcast peer
69 esp_now_peer_info_t broadcastPeer = {};
70 std::memcpy(broadcastPeer.peer_addr, BROADCAST_MAC, 6);
71 broadcastPeer.channel = 0; // Current channel
72 broadcastPeer.ifidx = WIFI_IF_STA;
73 broadcastPeer.encrypt = false;
74
75 ret = esp_now_add_peer(&broadcastPeer);
76 if (ret != ESP_OK && ret != ESP_ERR_ESPNOW_EXIST) {
77 ESP_LOGE(TAG, "esp_now_add_peer (broadcast) failed: %s", esp_err_to_name(ret));
78 esp_now_deinit();
79 return Error::SendFailed;
80 }
81
82 running_ = true;
83 ESP_LOGI(TAG, "ESP-NOW Face started (id=%u)", faceId_);
84
85 return Error::Success;
86}
87
89 if (!running_) {
90 return;
91 }
92
93 esp_now_unregister_recv_cb();
94 esp_now_unregister_send_cb();
95 esp_now_deinit();
96
97 running_ = false;
98 instance_ = nullptr;
99
100 ESP_LOGI(TAG, "ESP-NOW Face stopped");
101}
102
103Error EspNowFace::send(const uint8_t* data, size_t len) {
104 // Default is broadcast
105 return broadcast(data, len);
106}
107
108Error EspNowFace::sendTo(FaceId destFace, const uint8_t* data, size_t len) {
109 if (!running_) {
110 return Error::SendFailed;
111 }
112
113 if (len > ESPNOW_MAX_PAYLOAD) {
114 return Error::BufferTooSmall;
115 }
116
117 // Look up MAC address from FaceId
118 const PeerInfo* peer = findPeer(destFace);
119 if (peer == nullptr) {
120 ESP_LOGW(TAG, "Peer not found for faceId=%u", destFace);
121 return Error::NotFound;
122 }
123
124 // Add peer if not registered
125 if (!esp_now_is_peer_exist(peer->mac)) {
126 esp_now_peer_info_t peerInfo = {};
127 std::memcpy(peerInfo.peer_addr, peer->mac, 6);
128 peerInfo.channel = 0;
129 peerInfo.ifidx = WIFI_IF_STA;
130 peerInfo.encrypt = false;
131
132 esp_err_t ret = esp_now_add_peer(&peerInfo);
133 if (ret != ESP_OK && ret != ESP_ERR_ESPNOW_EXIST) {
134 ESP_LOGE(TAG, "esp_now_add_peer failed: %s", esp_err_to_name(ret));
135 return Error::SendFailed;
136 }
137 }
138
139 esp_err_t ret = esp_now_send(peer->mac, data, len);
140 if (ret != ESP_OK) {
141 ESP_LOGE(TAG, "esp_now_send failed: %s", esp_err_to_name(ret));
142 return Error::SendFailed;
143 }
144
145 return Error::Success;
146}
147
148Error EspNowFace::broadcast(const uint8_t* data, size_t len) {
149 if (!running_) {
150 return Error::SendFailed;
151 }
152
153 if (len > ESPNOW_MAX_PAYLOAD) {
154 return Error::BufferTooSmall;
155 }
156
157 esp_err_t ret = esp_now_send(BROADCAST_MAC, data, len);
158 if (ret != ESP_OK) {
159 ESP_LOGE(TAG, "esp_now_send (broadcast) failed: %s", esp_err_to_name(ret));
160 return Error::SendFailed;
161 }
162
163 return Error::Success;
164}
165
166FaceId EspNowFace::addPeer(const uint8_t* mac) {
167 // Search for existing peer
168 PeerInfo* existing = findPeerByMac(mac);
169 if (existing != nullptr) {
170 existing->lastSeen = currentTimeMs();
171 return existing->faceId;
172 }
173
174 // Search for empty slot
175 for (auto& peer : peers_) {
176 if (!peer.inUse) {
177 std::memcpy(peer.mac, mac, 6);
178 peer.faceId = macToFaceId(mac);
179 peer.inUse = true;
180 peer.lastSeen = currentTimeMs();
181
182 // Register as ESP-NOW peer
183 if (!esp_now_is_peer_exist(mac)) {
184 esp_now_peer_info_t peerInfo = {};
185 std::memcpy(peerInfo.peer_addr, mac, 6);
186 peerInfo.channel = 0;
187 peerInfo.ifidx = WIFI_IF_STA;
188 peerInfo.encrypt = false;
189 esp_now_add_peer(&peerInfo);
190 }
191
192 ESP_LOGI(TAG, "Peer added: %02x:%02x:%02x:%02x:%02x:%02x -> faceId=%u", mac[0], mac[1],
193 mac[2], mac[3], mac[4], mac[5], peer.faceId);
194
195 return peer.faceId;
196 }
197 }
198
199 ESP_LOGW(TAG, "Peer table full");
200 return FACE_ID_INVALID;
201}
202
204 PeerInfo* peer = findPeer(faceId);
205 if (peer != nullptr) {
206 esp_now_del_peer(peer->mac);
207 peer->inUse = false;
208 ESP_LOGI(TAG, "Peer removed: faceId=%u", faceId);
209 }
210}
211
212bool EspNowFace::getMacAddress(FaceId faceId, uint8_t* mac) const {
213 const PeerInfo* peer = findPeer(faceId);
214 if (peer != nullptr) {
215 std::memcpy(mac, peer->mac, 6);
216 return true;
217 }
218 return false;
219}
220
221size_t EspNowFace::peerCount() const {
222 size_t count = 0;
223 for (const auto& peer : peers_) {
224 if (peer.inUse) {
225 ++count;
226 }
227 }
228 return count;
229}
230
232 while (rxQueueHead_ != rxQueueTail_) {
233 RxPacket& pkt = rxQueue_[rxQueueHead_];
234 if (pkt.valid) {
235 handleReceive(pkt.srcMac, pkt.data, pkt.len);
236 pkt.valid = false;
237 }
238 rxQueueHead_ = (rxQueueHead_ + 1) % RX_QUEUE_SIZE;
239 }
240}
241
242void EspNowFace::onReceive(const esp_now_recv_info_t* info, const uint8_t* data, int len) {
243 if (instance_ == nullptr || len <= 0 || static_cast<size_t>(len) > ESPNOW_MAX_PAYLOAD) {
244 return;
245 }
246
247 // Add to receive queue (lightweight since in ISR context)
248 const size_t nextTail = (instance_->rxQueueTail_ + 1) % RX_QUEUE_SIZE;
249 if (nextTail != instance_->rxQueueHead_) {
250 RxPacket& pkt = instance_->rxQueue_[instance_->rxQueueTail_];
251 std::memcpy(pkt.srcMac, info->src_addr, 6);
252 std::memcpy(pkt.data, data, len);
253 pkt.len = static_cast<size_t>(len);
254 pkt.valid = true;
255 instance_->rxQueueTail_ = nextTail;
256 } else {
257 ESP_LOGW(TAG, "RX queue full, packet dropped");
258 }
259}
260
261void EspNowFace::onSend(const esp_now_send_info_t* info, esp_now_send_status_t status) {
262 if (status != ESP_NOW_SEND_SUCCESS && info != nullptr && info->des_addr != nullptr) {
263 const uint8_t* mac = info->des_addr;
264 ESP_LOGD(TAG, "Send failed to %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2],
265 mac[3], mac[4], mac[5]);
266 }
267}
268
269void EspNowFace::handleReceive(const uint8_t* srcMac, const uint8_t* data, size_t len) {
270 // MAC address filter check
271 if (macFilterEnabled_) {
272 bool allowed = false;
273 for (size_t i = 0; i < macFilterCount_; ++i) {
274 if (std::memcmp(srcMac, macFilters_[i], 6) == 0) {
275 allowed = true;
276 break;
277 }
278 }
279 if (!allowed) {
280 // Drop packets not matching the filter
281 ESP_LOGD(TAG, "Packet filtered: %02x:%02x:%02x:%02x:%02x:%02x", srcMac[0], srcMac[1],
282 srcMac[2], srcMac[3], srcMac[4], srcMac[5]);
283 return;
284 }
285 }
286
287 // Auto-register sender as peer
288 FaceId srcFaceId = addPeer(srcMac);
289 if (srcFaceId == FACE_ID_INVALID) {
290 srcFaceId = macToFaceId(srcMac); // Use temporary ID even when table is full
291 }
292
293 // Invoke callback
294 onPacketReceived(srcFaceId, data, len);
295}
296
297void EspNowFace::setMacFilter(const uint8_t* mac) {
298 if (mac == nullptr) {
300 } else {
301 setMacFilters(reinterpret_cast<const uint8_t(*)[6]>(mac), 1);
302 }
303}
304
305void EspNowFace::setMacFilters(const uint8_t macs[][6], size_t count) {
306 if (count == 0) {
308 return;
309 }
310
311 macFilterCount_ = (count > MAX_MAC_FILTERS) ? MAX_MAC_FILTERS : count;
312 for (size_t i = 0; i < macFilterCount_; ++i) {
313 std::memcpy(macFilters_[i], macs[i], 6);
314 }
315 macFilterEnabled_ = true;
316
317 ESP_LOGI(TAG, "MAC filter enabled: %zu addresses", macFilterCount_);
318 for (size_t i = 0; i < macFilterCount_; ++i) {
319 ESP_LOGI(TAG, " [%zu] %02x:%02x:%02x:%02x:%02x:%02x", i, macFilters_[i][0],
320 macFilters_[i][1], macFilters_[i][2], macFilters_[i][3], macFilters_[i][4],
321 macFilters_[i][5]);
322 }
323}
324
326 macFilterEnabled_ = false;
327 macFilterCount_ = 0;
328 ESP_LOGI(TAG, "MAC filter disabled");
329}
330
331PeerInfo* EspNowFace::findPeer(FaceId faceId) {
332 for (auto& peer : peers_) {
333 if (peer.inUse && peer.faceId == faceId) {
334 return &peer;
335 }
336 }
337 return nullptr;
338}
339
340PeerInfo* EspNowFace::findPeerByMac(const uint8_t* mac) {
341 for (auto& peer : peers_) {
342 if (peer.inUse && std::memcmp(peer.mac, mac, 6) == 0) {
343 return &peer;
344 }
345 }
346 return nullptr;
347}
348
349const PeerInfo* EspNowFace::findPeer(FaceId faceId) const {
350 for (const auto& peer : peers_) {
351 if (peer.inUse && peer.faceId == faceId) {
352 return &peer;
353 }
354 }
355 return nullptr;
356}
357
358} // namespace ndn
~EspNowFace() override
Destructor.
static constexpr size_t MAX_MAC_FILTERS
Maximum number of filter MACs.
void stop() override
Stop ESP-NOW.
void setMacFilters(const uint8_t macs[][6], size_t count)
Set multiple MAC address filters.
bool getMacAddress(FaceId faceId, uint8_t *mac) const
Get MAC address from FaceId.
void clearMacFilters()
Clear MAC address filters.
Error send(const uint8_t *data, size_t len) override
Default send (broadcast)
Error start() override
Initialize and start ESP-NOW.
void processReceiveQueue()
Process receive events.
size_t peerCount() const
Get number of registered peers.
void setMacFilter(const uint8_t *mac)
Set MAC address filter (single MAC)
Error sendTo(FaceId destFace, const uint8_t *data, size_t len) override
Unicast send to a specific Face.
EspNowFace(FaceId faceId=2)
Constructor.
void removePeer(FaceId faceId)
Remove a peer.
Error broadcast(const uint8_t *data, size_t len) override
Broadcast send.
FaceId addPeer(const uint8_t *mac)
Add a peer.
void onPacketReceived(FaceId faceId, const uint8_t *data, size_t len)
Internal handler for packet reception.
Definition face.hpp:144
TimeMs currentTimeMs()
Get current time (milliseconds)
Definition common.cpp:7
constexpr FaceId FACE_ID_INVALID
Invalid Face ID.
Definition common.hpp:99
uint16_t FaceId
Face identifier.
Definition common.hpp:96
Error
Error codes.
Definition common.hpp:24
ESP-NOW Face implementation.
constexpr uint8_t BROADCAST_MAC[6]
Broadcast MAC address.
FaceId macToFaceId(const uint8_t *mac)
Generate FaceId from MAC address.
constexpr size_t ESPNOW_MAX_PAYLOAD
ESP-NOW maximum payload size (v2.0)
Peer information.
uint8_t mac[6]
MAC address.
uint32_t lastSeen
Last received time (ms)
bool inUse
In-use flag.
FaceId faceId
Face ID.