Introduction: How to Make Multiple ESP Talk Via ESP-NOW Using ESP32 and ESP8266

On my ongoing project, I need multiple ESP to talk with each other without a router. To do this, I'll be using the ESP-NOW to make wireless communicate with each other without a router on the ESP.

Supplies

Step 1: Get the Board Mac Address

Through ESP-now, ESP devices talk to each other by sending data to their unique address while connected to an internal acces point network made upon intilaizing the esp now. . Thus, determine the MAC address of each device.Attached is my ESP32 and ESP8266 Board Settings

FOR ESP32

#include "WiFi.h"		//To access ESP32 WIFI capabilities
 
void setup(){
  Serial.begin(115200);	
  Serial.print("ESP32 Board MAC Address:  ");
  Serial.println(WiFi.macAddress());	//prints its MAC Address
}
void loop(){}

FOR ESP8266

#include <ESP8266WiFi.h>	//Library used to access ESP8266 WIFI capabilities

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

My MAC ADDRESS are:

  • ESP32 - 30:AE:A4:F5:03:A4
  • ESP8266: A4:CF:12:C7:9C:77

Step 2: How to Make ESP-NOW Work

Here's an overview on how to make it work:

  1. Include esp now and wifi libraries
  2. Save the mac address of the recipient ESP
  3. Define the data structure of the message send/received
  4. On the setup, set the wifi to station mode
  5. Initialize esp_now
  6. make and register the call back function called after sending and receiving data
  7. For Esp8266, define its role
  8. register the peer or recipient esp
  9. Send data

Step 3: ESP-NOW FUNCTIONS(ESP32)

esp_now_init(void)

Return:

  • ESP_OK : succeed
  • ESP_ERR_ESPNOW_INTERNAL : Internal error

Description:

Initialize ESPNOW function

<em><strong>esp_now_register_send_cb(cb)

Returns:

  • ESP_OK : succeed
  • ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
  • ESP_ERR_ESPNOW_INTERNAL : internal error

Parameters:

  • cb: callback function name after sending ESPNOW data with this parameters:
    • void cb (const uint8_t *mac_addr, esp_now_send_status_t status)
      • mac_addr: mac address of receiver
      • status:
        • 1 = success
        • 0 = fail

Description:

Call the function OnDataSent after sending ESPNOW data

<em><strong>esp_now_add_peerconst esp_now_peer_info_t *peer)

Returns:

  • ESP_OK : succeed
  • ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
  • ESP_ERR_ESPNOW_ARG : invalid argument
  • ESP_ERR_ESPNOW_FULL : peer list is full
  • ESP_ERR_ESPNOW_NO_MEM : out of memory
  • ESP_ERR_ESPNOW_EXIST : peer has existed

Parameters:

  • peer :peer information with the following data:
    • uint8_t
      • peer_addr[ESP_NOW_ETH_ALEN];
        ESPNOW peer MAC address that is also the MAC address of station or softap
    • uint8_t lmk[ESP_NOW_KEY_LEN]
      • ESPNOW peer local master key that is used to encrypt data
    • uint8_t channel
      • Wi-Fi channel that peer uses to send/receive ESPNOW data. If the value is 0, use the current channel which station or softap is on. Otherwise, it must be set as the channel that station or softap is on.
    • wifi_interface_t ifidx
      • Wi-Fi interface that peer uses to send/receive ESPNOW data
    • bool encrypt
      • ESPNOW data that this peer sends/receives is encrypted or not
    • void *priv
      • ESPNOW peer private data

Description:

Add a peer to peer list

<em><strong>esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len)

Returns:

  • ESP_OK : succeed

  • ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized

  • ESP_ERR_ESPNOW_ARG : invalid argument

  • ESP_ERR_ESPNOW_INTERNAL : internal error

  • ESP_ERR_ESPNOW_NO_MEM : out of memory

  • ESP_ERR_ESPNOW_NOT_FOUND : peer is not found

  • ESP_ERR_ESPNOW_IF : current WiFi interface doesn’t match that of peer

