arduino-audio-tools
Loading...
Searching...
No Matches
ESPNowStream.h
1#pragma once
2#include <WiFi.h>
3#include <esp_now.h>
4#include <esp_wifi.h>
5
6#include "AudioTools/Concurrency/RTOS.h"
7#include "AudioTools/CoreAudio/AudioBasic/StrView.h"
8#include "AudioTools/CoreAudio/BaseStream.h"
9
10// propose max data length based on esp-idf version
11#ifdef ESP_NOW_MAX_DATA_LEN_V2
12// 1470
13#define MY_ESP_NOW_MAX_LEN ESP_NOW_MAX_DATA_LEN_V2
14#else
15// 240
16#define MY_ESP_NOW_MAX_LEN ESP_NOW_MAX_DATA_LEN
17#endif
18
19// calculate buffer count: 96000 bytes should be enough for most use cases and
20// should not cause memory issues on the receiver side. With 240 bytes per
21// packet, this results in 400 packets.
22#define MY_ESP_NOW_BUFFER_SIZE (240 * 400)
23#define MY_ESP_NOW_BUFFER_COUNT (MY_ESP_NOW_BUFFER_SIZE / MY_ESP_NOW_MAX_LEN)
24
25namespace audio_tools {
26
27// forward declarations
28class ESPNowStream;
29static ESPNowStream* ESPNowStreamSelf = nullptr;
30static const char* BROADCAST_MAC_STR = "FF:FF:FF:FF:FF:FF";
31static const uint8_t BROADCAST_MAC[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
32
40 wifi_mode_t wifi_mode = WIFI_STA;
43 const char* mac_address = nullptr;
46 uint16_t buffer_size = MY_ESP_NOW_MAX_LEN;
49 uint16_t buffer_count = MY_ESP_NOW_BUFFER_COUNT;
51 int channel = 0;
53 const char* ssid = nullptr;
55 const char* password = nullptr;
56#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
59 uint32_t oui = 0;
60#endif
62 bool use_send_ack = true; // we wait for
65 // enable long_range mode: increases range but reduces throughput. Default:
66 // false
67 bool use_long_range = false;
69 int write_retry_count = 1; // -1 endless
70#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
72 void (*recveive_cb)(const esp_now_recv_info* info, const uint8_t* data,
73 int data_len) = nullptr;
74#else
76 void (*recveive_cb)(const uint8_t* mac_addr, const uint8_t* data,
77 int data_len) = nullptr;
78#endif
80 const char* primary_master_key = nullptr;
82 const char* local_master_key = nullptr;
84 wifi_phy_mode_t phymode = WIFI_PHY_MODE_11G;
86 wifi_phy_rate_t rate = WIFI_PHY_RATE_6M;
90 uint32_t ack_semaphore_timeout_ms = portMAX_DELAY;
93};
94
108class ESPNowStream : public BaseStream {
109 public:
110 ESPNowStream() { ESPNowStreamSelf = this; };
111
112 ~ESPNowStream() {
113 if (xSemaphore != nullptr) vSemaphoreDelete(xSemaphore);
114 }
115
116 ESPNowStreamConfig defaultConfig() {
117 ESPNowStreamConfig result;
118 return result;
119 }
120
122 const char* macAddress() {
123 static String mac_str = WiFi.macAddress();
124 return mac_str.c_str();
125 }
126
128 void setSendCallback(esp_now_send_cb_t cb) { send = cb; }
129
132 void setReceiveCallback(esp_now_recv_cb_t cb) { receive = cb; }
133
135 bool begin() { return begin(cfg); }
136
138 virtual bool begin(ESPNowStreamConfig cfg) {
139 this->cfg = cfg;
140 if (WiFi.getMode() == WIFI_MODE_NULL) {
141 WiFi.mode(cfg.wifi_mode);
142 } else {
143 cfg.wifi_mode = WiFi.getMode();
144 }
145
146 if (!setupMAC()) return false;
147
148 if (!setupWiFi()) return false;
149
150 WiFi.enableLongRange(cfg.use_long_range);
151
152#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
153 LOGI("Setting ESP-NEW rate");
154 if (esp_wifi_config_espnow_rate(getInterface(), cfg.rate) != ESP_OK) {
155 LOGW("Could not set rate");
156 }
157#endif
158 if (cfg.channel > 0) {
159 setChannel(cfg.channel);
160 }
161 Serial.print("mac: ");
162 Serial.println(WiFi.macAddress());
163 Serial.print("channel: ");
164 Serial.println(getChannel());
165 return setup();
166 }
167
169 virtual void end() {
170 if (is_init) {
171 if (esp_now_deinit() != ESP_OK) {
172 LOGE("esp_now_deinit");
173 }
174 if (buffer.size() > 0) buffer.resize(0);
175 is_init = false;
176 has_peers = false;
177 read_ready = false;
178 is_broadcast = false;
179 }
180 }
181
183 void setChannel(uint8_t ch) {
184 WiFi.setChannel(ch, WIFI_SECOND_CHAN_NONE);
185 cfg.channel = ch;
186 }
187
189 uint8_t getChannel() {
190 uint32_t ch = WiFi.channel();
191 return (uint8_t)ch ^ 0xff;
192 }
193
195 bool addPeer(esp_now_peer_info_t& peer) {
196 if (!is_init) {
197 LOGE("addPeer before begin");
198 return false;
199 }
200 if (memcmp(BROADCAST_MAC, peer.peer_addr, 6) == 0) {
201 LOGI("Using broadcast");
202 is_broadcast = true;
203 }
204 esp_err_t result = esp_now_add_peer(&peer);
205 if (result == ESP_OK) {
206 LOGI("addPeer: %s", mac2str(peer.peer_addr));
207#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
208 esp_now_rate_config_t rate_config = {.phymode = cfg.phymode,
209 .rate = cfg.rate,
210 .ersu = false,
211 .dcm = false};
212 result = esp_now_set_peer_rate_config(peer.peer_addr, &rate_config);
213 if (result != ESP_OK) {
214 LOGW("Could not set the ESP-NOW PHY rate (%d) %s.", result,
215 esp_err_to_name(result));
216 }
217#endif
218 has_peers = result == ESP_OK;
219 } else {
220 LOGE("addPeer: %d", result);
221 }
222 return result == ESP_OK;
223 }
224
226 bool addPeer(const uint8_t* address) {
227 esp_now_peer_info_t peer;
228 memcpy(peer.peer_addr, address, ESP_NOW_ETH_ALEN);
229
230 peer.channel = cfg.channel;
231 peer.ifidx = getInterface();
232 peer.encrypt = false;
233
234 if (isEncrypted()) {
235 peer.encrypt = true;
236 strncpy((char*)peer.lmk, cfg.local_master_key, 16);
237 }
238 return addPeer(peer);
239 }
240
242 bool addPeer(const char* address) {
243 if (StrView(address).equals(cfg.mac_address)) {
244 LOGW("Did not add own address as peer");
245 return true;
246 }
247
248 uint8_t mac[] = {0, 0, 0, 0, 0, 0};
249 if (!str2mac(address, (uint8_t*)&mac)) {
250 LOGE("addPeer - Invalid address: %s", address);
251 return false;
252 }
253 return addPeer((const uint8_t*)&mac);
254 }
255
257 template <size_t size>
258 bool addPeers(const char* (&array)[size]) {
259 bool result = true;
260 for (int j = 0; j < size; j++) {
261 const char* peer = array[j];
262 if (peer != nullptr) {
263 if (!addPeer(peer)) {
264 result = false;
265 }
266 }
267 }
268 return result;
269 }
270
272 template <size_t N>
273 bool addPeers(const uint8_t (&array)[N][6]) {
274 bool result = true;
275 for (int j = 0; j < N; j++) {
276 const uint8_t* peer = array[j];
277 if (peer != nullptr) {
278 if (!addPeer(peer)) {
279 result = false;
280 }
281 }
282 }
283 return result;
284 }
285
288 bool addBroadcastPeer() { return addPeer(BROADCAST_MAC); }
289
291 bool clearPeers() {
292 esp_now_peer_info_t peer;
293 uint8_t breakout_counter = 0;
294 while ((esp_now_fetch_peer(true, &peer) == ESP_OK) &&
295 (breakout_counter < ESP_NOW_MAX_TOTAL_PEER_NUM + 1)) {
296 esp_now_del_peer(peer.peer_addr);
297 breakout_counter++;
298 }
299
300 if (breakout_counter == ESP_NOW_MAX_TOTAL_PEER_NUM + 1) {
301 LOGE("Not all Peers seems to be removed.");
302 }
303 // return true when all peers are removed.
304 return breakout_counter <= ESP_NOW_MAX_TOTAL_PEER_NUM;
305 }
306
308 size_t write(const uint8_t* data, size_t len) override {
309 // nullptr means send to all registered peers
310 return write((const uint8_t*)nullptr, data, len);
311 }
312
314 size_t write(const char* peer, const uint8_t* data, size_t len) {
315 uint8_t mac[6];
316 if (!str2mac(peer, mac)) {
317 LOGE("write: invalid mac address %s", peer);
318 return 0;
319 }
320 return write(mac, data, len);
321 }
322
324 size_t write(const uint8_t* peer, const uint8_t* data, size_t len) {
325 // initialization: setup semaphore
326 setupSemaphore();
327
328 // initialization: if no peers registered and peer is nullptr, add broadcast
329 if (!has_peers && peer == nullptr) {
331 }
332
333 size_t total_sent = 0;
334 size_t remaining = len;
335
336 while (remaining > 0) {
337 size_t chunk_size = min(remaining, (size_t)MY_ESP_NOW_MAX_LEN);
338 int retry_count = 0;
339
340 bool success =
341 sendPacket(data + total_sent, chunk_size, retry_count, peer);
342
343 if (success) {
344 // Chunk sent successfully
345 total_sent += chunk_size;
346 remaining -= chunk_size;
347 } else {
348 // Max retries exceeded for this chunk
349 LOGE(
350 "write: failed to send chunk after %d attempts (sent %zu/%zu "
351 "bytes)",
352 retry_count, total_sent, len);
353 // Return bytes successfully sent so far (may be 0 if first chunk
354 // failed)
355 return total_sent;
356 }
357 }
358 return total_sent;
359 }
360
362 size_t readBytes(uint8_t* data, size_t len) override {
363 if (!read_ready) return 0;
364 if (buffer.size() == 0) return 0;
365 return buffer.readArray(data, len);
366 }
367
368 int available() override {
369 if (!buffer) return 0;
370 if (!read_ready) return 0;
371 return buffer.size() == 0 ? 0 : buffer.available();
372 }
373
374 int availableForWrite() override {
375 if (!buffer) return 0;
376 return cfg.use_send_ack ? available_to_write : cfg.buffer_size;
377 }
378
381 int size = buffer.size();
382 // prevent div by 0
383 if (size == 0) return 0.0;
384 // calculate percent
385 return 100.0 * buffer.available() / size;
386 }
387
389 BufferRTOS<uint8_t>& getBuffer() { return buffer; }
390
392 uint32_t getLastIoSuccessTime() const { return last_io_success_time; }
393
394 protected:
396 BufferRTOS<uint8_t> buffer{0};
397 esp_now_recv_cb_t receive = default_recv_cb;
398 esp_now_send_cb_t send = default_send_cb;
399 volatile size_t available_to_write = 0;
400 volatile bool last_send_success = true;
401 bool is_init = false;
402 SemaphoreHandle_t xSemaphore = nullptr;
403 bool has_peers = false;
404 bool read_ready = false;
405 bool is_broadcast = false;
406 uint32_t last_io_success_time = 0;
407
408 bool setupMAC() {
409 // set mac address
410 if (cfg.mac_address != nullptr) {
411 LOGI("setting mac %s", cfg.mac_address);
412 byte mac[ESP_NOW_KEY_LEN];
413 str2mac(cfg.mac_address, mac);
414 if (esp_wifi_set_mac((wifi_interface_t)getInterface(), mac) != ESP_OK) {
415 LOGE("Could not set mac address");
416 return false;
417 }
418
419 // On some boards calling macAddress to early leads to a race condition.
421
422 // checking if address has been updated
423 const char* addr = macAddress();
424 if (strcmp(addr, cfg.mac_address) != 0) {
425 LOGE("Wrong mac address: %s", addr);
426 return false;
427 }
428 }
429 return true;
430 }
431
432 bool setupWiFi() {
433 if (WiFi.status() != WL_CONNECTED) {
434 // start only when not connected and we have ssid and password
435 if (cfg.ssid != nullptr && cfg.password != nullptr) {
436 LOGI("Logging into WiFi: %s", cfg.ssid);
437 WiFi.begin(cfg.ssid, cfg.password);
438 while (WiFi.status() != WL_CONNECTED) {
439 Serial.print('.');
440 delay(1000);
441 }
442 }
443 Serial.println();
444 }
445
446 // in AP mode we neeed to be logged in!
447 if (WiFi.getMode() == WIFI_AP && WiFi.status() != WL_CONNECTED) {
448 LOGE("You did not start Wifi or did not provide ssid and password");
449 return false;
450 }
451
452 return true;
453 }
454
455 inline void setupSemaphore() {
456 // use semaphore for confirmations
457 if (cfg.use_send_ack && xSemaphore == nullptr) {
458 xSemaphore = xSemaphoreCreateBinary();
459 xSemaphoreGive(xSemaphore);
460 }
461 }
462
463 inline void setupReceiveBuffer() {
464 // setup receive buffer
465 if (!buffer) {
466 LOGI("setupReceiveBuffer: %d", cfg.buffer_size * cfg.buffer_count);
467 buffer.resize(cfg.buffer_size * cfg.buffer_count);
468 }
469 }
470
471 inline void resetAvailableToWrite() {
472 if (cfg.use_send_ack) {
473 available_to_write = 0;
474 }
475 }
476
478 virtual bool sendPacket(const uint8_t* data, size_t len, int& retry_count,
479 const uint8_t* destination = nullptr) {
480 TRACED();
481 const uint8_t* target = destination;
482 if (target == nullptr && is_broadcast) {
483 target = BROADCAST_MAC;
484 }
485
486 while (true) {
487 resetAvailableToWrite();
488
489 // Wait for previous send to complete (if using ACKs)
490 if (cfg.use_send_ack) {
491 TickType_t ticks = (cfg.ack_semaphore_timeout_ms == portMAX_DELAY)
492 ? portMAX_DELAY
493 : pdMS_TO_TICKS(cfg.ack_semaphore_timeout_ms);
494 if (xSemaphoreTake(xSemaphore, ticks) != pdTRUE) {
495 // Timeout waiting for previous send - check retry limit BEFORE
496 // incrementing
497 if (cfg.write_retry_count >= 0 &&
498 retry_count >= cfg.write_retry_count) {
499 LOGE("Timeout waiting for ACK semaphore after %d retries",
500 retry_count);
501 return false;
502 }
503 retry_count++;
504 LOGW("ACK semaphore timeout (attempt %d)", retry_count);
506 continue;
507 }
508 }
509
510 // Try to queue the packet
511 esp_err_t rc = esp_now_send(target, data, len);
512
513 if (rc == ESP_OK) {
514 // Packet queued - wait for transmission result
515 if (handleTransmissionResult(retry_count)) {
516 return true; // Success
517 }
518 // Transmission failed - check if we've exceeded the limit
519 // handleTransmissionResult returns false both when limit is reached
520 // and when we should retry, so check the limit here
521 if (cfg.write_retry_count >= 0 &&
522 retry_count >= cfg.write_retry_count) {
523 return false; // Give up - limit reached
524 }
525 // Continue to retry
526 } else {
527 // Failed to queue - callback will NOT be called
528 if (cfg.use_send_ack) {
529 xSemaphoreGive(xSemaphore); // Give back semaphore
530 }
531
532 // Check limit BEFORE incrementing
533 if (cfg.write_retry_count >= 0 &&
534 retry_count >= cfg.write_retry_count) {
535 LOGE("esp_now_send queue error (rc=%d/0x%04X) after %d retries", rc,
536 rc, retry_count);
537 return false;
538 }
539
540 retry_count++;
541 LOGW("esp_now_send failed (rc=%d/0x%04X) - retrying (attempt %d)", rc,
542 rc, retry_count);
544 }
545 }
546 }
547
549 bool handleTransmissionResult(int& retry_count) {
550 TRACED();
551 if (cfg.use_send_ack) {
552 // Wait for callback to signal result
553 TickType_t ticks = (cfg.ack_semaphore_timeout_ms == portMAX_DELAY)
554 ? portMAX_DELAY
555 : pdMS_TO_TICKS(cfg.ack_semaphore_timeout_ms);
556 if (xSemaphoreTake(xSemaphore, ticks) != pdTRUE) {
557 // Callback never came - check limit BEFORE incrementing
558 if (cfg.write_retry_count >= 0 &&
559 retry_count >= cfg.write_retry_count) {
560 LOGE("Transmission callback timeout after %d retries", retry_count);
561 return false;
562 }
563 retry_count++;
564 LOGW("Transmission callback timeout (attempt %d)", retry_count);
566 return false; // Retry
567 }
568
569 // Got callback - check result
570 if (last_send_success) {
571 xSemaphoreGive(xSemaphore); // Release for next send
572 return true;
573 } else {
574 xSemaphoreGive(xSemaphore); // Release for retry
575
576 // Check limit BEFORE incrementing
577 if (cfg.write_retry_count >= 0 &&
578 retry_count >= cfg.write_retry_count) {
579 LOGE("Transmission failed after %d retries", retry_count);
580 return false;
581 }
582
583 retry_count++;
584 LOGI("Transmission failed - retrying (attempt %d)", retry_count);
586 return false; // Retry
587 }
588 }
589 return true; // No ACK mode - assume success
590 }
591
593 bool handleQueueError(esp_err_t rc, int& retry_count) {
594 TRACED();
595 // esp_now_send failed to queue - callback will NOT be called
596 // Give back the semaphore we took earlier
597 if (cfg.use_send_ack) {
598 xSemaphoreGive(xSemaphore);
599 }
600
601 retry_count++;
602 LOGW("esp_now_send failed to queue (rc=%d/0x%04X) - retrying (attempt %d)",
603 rc, rc, retry_count);
604
605 if (cfg.write_retry_count >= 0 && retry_count > cfg.write_retry_count) {
606 LOGE("Send queue error after %d retries", retry_count);
607 return false;
608 }
609
611 return true; // Continue retrying
612 }
613
614 bool isEncrypted() {
615 return cfg.primary_master_key != nullptr && cfg.local_master_key != nullptr;
616 }
617
618 wifi_interface_t getInterface() {
619 // define wifi_interface_t
620 wifi_interface_t result;
621 switch (cfg.wifi_mode) {
622 case WIFI_STA:
623 result = (wifi_interface_t)ESP_IF_WIFI_STA;
624 break;
625 case WIFI_AP:
626 result = (wifi_interface_t)ESP_IF_WIFI_AP;
627 break;
628 default:
629 result = (wifi_interface_t)0;
630 break;
631 }
632 return result;
633 }
634
636 bool setup() {
637 esp_err_t result = esp_now_init();
638 if (result == ESP_OK) {
639 LOGI("esp_now_init: %s", macAddress());
640 } else {
641 LOGE("esp_now_init: %d", result);
642 }
643
644#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
645 if (cfg.oui) {
646 esp_now_set_user_oui((uint8_t*)cfg.oui);
647 }
648#endif
649
650 // encryption is optional
651 if (isEncrypted()) {
652 esp_now_set_pmk((uint8_t*)cfg.primary_master_key);
653 }
654
655 if (cfg.recveive_cb != nullptr) {
656 esp_now_register_recv_cb(cfg.recveive_cb);
657 } else {
658 esp_now_register_recv_cb(receive);
659 }
660 if (cfg.use_send_ack) {
661 esp_now_register_send_cb(send);
662 }
663 available_to_write = cfg.buffer_size;
664 is_init = result == ESP_OK;
665 return is_init;
666 }
667
668 bool str2mac(const char* mac, uint8_t* values) {
669 sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &values[0], &values[1],
670 &values[2], &values[3], &values[4], &values[5]);
671 return strlen(mac) == 17;
672 }
673
674 const char* mac2str(const uint8_t* array) {
675 static char macStr[18];
676 memset(macStr, 0, 18);
677 snprintf(macStr, 18, "%02x:%02x:%02x:%02x:%02x:%02x", array[0], array[1],
678 array[2], array[3], array[4], array[5]);
679 return (const char*)macStr;
680 }
681
682 static int bufferAvailableForWrite() {
683 return ESPNowStreamSelf->buffer.availableForWrite();
684 }
685
686 virtual void handle_recv_cb(const uint8_t* mac_addr, const uint8_t* data,
687 int data_len, bool broadcast, uint8_t rssi) {
688 LOGD("rec_cb: %d", data_len);
689 // make sure that the receive buffer is available - moved from begin to
690 // make sure that it is only allocated when needed
691 setupReceiveBuffer();
692
693 // update last io time
694 last_io_success_time = millis();
695
696 // blocking write
697 size_t result = buffer.writeArray(data, data_len);
698 if (result != data_len) {
699 LOGE("writeArray %d -> %d", data_len, result);
700 }
701 // manage ready state
702 if (read_ready == false) {
703 if (cfg.start_read_threshold_percent == 0) {
704 read_ready = true;
705 } else {
707 }
708 }
709 }
710
711#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
712 static void default_recv_cb(const esp_now_recv_info* info,
713 const uint8_t* data, int data_len) {
714 const bool broadcast =
715 memcmp(info->des_addr, BROADCAST_MAC, ESP_NOW_ETH_ALEN) == 0;
716 ESPNowStreamSelf->handle_recv_cb(info->src_addr, data, data_len, broadcast,
717 info->rx_ctrl->rssi);
718 }
719#else
720 static void default_recv_cb(const uint8_t* mac_addr, const uint8_t* data,
721 int data_len) {
722 ESPNowStreamSelf->handle_recv_cb(mac_addr, data, data_len, false, 255);
723 }
724#endif
725
726 virtual void handle_send_cb(const uint8_t* mac_addr,
727 esp_now_send_status_t status) {
728 static uint8_t first_mac[ESP_NOW_KEY_LEN] = {0};
729 // we use the first confirming mac_addr for further confirmations and
730 // ignore others
731 if (first_mac[0] == 0) {
732 strncpy((char*)first_mac, (char*)mac_addr, ESP_NOW_KEY_LEN);
733 }
734 LOGD("default_send_cb - %s -> %s", this->mac2str(mac_addr),
735 status == ESP_NOW_SEND_SUCCESS ? "+" : "-");
736
737 // ignore others
738 if (strncmp((char*)mac_addr, (char*)first_mac, ESP_NOW_KEY_LEN) == 0) {
739 this->available_to_write = this->cfg.buffer_size;
740
741 // Track send success/failure
742 this->last_send_success = (status == ESP_NOW_SEND_SUCCESS);
743
744 if (status == ESP_NOW_SEND_SUCCESS) {
745 last_io_success_time = millis();
746 } else {
747 LOGI(
748 "Send Error to %s! Status: %d (Possible causes: out of range, "
749 "receiver busy/offline, channel mismatch, or buffer full)",
750 this->mac2str(mac_addr), status);
751 }
752
753 // Release semaphore to allow write to check status and retry if needed
754 xSemaphoreGive(this->xSemaphore);
755 }
756 }
757
758#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
759 static void default_send_cb(const wifi_tx_info_t* tx_info,
760 esp_now_send_status_t status) {
761 const uint8_t* mac_addr = tx_info->des_addr;
762#else
763 static void default_send_cb(const uint8_t* mac_addr,
764 esp_now_send_status_t status) {
765#endif
766 ESPNowStreamSelf->handle_send_cb(mac_addr, status);
767 }
768};
769
770} // namespace audio_tools
Base class for all Streams. It relies on write(const uint8_t *buffer, size_t size) and readBytes(uint...
Definition BaseStream.h:36
Buffer implementation which is using a FreeRTOS StreamBuffer. The default allocator uses psram is ava...
Definition BufferRTOS.h:30
int available() override
provides the number of entries that are available to read
Definition BufferRTOS.h:137
int availableForWrite() override
provides the number of entries that are available to write
Definition BufferRTOS.h:142
int writeArray(const T data[], int len)
Fills the buffer data.
Definition BufferRTOS.h:95
bool resize(size_t size)
Re-Allocats the memory and the queue.
Definition BufferRTOS.h:51
int readArray(T data[], int len)
reads multiple values
Definition BufferRTOS.h:77
ESPNow as Arduino Stream. When use_send_ack is true we prevent any buffer overflows by blocking write...
Definition ESPNowStream.h:108
BufferRTOS< uint8_t > & getBuffer()
provides access to the receive buffer
Definition ESPNowStream.h:389
bool handleTransmissionResult(int &retry_count)
Handles the result of packet transmission (after queuing)
Definition ESPNowStream.h:549
bool handleQueueError(esp_err_t rc, int &retry_count)
Handles errors when queuing packets.
Definition ESPNowStream.h:593
bool clearPeers()
Deletes all registered peers.
Definition ESPNowStream.h:291
void setSendCallback(esp_now_send_cb_t cb)
Defines an alternative send callback.
Definition ESPNowStream.h:128
bool addPeers(const uint8_t(&array)[N][6])
Adds an array of peers.
Definition ESPNowStream.h:273
size_t readBytes(uint8_t *data, size_t len) override
Reeds the data from the peers.
Definition ESPNowStream.h:362
bool begin()
Initialization of ESPNow.
Definition ESPNowStream.h:135
uint32_t getLastIoSuccessTime() const
time when we were able to send or receive the last packet successfully
Definition ESPNowStream.h:392
bool addPeer(const char *address)
Adds a peer to which we can send info or from which we can receive info.
Definition ESPNowStream.h:242
void setChannel(uint8_t ch)
Defines the WiFi Channel.
Definition ESPNowStream.h:183
size_t write(const uint8_t *data, size_t len) override
Writes the data - sends it to all registered peers.
Definition ESPNowStream.h:308
bool addPeers(const char *(&array)[size])
Adds an array of peers.
Definition ESPNowStream.h:258
virtual bool begin(ESPNowStreamConfig cfg)
Initialization of ESPNow incl WIFI.
Definition ESPNowStream.h:138
void setReceiveCallback(esp_now_recv_cb_t cb)
Definition ESPNowStream.h:132
const char * macAddress()
Returns the mac address of the current ESP32.
Definition ESPNowStream.h:122
bool addPeer(const uint8_t *address)
Adds a peer to which we can send info or from which we can receive info.
Definition ESPNowStream.h:226
bool addPeer(esp_now_peer_info_t &peer)
Adds a peer to which we can send info or from which we can receive info.
Definition ESPNowStream.h:195
uint8_t getChannel()
Provies the WiFi Channel.
Definition ESPNowStream.h:189
virtual bool sendPacket(const uint8_t *data, size_t len, int &retry_count, const uint8_t *destination=nullptr)
Sends a single packet with retry logic.
Definition ESPNowStream.h:478
bool addBroadcastPeer()
Definition ESPNowStream.h:288
size_t write(const char *peer, const uint8_t *data, size_t len)
Writes the data - sends it to all the indicated peer mac address string.
Definition ESPNowStream.h:314
float getBufferPercent()
provides how much the receive buffer is filled (in percent)
Definition ESPNowStream.h:380
size_t write(const uint8_t *peer, const uint8_t *data, size_t len)
Writes the data - sends it to all the peers.
Definition ESPNowStream.h:324
virtual void end()
DeInitialization.
Definition ESPNowStream.h:169
bool setup()
Initialization.
Definition ESPNowStream.h:636
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:28
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
uint32_t millis()
Returns the milliseconds since the start.
Definition Time.h:12
Configuration for ESP-NOW protocolö.W.
Definition ESPNowStream.h:38
const char * mac_address
Definition ESPNowStream.h:43
uint16_t delay_after_failed_write_ms
Delay after failed write (ms). Default: 2000.
Definition ESPNowStream.h:64
wifi_mode_t wifi_mode
WiFi mode (station or access point). Default: WIFI_STA.
Definition ESPNowStream.h:40
const char * local_master_key
Local master key for encryption (16 bytes, optional). Default: nullptr.
Definition ESPNowStream.h:82
const char * primary_master_key
Primary master key for encryption (16 bytes, optional). Default: nullptr.
Definition ESPNowStream.h:80
uint32_t ack_semaphore_timeout_ms
Timeout for ACK semaphore (ms). Default: portMAX_DELAY.
Definition ESPNowStream.h:90
uint8_t start_read_threshold_percent
Buffer fill threshold (percent) to start reading. Default: 0.
Definition ESPNowStream.h:88
const char * ssid
WiFi SSID for connection (optional). Default: nullptr.
Definition ESPNowStream.h:53
uint16_t delay_after_updating_mac_ms
Delay after updating mac.
Definition ESPNowStream.h:92
const char * password
WiFi password for connection (optional). Default: nullptr.
Definition ESPNowStream.h:55
uint16_t buffer_count
Definition ESPNowStream.h:49
void(* recveive_cb)(const esp_now_recv_info *info, const uint8_t *data, int data_len)
Receive callback for ESP-NOW (esp-idf >= 5.0.0). Default: nullptr.
Definition ESPNowStream.h:72
uint32_t oui
Definition ESPNowStream.h:59
int write_retry_count
Number of write retries (-1 for endless). Default: 1.
Definition ESPNowStream.h:69
uint16_t buffer_size
Definition ESPNowStream.h:46
bool use_send_ack
Use send acknowledgments to prevent buffer overflow. Default: true.
Definition ESPNowStream.h:62
wifi_phy_rate_t rate
ESP-NOW bit rate. Default: WIFI_PHY_RATE_6M.
Definition ESPNowStream.h:86
int channel
WiFi channel to use (0 for auto). Default: 0.
Definition ESPNowStream.h:51
wifi_phy_mode_t phymode
ESP-NOW PHY mode. Default: WIFI_PHY_MODE_11G.
Definition ESPNowStream.h:84