ESP-NOW with ESP32: Receive Data from Multiple Boards (many-to-one)

This tutorial shows how to setup an ESP32 board to receive data from multiple ESP32 boards via ESP-NOW communication protocol (many-to-one configuration). This configuration is ideal if you want to collect data from several sensors nodes into one ESP32 board. The boards will be programmed using Arduino IDE.

ESP-NOW with ESP32: Receive Data from Multiple Boards many-to-one

We have other guides related with ESP-NOW that you might be interested in:

Project Overview

This tutorial shows how to setup an ESP32 board to receive data from multiple ESP32 boards via ESP-NOW communication protocol (many-to-one configuration) as shown in the following figure.

ESP-NOW with ESP32 Receive Data from Multiple Boards (many-to-one) Project Overview
  • One ESP32 board acts as a receiver/slave;
  • Multiple ESP32 boards act as senders/masters. We’ve tested this example with 5 ESP32 sender boards and it worked fine. You should be able to add more boards to your setup;
  • The sender board receives an acknowledge message indicating if the message was successfully delivered or not;
  • The ESP32 receiver board receives the messages from all senders and identifies which board sent the message;
  • As an example, we’ll exchange random values between the boards. You should modify this example to send commands or sensor readings (exchange sensor readings using ESP-NOW).

Note: in the ESP-NOW documentation there isn’t such thing as “sender/master” and “receiver/slave”. Every board can be a sender or receiver. However, to keep things clear we’ll use the terms “sender” and “receiver” or “master” and “slave”.

Prerequisites

We’ll program the ESP32 boards using Arduino IDE, so before proceeding with this tutorial, make sure you have these boards installed in your Arduino IDE.

Parts Required

To follow this tutorial, you need multiple ESP32 boards. All ESP32 models should work. We’ve experimented with different models of ESP32 boards and all worked well (ESP32 DOIT board, TTGO T-Journal, ESP32 with OLED board and ESP32-CAM).

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Getting the Receiver Board MAC Address

To send messages via ESP-NOW, you need to know the receiver board’s MAC address. Each board has a unique MAC address (learn how to Get and Change the ESP32 MAC Address).

Upload the following code to your ESP32 receiver board to get is MAC address.

// Complete Instructions to Get and Change ESP MAC Address: https://RandomNerdTutorials.com/get-change-esp32-esp8266-mac-address-arduino/

#ifdef ESP32
  #include <WiFi.h>
#else
  #include <ESP8266WiFi.h>
#endif

void setup(){
  Serial.begin(115200);
  Serial.println();
  Serial.print("ESP Board MAC Address:  ");
  Serial.println(WiFi.macAddress());
}
 
void loop(){

}

View raw code

After uploading the code, press the RST/EN button, and the MAC address should be displayed on the Serial Monitor.

ESP32 board MAC Address with Arduino IDE Serial Monitor

ESP32 Sender Code (ESP-NOW)

The receiver can identify each sender by its unique MAC address. However, dealing with different MAC addresses on the Receiver side to identify which board sent which message can be a little tricky.

So, to make things easier, we’ll identify each board with a unique number (id) that starts at 1. If you have three boards, one will have ID number 1, the other number 2, and finally number 3. The ID will be sent to the receiver alongside the other variables.

As an example, we’ll exchange a structure that contains the board id number and two random numbers x and y as shown in the figure below.

ESP-NOW with ESP32 Receive Data from Multiple Boards (many-to-one) Sample Data

Upload the following code to each of your sender boards. Don’t forget to increment the id number for each sender board.

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp-now-many-to-one-esp32/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

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

// REPLACE WITH THE RECEIVER'S MAC Address
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
    int id; // must be unique for each sender board
    int x;
    int y;
} struct_message;

//Create a struct_message called myData
struct_message myData;

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
 
void setup() {
  // Init Serial Monitor
  Serial.begin(115200);

  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

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

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_register_send_cb(OnDataSent);
  
  // Register peer
  esp_now_peer_info_t peerInfo;
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;
  
  // Add peer        
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}
 
void loop() {
  // Set values to send
  myData.id = 1;
  myData.x = random(0,50);
  myData.y = random(0,50);

  // Send message via ESP-NOW
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
   
  if (result == ESP_OK) {
    Serial.println("Sent with success");
  }
  else {
    Serial.println("Error sending the data");
  }
  delay(10000);
}

View raw code

How the Code Works

Include the WiFi and esp_now libraries.

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

Insert the receiver’s MAC address on the following line.

uint8_t broadcastAddress[] = {0x30, 0xAE, 0xA4, 0x15, 0xC7, 0xFC};

