Exploring ESP-NOW: Wireless Communication Between ESP32 Modules

Learn how to set up ESP-NOW for fast, low-power wireless communication between ESP32 devices, including indoor and outdoor range tests and long-range mode.
Aug 15, 2025 — 8 mins read — Electronics

Exploring ESP-NOW: Wireless Communication Between ESP32 Modules

I’ve been experimenting a lot with different ways to get devices talking to each other without relying on Wi-Fi networks or extra hardware. In one of my earlier projects, I used LoRa modules to send data over long distances, but this time I wanted to try something built right into the ESP boards themselves. That’s where ESP-NOW comes in.

ESP-NOW is a communication method developed by Espressif that lets ESP32 and ESP8266 boards send data directly to each other. There’s no need for a router, internet connection, or even pairing beforehand. It’s simple, quick, and uses very little power, which makes it great for things like smart home devices, sensors, or remote controls.

In this project, I set up two ESP modules so they can send and receive small bits of data, and I tested how far I could get the signal to travel in a real-world environment. I’ll walk you through the setup, the code, the range tests I did, and what worked well (and what didn’t) when using ESP-NOW.




Introduction to ESP-NOW

ESP-NOW is a feature made by Espressif that works on both ESP32 and ESP8266, and it lets two or more devices talk to each other directly. You don’t need a Wi-Fi network, router, or any complicated setup. As long as you know the MAC address of the device you want to reach, you can send data straight to it.

It runs on the same 2.4 GHz frequency that Wi-Fi uses, but instead of connecting through an access point, the devices communicate directly. The messages can’t be too big, only up to 250 bytes at a time, but they can be sent very quickly, which makes it fine for sending sensor readings or control signals.

This makes ESP-NOW a good option for things like home automation, wireless controls, or sensor networks. It’s low power, fast, and doesn’t need any extra parts, so it’s easy to set up and start experimenting with.


Project Setup and Goal

For this test, I set up two ESP modules. One would stay in the office and act as the sender, and the other would move around the house as the receiver. The sender’s job was to keep sending out small packets of data, and the receiver would light up its onboard LED whenever a packet arrived. That way, I could easily see when communication was working without having to stare at the serial monitor all the time.

To make them talk to each other, I first needed the MAC address of the receiver. I printed it out at the start of the receiver’s code so I could copy it into the sender’s sketch. That way, the sender knew exactly which device to target.

The goal was simple: send some data every two seconds, move around the house with the receiver, and see how far and how reliably the two devices could communicate using ESP-NOW.


Code

Below is the full code for the two devices. You can watch the video above for the full walkthrough and explanations. The example is already set with the long-range option enabled.

Sender Code

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

typedef struct struct_message {
 int id;
 float temperature;
 float humidity;
} struct_message;

struct_message outgoingData;

uint8_t broadcastAddress[] = {0x54, 0x32, 0x04, 0x0C, 0xDC, 0x04}; // Replace with your mobile ESP32's MAC

void onSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
 Serial.print("Send Status: ");
 Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail");
}

void setup() {
 Serial.begin(115200);

 WiFi.mode(WIFI_STA);
 esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR); // Long Range mode
 WiFi.disconnect();

 if (esp_now_init() != ESP_OK) {
  Serial.println("ESP-NOW init failed");
  return;
 }

 esp_now_register_send_cb(onSent);

 esp_now_peer_info_t peerInfo = {};
 memcpy(peerInfo.peer_addr, broadcastAddress, 6);
 peerInfo.channel = 0;
 peerInfo.encrypt = false;

 if (esp_now_add_peer(&peerInfo) != ESP_OK) {
  Serial.println("Failed to add peer");
  return;
 }

 Serial.println("Sender ready");
}

void loop() {
 outgoingData.id = 42;
 outgoingData.temperature = random(20, 35);
 outgoingData.humidity = random(30, 70);

 esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &outgoingData, sizeof(outgoingData));

 delay(2000);
}


Receiver Code

#include <esp_now.h>
#include <WiFi.h>
#include "esp_wifi.h"
#include <Adafruit_NeoPixel.h>

typedef struct struct_message {
 int id;
 float temperature;
 float humidity;
} struct_message;

struct_message incomingData;

// NeoPixel (WS2812) config
#define LED_PIN   8 
#define NUM_PIXELS 1

Adafruit_NeoPixel rgbLED(NUM_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);

float calculateDistance(int rssi) {
 float rssiAt1Meter = -56// Calibrate this
 float n = 2.0;       // For open space
 return pow(10.0, ((rssiAt1Meter - rssi) / (10.0 * n)));
}

void flashRGB(uint8_t r, uint8_t g, uint8_t b, int durationMs = 100) {
 rgbLED.setPixelColor(0, rgbLED.Color(r, g, b));
 rgbLED.show();
 delay(durationMs);
 rgbLED.clear();
 rgbLED.show();
}

