Repository navigation
Communication ESP NOW
ESP-NOW is Espressif's connectionless WiFi protocol: ESP32 devices send packets directly to each other by their MAC address, without an access point, a router or a login. It is easy to set up and has a low latency - a good choice for a wireless microphone or a link between two ESP32 boards. See Communication for the general considerations.
ESPNowStream (AudioTools/Communication/ESPNowStream.h) makes ESP-NOW an Arduino Stream: what you write is sent to the peers, what is received can be read. It is supported on the ESP32.
Each device needs to know the MAC addresses of its peers. You can define the device's own MAC address in the configuration, which makes this easy: give the sender and the receiver fixed addresses and add each other as peer.
| Method | Description |
|---|---|
begin(cfg) |
starts ESP-NOW with the configuration (defaultConfig()) |
addPeer(mac), addPeers(array)
|
adds the receivers (MAC as "A8:48:FA:0B:93:01" string or bytes) |
addBroadcastPeer() |
sends to all ESP-NOW devices in range |
setChannel(ch) |
WiFi channel; all devices must use the same one |
write(), readBytes(), available()
|
send and receive |
The most important configuration fields (ESPNowStreamConfig):
| Field | Description |
|---|---|
mac_address |
the device's own MAC address (nullptr: the default one) |
use_send_ack |
default true: each write waits until the receiver has confirmed the previous packet - no buffer overflow on the receiver, but slower. Broadcast does not support it. |
write_retry_count, delay_after_failed_write_ms
|
what to do if a packet was not confirmed |
buffer_size, buffer_count
|
receive buffers (one packet is at most 250 bytes, 1470 with ESP-NOW v2 in newer ESP-IDF versions) |
start_read_threshold_percent |
the receiver starts reading only when its buffer is filled to this level - absorbs jitter |
channel |
WiFi channel (0: automatic) |
ssid, password
|
connect to WiFi at the same time (then the channel is the one of the access point) |
use_long_range |
Espressif's long range mode: more range, less throughput |
rate, phymode
|
WiFi bit rate and PHY mode (default 6 Mbit/s, 802.11g) |
primary_master_key, local_master_key
|
encryption |
From the pcm examples. The sender:
#include "AudioTools.h"
#include "AudioTools/Communication/ESPNowStream.h"
AudioInfo info(8000, 1, 16);
SineGenerator<int16_t> sineWave(32000);
GeneratedSoundStream<int16_t> sound(sineWave);
ESPNowStream now;
StreamCopy copier(now, sound);
const char *peers[] = {"A8:48:FA:0B:93:01"}; // the receiver
void setup() {
auto cfg = now.defaultConfig();
cfg.mac_address = "A8:48:FA:0B:93:02"; // the sender
now.begin(cfg);
now.addPeers(peers);
copier.setRetry(0);
sineWave.begin(info, N_B4);
}
void loop() { copier.copy(); }With use_send_ack (the default) the sender is slowed down to the receiver's speed, so the generator needs no Throttle. The receiver:
AudioInfo info(8000, 1, 16);
ESPNowStream now;
I2SStream out;
StreamCopy copier(out, now);
const char *peers[] = {"A8:48:FA:0B:93:02"}; // the sender
void setup() {
auto cfg = now.defaultConfig();
cfg.mac_address = "A8:48:FA:0B:93:01"; // the receiver
now.begin(cfg);
now.addPeers(peers);
auto config = out.defaultConfig(TX_MODE);
config.copyFrom(info);
out.begin(config);
}
void loop() { copier.copy(); }To reach several receivers, send to the broadcast address - without acknowledgments, so the sender must be throttled to the audio rate, and the receivers should buffer a bit. From the broadcast examples:
// sender
ESPNowStream now;
Throttle throttle(now);
StreamCopy copier(throttle, sound);
...
auto cfg = now.defaultConfig();
cfg.use_send_ack = false; // broadcast does not support acknowledgments
now.begin(cfg);
now.addBroadcastPeer();
// receiver
auto cfg = now.defaultConfig();
cfg.start_read_threshold_percent = 50; // start playing when the buffer is half fullESP-NOW is fast enough for low sample rates in PCM (8 kHz, 16 bit mono = 128 kbit/s), but the throughput drops with distance and interference - measure it with the speed-test examples, or the *_measure receivers. For better quality use a codec: the codec examples send SBC:
SBCEncoder sbc;
EncodedAudioStream encoder(&now, &sbc); // sender: encode and send
EncodedAudioStream decoder(&out, new SBCDecoder(256)); // receiver: a packet has at most 256 bytes- All devices on the same WiFi channel: if one device is also connected to an access point, it uses the access point's channel - set the same channel on the others.
- Clock drift: sender and receiver clocks differ slightly - see Communication.
- Multiple receivers with acknowledgments: only the first receiver that acknowledges is used as coordinator - use broadcast for several receivers.