ESP32 ESP-NOW Getting Started Tutorial with Arduino IDE

In this tutorial, we will see how to perform wireless communication between ESP32 devices by using ESP- NOW communication protocol developed by Expressif. ESP-NOW is a low-power, secure, and direct wireless communication protocol that enables multiple ESP32 devices to communicate with each other without the need for WiFi or router.

ESP-NOW Protocol Introduction

Using ESP-NOW, we can perform one way and even two-way communication between ESP MCU devices without using a Wi-Fi network. It allows low overhead peer-to-peer wireless data transfers but in small packets. A maximum of 250 bytes of data can be transferred. Thus if a larger amount of data needs to be transferred then using this protocol is not useful. Using ESP-NOW, the connection protocol is simplified which results in low power consumption as a lesser amount of time is required for the transmission of data. Additionally, the ESP-NOW uses the same 2.4 GHz band as the Wi-Fi but does not need to connect or interfere with the local network connection. It is a fast and convenient communication protocol for the transmission of a smaller amount of data.

ESP32 esp now gettig started tutorial arduino ide

ESP32 ESP-NOW Key Features

  1. One-to-one transmission of data (both encrypted and unencrypted).
  2. Supports both encrypted and unencrypted peer devices. For encrypted devices, there is a limitation of a maximum of 10 peers in the station mode and a maximum of 6 in the SoftAP or a mixture of both modes. However, for unencrypted devices, a maximum of 19 devices are allowed with no limitation of the mode they are operating in.
  3. A maximum of 250 bytes of data can be transmitted in the form of small packets.
  4. It generates a callback function that notifies the application layer whether the data was transmitted successfully or not.

To initialize the ESP-NOW connection, we will have to pair our ESP32 boards first. This connection will stay regardless of any one of the board restarts and the transmission will continue smoothly.

In this tutorial, we will demonstrate an Arduino sketch through which we will wirelessly transmit data from one ESP32 board to another (one-way communication). Before doing that, let us first have a look at a few configurations in which this communication protocol can be used.

ESP32 ESP-NOW one-way communication

In one-way communication, one peered device acts as the sender/master and the other as the receiver/slave. We can have multiple configurations of the sender-receiver in this situation.

  • One ESP32 board sends data to another ESP32 board

As you can view in the picture below, one ESP32 board act as the sender, and the other board receives the data and hence acts as the receiver.
Uses: Sending sensor data, controlling ESP outputs including LEDs, relays, buzzers, etc.

ESP NOW one way communication configuration1
  • One ESP32 sender board sends data to various other ESP32 receiver boards

In this scenario, one ESP32 board will act as the sender/master and send data to multiple ESP32 boards that will act as receivers/slaves.
Uses: remote control

ESP NOW one way communication configuration2
  • One ESP32 board receives data from various other ESP32 sender boards

Lastly, in this case, one ESP32 board (receiver/slave) receives data from multiple ESP32 boards (senders/masters).
Uses: Receiving sensor data from various sensors.

ESP NOW one way communication configuration3

ESP-NOW two-way communication

The ESP-NOW protocol also enables two-way communication between peered devices. In this case, the ESP32 board can act as both the sender (master) and the receiver (slave) simultaneously. We can have two ESP32 boards transmitting data as shown below:

ESP NOW two way communication configuration1

Or even have a network of ESP32 boards transmitting data:

ESP NOW two way communication configuration2

ESP-NOW Helpful Functions

Below is a list of ESP-NOW functions that come in handy and are used often while using ESP-NOW with ESP32. To view more information, take a look at the official ESP-NOW documentation.

  • esp_now_init() : This function will initialize the ESP-NOW protocol. Remember to initiate Wi-Fi before initializing the ESP-NOW communication protocol.
  • esp_now_add_peer() : This function will connect a device by passing the unique MAC address as an argument inside it.
  • esp_now_send() : This function will send the data via ESP-NOW.
  • esp_now_register_send_cb() : This function will register a callback function which will be called whenever some data will be sent from one peered device to another. It will return a message indicating whether the data was sent successfully or not.
  • esp_now_register_rcv_cb() : This function will register a callback function which will be called whenever some data will be received by one peered device from another.

