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

This tutorial shows how to setup an ESP8266 board to receive data from multiple ESP8266 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 ESP8266 board. The boards will be programmed using Arduino IDE.

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

Other ESP-NOW Guides with the ESP8266:

Project Overview

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

Project overview ESP-NOW with ESP8266 NodeMCU Receive Data from Multiple Boards many-to-one configuration
  • One ESP8266 board acts as a receiver/slave;
  • Multiple ESP8266 boards act as senders/masters. As an example, we’ll use two senders. 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 ESP8266 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.

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 ESP8266 boards using Arduino IDE, so before proceeding with this tutorial, make sure you have these boards installed in your Arduino IDE.

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 ESP8266 MAC Address).

Upload the following code to your ESP8266 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.

Getting the ESP8266 NodeMCU Receiver Board MAC Address

ESP8266 NodeMCU 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-esp8266-nodemcu/
  
  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 <ESP8266WiFi.h>
#include <espnow.h>

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

// Set your Board ID (ESP32 Sender #1 = BOARD_ID 1, ESP32 Sender #2 = BOARD_ID 2, etc)
#define BOARD_ID 2

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

// Create a struct_message called test to store variables to be sent
struct_message myData;

unsigned long lastTime = 0;
unsigned long timerDelay = 10000;

// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  Serial.print("\r\nLast Packet Send Status: ");
  if (sendStatus == 0){
    Serial.println("Delivery success");
  }
  else{
    Serial.println("Delivery fail");
  }
}
 
void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
 
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  // Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  } 
  // Set ESP-NOW role
  esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);

  // 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_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);

}
 
void loop() {
  if ((millis() - lastTime) > timerDelay) {
    // Set values to send
    myData.id = BOARD_ID;
    myData.x = random(1, 50);
    myData.y = random(1, 50);

    // Send message via ESP-NOW
    esp_now_send(0, (uint8_t *) &myData, sizeof(myData));
    lastTime = millis();
  }
}

View raw code

How the Code Works

Include the ESP8266WiFi and espnow libraries.

#include <ESP8266WiFi.h>
#include <espnow.h>

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

uint8_t broadcastAddress[] = {0x5C, 0xCF, 0x7F, 0x99, 0xA1, 0x70};

Set the board ID on the BOARD_ID variable. Don’t forget to change this variable for each of your sender boards.

// Set your Board ID (ESP8266 Sender #1 = BOARD_ID 1, ESP8266 Sender #2 = BOARD_ID 2, etc)
#define BOARD_ID 2

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;
    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(uint8_t *mac_addr, uint8_t sendStatus) {
  Serial.print("\r\nLast Packet Send Status: ");
  if (sendStatus == 0){
    Serial.println("Delivery success");
  }
  else{
    Serial.println("Delivery fail");
  }
}

setup()

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

Serial.begin(115200);

Set the device as a Wi-Fi station and disconnect Wi-Fi:

WiFi.mode(WIFI_STA);
WiFi.disconnect();

Initialize ESP-NOW:

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

Set the board role. This is a sender board, so we’ll set it to ESP_NOW_ROLE_CONTROLLER.

esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);

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 line registers a new peer.

esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);

The esp_now_add_peer() function accepts the following arguments, in this order: mac address, peer role, wi-fi channel, key, and key length.

loop()

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

Assign a value to each variable.