Parameters:

  • peer_addr: peer MAC address
  • data: data to send
  • len: length of data

Description:

Send ESPNOW data. For some cases, this happens:

  • If peer_addr is not NULL, send data to the peer whose MAC address matches peer_addr
  • If peer_addr is NULL, send data to all of the peers that are added to the peer list
  • The maximum length of data must be less than ESP_NOW_MAX_DATA_LEN
  • The buffer pointed to by data argument does not need to be valid after esp_now_send returns
<em><strong>esp_now_register_recv_cb(cb)

Returns:

  • ESP_OK : succeed

  • ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized

  • ESP_ERR_ESPNOW_INTERNAL : internal error

Parameters:

  • cb: callback function for receiving ESPNOW data
    • void cb (const uint8_t *mac_addr, const uint8_t *data, int data_len)
      • mac_addr:
        • mac address of receiver
      • *data:
        • data receive
      • data_len
        • data byte length

Description:

Call the function cb after receiving ESPNOW data

Step 4: ESP-NOW FUNCTIONS(ESP8266)

FUNCTIONS DESCRIPTION ESP32 ESP8266

int<em> esp_now_init(void)

Returns:

  • 1 = success
  • 0=fail

Description

Initialize ESPNOW function

int <em><strong>esp_now_set_self_role(u8 role)

Parameters:

  • ESP_NOW_ROLE_IDLE: data transmission is not allowed.
  • ESP_NOW_ROLE_CONTROLLER: priority is given to Sation interface
  • ESP_NOW_ROLE_SLAVE: priority is given to SoftAP interface
  • ESP_NOW_ROLE_COMBO: priority is given to SoftAP
    interface

Description

Sets the device Role

int <em><strong>esp_now_register_send_cb(cb)

Returns:

  • 1 = success
  • 0 = fail

Parameters:

  • cb: callback function name after sending ESPNOW data with this parameters:
    • void cb (const uint8_t *mac_addr, esp_now_send_status_t status)
      • mac_addr: mac address of receiver
      • status:
        • 1 = success
        • 0 = fail

Description

Call the function OnDataSent after sending ESPNOW data

int <em><strong>esp_now_add_peer(u8 *mac_addr, u8 role, u8 channel, u8 *key, u8 key_len)

Returns:

  • 1 = success
  • 0 = fail

Parameters:

  • mac_addr
    • mac address of peer
  • role
  • channel
    • If the value is 0, use the current channel which station or softap is on. Otherwise, it must be set as the channel that station or softap is on
  • *key
    • key for encryption
  • key_len
    • length of key

Description:

Add a peer to peer list

int <em><strong>esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len)

Returns:

  • 1 = Success
  • 0 = Fail

Parameters:

  • peer_addr: peer MAC address
  • data: data to send
  • len: length of data

Description:

Send ESPNOW data. For some cases, this happens:

  • If peer_addr is not NULL, send data to the peer whose MAC address matches peer_addr
  • If peer_addr is NULL, send data to all of the peers that are added to the peer list
  • The maximum length of data must be less than ESP_NOW_MAX_DATA_LEN
  • The buffer pointed to by data argument does not need to be valid after esp_now_send returns
int<em><strong> esp_now_register_recv_cb(cb)

Returns:

  • 1 = Success
  • 0 = Fail

Parameters:

  • cb: callback function for receiving ESPNOW data
    • void cb (const uint8_t *mac_addr, const uint8_t *data, int data_len)
      • mac_addr:
        • mac address of receiver
      • *data:
        • data receive
      • data_len
        • data byte length

Description:

Call the function cb after receiving ESPNOW data

Step 5: One Way Communication(ESP32 As Sender)

The ESP32 sends data to a ESP8266 . with this code. Change the broadcastAddress[] to your corrsponding receiver mac address. Mine was A4:CF:12:C7:9C:77

