arduino-audio-tools
ESPNowStream.h
1 #pragma once
2 #include <WiFiUdp.h>
3 #include <esp_now.h>
4 
5 #include "AudioTools/BaseStream.h"
6 #include "AudioBasic/Str.h"
7 #include "AudioTools/Buffers.h"
8 
9 
10 namespace audio_tools {
11 
12 
13 // forward declarations
14 class ESPNowStream;
15 ESPNowStream *ESPNowStreamSelf = nullptr;
16 
22 class Lock {
23  public:
24  Lock(_lock_t &lock) {
25  this->p_lock = &lock;
26  _lock_acquire(p_lock);
27  }
28  ~Lock() { _lock_release(p_lock); }
29 
30  protected:
31  _lock_t *p_lock = nullptr;
32 };
33 
34 
35 
42  wifi_mode_t wifi_mode = WIFI_STA;
43  const char *mac_address = nullptr;
44  int channel = 0;
45  const char *ssid = nullptr;
46  const char *password = nullptr;
47  bool use_send_ack = true; // we wait for
48  uint16_t delay_after_write_ms = 2;
49  uint16_t delay_after_failed_write_ms = 2000;
50  uint16_t buffer_size = ESP_NOW_MAX_DATA_LEN;
51  uint16_t buffer_count = 400;
52  int write_retry_count = -1; // -1 endless
53 #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
54  void (*recveive_cb)(const esp_now_recv_info *info, const uint8_t *data,
55  int data_len) = nullptr;
56 #else
57  void (*recveive_cb)(const uint8_t *mac_addr, const uint8_t *data,
58  int data_len) = nullptr;
59 #endif
61  const char *primary_master_key = nullptr;
62  const char *local_master_key = nullptr;
64  wifi_phy_rate_t rate = WIFI_PHY_RATE_2M_S;
65 };
66 
73 class ESPNowStream : public BaseStream {
74  public:
75  ESPNowStream() { ESPNowStreamSelf = this; };
76 
77  ~ESPNowStream() {
78  if (p_buffer != nullptr) delete p_buffer;
79  }
80 
81  ESPNowStreamConfig defaultConfig() {
82  ESPNowStreamConfig result;
83  return result;
84  }
85 
87  const char *macAddress() {
88  static const char* result = WiFi.macAddress().c_str();
89  return result;
90  }
91 
93  void setSendCallback(esp_now_send_cb_t cb) { send = cb; }
94 
97  void setReceiveCallback(esp_now_recv_cb_t cb) { receive = cb; }
98 
100  bool begin() { return begin(cfg); }
101 
104  this->cfg = cfg;
105  WiFi.mode(cfg.wifi_mode);
106  // set mac address
107  if (cfg.mac_address != nullptr) {
108  LOGI("setting mac %s", cfg.mac_address);
109  byte mac[ESP_NOW_KEY_LEN];
110  str2mac(cfg.mac_address, mac);
111  if (esp_wifi_set_mac((wifi_interface_t)getInterface(), mac) != ESP_OK) {
112  LOGE("Could not set mac address");
113  return false;
114  }
115  // checking if address has been updated
116  const char *addr = macAddress();
117  if (strcmp(addr, cfg.mac_address) != 0) {
118  LOGE("Wrong mac address: %s", addr);
119  return false;
120  }
121  }
122 
123  if (WiFi.status() != WL_CONNECTED && cfg.ssid != nullptr &&
124  cfg.password != nullptr) {
125  WiFi.begin(cfg.ssid, cfg.password);
126  while (WiFi.status() != WL_CONNECTED) {
127  Serial.print('.');
128  delay(1000);
129  }
130  }
131 
132 #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
133  LOGI("Setting ESP-NEW rate");
134  if (esp_wifi_config_espnow_rate(getInterface(), cfg.rate) !=
135  ESP_OK) {
136  LOGW("Could not set rate");
137  }
138 #endif
139 
140  Serial.println();
141  Serial.print("mac: ");
142  Serial.println(WiFi.macAddress());
143  return setup();
144  }
145 
147  void end() {
148  if (esp_now_deinit() != ESP_OK) {
149  LOGE("esp_now_deinit");
150  }
151  is_init = false;
152  }
153 
155  bool addPeer(esp_now_peer_info_t &peer) {
156  if (!is_init) {
157  LOGE("addPeer before begin");
158  return false;
159  }
160  esp_err_t result = esp_now_add_peer(&peer);
161  if (result == ESP_OK) {
162  LOGI("addPeer: %s", mac2str(peer.peer_addr));
163  } else {
164  LOGE("addPeer: %d", result);
165  }
166  return result == ESP_OK;
167  }
168 
170  template <size_t size>
171  bool addPeers(const char *(&array)[size]) {
172  bool result = true;
173  for (int j = 0; j < size; j++) {
174  const char *peer = array[j];
175  if (peer != nullptr) {
176  if (!addPeer(peer)) {
177  result = false;
178  }
179  }
180  }
181  return result;
182  }
183 
185  bool addPeer(const char *address) {
186  esp_now_peer_info_t peer;
187  peer.channel = cfg.channel;
188  peer.ifidx = getInterface();
189  peer.encrypt = false;
190 
191  if (Str(address).equals(cfg.mac_address)){
192  LOGW("Did not add own address as peer");
193  return true;
194  }
195 
196  if (isEncrypted()) {
197  peer.encrypt = true;
198  strncpy((char *)peer.lmk, cfg.local_master_key, 16);
199  }
200 
201  if (!str2mac(address, peer.peer_addr)) {
202  LOGE("addPeer - Invalid address: %s", address);
203  return false;
204  }
205  return addPeer(peer);
206  }
207 
209  size_t write(const uint8_t *data, size_t len) override {
210  int open = len;
211  size_t result = 0;
212  int retry_count = 0;
213  while (open > 0) {
214  if (available_to_write > 0) {
215  resetAvailableToWrite();
216  size_t send_len = min(open, ESP_NOW_MAX_DATA_LEN);
217  esp_err_t rc = esp_now_send(nullptr, data + result, send_len);
218  // wait for confirmation
219  if (cfg.use_send_ack) {
220  while (available_to_write == 0) {
221  delay(1);
222  }
223  } else {
224  is_write_ok = true;
225  }
226  // check status
227  if (rc == ESP_OK && is_write_ok) {
228  open -= send_len;
229  result += send_len;
230  } else {
231  LOGW("Write failed - retrying again");
232  retry_count++;
233  if (cfg.write_retry_count>0 && retry_count>=cfg.write_retry_count){
234  LOGE("Write error after %d retries", cfg.write_retry_count);
235  // break loop
236  return 0;
237  }
238  }
239  // if we do have no partner to write we stall and retry later
240  } else {
241  delay(cfg.delay_after_write_ms);
242  }
243 
244  // Wait some time before we retry
245  if (!is_write_ok) {
246  delay(cfg.delay_after_failed_write_ms);
247  }
248  }
249  return result;
250  }
251 
253  size_t readBytes(uint8_t *data, size_t len) override {
254  if (p_buffer == nullptr) return 0;
255  Lock lock(write_lock);
256  return p_buffer->readArray(data, len);
257  }
258 
259  int available() override {
260  return p_buffer == nullptr ? 0 : p_buffer->available();
261  }
262 
263  int availableForWrite() override {
264  return cfg.use_send_ack ? available_to_write : cfg.buffer_size;
265  }
266 
267  protected:
268  ESPNowStreamConfig cfg;
269  BaseBuffer<uint8_t> *p_buffer = nullptr;
270  esp_now_recv_cb_t receive = default_recv_cb;
271  esp_now_send_cb_t send = default_send_cb;
272  volatile size_t available_to_write;
273  bool is_init = false;
274  bool is_write_ok = false;
275  _lock_t write_lock;
276 
277  inline void setupReceiveBuffer(){
278  // setup receive buffer
279  if (p_buffer == nullptr && cfg.buffer_count > 0) {
280  // p_buffer = new NBuffer<uint8_t>(cfg.buffer_size , cfg.buffer_count);
281  p_buffer = new RingBuffer<uint8_t>(cfg.buffer_size * cfg.buffer_count);
282  }
283  }
284 
285  inline void resetAvailableToWrite() {
286  if (cfg.use_send_ack) {
287  available_to_write = 0;
288  }
289  }
290 
291  bool isEncrypted() {
292  return cfg.primary_master_key != nullptr && cfg.local_master_key != nullptr;
293  }
294 
295  wifi_interface_t getInterface() {
296  // define wifi_interface_t
297  wifi_interface_t result;
298  switch (cfg.wifi_mode) {
299  case WIFI_STA:
300  result = (wifi_interface_t)ESP_IF_WIFI_STA;
301  break;
302  case WIFI_AP:
303  result = (wifi_interface_t)ESP_IF_WIFI_AP;
304  break;
305  default:
306  result = (wifi_interface_t)0;
307  break;
308  }
309  return result;
310  }
311 
313  bool setup() {
314  esp_err_t result = esp_now_init();
315  if (result == ESP_OK) {
316  LOGI("esp_now_init: %s", macAddress());
317  } else {
318  LOGE("esp_now_init: %d", result);
319  }
320 
321  // encryption is optional
322  if (isEncrypted()) {
323  esp_now_set_pmk((uint8_t *)cfg.primary_master_key);
324  }
325 
326  if (cfg.recveive_cb != nullptr) {
327  esp_now_register_recv_cb(cfg.recveive_cb);
328  } else {
329  esp_now_register_recv_cb(receive);
330  }
331  if (cfg.use_send_ack) {
332  esp_now_register_send_cb(send);
333  }
334  available_to_write = cfg.buffer_size;
335  is_init = result == ESP_OK;
336  return is_init;
337  }
338 
339  bool str2mac(const char *mac, uint8_t *values) {
340  sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &values[0], &values[1],
341  &values[2], &values[3], &values[4], &values[5]);
342  return strlen(mac) == 17;
343  }
344 
345  const char *mac2str(const uint8_t *array) {
346  static char macStr[18];
347  memset(macStr, 0, 18);
348  snprintf(macStr, 18, "%02x:%02x:%02x:%02x:%02x:%02x", array[0], array[1],
349  array[2], array[3], array[4], array[5]);
350  return (const char *)macStr;
351  }
352 
353  static int bufferAvailableForWrite() {
354  Lock lock(ESPNowStreamSelf->write_lock);
355  return ESPNowStreamSelf->p_buffer->availableForWrite();
356  }
357 
358 #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
359  static void default_recv_cb(const esp_now_recv_info *info, const uint8_t *data, int data_len)
360 #else
361  static void default_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int data_len)
362 #endif
363 {
364  LOGD("rec_cb: %d", data_len);
365  // make sure that the receive buffer is available - moved from begin to make sure that it is only allocated when needed
366  ESPNowStreamSelf->setupReceiveBuffer();
367  // blocking write
368  while (bufferAvailableForWrite() < data_len) {
369  delay(2);
370  }
371  Lock lock(ESPNowStreamSelf->write_lock);
372  size_t result = ESPNowStreamSelf->p_buffer->writeArray(data, data_len);
373  if (result!=data_len){
374  LOGE("writeArray %d -> %d", data_len, result);
375  }
376  }
377 
378  static void default_send_cb(const uint8_t *mac_addr,
379  esp_now_send_status_t status) {
380  static uint8_t first_mac[ESP_NOW_KEY_LEN] = {0};
381  // we use the first confirming mac_addr for further confirmations and ignore
382  // others
383  if (first_mac[0] == 0) {
384  strncpy((char *)first_mac, (char *)mac_addr, ESP_NOW_KEY_LEN);
385  }
386  LOGD("default_send_cb - %s -> %s", ESPNowStreamSelf->mac2str(mac_addr),
387  status == ESP_NOW_SEND_SUCCESS ? "+" : "-");
388 
389  // ignore others
390  if (strncmp((char *)mac_addr, (char *)first_mac, ESP_NOW_KEY_LEN) == 0) {
391  ESPNowStreamSelf->available_to_write = ESPNowStreamSelf->cfg.buffer_size;
392  if (status == ESP_NOW_SEND_SUCCESS) {
393  ESPNowStreamSelf->is_write_ok = true;
394  } else {
395  ESPNowStreamSelf->is_write_ok = false;
396  }
397  }
398  }
399 };
400 
401 }
virtual int readArray(T data[], int len)
reads multiple values
Definition: Buffers.h:41
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition: Buffers.h:65
virtual int availableForWrite()=0
provides the number of entries that are available to write
virtual int available()=0
provides the number of entries that are available to read
Base class for all Streams. It relies on write(const uint8_t *buffer, size_t size) and readBytes(uint...
Definition: BaseStream.h:34
ESPNow as Arduino Stream.
Definition: ESPNowStream.h:73
void setSendCallback(esp_now_send_cb_t cb)
Defines an alternative send callback.
Definition: ESPNowStream.h:93
const char * macAddress()
Returns the mac address of the current ESP32.
Definition: ESPNowStream.h:87
size_t readBytes(uint8_t *data, size_t len) override
Reeds the data from the peers.
Definition: ESPNowStream.h:253
bool begin()
Initialization of ESPNow.
Definition: ESPNowStream.h:100
bool addPeer(const char *address)
Adds a peer to which we can send info or from which we can receive info.
Definition: ESPNowStream.h:185
size_t write(const uint8_t *data, size_t len) override
Writes the data - sends it to all the peers.
Definition: ESPNowStream.h:209
bool addPeers(const char *(&array)[size])
Adds an array of.
Definition: ESPNowStream.h:171
void setReceiveCallback(esp_now_recv_cb_t cb)
Definition: ESPNowStream.h:97
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:155
void end()
DeInitialization.
Definition: ESPNowStream.h:147
bool begin(ESPNowStreamConfig cfg)
Initialization of ESPNow incl WIFI.
Definition: ESPNowStream.h:103
bool setup()
Initialization.
Definition: ESPNowStream.h:313
A simple RIA locking class for the ESP32 using _lock_t.
Definition: ESPNowStream.h:22
A simple wrapper to provide string functions on char*. If the underlying char* is a const we do not a...
Definition: Str.h:27
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10
void delay(uint32_t ms)
Waits for the indicated milliseconds.
Definition: Millis.h:11
Configuration for ESP-NOW protocolö.W.
Definition: ESPNowStream.h:41
const char * primary_master_key
to encrypt set primary_master_key and local_master_key to 16 byte strings
Definition: ESPNowStream.h:61
wifi_phy_rate_t rate
esp-now bit rate
Definition: ESPNowStream.h:64