Today we will be working with ESP-NOW, a connectionless protocol developed by Espressif for the ESP32 and ESP08266 microcontrollers. With ESP-NOW we can build a private network without WiFi or a Router!



The ESP32 and its cousin, the ESP8266, are undoubtedly remarkable microcontrollers. Aside from a high-speed 32-bit architecture, they also have built-in Bluetooth and WiFi.

The Bluetooth and WiFi capabilities on these devices are made possible by an integrated 2.4GHz radio transceiver module. And this module can also be used for other communications applications that use the unlicensed 2.4GHz band.

Espressif, the makers of the ESP8266 and ESP32, have developed a protocol that allows all these devices to create a private, wireless network using the 2.5GHz transceivers. This is a separate network from the WiFi network and can only be used by ESP-type microcontrollers.

The protocol is called ESP-NOW.


ESP-NOW allows simple packet communications between ESP devices, using the 2.4 GHz band. These transmissions operate a lot like those used by wireless mice and keypads and are limited to packets of 250 bytes or fewer.

Yes, I said 250 bytes! Not quite enough for voice and video perhaps, although there are ways of packetizing both of those, but quite sufficient to deliver remote control commands or data from sensors.

The data can be unidirectional or bidirectional, i.e. single-duplex or full-duplex. Most data types are supported.

Data can be encrypted or unencrypted, and no external source of WiFi or a router is required. Depending upon your configuration, you can have anywhere from 2 to 20 devices communicating between themselves.

The range can vary dramatically due to the environment, but under the right conditions (and with proper antennas) you can achieve over 400 meters. Just using the built-in antennas on the modules should still allow you to communicate through a medium-sized home without a problem.

ESP-NOW Networking Modes

You can place your ESP-NOW network in many configurations. You can mix and match ESP32 and ESP8266 devices within the same network.

Initiators and Responders

A device participating in an ESP-NOW network can be operated in one of two modes. 

  • Initiator – This device initiates the transmission. It will require the MAC address of the receiving device.
  • Responder – This device receives the transmission.

In unidirectional (half-duplex) mode, the transmitting device is the Initiator and the receiving device is the Responder.

In a 2-way (full-duplex) communications mode, each device is both an Initiator and Responder.

One-Way Communication

The simplest communications topology is one-way, unidirectional communications.

In this arrangement, the Initiator ESP32 transmits data to the Responder ESP32. The Initiator can tell if the Responder received the message successfully.

This is a simple arrangement, but it has many uses in remote control applications.

One Initiator & Multiple Responders

This setup consists of one Initiator that is communicating with multiple responders.

The configuration can be used in two fashions:

  • The Initiator communicates with each Responder individually.
  • The Initiator initiates a broadcast message to communicate with all the Responders.

An alarm system might use this sort of configuration to activate remote sounders or communicate with remote monitors when an alarm has been triggered

One Responder & Multiple Initiators

This is the reverse of the previous ESP-NOW network configuration. In this arrangement, we have one Responder and multiple Initiators.

This arrangement is very common as it is used for remote sensor applications, with the Responder gathering data sent by the Initiator.

Two-Way Communications

The ESP-NOW protocol can also handle bidirectional, or full-duplex, communications.

This illustrates the simplest arrangement, with two devices communicating with one another.

In this, and all bidirectional communications configurations, the ESP-32 acts as both Initiator and Resolver.

Two-Way Networking

Expanding upon the previous configuration even further, we come up with this arrangement, four boards that have bidirectional communications established with one another.

Note that the boards can be a mix of different models of both ESP32 and ESP8266 devices.

MAC Addresses

When Initiators communicate with Responders, they need to know the Responder’s MAC Address.

A MAC, or Media Access Control, Address is a unique 6-digit hexadecimal number assigned to every device on a network. It is generally burned into the device by the manufacturer, although it is possible to manually set it.

Every ESP32 and ESP8266 has its own unique MAC address, and you’ll need to know that address to use it as a Responder in the experiments we will be doing today.

MAC Address Sketch

Here is a very simple sketch that you can run on an ESP32 to determine its unique MAC Address:

All we are doing is including the WiFi Library, initializing the serial monitor, placing the ESP32 into Station mode, and then asking it for its MAC address. The result is printed on the serial monitor.

Running The MAC Sketch

The entire sketch runs in the Setup section, so after loading it to the ESP32, it will likely run before you get a chance to view it on the Serial monitor.

