Most ESP32 projects assume a WiFi router somewhere. That assumption breaks the moment you want a remote control to talk to a robot, two sensor nodes to coordinate without internet, or a cluster of devices in a place that has no router (a workshop, a barn, a remote installation). ESP-NOW solves exactly those problems — it is a peer-to-peer protocol Espressif built on top of the WiFi radio that lets two ESP boards exchange short messages without any infrastructure.
This article walks the protocol, the use cases, and the code patterns. Once you have done one ESP-NOW project, you reach for it constantly.
What ESP-NOW is
A connectionless protocol on top of the WiFi PHY. Two ESPs that know each other's MAC addresses can exchange 250-byte payloads with sub-10 ms latency. No SSID, no DHCP, no IP stack. The radio chip handles everything.
- Latency: ~3–5 ms typical, occasionally up to 50 ms.
- Packet size: up to 250 bytes payload.
- Range: similar to WiFi (100–200 m line-of-sight, 30–50 m through walls).
- Peers: up to 20 simultaneous (encrypted) or 250 (unencrypted) per device.
- Reliability: link-layer ACK with optional retry; no built-in deduplication.
It works on all ESP variants: ESP8266, ESP32 classic, S2, S3, C3, C6, H2 (the last one has it but is technically lower power because it lacks WiFi otherwise).
What ESP-NOW is good for
Three classic ESP-NOW patterns. Each one is awkward on regular WiFi but trivial with ESP-NOW.
Sender and receiver in code
Receiver
#include <esp_now.h>
#include <WiFi.h>
typedef struct {
uint8_t cmd;
int16_t value1;
int16_t value2;
} __attribute__((packed)) Message;
void onReceive(const uint8_t *mac, const uint8_t *data, int len) {
if (len != sizeof(Message)) return;
Message m;
memcpy(&m, data, sizeof(m));
Serial.printf("cmd=%u v1=%d v2=%d from %02X:%02X:%02X:%02X:%02X:%02X\n",
m.cmd, m.value1, m.value2,
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
Serial.println(WiFi.macAddress()); // print so sender knows our MAC
if (esp_now_init() != ESP_OK) { Serial.println("init failed"); return; }
esp_now_register_recv_cb(onReceive);
}
void loop() {}Sender
#include <esp_now.h>
#include <WiFi.h>
// Receiver's MAC address (printed by the receiver above)
static const uint8_t RECEIVER_MAC[] = { 0xC8, 0xC9, 0xA3, 0x00, 0x00, 0x00 };
typedef struct {
uint8_t cmd;
int16_t value1;
int16_t value2;
} __attribute__((packed)) Message;
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) { Serial.println("init failed"); return; }
esp_now_peer_info_t peer = {};
memcpy(peer.peer_addr, RECEIVER_MAC, 6);
peer.channel = 0;
peer.encrypt = false;
esp_now_add_peer(&peer);
}
void loop() {
Message m = { .cmd = 1, .value1 = 100, .value2 = 200 };
esp_now_send(RECEIVER_MAC, (uint8_t*)&m, sizeof(m));
delay(100);
}Topologies
- Point-to-point. Two devices, each registered as the other's peer. Used for a remote and its target.
- Star (one-to-many). A hub registers all sensors as peers; sensors only register the hub. The hub aggregates and forwards (often to WiFi for upstream reporting).
- Mesh / multi-hop. Each device knows several neighbors and forwards messages on. Espressif provides the ESP-MESH library that builds on ESP-NOW for this. Useful for installations spread across distances larger than a single hop.
- Broadcast. Send to a special address (FF:FF:FF:FF:FF:FF) and every ESP-NOW device on the same channel receives. Useful for time synchronisation or discovery.
Coexisting with WiFi
ESP-NOW shares the WiFi radio. Two facts to know:
- If the device is connected to a WiFi access point, ESP-NOW must use the same channel as that AP. The sender does not control the channel; it inherits.
- If the device is not connected, you can set ESP-NOW to any channel (1–13). Both ends must be on the same channel to communicate.
Mixing ESP-NOW with active WiFi works, but performance can suffer when WiFi traffic is heavy. For best ESP-NOW reliability, dedicate the device to ESP-NOW (no WiFi connection).
Practical tips
- Use packed structs for payloads. The 250-byte limit is real. Packed structs prevent the compiler from inserting padding bytes that waste payload space.
- Add your own sequence numbers. ESP-NOW does not deduplicate. If you retry, the receiver may see the same message twice. A 1-byte sequence number is enough for most projects.
- Encrypt sensitive payloads. ESP-NOW supports symmetric AES encryption with a shared key (set per peer). Without it, anyone in range can sniff your traffic.
- Print MAC addresses early. The receiver's MAC must be hardcoded in the sender. Printing it from the receiver's serial console is the fastest way to know it.
- WiFi.channel() can shift. If your gateway is also a WiFi station, the channel can change when the AP roams. Make peer adjustments accordingly or stay disconnected from WiFi.
Frequently Asked Questions
What is the maximum range?
Realistic outdoors with stock antennas: 200 m line-of-sight. With external antennas: 1 km is achievable. Indoors through walls: 30–50 m. Range is similar to a regular WiFi link, governed by the same physics.
How does ESP-NOW compare to BLE?
BLE is lower power and more universal (phones can talk to it), but lower throughput and latency. ESP-NOW is faster and supports larger groups but only ESP devices can participate. For ESP-to-ESP communication without infrastructure, ESP-NOW wins. For ESP-to-phone, BLE wins.
Can I use ESP-NOW from an Arduino library?
The Arduino ESP32 core ships with ESP-NOW support — the code samples above compile against it. The ESP8266 Arduino core also has ESP-NOW (slightly different API).
Is ESP-NOW reliable for safety-critical applications?
No. The link-layer ACK gives basic reliability but the protocol has no formal guarantees, no in-order delivery, no congestion control. For controlling a remote-control plane or a bench drill, fine. For an industrial actuator that could injure someone, use a protocol with proper guarantees (or wired control).
How does ESP-MESH differ from raw ESP-NOW?
ESP-MESH adds multi-hop routing, automatic neighbor discovery, parent-child topology, and persistent connections on top of the radio. More complex to set up; useful when you need a network larger than a single ESP-NOW hop. For most projects, raw ESP-NOW is sufficient.
Share your thoughts
Worked with this in production and have a story to share, or disagree with a tradeoff? Email us at support@mybytenest.com — we read everything.