//Add necessary libraries
#include <esp_now.h>  //To access the esp now functions
#include <WiFi.h>     //To Add Wifi Capabilities on ESP32

//save the MAC Address in an array named broadcastAddress;
uint8_t broadcastAddress[] = {0xA4, 0xCF, 0x12, 0xC7, 0x9C, 0x77}; //MAC address of my receiver

/*define the data types of  the multiple variables structured and
renamed all of it as struct_message*/
typedef struct struct_message {
  char a[32];
  int b;
  float c;
  String d;
  bool e;
} struct_message;

// Create a struct_message called myData
struct_message myData;

// function called when data is sent to print its status
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() {
  //Set the baud rate for serial communication with ESP
  Serial.begin(115200);
 
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);//Starts the wifi

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

  //call the function OnDataSent after sending ESPNOW data
  esp_now_register_send_cb(OnDataSent);
  
  // Register peer
  esp_now_peer_info_t peerInfo; //initialize and assign the peer information as a pointer to an addres
  memcpy(peerInfo.peer_addr, broadcastAddress, 6); //copy the value of  broadcastAddress with 6 bytes to peerInfo.peer_addr
  peerInfo.channel = 0;  //channel at which the esp talk. 0 means undefined and data will be sent on the current channel. 1-14 are valid channels which is the same with the local device 
  peerInfo.encrypt = false; //not encrypted
  
  
  //Add the device to the paired device list 
  if (esp_now_add_peer(&peerInfo) != ESP_OK){ 
    Serial.println("Failed to add peer");
    return;
  }
}
 
void loop() {
  // Set values to send
  strcpy(myData.a, "THIS IS A CHAR"); //save "THIS IS A CHAR" to variable a of my "data" defined earlier
  myData.b = random(1,20); //save a random value
  myData.c = 1.2; //save a float
  myData.d = "Hello"; //save a string
  myData.e = false; //save a bool

  //Send data less than or equal 250 bytes via ESP-NOW and returns its status
  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(2000);
}

The ESP8266 receives data from the ESP32 using this code.

//Add necessary libraries
#include <ESP8266WiFi.h>  //To Add Wifi Capabilities on ESP32
#include <espnow.h>       //To access the esp now functions

/*define the data types of  the multiple variables structured and
renamed all of it as struct_message*/
typedef struct struct_message {
    char a[32];
    int b;
    float c;
    String d;
    bool e;
} struct_message;

// Create a variable struct_message called myData
struct_message myData;

// function called when the data is received and prints it
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&myData, incomingData, sizeof(myData));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("Char: ");
  Serial.println(myData.a);
  Serial.print("Int: ");
  Serial.println(myData.b);
  Serial.print("Float: ");
  Serial.println(myData.c);
  Serial.print("String: ");
  Serial.println(myData.d);
  Serial.print("Bool: ");
  Serial.println(myData.e);
  Serial.println();
}
 
void setup() {
  //Set the baud rate for serial communication with ESP
  Serial.begin(115200);
  
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);  //Starts the wifi

 // Init ESP-NOW and returns its status
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);  //Defines the role of this esp
  esp_now_register_recv_cb(OnDataRecv); //call the function OnDataRecv after receiving ESPNOW data
}

void loop() {}

Step 6: One Way Communication(ESP8266 As Sender)

The ESP8266 sends data to an ESP32 . with this code. Change the broadcastAddress[] to your corrsponding receiver mac address. My esp32 address is 30:AE:A4:F5:03:A4. For other functions for esp8266 go here

//Add necessary libraries
#include <ESP8266WiFi.h>  //To Add Wifi Capabilities on ESP32
#include <espnow.h>       //To access the esp now functions

//save the MAC Address in an array named broadcastAddress;
uint8_t broadcastAddress[] = {0x30, 0xAE, 0xA4, 0xF5, 0x03, 0xA4};