You can press the Reset key on your module to force it to run again.

The MAC Address will be at the bottom of the screen. Copy it to a safe location, so that you can use it later.

Coding for ESP-NOW

The ESP-NOW Library is included in your ESP device boards manager installation.  It has a number of functions and methods to assist with coding for ESP-NOW.

In order to see how it works, you need to examine the sending and receiving of an ESP-NOW message packet.


A callback is a bit like an interrupt, it is generated every time a specific event has occurred.

In the ESP-NOW protocol, there are two callbacks of interest:

  • Sending Data – The esp_now_register_send_cb() callback is called whenever data is sent.
  • Receiving Data – The esp_now_register_rcv_cb() callback is called when data is received.

In your code, you will create a callback function that you will bind to either the sending or receiving callback using the functions listed above.  Your function will run every time the event occurs.

The callback functions also return some useful data:

  • The Sending callback returns the status of the sent data.
  • The Receiving callback includes the received data packet.

Coding for ESP-NOW – Sending

If you are writing code to use the ESP32 or ESP8266 as the Initiator, then this is what you need to accomplish:

  • You need to initialize the ESP-NOW library.
  • Next, you’ll register your send callback function
  • You need to add a peer device, which is the responder device. You add the peer by specifying its MAC address.
  • Finally, you can packetize and send the message.

Coding for ESP-NOW – Receiving

To write code for the ESP-NOW responder, you’ll need to do the following:

  • You need to initialize the ESP-NOW library.
  • Next, you’ll register your receive callback function.
  • In the receive callback, you’ll capture the incoming message data and pass it to a variable.

Let’s dig out some ESP boards and start experimenting with ESP-NOW.

Experimenting with ESP-NOW

I’ll be playing with the ESP32 today, but you should be able to run everything on an ESP8266 as well. If you do, you’ll need to change the WiFi library, as the ESP8266 uses a different one.

Gather a few modules, a mix of different manufacturers boards is fine, and run the MAC Address sketch on each of them and save the address to a text file or spreadsheet.

One-Way Data Test

Our first test is very simple and is a great way to learn the fundamentals of sending and receiving data using ESP-NOW.

We will need two ESP32 boards for this test. One board will be the Initiator (sender), and the other will be our Responder (receiver).  We don’t require any additional hardware on the modules, as we’ll be monitoring the results with the serial monitor in the Arduino IDE.

As one board is Initiator and the other Responder, they will be running different sketches, which are shown below. You’ll need the MAC address of the Responder, as it goes into the Initiators code.

One-Way Initiator Sketch

The first sketch we will be running is the One-Way Initiator Sketch. This is run on the board that is transmitting the data.

This is the simplest mode of communication with ESP-NOW, and it will show you the fundamentals of sending data using the ESP-NOW protocol.

We start by including both the WiFi and ESP-NOW libraries. Both were installed on your Arduino IDE when you set up the ESP32 Boards Manager, so there is no need to install them from the Library Manager.

We then set up a few variables we are going to transmit to the receiver. There is an integer, a float, and a boolean. We will also be sending some text in a character variable, which you’ll see shortly.

Now we need to enter the MAC address of the Responder board, which is the other ESP32 board. You will need to change the value in the sketch to match your board’s reading.

Now we define a data structure. This is the structure of the data message we want to send, and it should match the types of variables we are using. You’ll see that we start with a character array variable, followed by an integer, float, and boolean. Those data types match the types we just defined.

We then create a structured data object called myData to hold the data we will be transmitting.

Now we define our callback function, which we are calling OnDataSent. This will be called when data has been transmitted. In the function, we just print the status of the t transmission to the serial monitor.

Now we go to the Setup.

In Setup, we initialize our serial monitor and set the ESP32 temporarily as a WiFi Station. 

Then we initialize ESP-Now

Assuming that it initializes, we register our callback function and then add information about the peer, which is our Responder.

Now to the Loop.

In the Loop we create some test data for the integer, float, and boolean. Then we add it to the myData object, along with the string “Welcome to the Workshop”.

Then we send the message. We print the result and then delay two seconds before starting at the top of the Loop.

One-Way Responder Sketch

Receiving has several similarities to transmitting.

We start by loading the same two libraries, and we then define a matching data structure and c a structured data object.

We then define our callback function, which in this case is called whenever data is received.