void onReceiveData(const esp_now_recv_info_t *recv_info, const uint8_t *incomingDataBytes, int len) {
 memcpy(&incomingData, incomingDataBytes, sizeof(incomingData));

 int rssi = recv_info->rx_ctrl->rssi;
 float distance = calculateDistance(rssi);

 flashRGB(0, 255, 0); // Flash GREEN on receive

 Serial.println("📡 Data Received:");
 Serial.print("ID: "); Serial.println(incomingData.id);
 Serial.print("Temp: "); Serial.print(incomingData.temperature); Serial.println(" °C");
 Serial.print("Humidity: "); Serial.print(incomingData.humidity); Serial.println(" %");
 Serial.print("RSSI: "); Serial.print(rssi); Serial.println(" dBm");
 Serial.print("Estimated Distance: "); Serial.print(distance, 2); Serial.println(" meters");
 Serial.println("-------------------------------");
}

void setup() {
 Serial.begin(115200);

 rgbLED.begin();
 rgbLED.clear();
 rgbLED.show();

 WiFi.mode(WIFI_STA);
 esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR); // Long Range mode
 WiFi.disconnect();

 Serial.print("Receiver MAC: ");
 Serial.println(WiFi.macAddress());

 if (esp_now_init() != ESP_OK) {
  Serial.println("ESP-NOW init failed");
  return;
 }

 esp_now_register_recv_cb(onReceiveData);
 Serial.println("Receiver ready");
}

void loop() {
 // Nothing here
}


Indoor Range Test

Once everything was set up, I started testing how far the signal could reach inside the house. I left the sender on the desk in the office and walked around with the receiver, watching the LED and the serial monitor to see when the messages were coming through. At first, it worked fine just a few meters away, but as soon as I moved behind walls or into different rooms, the signal started dropping.

I noticed that even small changes in how I held or oriented the receiver affected the signal a lot. Turning it one way could improve the reception, while tilting it slightly could make messages stop arriving entirely. The distance readings based on signal strength were also unreliable — sometimes they said we were meters apart when we were just a few steps away.

By the time I reached rooms farther from the sender or tried going down a floor, the connection became very intermittent. The LED would flash occasionally, but most of the packets were lost. It was clear that with the built-in antennas on these boards, ESP-NOW works for short distances indoors, but walls and orientation have a big effect on reliability.


Enabling Long-Range Mode

Since the indoor range was limited, I decided to try ESP-NOW’s long-range mode. This is just a single line of code you add to the setup, and it boosts the range by lowering the data rate. Everything else in the sketches stayed the same, so sending and receiving data worked just like before.

esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR); // Long Range mode

With long-range enabled, I noticed a small improvement. The receiver could pick up signals a bit farther away and in some tricky spots, but it still wasn’t perfect. Orientation still mattered a lot, and moving the module slightly could break the connection. The range was better than before, but not dramatically — I was still mostly limited to a few meters indoors with the stock antennas.

The trade-off is that long-range mode cuts the data bandwidth in half. Since my test was sending data only every couple of seconds, it didn’t matter, but it’s something to keep in mind if you plan to send more frequent or larger packets. It’s a useful option if you just need to stretch the reach a bit without changing hardware.


Outdoor Test

After testing indoors, I took the modules outside to see how far ESP-NOW could really go. I placed the sender on some rocks and held the receiver a good distance away. At first, it worked, but even slight tilts or changes in orientation caused the signal to drop. It became clear that the modules were very sensitive to positioning.

Even in open space, I was only able to get reliable communication up to around 20 meters. Beyond that, the packets started missing, and the LED on the receiver stopped flashing consistently. This was far below the advertised range of ESP-NOW, but I was using the built-in PCB antennas on the ESP32 boards, which are not optimized for long-distance transmission.

The outdoor test confirmed that ESP-NOW can work for short-range applications without extra hardware, but for anything that needs stable communication over tens or hundreds of meters, external antennas or higher-powered modules would be necessary.


Conclusion

In the end, testing ESP-NOW showed me that it’s a simple way to make ESP32 modules talk to each other without Wi-Fi routers or extra hardware. It’s fast, low-power, and works well for sending small packets between nearby devices. For things like smart home sensors or short-range controls, it’s easy to set up and reliable enough.

The biggest limitations I ran into were range and sensitivity to orientation. Walls, floors, and even how the modules were held could cause signals to drop. Long-range mode helped a little, but it still doesn’t match the advertised distances unless you use external antennas or specially designed modules.

Overall, ESP-NOW is a fun tool to experiment with, and it’s perfect for projects where devices stay close together. If you’re planning anything that needs more distance, you’ll need to think carefully about placement, antennas, and obstacles, but for simple indoor or nearby outdoor communication, it works surprisingly well.



The links below are affiliate links. When you click on affiliate links in my articles or videos, it means I may earn a small commission if you make a purchase. These links are a way to support my work without costing you anything extra—the price you pay stays the same, whether you use the link or not. The commissions I earn help cover the costs of creating free content, so I can keep sharing knowledge and helping you with your DIY projects. It’s a win-win: you get the tools or products you need, and I get to keep creating helpful resources for everyone. Thank you for your support!


esp32 remote esp8266 esp-now
Read next

ZBDongle-E Setup: Adding Thread & Matter Support to Home Assistant

I was using the ZBDongle-P with Home Assistant and thought it would be enough for everything I needed. It worked fine for Zigbee, but when I...

You might also enojy this

Setting up Zigbee Dongle on Home Assistant in a Proxmox VM

If you’re like me and love building smart home devices, you’ve probably noticed how quickly your Wi-Fi network can get overwhelmed. Every ne...