ESP32/Other radios/64. ESP-NOW
Your chip
Your language
Other radios · 64 of 81

ESP-NOW

Boards talking straight to each other by MAC address, with no router, no IP address and no handshake. A battery sensor can wake, send and be asleep again in 15 milliseconds - about two hundred times faster than joining Wi-Fi.

/esp32/esp-now · arduino · S3

Fifteen milliseconds against three seconds

ESP-NOW, no router
14 ms
Peers4
Wake to delivered
14 ms
On a 2000 mAh cell, once a minute
5.7 yr
Payload
250 B
14 ms of radio per message, because there is no association, no DHCP and no TCP. You address peers by MAC address on a fixed channel. What you give up: no internet, no phone can talk to it, 250 bytes per packet, and every board must sit on the same Wi-Fi channel — so a receiver that is also on your Wi-Fi network is locked to whatever channel the router chose.

What you give up

  • No internet. Nothing outside the radio's range exists.
  • No phone. ESP-NOW is an Espressif protocol; phones do not speak it.
  • 250 bytes a packet. Send a packed struct, not JSON.
  • One channel for everybody. If any board is also on Wi-Fi, that board's router chooses the channel and everyone else must follow.

The shape most projects end up with

Battery sensors send ESP-NOW to one mains-powered board. That board is on Wi-Fi and forwards everything to MQTT or a web page. The sensors get years on a battery; the gateway gets a wall socket; nobody has to choose.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

The code

Send a struct, not a string. Both ends compile the same definition, so there is no parsing and the whole reading fits in one packet with room to spare.

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

uint8_t peer[] = { 0x24, 0x6F, 0x28, 0x00, 0x00, 0x00 };  // the receiver

typedef struct { uint8_t id; float temp; uint16_t mv; } Reading;

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

  esp_now_peer_info_t info = {};
  memcpy(info.peer_addr, peer, 6);
  info.channel = 1;                 // both ends must agree
  esp_now_add_peer(&info);

  Reading r = { 3, 21.4, 3820 };
  esp_err_t err = esp_now_send(peer, (uint8_t *)&r, sizeof(r));
  Serial.println(err == ESP_OK ? "queued" : "failed");
}

void loop() {}

The receiver's MAC address is printed by WiFi.macAddress() on that board. There is no discovery - you either hard-code it or broadcast to FF:FF:FF:FF:FF:FF.

When it does not work

Packets are sent successfully and never arrive

The two boards are on different channels. ESP-NOW has no channel negotiation. If one board is also joined to Wi-Fi, it is locked to the router's channel and the other must match it.

It works between two boards and breaks when one joins Wi-Fi

Same cause. A receiver that is also a Wi-Fi station follows the router. Read WiFi.channel() on that board and set the sender to match.

The send callback says success and nothing was delivered

Success means the packet left the radio, not that anybody heard it. Add an acknowledgement of your own if delivery matters.

Range is much worse than Wi-Fi

It should be similar. Check that neither board is in a metal case and that you are not sending from inside a deep-sleep wake-up before the radio has settled - give it a few milliseconds.

Where this goes next

Peer to peer works to the edge of range. Past that, nodes have to relay for each other.

ESP-WIFI-MESH

Edit this page — content/esp32/esp-now.mdx

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse ESP32 on the forum