In previous entries, we have covered the basics of ESP-NOW, how to set up 1-to-1 communication, 1-to-many communications, and many-to-1.
We still need to see many-to-many communication. Which, as you might imagine, is similar to the ones we saw earlier.
Again, in this tutorial I will send a string. But you can easily adapt it to send structs, JSON, or whatever you want.
What is many-to-many communication?
In a many-to-many communication, multiple devices act as both senders and receivers.
This type of communication is useful in applications where multiple nodes need to share information with each other (such as in collaborative sensor networks, distributed control systems, or mesh networks).
As I said, basically the code is a combination of the variations we saw in 1-to-many and many-to-1.
That is, we now have both many senders and receivers.
const uint8_t MAC_SENDER_1[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const uint8_t MAC_RECEIVER_1[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const uint8_t* SENDERS_MACS[] = { MAC_SENDER_1 };
const uint8_t SENDERS_COUNT = sizeof(SENDERS_MACS) / sizeof(uint8_t*);
const uint8_t* RECEIVERS_MACS[] = { MAC_RECEIVER_1 };
const uint8_t RECEIVERS_COUNT = sizeof(RECEIVERS_MACS) / sizeof(uint8_t*);
So we need to register all the receiver MACs in the senders (as we saw in the 1-to-many case).
void static RegisterPeeks()
{
for(auto peek_id = 0; peek_id < RECEIVERS_COUNT; peek_id ++)
{
RegisterPeek(peek_id);
}
}
How to identify the sender’s MAC in the receiver.
int SearchSender(const uint8_t* mac)
{
for(auto sender_id = 0; sender_id < SENDERS_COUNT; sender_id++)
{
if(AreMacEquals(mac, SENDERS_MACS[sender_id])) return sender_id;
}
return -1;
}
void OnMessageReceived(const uint8_t* mac, const uint8_t* data, int len)
{
/* ... */
auto sender_id = SearchSender(mac);
Serial.printf("Sender id: %d\n", sender_id);
}
Complete example
Let’s see how all this would look together. I will assume a project where there are
- Multiple senders
- Multiple receivers
- But they are different from each other
Therefore, I have two lists of MACs (senders and receivers). If you wanted a mesh network (where everyone sends to everyone), you would have a single list of MACs.
- Initialization of ESP-NOW: ESP-NOW is initialized, and a callback is registered to know if the message was sent successfully.
- Peer registration: The MAC addresses of the receivers are registered as “peers” in the ESP-NOW network.
- Sending the message: A string is sent to each receiver using
esp_now_send
. The string is converted to a byte array usingpayload.c_str()
.
#include <esp_now.h>
#include <WiFi.h>
#include "const.h"
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 SendMessage(uint8_t peer_id)
{
String payload = "MY STRING";
esp_err_t result = esp_now_send(RECEIVERS_MACS[peer_id], (uint8_t*)payload.c_str(), payload.length());
if(result == ESP_OK)
{
Serial.println("Sent with success");
}
else
{
Serial.println("Error sending the data");
}
}
void static RegisterPeek(uint8_t id)
{
esp_now_peer_info_t peerInfo;
memcpy(peerInfo.peer_addr, RECEIVERS_MACS[id], 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
if(esp_now_add_peer(&peerInfo) != ESP_OK)
{
Serial.println("Failed to add peer");
}
else
{
Serial.print("Registered peer ");
Serial.println(id);
}
}
void static RegisterPeeks()
{
for(auto peek_id = 0; peek_id < RECEIVERS_COUNT; peek_id ++)
{
RegisterPeek(peek_id);
}
}
void static InitEspNow()
{
if(esp_now_init() != ESP_OK)
{
Serial.println("Error initializing ESP-NOW");
}
else
{
esp_now_register_send_cb(OnDataSent);
RegisterPeeks();
}
}
void setup()
{
Serial.begin(115200);
delay(2000);
WiFi.mode(WIFI_STA);
InitEspNow();
}
void loop()
{
SendMessage(0);
delay(2000);
}
- Initialization of ESP-NOW: ESP-NOW is initialized, and a callback is registered to handle received messages.
- Receiving the message: When a message is received, the string is reconstructed from the received bytes and printed on the serial monitor.
- Identifying the sender: The sender’s MAC address is used to identify which device sent the message.
#include <esp_now.h>
#include <WiFi.h>
#include "const.h"
bool AreMacEquals(const uint8_t* src, const uint8_t* dst)
{
for(auto i = 0; i < 6; i++)
{
if(src[i] != dst[i]) return false;
}
return true;
}
int SearchSender(const uint8_t* mac)
{
for(auto sender_id = 0; sender_id < SENDERS_COUNT; sender_id++)
{
if(AreMacEquals(mac, SENDERS_MACS[sender_id])) return sender_id;
}
return -1;
}
void OnMessageReceived(const uint8_t* mac, const uint8_t* data, int len)
{
Serial.printf("Packet received from: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
Serial.printf("Bytes received: %d\n", len);
String payload;
payload.reserve(len);
for(auto i = 0; i < len; i++)
{
payload += (char)data[i];
}
Serial.println(payload);
auto sender_id = SearchSender(mac);
Serial.printf("Sender id: %d\n", sender_id);
}
void InitEspNow()
{
if(esp_now_init() != ESP_OK)
{
Serial.println("Error initializing ESP-NOW");
return;
}
else
{
esp_now_register_recv_cb(OnMessageReceived);
}
}
void setup()
{
Serial.begin(115200);
WiFi.mode(WIFI_STA);
InitEspNow();
}
void loop()
{
}
const uint8_t MAC_SENDER_1[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const uint8_t MAC_RECEIVER_1[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const uint8_t* SENDERS_MACS[] = { MAC_SENDER_1 };
const uint8_t SENDERS_COUNT = sizeof(SENDERS_MACS) / sizeof(uint8_t*);
const uint8_t* RECEIVERS_MACS[] = { MAC_RECEIVER_1 };
const uint8_t RECEIVERS_COUNT = sizeof(RECEIVERS_MACS) / sizeof(uint8_t*);