ESP-NOW Explained: ESP32-to-ESP32 Wireless Without Wi-Fi

ESP-NOW is Espressif's connectionless 2.4 GHz protocol that lets ESP32 boards talk directly — no router, no internet, no broker. This guide explains peers, MAC addresses, payloads and callbacks, then walks through a complete sender and receiver build in Arduino, plus the silent-failure gotchas (channel mismatch, struct alignment, weak power) and when to choose ESP-NOW over MQTT, BLE, or LoRa.

Written by Projectech7 min readPublished
For IoT and embedded students comfortable with ESP32 and Arduino who want fast board-to-board wireless links without a router, broker, or the internet. Topics: ESP32, ESP-NOW, Arduino IDE, 2.4 GHz radio
Blueprint-style technical illustration of two microcontroller boards exchanging radio waves directly with each other, with no router between them.
Illustration generated for this guide.
In this guide

Two ESP32 boards can exchange sensor data directly over the air — no Wi-Fi router, no internet connection, no MQTT broker, no cloud account. The protocol that makes this possible is ESP-NOW, Espressif's connectionless 2.4 GHz peer-to-peer protocol. It is the simplest way to build multi-node projects: a sensor node in the field talking to a gateway node next to your laptop, a handheld remote driving a robot, several buttons reporting to one display. This guide explains how ESP-NOW works, walks through a complete sender and receiver build, and covers the gotchas that make a beginner's first ESP-NOW project fail silently.

The problem ESP-NOW solves

Every wireless option you already know comes with baggage:

  • Wi-Fi needs infrastructure: a router, credentials, IP configuration. Fine at home, awkward in a demo hall, a field, or an exam lab where you don't control the network.
  • Bluetooth/BLE is built for phone-to-device links — pairing friction, short range, and modest throughput for frequent sensor data.
  • LoRa reaches kilometres but at very low data rates, needs extra modules, and is overkill for two boards in the same room.

ESP-NOW sits in the gap: fast, no handshake, no router, no pairing dance. Two boards that know each other's MAC address just talk.

How ESP-NOW works

ESP-NOW sends connectionless datagrams on the 2.4 GHz band. There is no association, no IP address, no DHCP — a board transmits, and any peer in range that recognises the sender picks the packet up.

  • Peers are identified by MAC address. You add each peer's 6-byte MAC to a peer list on the sender. No discovery, no pairing codes.
  • Payloads are small by design: up to 250 bytes. Send readings, commands, and status — not files or images.
  • Callbacks report what happened. A send callback tells you whether the packet was acknowledged; a receive callback fires with the raw bytes on arrival.
  • Both devices must use the same Wi-Fi channel. This is the number-one silent failure (more below).
  • Encryption is optional, using Espressif's PMK/LMK key scheme for projects where the airwaves shouldn't be readable.
  • It works ESP32-to-ESP32 and ESP32-to-ESP8266, which is handy when you have a mixed parts bin.

Think of it as passing notes in class: short messages, direct delivery, no post office involved.

Build: a sender and a receiver

You need two ESP32 boards and the Arduino IDE with the ESP32 board package. We'll send a small struct — node ID, temperature, humidity — every two seconds. Replace the dummy values with a real sensor reading once it works.

Step 1 — Get the receiver's MAC address. Upload this once to the board that will receive, open the Serial Monitor, and write the address down:

#include <WiFi.h>

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  Serial.println(WiFi.macAddress());
}

void loop() {}

Step 2 — The sender. Paste the receiver's MAC into receiverMac:

#include <esp_now.h>
#include <WiFi.h>

uint8_t receiverMac[] = {0x24, 0x6F, 0x28, 0xAA, 0xBB, 0xCC};

typedef struct {
  float temperature;
  float humidity;
  int nodeId;
} SensorData;

SensorData data;

void onSent(const uint8_t *mac, esp_now_send_status_t status) {
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Sent OK" : "Send failed");
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  esp_now_init();
  esp_now_register_send_cb(onSent);
  esp_now_peer_info_t peer = {};
  memcpy(peer.peer_addr, receiverMac, 6);
  esp_now_add_peer(&peer);
}