/*define the data types of  the multiple variables structured and
renamed all of it as struct_message*/
typedef struct struct_message {
  char a[32];
  int b;
  float c;
  String d;
  bool e;
} struct_message;

// Create a structured variable called myData
struct_message myData;

// function called when data is sent and print its status
void OnDataSent( uint8_t *mac_addr, uint8_t sendStatus) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(sendStatus == 1 ? "Delivery Success" : "Delivery Fail");
}


void setup() {
  //Set the baud rate for serial communication with ESP
  Serial.begin(115200);
 
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);//Starts the wifi

  // Init ESP-NOW and returns its status
  if (esp_now_init()) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

 
   esp_now_register_send_cb(OnDataSent);		 //call the function OnDataSent after sending ESPNOW data<br>
  
  //Add the device to the paired device list 
  if (esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_CONTROLLER, 1, NULL, 0)){ 
    Serial.println("Failed to add peer");
    return;
  }
}
 
void loop() {
  // Set values to send
  strcpy(myData.a, "THIS IS A CHAR"); //save "THIS IS A CHAR" to variable a of my "data" defined earlier
  myData.b = random(1,20); //save a random value
  myData.c = 1.2; //save a float
  myData.d = "SP8266"; //save a string
  myData.e = false; //save a bool

  //Send data less than or equal 250 bytes via ESP-NOW and returns its status
  int result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
   
  if (esp_now_init() != 0) {
    Serial.println("Sent with success");
  }
  else {
    Serial.println("Error sending the data");
  }
  delay(2000);
}

The ESP32 receives data from a ESP8266 . with this code. For other functions refere here

//Add necessary libraries
#include <esp_now.h>  //To access the esp now functions
#include <WiFi.h>     //To Add Wifi Capabilities on ESP32

/*define the data types of  the multiple variables structured and
renamed all of it as struct_message*/
typedef struct struct_message {
    char a[32];
    int b;
    float c;
    String d;
    bool e;
} struct_message;

// Create a variable struct_message called myData
struct_message myData;

// function called when the data is received and prints it
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&myData, incomingData, sizeof(myData));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("Char: ");
  Serial.println(myData.a);
  Serial.print("Int: ");
  Serial.println(myData.b);
  Serial.print("Float: ");
  Serial.println(myData.c);
  Serial.print("String: ");
  Serial.println(myData.d);
  Serial.print("Bool: ");
  Serial.println(myData.e);
  Serial.println();
}
 
void setup() {
  //Set the baud rate for serial communication with ESP
  Serial.begin(115200);
  
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);  //Starts the wifi

 // Init ESP-NOW and returns its status
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  esp_now_register_recv_cb(OnDataRecv); //call the function OnDataRecv after receiving ESPNOW data
}
void loop() {}

Step 7: TWO WAY COMMUNICATION

The ESP32 sends data on startup to the ESP8266. The ESP8266 prints the received message and then replies of which the ESP32 prints on its serial monitor.

ESP32 CODE

//Add necessary libraries
#include <esp_now.h>  //To access the esp now functions
#include <WiFi.h>     //To Add Wifi Capabilities on ESP32

//save the MAC Address in an array named broadcastAddress;
uint8_t broadcastAddress[] = {0xA4, 0xCF, 0x12, 0xC7, 0x9C, 0x77}; //MAC address of my receiver

/*define the data types of  the multiple variables structured and
renamed all of it as struct_message*/
typedef struct struct_message {
  char a[32];
  int b;
  float c;
  String d;
  bool e;
} struct_message;

// Create a struct_message called myData
struct_message myData;

// function called when data is sent to print its status
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");
  if(status!=ESP_NOW_SEND_SUCCESS ){send_data();}
}

void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&myData, incomingData, sizeof(myData));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("Char: ");
  Serial.println(myData.a);
  Serial.print("Int: ");
  Serial.println(myData.b);
  Serial.print("Float: ");
  Serial.println(myData.c);
  Serial.print("String: ");
  Serial.println(myData.d);
  Serial.print("Bool: ");
  Serial.println(myData.e);
  Serial.println();
}