Setting up Arduino IDE

We will use Arduino IDE to program our ESP32 development boards. Thus, you should have the latest version of Arduino IDE. Additionally, you also need to install the ESP32 plugin. If your IDE does not have the plugin installed you can visit the link below:

Installing ESP32 library in Arduino IDE and upload code.

Now, first, we will introduce you to an Arduino sketch which will help us to identify our ESP32 board so that we know exactly which board to transmit the data to. It will display our ESP32 module’s MAC address on the serial monitor which we will later use in another sketch.

Arduino Sketch for obtaining the MAC Address for the ESP32 board

Open your Arduino IDE and go to File > New to open a new file. Copy the code given below in that file and save it.

#include "WiFi.h"
 
void setup(){
  Serial.begin(115200);
  WiFi.mode(WIFI_MODE_STA);
  Serial.println(WiFi.macAddress());
}
 
void loop(){

}

In the setup() function, we are first setting our ESP32 board in station mode. Then by using the WiFi.macAddress() method we will obtain the unique MAC address in our serial monitor.

Make sure you choose the correct board and COM port before uploading your code to the board. Go to Tools > Board and select ESP32 Dev Module. Next, go to Tools > Port and select the appropriate port through which your board is connected. In this case, we are using the ESP32 development board which is connected to COM5.

Click on the upload button to upload the code into the ESP32 development board. Press its ENABLE button after the sketch has been uploaded.

ESP32 enable reset button

In your Arduino IDE, open up the serial monitor and you will be able to see the unique MAC address of your ESP32 module.

ESP NOW MAC address esp32 board serial monitor
Serial Monitor

We will use this unique address to identify the receiver ESP32 module later on.

ESP-NOW One-way Communication

Now, let us learn how to send data from one ESP32 board to another, using the ESP-NOW protocol. We will use the simplest configuration, where one ESP32 board acts as the sender and the other ESP32 board acts as the receiver. You can use the method given below to transmit sensor data or any type of information from one ESP board to another. We will keep it simple and create a structure in our sketch which will store values of type char, int, float, and Boolean. This will help us in transmitting any type of data by just changing the structure in the sketch. For this project, we will require two Arduino sketches one for the sender and the other for the receiver.

ESP-NOW ESP32 Arduino Sketch for Sender Side

Open your Arduino IDE and go to File > New to open a new file. Copy the code given below in that file and save it. This sketch follows the points given below:

  • Firstly, we will initialize ESP NOW by using esp_now_init() function.
  • Then we will create a callback function called data_sent() and register it as a callback function using esp_now_register_send_cb() . This will return a message in the serial monitor showing whether the data was transmitted successfully or not.
  • The next step will be to add the receiver ESP32 board by using its unique MAC address.
  • We will then send the data to this peer device that we set up.
#include <esp_now.h>
#include <WiFi.h>

// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x7C, 0x9E, 0xBD, 0x37, 0xCA, 0x84};

typedef struct struct_message {
  char character[100];
  int integer;
  float floating_value;
  bool bool_value;
} struct_message;

struct_message message;

void data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nStatus of Last Message Sent:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
 
void setup() {

  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  esp_now_register_send_cb(data_sent);
  
  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;
  }
}
 
void loop() {
  
  strcpy(message.character, "Welcome to Microcontrollerslab! This is test example.");
  message.integer = random(1,10);
  message.floating_value = 5.6;
  message.bool_value = true;
  
  esp_err_t outcome = esp_now_send(broadcastAddress, (uint8_t *) &message, sizeof(message));
   
  if (outcome == ESP_OK) {
    Serial.println("Mesage sent successfully!");
  }
  else {
    Serial.println("Error sending the message");
  }
  delay(2000);
}

