6#include "TinyRobotics/utils/BaseStream.h"
7#include "TinyRobotics/concurrency/RTOS/BufferRTOS.h"
9#define LOGE TRLogger.error
10#define LOGW TRLogger.warn
11#define LOGI TRLogger.info
12#define LOGD TRLogger.debug
15#ifdef ESP_NOW_MAX_DATA_LEN_V2
17#define MY_ESP_NOW_MAX_LEN ESP_NOW_MAX_DATA_LEN_V2
20#define MY_ESP_NOW_MAX_LEN ESP_NOW_MAX_DATA_LEN
26#define MY_ESP_NOW_BUFFER_SIZE (240
* 400
)
29namespace tinyrobotics {
34static const char* BROADCAST_MAC_STR =
"FF:FF:FF:FF:FF:FF";
35static const uint8_t BROADCAST_MAC[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
38
39
40
41
57 const char*
ssid =
nullptr;
60#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5
, 5
, 0
)
71 bool use_long_range =
false;
74#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5
, 0
, 0
)
80 void (*recveive_cb)(
const uint8_t* mac_addr,
const uint8_t* data,
81 int data_len) =
nullptr;
100
101
102
103
104
105
106
107
108
109
110
111
114 ESPNowStream() { ESPNowStreamSelf =
this; };
117 if (xSemaphore !=
nullptr) vSemaphoreDelete(xSemaphore);
127 static String mac_str = WiFi.macAddress();
128 return mac_str.c_str();
139 bool begin() {
return begin(cfg); }
144 if (WiFi.getMode() == WIFI_MODE_NULL) {
145 WiFi.mode(cfg.wifi_mode);
147 cfg.wifi_mode = WiFi.getMode();
150 if (!setupMAC())
return false;
152 if (!setupWiFi())
return false;
154 WiFi.enableLongRange(cfg.use_long_range);
156#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5
, 0
, 0
)
157 LOGI(
"Setting ESP-NEW rate");
158 if (esp_wifi_config_espnow_rate(getInterface(), cfg.rate) != ESP_OK) {
159 LOGW(
"Could not set rate");
165 Serial.print(
"mac: ");
166 Serial.println(WiFi.macAddress());
167 Serial.print(
"channel: ");
168 Serial.println(getChannel());
175 if (esp_now_deinit() != ESP_OK) {
176 LOGE(
"esp_now_deinit");
178 if (buffer.size() > 0) buffer.resize(0);
182 is_broadcast =
false;
188 WiFi.setChannel(ch, WIFI_SECOND_CHAN_NONE);
194 uint32_t ch = WiFi.channel();
195 return (uint8_t)ch ^ 0xff;
199 bool addPeer(esp_now_peer_info_t& peer) {
201 LOGE(
"addPeer before begin");
204 if (memcmp(BROADCAST_MAC, peer.peer_addr, 6) == 0) {
205 LOGI(
"Using broadcast");
208 esp_err_t result = esp_now_add_peer(&peer);
209 if (result == ESP_OK) {
210 LOGI(
"addPeer: %s", mac2str(peer.peer_addr));
211#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5
, 0
, 0
)
212 esp_now_rate_config_t rate_config = {.phymode = cfg.phymode,
216 result = esp_now_set_peer_rate_config(peer.peer_addr, &rate_config);
217 if (result != ESP_OK) {
218 LOGW(
"Could not set the ESP-NOW PHY rate (%d) %s.", result,
219 esp_err_to_name(result));
222 has_peers = result == ESP_OK;
224 LOGE(
"addPeer: %d", result);
226 return result == ESP_OK;
230 bool addPeer(
const uint8_t* address) {
231 esp_now_peer_info_t peer;
232 memcpy(peer.peer_addr, address, ESP_NOW_ETH_ALEN);
234 peer.channel = cfg.channel;
235 peer.ifidx = getInterface();
236 peer.encrypt =
false;
240 strncpy((
char*)peer.lmk, cfg.local_master_key, 16);
242 return addPeer(peer);
246 bool addPeer(
const char* address) {
247 if (strcmp(address, cfg.mac_address)==0) {
248 LOGW(
"Did not add own address as peer");
252 uint8_t mac[] = {0, 0, 0, 0, 0, 0};
253 if (!str2mac(address, (uint8_t*)&mac)) {
254 LOGE(
"addPeer - Invalid address: %s", address);
261 template <size_t size>
262 bool addPeers(
const char* (&array)[size]) {
264 for (
int j = 0; j < size; j++) {
265 const char* peer = array[j];
266 if (peer !=
nullptr) {
277 bool addPeers(
const uint8_t (&array)[N][6]) {
279 for (
int j = 0; j < N; j++) {
280 const uint8_t* peer = array[j];
281 if (peer !=
nullptr) {
296 esp_now_peer_info_t peer;
297 uint8_t breakout_counter = 0;
298 while ((esp_now_fetch_peer(
true, &peer) == ESP_OK) &&
299 (breakout_counter < ESP_NOW_MAX_TOTAL_PEER_NUM + 1)) {
300 esp_now_del_peer(peer.peer_addr);
304 if (breakout_counter == ESP_NOW_MAX_TOTAL_PEER_NUM + 1) {
305 LOGE(
"Not all Peers seems to be removed.");
308 return breakout_counter <= ESP_NOW_MAX_TOTAL_PEER_NUM;
314 return write((
const uint8_t*)
nullptr, data, len);
320 if (!str2mac(peer, mac)) {
321 LOGE(
"write: invalid mac address %s", peer);
324 return write(mac, data, len);
333 if (!has_peers && peer ==
nullptr) {
337 size_t total_sent = 0;
338 size_t remaining = len;
340 while (remaining > 0) {
345 sendPacket(data + total_sent, chunk_size, retry_count, peer);
349 total_sent += chunk_size;
350 remaining -= chunk_size;
354 "write: failed to send chunk after %d attempts (sent %zu/%zu "
356 retry_count, total_sent, len);
367 if (!read_ready)
return 0;
368 if (buffer.size() == 0)
return 0;
369 return buffer.readArray(data, len);
372 int available()
override {
373 if (!buffer)
return 0;
374 if (!read_ready)
return 0;
375 return buffer.size() == 0 ? 0 : buffer.available();
378 int availableForWrite()
override {
379 if (!buffer)
return 0;
380 return cfg.use_send_ack ? available_to_write : cfg.buffer_size;
385 int size = buffer.size();
387 if (size == 0)
return 0.0;
389 return 100.0 * buffer.available() / size;
400 BufferRTOS<uint8_t> buffer{0};
403 volatile size_t available_to_write = 0;
404 volatile bool last_send_success =
true;
405 bool is_init =
false;
406 SemaphoreHandle_t xSemaphore =
nullptr;
407 bool has_peers =
false;
408 bool read_ready =
false;
409 bool is_broadcast =
false;
410 uint32_t last_io_success_time = 0;
414 if (cfg.mac_address !=
nullptr) {
415 LOGI(
"setting mac %s", cfg.mac_address);
416 byte mac[ESP_NOW_KEY_LEN];
417 str2mac(cfg.mac_address, mac);
418 if (esp_wifi_set_mac((wifi_interface_t)getInterface(), mac) != ESP_OK) {
419 LOGE(
"Could not set mac address");
424 delay(cfg.delay_after_updating_mac_ms);
428 if (strcmp(addr, cfg.mac_address) != 0) {
429 LOGE(
"Wrong mac address: %s", addr);
437 if (WiFi.status() != WL_CONNECTED) {
439 if (cfg.ssid !=
nullptr && cfg.password !=
nullptr) {
440 LOGI(
"Logging into WiFi: %s", cfg.ssid);
441 WiFi.begin(cfg.ssid, cfg.password);
442 while (WiFi.status() != WL_CONNECTED) {
451 if (WiFi.getMode() == WIFI_AP && WiFi.status() != WL_CONNECTED) {
452 LOGE(
"You did not start Wifi or did not provide ssid and password");
459 inline void setupSemaphore() {
461 if (cfg.use_send_ack && xSemaphore ==
nullptr) {
462 xSemaphore = xSemaphoreCreateBinary();
463 xSemaphoreGive(xSemaphore);
467 inline void setupReceiveBuffer() {
470 LOGI(
"setupReceiveBuffer: %d", cfg.buffer_size * cfg.buffer_count);
471 buffer.resize(cfg.buffer_size * cfg.buffer_count);
475 inline void resetAvailableToWrite() {
476 if (cfg.use_send_ack) {
477 available_to_write = 0;
482 virtual bool sendPacket(
const uint8_t* data, size_t len,
int& retry_count,
483 const uint8_t* destination =
nullptr) {
485 const uint8_t* target = destination;
486 if (target ==
nullptr && is_broadcast) {
487 target = BROADCAST_MAC;
491 resetAvailableToWrite();
494 if (cfg.use_send_ack) {
495 TickType_t ticks = (cfg.ack_semaphore_timeout_ms == portMAX_DELAY)
497 : pdMS_TO_TICKS(cfg.ack_semaphore_timeout_ms);
498 if (xSemaphoreTake(xSemaphore, ticks) != pdTRUE) {
501 if (cfg.write_retry_count >= 0 &&
502 retry_count >= cfg.write_retry_count) {
503 LOGE(
"Timeout waiting for ACK semaphore after %d retries",
508 LOGW(
"ACK semaphore timeout (attempt %d)", retry_count);
509 delay(cfg.delay_after_failed_write_ms);
515 esp_err_t rc = esp_now_send(target, data, len);
525 if (cfg.write_retry_count >= 0 &&
526 retry_count >= cfg.write_retry_count) {
532 if (cfg.use_send_ack) {
533 xSemaphoreGive(xSemaphore);
537 if (cfg.write_retry_count >= 0 &&
538 retry_count >= cfg.write_retry_count) {
539 LOGE(
"esp_now_send queue error (rc=%d/0x%04X) after %d retries", rc,
545 LOGW(
"esp_now_send failed (rc=%d/0x%04X) - retrying (attempt %d)", rc,
547 delay(cfg.delay_after_failed_write_ms);
555 if (cfg.use_send_ack) {
557 TickType_t ticks = (cfg.ack_semaphore_timeout_ms == portMAX_DELAY)
559 : pdMS_TO_TICKS(cfg.ack_semaphore_timeout_ms);
560 if (xSemaphoreTake(xSemaphore, ticks) != pdTRUE) {
562 if (cfg.write_retry_count >= 0 &&
563 retry_count >= cfg.write_retry_count) {
564 LOGE(
"Transmission callback timeout after %d retries", retry_count);
568 LOGW(
"Transmission callback timeout (attempt %d)", retry_count);
569 delay(cfg.delay_after_failed_write_ms);
574 if (last_send_success) {
575 xSemaphoreGive(xSemaphore);
578 xSemaphoreGive(xSemaphore);
581 if (cfg.write_retry_count >= 0 &&
582 retry_count >= cfg.write_retry_count) {
583 LOGE(
"Transmission failed after %d retries", retry_count);
588 LOGI(
"Transmission failed - retrying (attempt %d)", retry_count);
589 delay(cfg.delay_after_failed_write_ms);
601 if (cfg.use_send_ack) {
602 xSemaphoreGive(xSemaphore);
606 LOGW(
"esp_now_send failed to queue (rc=%d/0x%04X) - retrying (attempt %d)",
607 rc, rc, retry_count);
609 if (cfg.write_retry_count >= 0 && retry_count > cfg.write_retry_count) {
610 LOGE(
"Send queue error after %d retries", retry_count);
614 delay(cfg.delay_after_failed_write_ms);
619 return cfg.primary_master_key !=
nullptr && cfg.local_master_key !=
nullptr;
622 wifi_interface_t getInterface() {
624 wifi_interface_t result;
625 switch (cfg.wifi_mode) {
627 result = (wifi_interface_t)ESP_IF_WIFI_STA;
630 result = (wifi_interface_t)ESP_IF_WIFI_AP;
633 result = (wifi_interface_t)0;
641 esp_err_t result = esp_now_init();
642 if (result == ESP_OK) {
645 LOGE(
"esp_now_init: %d", result);
648#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5
, 5
, 0
)
650 esp_now_set_user_oui((uint8_t*)cfg.oui);
656 esp_now_set_pmk((uint8_t*)cfg.primary_master_key);
659 if (cfg.recveive_cb !=
nullptr) {
660 esp_now_register_recv_cb(cfg.recveive_cb);
662 esp_now_register_recv_cb(receive);
664 if (cfg.use_send_ack) {
665 esp_now_register_send_cb(send);
667 available_to_write = cfg.buffer_size;
668 is_init = result == ESP_OK;
672 bool str2mac(
const char* mac, uint8_t* values) {
673 sscanf(mac,
"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &values[0], &values[1],
674 &values[2], &values[3], &values[4], &values[5]);
675 return strlen(mac) == 17;
678 const char* mac2str(
const uint8_t* array) {
679 static char macStr[18];
680 memset(macStr, 0, 18);
681 snprintf(macStr, 18,
"%02x:%02x:%02x:%02x:%02x:%02x", array[0], array[1],
682 array[2], array[3], array[4], array[5]);
683 return (
const char*)macStr;
686 static int bufferAvailableForWrite() {
687 return ESPNowStreamSelf->buffer.availableForWrite();
690 virtual void handle_recv_cb(
const uint8_t* mac_addr,
const uint8_t* data,
691 int data_len,
bool broadcast, uint8_t rssi) {
692 LOGD(
"rec_cb: %d", data_len);
695 setupReceiveBuffer();
698 last_io_success_time = millis();
701 size_t result = buffer.writeArray(data, data_len);
702 if (result != data_len) {
703 LOGE(
"writeArray %d -> %d", data_len, result);
706 if (read_ready ==
false) {
707 if (cfg.start_read_threshold_percent == 0) {
710 read_ready = getBufferPercent() >= cfg.start_read_threshold_percent;
715#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5
, 0
, 0
)
724 static void default_recv_cb(
const uint8_t* mac_addr,
const uint8_t* data,
726 ESPNowStreamSelf->handle_recv_cb(mac_addr, data, data_len,
false, 255);
730 virtual void handle_send_cb(
const uint8_t* mac_addr,
731 esp_now_send_status_t status) {
732 static uint8_t first_mac[ESP_NOW_KEY_LEN] = {0};
735 if (first_mac[0] == 0) {
736 strncpy((
char*)first_mac, (
char*)mac_addr, ESP_NOW_KEY_LEN);
738 LOGD(
"default_send_cb - %s -> %s",
this->mac2str(mac_addr),
739 status == ESP_NOW_SEND_SUCCESS ?
"+" :
"-");
742 if (strncmp((
char*)mac_addr, (
char*)first_mac, ESP_NOW_KEY_LEN) == 0) {
743 this->available_to_write =
this->cfg.buffer_size;
746 this->last_send_success = (status == ESP_NOW_SEND_SUCCESS);
748 if (status == ESP_NOW_SEND_SUCCESS) {
749 last_io_success_time = millis();
752 "Send Error to %s! Status: %d (Possible causes: out of range, "
753 "receiver busy/offline, channel mismatch, or buffer full)",
754 this->mac2str(mac_addr), status);
758 xSemaphoreGive(
this->xSemaphore);
762#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5
, 5
, 0
)
767 static void default_send_cb(
const uint8_t* mac_addr,
768 esp_now_send_status_t status) {
770 ESPNowStreamSelf->handle_send_cb(mac_addr, status);
#define LOGI
Definition: ESPNowStream.h:11
#define MY_ESP_NOW_MAX_LEN
Definition: ESPNowStream.h:20
#define LOGD
Definition: ESPNowStream.h:12
#define MY_ESP_NOW_BUFFER_SIZE
Definition: ESPNowStream.h:26
#define MY_ESP_NOW_BUFFER_COUNT
Definition: ESPNowStream.h:27
#define LOGW
Definition: ESPNowStream.h:10
#define LOGE
Definition: ESPNowStream.h:9
Base class for all Streams. It relies on write(const uint8_t *buffer, size_t size) and readBytes(uint...
Definition: BaseStream.h:20
ESPNow as Arduino Stream. When use_send_ack is true we prevent any buffer overflows by blocking write...
Definition: ESPNowStream.h:112
BufferRTOS< uint8_t > & getBuffer()
provides access to the receive buffer
Definition: ESPNowStream.h:393
bool handleTransmissionResult(int &retry_count)
Handles the result of packet transmission (after queuing)
Definition: ESPNowStream.h:553
bool handleQueueError(esp_err_t rc, int &retry_count)
Handles errors when queuing packets.
Definition: ESPNowStream.h:597
bool clearPeers()
Deletes all registered peers.
Definition: ESPNowStream.h:295
void setSendCallback(esp_now_send_cb_t cb)
Defines an alternative send callback.
Definition: ESPNowStream.h:132
bool addPeers(const uint8_t(&array)[N][6])
Adds an array of peers.
Definition: ESPNowStream.h:277
size_t readBytes(uint8_t *data, size_t len) override
Reeds the data from the peers.
Definition: ESPNowStream.h:366
bool begin()
Initialization of ESPNow.
Definition: ESPNowStream.h:139
uint32_t getLastIoSuccessTime() const
time when we were able to send or receive the last packet successfully
Definition: ESPNowStream.h:396
bool addPeer(const char *address)
Adds a peer to which we can send info or from which we can receive info.
Definition: ESPNowStream.h:246
void setChannel(uint8_t ch)
Defines the WiFi Channel.
Definition: ESPNowStream.h:187
size_t write(const uint8_t *data, size_t len) override
Writes the data - sends it to all registered peers.
Definition: ESPNowStream.h:312
bool addPeers(const char *(&array)[size])
Adds an array of peers.
Definition: ESPNowStream.h:262
virtual bool begin(ESPNowStreamConfig cfg)
Initialization of ESPNow incl WIFI.
Definition: ESPNowStream.h:142
void setReceiveCallback(esp_now_recv_cb_t cb)
Definition: ESPNowStream.h:136
const char * macAddress()
Returns the mac address of the current ESP32.
Definition: ESPNowStream.h:126
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:230
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:199
uint8_t getChannel()
Provies the WiFi Channel.
Definition: ESPNowStream.h:193
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:482
bool addBroadcastPeer()
Definition: ESPNowStream.h:292
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:318
float getBufferPercent()
provides how much the receive buffer is filled (in percent)
Definition: ESPNowStream.h:384
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:328
virtual void end()
DeInitialization.
Definition: ESPNowStream.h:173
bool setup()
Initialization.
Definition: ESPNowStream.h:640
Configuration for ESP-NOW protocolö.W.
Definition: ESPNowStream.h:42
const char * mac_address
Definition: ESPNowStream.h:47
uint16_t delay_after_failed_write_ms
Delay after failed write (ms). Default: 2000.
Definition: ESPNowStream.h:68
wifi_mode_t wifi_mode
WiFi mode (station or access point). Default: WIFI_STA.
Definition: ESPNowStream.h:44
const char * local_master_key
Local master key for encryption (16 bytes, optional). Default: nullptr.
Definition: ESPNowStream.h:86
const char * primary_master_key
Primary master key for encryption (16 bytes, optional). Default: nullptr.
Definition: ESPNowStream.h:84
uint32_t ack_semaphore_timeout_ms
Timeout for ACK semaphore (ms). Default: portMAX_DELAY.
Definition: ESPNowStream.h:94
uint8_t start_read_threshold_percent
Buffer fill threshold (percent) to start reading. Default: 0.
Definition: ESPNowStream.h:92
const char * ssid
WiFi SSID for connection (optional). Default: nullptr.
Definition: ESPNowStream.h:57
uint16_t delay_after_updating_mac_ms
Delay after updating mac.
Definition: ESPNowStream.h:96
const char * password
WiFi password for connection (optional). Default: nullptr.
Definition: ESPNowStream.h:59
uint16_t buffer_count
Definition: ESPNowStream.h:53
int write_retry_count
Number of write retries (-1 for endless). Default: 1.
Definition: ESPNowStream.h:73
uint16_t buffer_size
Definition: ESPNowStream.h:50
bool use_send_ack
Use send acknowledgments to prevent buffer overflow. Default: true.
Definition: ESPNowStream.h:66
wifi_phy_rate_t rate
ESP-NOW bit rate. Default: WIFI_PHY_RATE_6M.
Definition: ESPNowStream.h:90
int channel
WiFi channel to use (0 for auto). Default: 0.
Definition: ESPNowStream.h:55
wifi_phy_mode_t phymode
ESP-NOW PHY mode. Default: WIFI_PHY_MODE_11G.
Definition: ESPNowStream.h:88