void setup() {
  //Set the baud rate for serial communication with ESP
  Serial.begin(115200);
 
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);//Starts the wifi

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

  //call the function OnDataSent after sending ESPNOW data
  esp_now_register_send_cb(OnDataSent);
  
  // Register peer
  esp_now_peer_info_t peerInfo; //initialize and assign the peer information as a pointer to an addres
  memcpy(peerInfo.peer_addr, broadcastAddress, 6); //copy the value of  broadcastAddress with 6 bytes to peerInfo.peer_addr
  peerInfo.channel = 0;  //channel at which the esp talk. 0 means undefined and data will be sent on the current channel. 1-14 are valid channels which is the same with the local device 
  peerInfo.encrypt = false; //not encrypted
  
  
  //Add the device to the paired device list 
  if (esp_now_add_peer(&peerInfo) != ESP_OK){ 
    Serial.println("Failed to add peer");
    return;
  }

esp_now_register_recv_cb(OnDataRecv); //call the function OnDataRecv after receiving ESPNOW data
send_data(); 
}

void loop() {}

void send_data(){
  Serial.println("Sending");
  // Set values to send
  strcpy(myData.a, "THIS IS A CHAR"); //save "THIS IS A CHAR" to variable a of my "data" defined earlier
  myData.b = random(1,20); //save a random value
  myData.c = 1.2; //save a float
  myData.d = "ESP32"; //save a string
  myData.e = false; //save a bool

  //Send data less than or equal 250 bytes via ESP-NOW and returns its status
  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"); }  
}


ESP8266 CODE

//Add necessary libraries
#include <ESP8266WiFi.h>  //To Add Wifi Capabilities on ESP32
#include <espnow.h>       //To access the esp now functions

//save the MAC Address in an array named broadcastAddress;
uint8_t broadcastAddress[] = {0x30, 0xAE, 0xA4, 0xF5, 0x03, 0xA4};


/*define the data types of  the multiple variables structured and
renamed all of it as struct_message*/
typedef struct struct_message {
    char a[32];
    int b;
    float c;
    String d;
    bool e;
} struct_message;

// Create a variable struct_message called myData
struct_message myData;

// function called when the data is received and prints it
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&myData, incomingData, sizeof(myData));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("Char: ");
  Serial.println(myData.a);
  Serial.print("Int: ");
  Serial.println(myData.b);
  Serial.print("Float: ");
  Serial.println(myData.c);
  Serial.print("String: ");
  Serial.println(myData.d);
  Serial.print("Bool: ");
  Serial.println(myData.e);
  Serial.println();
  send_data();
}

void OnDataSent( uint8_t *mac_addr, uint8_t sendStatus) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(sendStatus == 1 ? "Delivery Success" : "Delivery Fail");
  if(sendStatus !=1){ send_data(); }
}


 
  void send_data(){
  // Set values to send
  strcpy(myData.a, "THIS IS A CHAR"); //save "THIS IS A CHAR" to variable a of my "data" defined earlier
  myData.b = random(1,20); //save a random value
  myData.c = 1.2; //save a float
  myData.d = "ESP8266"; //save a string
  myData.e = false; //save a bool
  esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
}
 
void setup() {
  //Set the baud rate for serial communication with ESP
  Serial.begin(115200);
  
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);  //Starts the wifi

 // Init ESP-NOW and returns its status
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
   if (esp_now_add_peer(broadcastAddress,ESP_NOW_ROLE_SLAVE, 1, NULL, 0)){ 
    Serial.println("Failed to add peer");
    return;
  }
   esp_now_set_self_role(ESP_NOW_ROLE_COMBO);
   esp_now_register_send_cb(OnDataSent);
  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);  //Defines the role of this esp
  esp_now_register_recv_cb(OnDataRecv); //call the function OnDataRecv after receiving ESPNOW data
}

void loop() {}

Step 8: REFERENCES