void loop() {
  data.temperature = 27.5;  // replace with your sensor reading
  data.humidity = 61.0;
  data.nodeId = 1;
  esp_now_send(receiverMac, (uint8_t *)&data, sizeof(data));
  delay(2000);
}

Step 3 — The receiver:

#include <esp_now.h>
#include <WiFi.h>

typedef struct {
  float temperature;
  float humidity;
  int nodeId;
} SensorData;

void onReceive(const uint8_t *mac, const uint8_t *incoming, int len) {
  SensorData data;
  memcpy(&data, incoming, sizeof(data));
  Serial.print("Node "); Serial.print(data.nodeId);
  Serial.print(": "); Serial.print(data.temperature);
  Serial.print(" C, "); Serial.print(data.humidity);
  Serial.println(" %");
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  esp_now_init();
  esp_now_register_recv_cb(onReceive);
}

void loop() {}

Upload the sender to one board and the receiver to the other, open both Serial Monitors, and you should see readings arriving every two seconds.

Step 4 — Make it two-way. Register both callbacks on both boards and add each board as the other's peer. Now either side can initiate — a remote control and its robot, for example.

Step 5 — One-to-many. Add several peers with esp_now_add_peer and loop over them with esp_now_send. One gateway board can collect from a handful of sensor nodes this way (up to 20 peers).

Gotchas that break first projects

  1. Channel mismatch — the silent killer. Both boards must be on the same Wi-Fi channel or packets vanish without a trace. If either board is also connected to a router as a station, ESP-NOW follows that connection's channel. Simplest rule for beginners: keep both boards off router Wi-Fi while learning ESP-NOW.
  2. A typo in the MAC address. Double-check every byte. A wrong MAC means packets go nowhere, and the only symptom is the "Send failed" callback.
  3. Struct mismatch. Both sketches must define the struct identically — same fields, same order, same types. If one side adds a field, the bytes stop lining up and values come out as garbage.
  4. Payload over 250 bytes. Split larger messages across packets or reconsider whether ESP-NOW is the right transport.
  5. Forgetting WiFi.mode(WIFI_STA) before esp_now_init(). The radio must be in station mode first.
  6. Weak power on transmit. Radio transmission draws current spikes; a feeble USB port or thin cable causes brownouts and resets exactly when sending. Power the boards properly during development.

Frequently asked questions

ESP-NOW or MQTT?
Different jobs. MQTT needs a broker and an IP network; ESP-NOW needs nothing. The classic student architecture uses both: ESP-NOW for the local hop (sensor nodes to gateway), MQTT for the internet hop (gateway to dashboard).

ESP-NOW or BLE?
BLE for phone-to-device (an app talking to your gadget); ESP-NOW for device-to-device (your gadgets talking to each other).

Can an ESP32 talk to an ESP8266 over ESP-NOW?
Yes. Both chips speak the protocol, so a mixed-parts-bin project works.

What range can I expect?
Roughly what ordinary 2.4 GHz Wi-Fi gives you in the same environment — walls, metal, and crowded spectrum all matter. Always test in the actual room where you'll demo.

Can ESP-NOW reach the internet?
No — there is no IP layer. Use one ESP32 as a gateway: it receives ESP-NOW packets and forwards them over Wi-Fi/MQTT.

Is it secure?
Optional encryption exists via PMK/LMK keys. Treat it as a convenient local link, not a hardened channel — don't send anything you wouldn't want overheard.

Limitations

ESP-NOW is a specialist tool with honest boundaries. The 250-byte payload rules out images, audio, and files. There is no IP, no routing, and no internet — it is a local link only. Delivery is reported by callback, not guaranteed like TCP, so design your messages to be idempotent or retried rather than assuming each one lands. The 2.4 GHz band is shared with Wi-Fi and Bluetooth, so a crowded lab or auditorium can interfere. And 20 peers is plenty for student meshes but not a large network. Within those limits, it is the fastest way to get two boards talking.

More project guides

More in IoT & Embedded