How the Code Works?

Including Libraries

Firstly, we will include the necessary libraries. For the sender sketch, we are using two of them. These include esp_now.h for the ESP-NOW communication protocol and WiFi.h will allow our ESP32 board to use the Wi-Fi functionalities.

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

Specifying Receiver MAC Address

Secondly, we will specify the MAC Address of the ESP32 board which will act as the receiver. We will use the same ESP32 board which we used in the sketch above when finding the MAC address. Replace the address with the unique MAC address of your own ESP32 board. You can use the sketch which was given previously, to find the MAC address of your module.

// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x7C, 0x9E, 0xBD, 0x37, 0xCA, 0x84};

Defining structure for sending data

Now, we will define a structure named ‘struct_message.’ Inside the structure, we will initialize the variables which will hold our data that we will transmit to the receiver board via ESP-NOW. These will be of type char, int, float, and bool.

typedef struct struct_message {
  char character[100];
  int integer;
  float floating_value;
  bool bool_value;
} struct_message;

Next, we will create a new variable of type struct_message and call it message. This will be used later on in the sketch to acquire the data and transmit it accordingly.

struct_message message;

data_sent()

The data_sent() function acts as the callback function which we will define now. It will be used as a parameter when we will register this callback function for sending messages. This prints whether the message was successfully delivered or not on the serial monitor whenever a message will be sent from the ESP32 sender side.

void data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nStatus of Last Message Sent:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

setup()

Inside the setup() function, we will open a serial connection at a baud rate of 115200 and set up the ESP32 board in station mode.

 Serial.begin(115200);
 WiFi.mode(WIFI_STA);

The following lines of code will initialize the ESP-NOW protocol. In case of an unsuccessful connection, the serial monitor will display ‘Error initializing ESP-NOW.’

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

Now we will register the data_sent() function as the callback function. This will make sure that whenever a message will be sent from the sender side, the data_sent() function will be called.

 esp_now_register_send_cb(data_sent);

The following lines of code pair the two ESP32 boards (both the sender and the receiver).

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;
  }

loop()

Inside the loop() function, we will transmit the message to the receiver ESP32 board. After every 2 seconds, the following message will be sent:

“Welcome to Microcontrollerslab! This is a test example” as the character, a random integer between 1-10, the floating number 5.6 and the value 1 as we have set the bool_value to ‘true.’

You can easily change the structure ‘message’ to send data according to your needs. We have incorporated the common data types here.

 strcpy(message.character, "Welcome to Microcontrollerslab! This is a test      example.");
  message.integer = random(1,10);
  message.floating_value = 5.6;
  message.bool_value = true;

Then we will send the message and monitor if it was sent successfully or not.

esp_err_t outcome = esp_now_send(broadcastAddress, (uint8_t *) &message, sizeof(message));
   
  if (outcome == ESP_OK) {
    Serial.println("Mesage sent successfully!");
  }
  else {
    Serial.println("Error sending the message");
  }

ESP-NOW ESP32 Arduino Sketch for Receiver Side

Open your Arduino IDE and go to File > New to open a new file. Copy the code given below in that file and save it. This sketch follows the points given below:

  • Again we will first initialize ESP NOW by using esp_now_init() function.
  • Then, we will create another function called data_receive() and register it as a callback function using esp_now_register_rcv_cb(). This callback function will be called whenever the data will be received by the receiver ESP32 board.
#include <esp_now.h>
#include <WiFi.h>

typedef struct struct_message {
    char character[100];
    int integer;
    float floating_value;
    bool bool_value;
} struct_message;


struct_message message;

void data_receive(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&message, incomingData, sizeof(message));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("Char: ");
  Serial.println(message.character);
  Serial.print("Int: ");
  Serial.println(message.integer);
  Serial.print("Float: ");
  Serial.println(message.floating_value);
  Serial.print("Bool: ");
  Serial.println(message.bool_value);
  Serial.println();
}
 