Then, create a structure that contains the data we want to send. We called this structure struct_message and it contains three integer variables: the board id, x and y. You can change this to send whatever variable types you want (but don’t forget to change that on the receiver side too).

typedef struct struct_message {
  int id; // must be unique for each sender board
  int x;
  int y;
} struct_message;

Create a new variable of type struct_message that is called myData that will store the variables values.

struct_message myData;

OnDataSent() callback function

Next, define the OnDataSent() function. This is a callback function that will be executed when a message is sent. In this case, this function prints if the message was successfully delivered or not.

void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

setup()

In the setup(), initialize the serial monitor for debugging purposes:

Serial.begin(115200);

Set the device as a Wi-Fi station:

WiFi.mode(WIFI_STA);

Initialize ESP-NOW:

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

After successfully initializing ESP-NOW, register the callback function that will be called when a message is sent. In this case, register for the OnDataSent() function created previously.

esp_now_register_send_cb(OnDataSent);

Add peer device

To send data to another board (the receiver), you need to pair it as a peer. The following lines register and add a new peer.

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

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

loop()

In the loop(), we’ll send a message via ESP-NOW every 10 seconds (you can change this delay time).

Assign a value to each variable.

myData.id = 1;
myData.x = random(0,50);
myData.y = random(0,50);

Don’t forget to change the id for each sender board.

Remember that myData is a structure. Here assign the values that you want to send inside the structure. In this case, we’re just sending the id and random values x and y. In a practical application these should be replaced with commands or sensor readings, for example.

Send ESP-NOW message

Finally, send the message via ESP-NOW.

// Send message via ESP-NOW
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
 
if (result == ESP_OK) {
  Serial.println("Sent with success");
}
else {
  Serial.println("Error sending the data");
}

ESP32 Receiver Code (ESP-NOW)

Upload the following code to your ESP32 receiver board. The code is prepared to receive data from three different boards. You can easily modify the code to receive data from a different number of boards.

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp-now-many-to-one-esp32/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

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

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
  int id;
  int x;
  int y;
}struct_message;

// Create a struct_message called myData
struct_message myData;

// Create a structure to hold the readings from each board
struct_message board1;
struct_message board2;
struct_message board3;

// Create an array with all the structures
struct_message boardsStruct[3] = {board1, board2, board3};

// callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) {
  char macStr[18];
  Serial.print("Packet received from: ");
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.println(macStr);
  memcpy(&myData, incomingData, sizeof(myData));
  Serial.printf("Board ID %u: %u bytes\n", myData.id, len);
  // Update the structures with the new incoming data
  boardsStruct[myData.id-1].x = myData.x;
  boardsStruct[myData.id-1].y = myData.y;
  Serial.printf("x value: %d \n", boardsStruct[myData.id-1].x);
  Serial.printf("y value: %d \n", boardsStruct[myData.id-1].y);
  Serial.println();
}
 
void setup() {
  //Initialize Serial Monitor
  Serial.begin(115200);
  
  //Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  //Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_register_recv_cb(OnDataRecv);
}
 
void loop() {
  // Acess the variables for each board
  /*int board1X = boardsStruct[0].x;
  int board1Y = boardsStruct[0].y;
  int board2X = boardsStruct[1].x;
  int board2Y = boardsStruct[1].y;
  int board3X = boardsStruct[2].x;
  int board3Y = boardsStruct[2].y;*/

  delay(10000);  
}

View raw code

How the Code Works

Similarly to the sender, start by including the libraries:

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

Create a structure to receive the data. This structure should be the same defined in the sender sketch.

typedef struct struct_message {
  int id;
  int x;
  int y;
} struct_message;

Create a struct_message variable called myData that will hold the data received.

struct_message myData;

Then, create a struct_message variable for each board, so that we can assign the received data to the corresponding board. Here we’re creating structures for three sender boards. If you have more sender boards, you need to create more structures.

struct_message board1;
struct_message board2;
struct_message board3;

Create an array that contains all the board structures. If you’re using a different number of boards you need to change that.

struct_message boardsStruct[3] = {board1, board2, board3};

onDataRecv()

Create a callback function that is called when the ESP32 receives the data via ESP-NOW. The function is called onDataRecv() and should accept several parameters as follows:

void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {

Get the board MAC address:

char macStr[18];
Serial.print("Packet received from: ");
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
         mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
Serial.println(macStr);

Copy the content of the incomingData data variable into the myData variable.

memcpy(&myData, incomingData, sizeof(myData));

Now, the myData structure contains several variables with the values sent by one of the ESP32 senders. We can identify which board send the packet by its ID: myData.id.

This way, we can assign the values received to the corresponding boards on the boardsStruct array:

boardsStruct[myData.id-1].x = myData.x;
boardsStruct[myData.id-1].y = myData.y;

For example, imagine you receive a packet from board with id 2. The value of myData.id, is 2.

So, you want to update the values of the board2 structure. The board2 structure is the element with index 1 on the boardsStruct array. That’s why we subtract 1, because arrays in C have 0 indexing. It may help if you take a look at the following image.

setup()

In the setup(), initialize the Serial Monitor.

Serial.begin(115200);

Set the device as a Wi-Fi Station.

WiFi.mode(WIFI_STA);

Initialize ESP-NOW:

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

Register for a callback function that will be called when data is received. In this case, we register for the OnDataRecv() function that was created previously.

esp_now_register_recv_cb(OnDataRecv);

The following lines commented on the loop exemplify what you need to do if you want to access the variables of each board structure. For example, to access the x value of board1:

int board1X = boardsStruct[0].x;

Demonstration

Upload the sender code to each of your sender boards. Don’t forget to give a different ID to each board.

Upload the receiver code to the ESP32 receiver board. Don’t forget to modify the structure to match the number of sender boards.

On the senders’ Serial Monitor, you should get a “Delivery Success” message if the messages are delivered properly.

ESP-NOW Sending Packets Delivery Success ESP32 Serial Monitor

On the receiver board, you should be receiving the packets from all the other boards. In this test, we were receiving data from 5 different boards.

ESP-NOW Receive Data from Multiple Boards ESP32 Serial Monitor

Wrapping Up

In this tutorial you’ve learned how setup an ESP32 to receive data from multiple ESP32 boards using ESP-NOW (many-to-one configuration).

As an example, we’ve exchanged random numbers. In a real application those should be replaced with actual sensor readings or commands. This is ideal if you want to collect data from several sensor nodes. You can take this project further and create a web server on the receiver board to displays the received messages.

To use Wi-Fi to create a web server and use ESP-NOW simultaneously, you need to set up the ESP32 both as a Wi-Fi station and access point. Additionally, you need to set a different Wi-Fi channel: one for ESP-NOW and the other for the station. This will be covered in a future tutorial soon. So, stay tuned.

We have other tutorials related with ESP-NOW that you may like:

Learn more about ESP32 with our resources:

Thanks for reading.


Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »

Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our weekly newsletter!

27 thoughts on “ESP-NOW with ESP32: Receive Data from Multiple Boards (many-to-one)”

      • I tried this a few months ago but it didn’t go well. I had stopped. I don’t know enough to understand everything. I can’t do that either. I have a small “thinking” handicap. My hamlet has been very bad for years and I write too many mistakes. Google tranlate is a friend of mine. haha
        So I am curious about Julli Esp8266 version.
        Thank you for all your beautiful work.

        Reply
  1. Hi Rui, great tutorial again I will try it soon 😉
    I was wondering if it would be possible to synchronize all boards to go to deep sleep in between communications (say sleep 15 minutes, everyone wakes up and sends sensor readings to the receiver, everyone sleep again). Would you have an option for that (maybe synchronizing time between boards)? The goal being to save battery when constant sensor readings are not required. Thanks again for all these great tutorials which are clearly explained!

    Reply
    • Hi Oli.
      Thanks for your comment.
      I was thinking that to make all boards go sleep at the same time, the receiver board could send a command at the same time to all boards, so that they know it is time to go to sleep.
      The boards would receive the message approximately at the same time (maybe a few seconds of difference). This would be one of the easiest solutions in this scenario. However, I’m sure there should be a more “precise” way to do that.
      Regards,
      Sara

      Reply
      • Hi
        it is a solution but not very convenient. The boards sleep mode should be independent and random in time.

        Thanks a lot Sara,

        Reply
  2. Hi Sara, thank you for this work !
    In the first sketch intended to get the MAC address, there is no #define ESP32 on top
    Is it defined implicitely by the IDE when we select an ESP32 card ?

    Reply
  3. Hi Sara!
    Question on the topic ESP32. I recently found out that there is an “old” and “new” version of the ESP32 module. Are there any fundamental differences in the programming of these modules in ARDUINO IDE?

    Reply
  4. If something follows a fixed scheme it is pre-destinated to be done by a computer.
    Mac-adresses and writing code is such a thing. So I wrote a function that prints out the mac-adress in a way that produces the code-line for defining the mac-adress with all details except the variable-name

    here it is:
    void PrintWiFiMacAdress()
    {
    char MacAdr_AoC[18]; //suffix _AoC for easier remembering variable-type is ArrayOfChar
    char HexByteDigits[3];

    for (uint8_t i = 0; i < 18; i = i + 1)
    {
    MacAdr_AoC[i] = WiFi.macAddress()[i];
    }
    MacAdr_AoC[17] = 0; // zero to terminate the string
    Serial.print(“ESP Board Wifi.macAddress: “);
    Serial.println(MacAdr_AoC);

    Serial.println();
    Serial.println(“copy the line below and replace the ‘#’ with variablename of your choice”);
    Serial.println();
    Serial.print(“uint8_t #[] = { “);
    for (uint8_t i = 0; i < 16; i = i + 3)
    {
    HexByteDigits[0] = MacAdr_AoC[i];
    HexByteDigits[1] = MacAdr_AoC[i+1];
    HexByteDigits[2] = 0; // zero for terminating the string
    Serial.print(“0x”);
    Serial.print(HexByteDigits);
    if (i < 14) Serial.print(“, “);
    }
    Serial.println(” };”);
    Serial.println();
    }

    So you simply add the function to your code
    and insert the line

    PrintWiFiMacAdress();

    into the setup-function
    when booting the device prints
    something similar to

    copy the line below and replace the ‘#’ with variablename of your choice

    uint8_t #[] = { 0x5C, 0xCF, 0x7F, 0x00, 0xD2, 0x9C };

    with the mac-adress of the device running this code.

    Remember THIS mac-adress must be used in the RECEIVING device

    It is just a few lines of code so you can keep it inside your code and always have
    the mac-adress handy through serial-output without the need to load another code.

    I’m using array of chars in this code because variable-Type “String” is dangerous to use.

    best regards

    Stefan

    Reply
  5. hm this comment-function eliminates spaces that were used to format the code.

    @Rui & Sara, is this something that could be changed in this comment-software?
    best regards

    Stefan

    Reply
  6. Regarding the comment by Oli, I am, at the moment, doing what he suggests with two boards one sending, one receiving. The ‘sender’ wakes up every 15 minutes and sends date from a BME280 to the ‘receiver’. This works really well so I guess it would work with multiple senders, I’ll try it when I get chance. The receiver sits there constantly running waiting for a message so it is mains powered. I want to find a way to measure the battery voltage on the sender (units) using minimum current so a resistor divider to the on board ADC isn’t ideal unless it is switched with an output pin.

    Reply
  7. To clarify my last post, I ran this sketch :
    #ifdef ESP32
    String msg = “ESP32!”;
    #endif
    #ifdef ESP8266
    String msg = “ESP8266!”;
    #endif

    void setup() {
    Serial.begin(115200);
    Serial.println(msg);
    }

    void loop() {
    }
    With an ESP32, result is ESP32!
    With an ESP8266, result is ESP8266!
    Then the IDE processes the user sketch, and inserts, among other things, the right #include ESPxx on top of the code submitted to the compiler
    I do not know if there is a way to see this processed code ?
    Alain

    Reply
  8. Hi Sara Id like to know if wifi connection can be used to send data to server while espnow is working to collect data from other esp32?.

    Reply
    • Hi Marcos.
      Yes.
      You need to set the ESP32 as a wi-fi station and access point at the same time.
      You also need to set the ESP-NOW to use channel 2.
      We’ll be publishing a ESP-NOW + Web Server project this week. The ESP32 receives data from other boards and displays the readings on a web server.
      So, stay tuned.
      Regards,
      Sara

      Reply
  9. Hello, First thank you for all the information shared with everyone.
    I tried to do with the esp826 the programming that you did with the esp32 (several esp32 which send data to an Esp32, in fact temperature and humidity as well as the ID of the board). There is a part of the code where I hang, it is the structure. I managed to display the id but the temperature and humidity is 0. On the other hand with an esp8266 can send to another esp8266 the id., Temperature and humidity without problem. So I send you the transmitter and receiver program. the transmitter is an esp01 and the receiver is a wemos d1 r1. I also send you the result received from the receiver on the serial port.

    p.s excuse my english
    transmiter
    (…)

    Reply
    • Hi Michel,
      If you want assistance sharing your code is a must.
      From what you describe – if I look into my RGB-LED enlighted glas-sphere I can see something through the fog like

      The structures of sender and receiver deviate from each other
      you are using a variable of type String in the structure (very bad idea)
      the memcopy-command copies too less bytes
      the send_ESP_Now-message sends too less bytes

      This comment-section is not well suited for such discussions and support.
      Sorry Rui and Sara: if you keep this simple comment-function I will repeat to suggest asking the questions in the arduino-forum.

      here is the link to the right sub-forum
      https://forum.arduino.cc/index.php?board=4.0

      best regards Stefan

      Reply

Leave a Comment

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.