Remember, the receive callback returns the data in the message, so that is where all the “action” takes place! We grab the data and print it to the serial monitor.

We then go to the Setup, where we initialize the serial monitor and set up the ESP32 up as a WiFi Station. 

Next, we attempt to initialize ESP-NOW. If we succeed, then we register our callback function.

And we are done. Nothing happens in the Loop, as everything is handled in the callback function.

Testing it Out

Load the sketches to their respective ESP32 boards.

Ideally, you’ll want to observe both of the boards’ output on a serial monitor. Instead of using two computers, you can just open two instances of the Arduino IDE on your computer and run them with different COM ports selected. That way, you can open up two serial monitors.

You should see the Initiator display the status of its sent messages. The Responders serial monitor will display the data that is received, and note how the variables change on each transmission.

Broadcast Mode

Our next demonstration will put the ESP32 in a 2-way Broadcast mode. You’ll need at least two ESP32 boards to do this experiment, but it is more fun if you have three or more!

We will have an LED and pushbutton attached to each of our ESP 32 boards.

All the LEDs will remain in the same state on every board. So if one is on, they are all on. The pushbuttons can toggle the LED states, any pushbutton will affect all the LEDs.

Broadcast Mode Hookup

Here is how we will hook up each of the ESP32 boards.

You can use any color of LED you like, and they don’t have to be the same color on each board. The resistor I used had a value of 150-ohms, but any value from 100 to 220 ohms will work fine.

The pushbutton is a standard momentary contact, normally-open switch.

Broadcast Mode Sketch

Here is the sketch that you will run on every board. As we are using broadcast mode, we don’t need to know the other boards’ MAC addresses, so every board can run identical code.

We start with the two libraries, after that we define boolean variables to represent the state of the LED and pushbutton.  We also define the pins these devices connect to.

There is also a function to correctly format the MAC addresses that are returned to us when we scan for recipients.

As we are both an Initiator and a Responder, we have two callbacks, one for send and one for receive.

The receive callback grabs the data and looks for the word “on”. If it gets it, then it turns on the LED, otherwise, it turns the LED off.

The send callback just prints up information about the sent packets on the serial monitor.

The broadcast function creates the broadcast message, by sending out the special MAC address of FF:FF:FF:FF:FF:FF. Each peer responds with its MAC address, which is used to send out the data.

In Setup, we do the usual – start the serial monitor, start the ESP32 in station mode before starting ESP-NOW.

We then register both callbacks.

Finally, we define the LED and pushbutton pins and an OUTPUT and INPUT, respectively. We use the internal pull-up for the pushbutton, which saves us the trouble of wiring up a resistor of our own.

In the Loop we examine the pushbutton status, if it is down then we toggle the state of the ledOn variable and send a broadcast message with a value of “on” or “off”, dependent upon its final value.

Testing Broadcast Mode

Load up the sketch onto each ESP32 module that you have wired up and power them all up. You should observe that the LED status on each board is identical and that you can change the status by pressing the pushbutton on any of the boards.

Remote Temperature & Humidity Sensor

Let’s put our ESP-NOW knowledge to work and build something practical – a remote temperature and humidity sensor.

We will have our old friend the DHT22 temperature and humidity sensor attached to the Initiator ESP32, broadcasting its values every two seconds.  Our Responder will display the readings on the serial monitor.

This could be the basis for a practical project, with a display instated of a serial monitor.

Temperature Sensor Hookup

Here is how we will wire up our Initiator board.

Note that we are also using a 10K pull-up resistor on the data input line. This is actually optional, but you may find it more reliable with it.

The Responder (receiver) does not need any additional components wired to it.  You will need to know its MAC address for all of this to work.

Initiator Sketch (Temperature Sender)

Here is the sketch that we will run on the Initiator board:

We include the two ESP libraries, plus the DHT library from Adafruit. If you don’t already have this library installed, you can find it in your Library Manager by searching for “DHT”. You want the library from Adafruit.

After setting up the DHT sensor parameters (which you can change if you want to use a DHT11 instead) we define a couple of floats for temperature and humidity.

We then have the MAC address of the responder. You will need to change this to the MAC address of your Responder ESP32 board.

We next define our data structure. As we are sending a couple of floating variables, we define exactly that – two floats.   We then use this definition to create a structured data object.

Next is the sent callback function, which just prints some diagnostic data on the serial monitor.