void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);

  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  esp_now_register_recv_cb(data_receive);
}
 
void loop() {

}

How the Code Works?

Including Libraries

Firstly, we will include the necessary libraries. For the receiver sketch, we are also using the same two libraries which we did for the sender sketch. These include esp_now.h for the ESP-NOW communication protocol and WiFi.h will allow our ESP32 board to use the Wi-Fi functionalities.

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

Defining structure for receiving data

Now, we will define the same structure named ‘struct_message’ which we did for the sender sketch. This structure will be used to receive the data that will be received from the receiver ESP32 board via ESP NOW. Make sure that the structure is the same in both the sketches.

typedef struct struct_message {
    char character[100];
    int integer;
    float floating_value;
    bool bool_value;
} struct_message;

Next, we will create a new variable of type struct_message and call it a message. This will be used later on in the sketch to receive the data.

struct_message message;

data_receive()

The data_receive() function acts as the callback function which we will define now. It will be used as a parameter when we will register this callback function for receiving messages. This prints the message on the serial monitor whenever a message is received from the ESP32 sender side.

void data_receive(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&message, incomingData, sizeof(message));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("Char: ");
  Serial.println(message.character);
  Serial.print("Int: ");
  Serial.println(message.integer);
  Serial.print("Float: ");
  Serial.println(message.floating_value);
  Serial.print("Bool: ");
  Serial.println(message.bool_value);
  Serial.println();
}

setup()

Inside the setup() function, we will open a serial connection at a baud rate of 115200 and set up the ESP32 receiver board in station mode as well.

 Serial.begin(115200);
 WiFi.mode(WIFI_STA);

The following lines of code will initialize the ESP-NOW protocol. In case of an unsuccessful connection, the serial monitor will display ‘Error initializing ESP-NOW.’

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

Now we will register the data_receive() function as the callback function as shown below. This will make sure that whenever a message will be received from the sender side, the data_receive() function will be called.

  esp_now_register_recv_cb(data_receive);

Demonstration ESP32 ESP-NOW one way communication

Now after saving both of the sender and receiver sketches upload them onto their respective ESP32 boards.

First, open the sender sketch. Choose the correct board and COM port before uploading your code to the sender ESP32 board. Go to Tools > Board and select ESP32 Dev Module.

select esp32 board

Next, go to Tools > Port and select the appropriate port through which your board is connected.

Selecting COM PORT ESP32

Click on the upload button to upload the code into the ESP32 board (sender). After you have uploaded your code to the board press its ENABLE button.

ESP32 enable reset button

Next, follow the same steps and upload the receiver side sketch to the receiver ESP32 module. Make sure you choose the correct COM port through which it is connected. Notice, both of the boards will be connected to different COM ports so select the COM port accordingly. Click on the upload button to upload the code into the ESP32 board (receiver). After you have uploaded your code to the board press its ENABLE button.

Now, open both the serial monitors. You will be able to see the messages being displayed on the receiver side after every 2 seconds. These include the char, int, float, and bool parameters which we specified in the program sketch.

ESP NOW one way communication receiver side serial monitor demo
Serial Monitor

In the sender side’s serial monitor you will be able to view the text that the message was successfully delivered.

ESP NOW one way communication sender side serial monitor demo
Serial Monitor

Notice that both the COM ports are different. In our case, it is COM5 for the receiver side and COM11 for the sender side.

Conclusion

In conclusion, we have learned about the ESP-NOW communication protocol and its various features. As an example, we sent a small packet of data from one ESP32 board to another in a fast and convenient manner. You can use the same sketch to transfer useful data including sensor readings or even control the output pins of one ESP32 board through another ESP32 board easily. Although, we have used two ESP32 boards to communicate with each other. But you can also use ESP-NOW for communication between ESP8266 and ESP32 boards.

Leave a Comment