myData.id = BOARD_ID;
myData.x = random(1, 50);
myData.y = random(1, 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.

esp_now_send(0, (uint8_t *) &myData, sizeof(myData));

ESP8266 NodeMCU Receiver Code (ESP-NOW)

Upload the following code to your ESP8266 receiver board. The code is prepared to receive data from two 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-esp8266-nodemcu/
  
  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 <ESP8266WiFi.h>
#include <espnow.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;

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

// Callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac_addr, uint8_t *incomingData, uint8_t 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);
  WiFi.disconnect();

  // Init ESP-NOW
  if (esp_now_init() != 0) {
    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_set_self_role(ESP_NOW_ROLE_SLAVE);
  esp_now_register_recv_cb(OnDataRecv);
}

void loop(){
  // Access 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;
  */
}

View raw code

How the Code Works

Similarly to the sender, start by including the libraries:

#include <ESP8266WiFi.h>
#include <espnow.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 two sender boards. If you have more sender boards, you need to create more structures.

struct_message board1;
struct_message board2;

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[2] = {board1, board2};

onDataRecv()

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

void OnDataRecv(uint8_t * mac_addr, uint8_t *incomingData, uint8_t len) {

Get the sender board’s MAC address:

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 ESP8266 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.

ESP-NOW ESP8266 NodeMCU data structure

setup()

In the setup(), initialize the Serial Monitor.

Serial.begin(115200);

Set the device as a Wi-Fi Station and disconnect Wi-Fi.

WiFi.mode(WIFI_STA);
WiFi.disconnect();

Initialize ESP-NOW:

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

Set the board role. This is a receiver board, so we’ll set it to ESP_NOW_ROLE_SLAVE.

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 ESP8266 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 successfully.

ESP-NOW Send Data from Multiple Boards to one ESP8266 NodeMCU Receiver Serial Monitor Arduino IDE

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

ESP-NOW Receive Data from Multiple Boards ESP8266 NodeMCU Arduino IDE Serial Monitor

Wrapping Up

In this tutorial you’ve learned how setup an ESP8266 to receive data from multiple ESP8266 boards using ESP-NOW (many-to-one configuration). You can do something similar with ESP32 boards (ESP32 ESP-NOW: Many-to-one).

As an example, we’ve exchanged random numbers. In a practical application those should be replaced with actual sensor readings or commands. This is ideal if you want to collect data from several sensor nodes.

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

Learn more about ESP8266 with our resources:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

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

  1. Must messages from all sending devices have the same message structure? For example can Sender #1 have say a structure as per the tutorial above i.e.
    typedef struct struct_message {
    int id;
    int x;
    int y;
    } struct_message;

    and Sender #2 have a different structure like following
    typedef struct another_message {
    int id;
    analog z;
    char c;
    } another_message;

    Then in the receiver both structures would then need to be defined and code changed to suit. Nice to see an example

    Reply
  2. I have managed to send sensor data from Nodemcu Slave to Nodemcu Master. But Slave can’t send sensor data to Master when Master is connected by Wifi (Connected with Thinger.io). What is the solution to solve this problem?

    Reply
  3. I’m using this good tutorial for my project where I have many masters (more than 3) that are sending data to a slave. There I collect rhe data and I save them to an SD card. OK perfect It’s work fine but sometimes I some data are missing, so I may have a double info for the same value… Probably it’s due to the fact that they are asynchronous. Is it possible to make the receiver that acts as a periodic request of data fron senders and collect them in a perfect ordered sequence? If 2 senders are sending data at the same time, there should be a missed data from the receiver and the sequence is then not correct.
    Thanks for any suggestion I can have.
    Fiorenzo

    Reply
    • hi,
      I’m trying to use the same tutorial to send sensors reading but isn’t work, the sender collect data using sensor and send them (delivery success) but the slave recieves 0 as value (not the collected data , for ex temperature 34)
      I’ll appreciate it if you share your code with me, or explain me how you did it and if you reply as soon as possible
      best regards

      Reply
  4. Hello Sara,
    Thank you for this wonderful tutorial!. I have a question, if I use the receiver in both modes (to receive data from other ESP and to send it to a server). Do you know how could I achieve that after receiving a packet and while sending that data (connect to wifi, send a post to an endpoint…) stop the ESP receiver from getting more packages, and after sending the data, start listening again for other packages.

    Thank you!

    Reply
  5. Hi Very nice tutorial, I have one doubt i.e.,
    boardsStruct[myData.id-1].x = myData.x;
    boardsStruct[myData.id-1].y = myData.y;

    From the above lines of code, how do I display the individual board’s data on a OLED display like this, Example:
    display.drawString(0, 0, “Board 1 :”);
    display.drawString(55, 0, myData.x);
    display.drawString(0, 14, “Board 2 :”);
    display.drawString(55, 14, myData.x);
    Please advice as I am not clear.

    Reply
  6. Hello,

    I have a working code that pairs two boards automatically by using a temporary MAC on one of the ESPs. The temporary MAC needs to be hardcoded but at least you can pair any two boards by resetting them and putting them in their first boot cycle at the same time.

    If this is helpful, I can share the code.

    Reply
  7. Hello!
    Thank you for the tutorial, it has helped me a lot bit i have a question, I adapted the code to read an accelerometer and in the sender the accelerometer sends the data complete but when it gets to the server/slave I can’t see all the value, it just give me whole numbers but without the decimals, how can I solve this problem?
    Thanks for any suggestion I can have.
    Regards
    Itzel

    Reply
  8. Hello.
    I’m just started with ESP-NOW and need some help to get gooing. How can i print out boardsStruct[myData.id-1].x = myData.x; to an oled i2c display? The Serial.printf(“x value: %d \n”, boardsStruct[myData.id-1].x); works like a charm.
    Please help

    Best regard
    Glenn

    Reply
  9. Hello.
    I’m just started with ESP-NOW and need some help to get gooing.
    How can i print out boardsStruct[myData.id-1].x = myData.x; to an oled i2c display?
    The Serial.printf(“x value: %d \n”, boardsStruct[myData.id-1].x); works like a charm.
    Please help

    Best regard
    Glenn

    Reply
  10. Can the receiver listen to ESP-NOW and then send the data to a web server? How can the receiver be configured to work both as a web server and to use ESP-NOW communication, since the configuration for ESP-NOW uses the following commands:
    WiFi.mode (WIFI_STA);
    WiFi.disconnect ();

    Reply
  11. Can all ESP8266 run on the same Arduino microprocessor (UNO in my case), or do I need a separate UNO for each ESP8266?

    Reply
  12. Hello,
    As always, very good instructions. But for me, not being a good programmer is still complicated. How would you change the code to have the keys on the transmitting page and the LEDs on the receiver? So I could see on wich sender is button pressed?
    Thanks and all the best.

    Reply
  13. Hey !
    This is a great tutorial. I am doing a project in which i want to recieve data on one nodemcu esp 8266 from another three nodemcu esp 8266.
    And then the data which i recieve on the reciever nodemcu is to be transmitted to Arduino uno for further processing.
    How can i do it ?

    Reply
    • Hi.
      The Arduino doesn’t have Wi-Fi.
      So, I recommend sending the messages to the Arduino via Serial.
      Search for “ESP8266 with Arduino via Serial” or something similar.
      Regards,
      Sara

      Reply
  14. hi thank you for this project.
    I would like to know ,how we user input and other client one board side output .let say one board (A) sending 4 inputs to other client board will get 4 output then other Board (B) also get 4 inputs to client board 4 outputs ,also i have other question then its possible to get all notification to Telegram Bot ? also from the Telegram Bot can be send LED on and off signal to each server (sender boards (A)&(B) ) ?

    Reply
  15. Hi Sara,
    I substitute the x,y variable by a float variable an I receive only “0” , while using a String the receiver always shows a casual number (1073737176).
    Have I to adapt and following lines, and how?
    It is possible to use only integers?
    Thanks
    Renzo

    Reply
  16. I would like to ask is it possible to still receive data from the sender board if the receiving board is connected to WiFi to send the received data from the sender board? Please give explanation. TQ. ASAP. Please.

    Reply
  17. Hi,
    I’m having a couple of problems with the receiving sketch and I wonder if anyone can help?

    Is there anyway of getting a decimal readout on the receiver?

    When using the ESP8266 on the CH340 channel the receiver works fine (less the decimal) however when using an ESP8266 that works on the 2102 channel it throws up garbage, any work around? I’ve run a blink sketch and it works fine.
    Its not a major problem as the CH340’s are quite cheap, just would like to use the stock of the 2102’s that I have.

    Cheers
    Paul

    Reply
  18. Hi Its Great Tutorial, How can used with ESP32 ?, any Change ?
    How many master connect with one slaw ?, i want 12 master with 1 slaw

    Reply
  19. Hi,
    I have a problem. I want to send sensor datas, Temp and Hum. and in the struct I need double oder float. This doesn’t work. With int i have no problems but i want to use 35,00 datas.

    Can you help me please?
    Regards,
    Christian

    Reply
  20. hi sara, thank you for this tuto
    I have question about data type , when I used “float” variable (temperature, humidity ) the slave receives 0 as value (code doesn’t work correctly)
    but when I changed variable’s type from float to int ….> the code worked
    would you please explain me this !!!!!

    Reply

Leave a Reply to Sara Santos Cancel reply

Download Our Free eBooks and Resources

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