In the Setup, we start the serial monitor and the DHT22. We do the usual and start the hESP32 in station mode before starting ESP-NOW.

We then register our callback and add our peer, as we saw in earlier sketches.

In the Loop, we query the DHT 22 and get the temperature and humidity values, which we assign to their respective variables. We also print those to the Serial Monitor.

We then add those values to the myData structured data object.

Then we send the data to the Responder.

After that we delay for a couple of seconds, this is for the benefit of the DHT22. Then we go back and start the Loop again.

Responder Sketch (Temperature Display)

As you have probably started to notice, the Responder sketches are much shorter than the Initiator ones. This is because we have been doing all of our work in our callback function, with no code in the Loop. There is also less to set up for a Responder, as it doesn’t need the Initiator’s MAC address.

Here is the sketch for our Responder, which receives and displays the temperature and humidity values sent by the Initiator:

We start with the usual two libraries, followed by a definition of a data structure with two floats, to match the transmitted one.

We then define our callback, which, like in other Responder sketches, does most of the work.

In this callback function, we grab the data and display it on the serial monitor.

The Setup is pretty straightforward, start everything and register the callback function. And there is nothing in the Loop, as the callback is what drives the serial monitor.

Testing the Remote Temperature Sensor.

Once again, you can use two serial monitors on the same machine if you wish, just open two instances of the Arduino IDE. Or you can choose to only monitor the Responder serial monitor, which will display the temperature and humidity values from the Initiator.

You should be able to move the initiator a fair distance from the Responder, so this can make a good remote sensor.

Multiple Sensors Modification

We can modify our Remote Temperature and Humidity Sensor project to use multiple Initiators. That way, we can monitor the temperature in more than one location.

We will just add one more board for this test, but you could expand it to several more. If you did expand it you’ll want to improve the Responder side to keep track of the data better, in this demonstration we are still using the serial monitor but an OLED would probably be more practical for a multi-sensor unit.

Of course, you will need an additional ESP32, along with a DHT22 and pull-up resistor for this experiment. It is wired identically to the first one.

We will also need to modify both of our sketches to make our multiple-sensor configuration more reliable.  All we really need to add is a unique identifier for each Initiator so that it can be identified at the Responder end.

While we could have used the MAC address of the Initiator, it is a lot easier to define our own identity tag. In our case, we will just use an integer with a unique value for each Initiator.

Initiator Sketch Modifications

Here is the Initiator sketch, which will need to be modified with a unique identity value and then loaded onto both Temperature & Humidity sensor ESP32 boards.

It’s the same initiator sketch, with a slight modification for the unique identity value. This value is sent along with the temperature and humidity information. This way, the sender can be identified on the Responder end.

There are only three modifications to make to our sketch.

First, we add an ident variable, an integer. This is the unique value, it needs to be set to a different value for each board.

The second modification is to the data structure, where we add a third data variable, an integer. This will be what holds our new identity integer value.

And finally, when we add the temperature and humidity to the structured data object, we also add the identity. So it gets transmitted along with the other information.

Responder Sketch Modifications

On the Responder side, we also make a few modifications to accommodate the new third variable, the integer with the Initiator identity.

The modification to the data structure is the same as it was for the transmitter, with the addition of an integer variable.

As most of our “action” in the Responder sketch is in the callback, this is where we use our new variable. We just incorporate it into the temperature and humidity reading, to identify the sender.

Running the Modified System

Load the modified sketches onto their respective boards, making sure to have changed the integer identity value for each Initiator. 

Then turn it all on and observe the serial monitor on the Responder. You should see the temperature and humidity readings from both sensors.

If they are too fast, try increasing the delay on each board above the current 2 seconds.

This is more effective if the two transmitters are in separate environments, perhaps indoors and outdoors.


ESP-NOW is a very useful protocol that allows you to build even more advanced ESP32 projects.

For remote control and remote sensing applications, this has a lot of potential, and you’ll be seeing more ESP-NOW projects here in the workshop soon.


ESP-NOW – Official documentation by Espressif

Code – All of the sketches used in this article, in a ZIP file.


ESP NOW – Peer to Peer ESP32 Communications
ESP NOW - Peer to Peer ESP32 Communications
Article Name
ESP NOW - Peer to Peer ESP32 Communications
ESP-NOW is a protocol that allows ESP32 and ESP8266 boards to form a private peer-to-peer network. No WiFi or router required!
Publisher Name
DroneBot Workshop
Publisher Logo
Tagged on:

If you have a question...

Comments about this article are encouraged and appreciated. However, due to the large volume of comments that I receive, it may not be possible for me to answer you directly here on the website.

You are much more likely to get answers to technical questions by making a post on the DroneBot Workshop Forum. Your post will be seen not only by myself, but by a large group of tech enthusiasts who can quickly answer your question. You may also add code samples, images and videos to your forum posts.

Having said that, please feel free to leave constructive comments here. Your input is always welcome. Please note that all comments may be held for moderation.

Newest Most Voted
Inline Feedbacks
View all comments
Hans Haerdtle
6 months ago

Brilliant demo and elegant solution which saves the use of nRFxx or other transmitters which I use in similar meshed projects, at least within the shorter range coverage normally sufficient for home automation projects. I’m a keen follower and subscriber of your channel. Thank you.

6 months ago

Thank you very mych for this comprehensive tutorial on ESP communications. Others hsve tried to cover this topic and never came close to the clarity that you easily display here. Am glad I waited for it to come.

Rashid Mostafa
6 months ago

Can’t get this moving with esp8266. replaced the wifi.h with esp8266.h but now the error is ‘esp_now_peer_info_t’ does not name a type. Do I have to chuck out my 8266s and get esp32s to make this work?

Larry Playzek
6 months ago

I enjoyed your TUTORIAL

Larry Platzek
5 months ago

Love your Tutorials but HATE the circles on every page over important information as I usually print the tutorial out! is there a way to not have them cover information on every page?

Rob Pardy
5 months ago

Yet another clearly presented demonstration. I’m certainly going to be adding this capability to my ESP32 projects. I’ve learnt so much from you Bill. I’m a keen follower and subscriber since I found your channel earlier this year. I was struggling before then, but now I’m gaining confidence even more rapidly. Thank you so much and please keep these tips coming.

Dr Rob
5 months ago

Well presented — no frills, no gimmicks. Well done!

Muhammad Usman
5 months ago

You are amazing keep it up Sir

John Smith
5 months ago

Well done with the explanation. I have to understand and get esp-now going quickly to replace the virtualwire link I usually use.
I have made a LED matrix scoreboard running DMD2, but the virtualwire for the remote control sharing of the timer clashes, so I am praying that I can run the DMD2 and esp-now ?

5 months ago

Always, wonderful presentation and teacher, thanks

Thomas Nutbrown
5 months ago

This is a great ESP32 platform, but what would be the changes to make it ESP8266

4 months ago

This article and of course all of your previous ones are superb and excellent.
You are a row model on how tutorials must be presented to others.
Everything is so well explained, with plenty of preparation and wisely done.
What and how you do this tutorials are no obvious
A huge thank from me
Dany lipsker

3 months ago

Hi Great tutorials really enjoy your explinanations. I was wondering what happens if two esp now senders send at sams time ? Can both messages be received ?

3 months ago

Very good post which helped me a lot.

One coding error in the receiver code example line 27, there is a redundant blank space betweem the * and the variable mac which cause failure of delivery.

3 months ago

can we have library of it

david garner
2 months ago

Great Tutorial! Thought I was going to have to spend hours learning ESP-NOW. NOT! Using your tutorial and sample code I had things up-n-run’n with my own modifications in an afternoon. I previously tried “Arduino IOT Cloud” but found it a bit clunky and some of the concepts difficult, also, there is a fee. I needed to control a remote ESP32 with 3 commands. That led me here. Previously figured out how to use an old Ruku remote to talk an ESP, now the Initiator (in my home) which talks to the Responder out in the back 40. LOVE IT!… Read more »

Chandrakant Mahajan
1 month ago

Very Useful example and explaination

29 days ago

I would like to see some tutorials for three ESP32 to communicate with each other. Both ways to communicate. Each ESP32 will have two LEDs and two Buttons. I don’t know how to explain this. But I will try my best. First ESP32: 1st button (press to make the second ESP32 LED light on). 2nd button (press to make the third ESP32 LED Light on). Second ESP32: 1st button (press to make the first ESP32 LED light on). 2nd Button (press to make the third ESP32 LED light on). Third ESP32: 1st button (press to make the first ESP32 LED… Read more »

Would love your thoughts, please comment.x