En entradas anteriores, hemos cubierto los conceptos básicos de ESP-NOW, cómo configurar una comunicación 1 a 1, comunicaciones 1 a muchos y muchos a 1.
Nos falta ver la comunicación muchos a muchos. Qué, como imaginaréis, es parecida a juntar las que hemos visto anteriormente.
Nuevamente, en este tutorial voy a enviar un string. Pero podéis fácilmente adaptarlo a enviar structs, JSON, o lo que queráis.
¿Qué es una comunicación muchos a muchos?
En una comunicación muchos a muchos, múltiples dispositivos actúan tanto como emisores como receptores.
Este tipo de comunicación es útil en aplicaciones donde varios nodos deben compartir información entre sí (como en redes de sensores colaborativos, sistemas de control distribuido, o redes mesh).
Como decía, básicamente el código es una combinación de las variaciones que vimos en el 1 a muchos y muchos a 1.
Es decir, ahora tenemos tanto muchos emisores como receptores.
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*);
Por lo que tenemos que registrar en los emisores todas las MAC de los receptores (como vimos en el caso 1 a muchos)
void static RegisterPeeks()
{
for(auto peek_id = 0; peek_id < RECEIVERS_COUNT; peek_id ++)
{
RegisterPeek(peek_id);
}
}
Cómo identificar en el receptor la MAC del emisor
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);
}
Ejemplo completo
Vamos a ver cómo quedaría todo esto junto. Voy a suponer un proyecto en el que hay
- Varios emisorres
- Varios receptores
- Pero entre ellos son diferentes
Por tanto, tengo dos listas de MACs (emisores y receptores). Si quisierais una red mallada (donde todos envían a todos) tendrías una única lista de MACs.
- Inicialización de ESP-NOW: Se inicializa ESP-NOW y se registra un callback para saber si el mensaje se envió correctamente.
- Registro de peers: Se registran las direcciones MAC de los receptores como “peers” en la red ESP-NOW.
- Envío del mensaje: Se envía un string a cada receptor utilizando
esp_now_send
. El string se convierte a un array de bytes utilizandopayload.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);
}
- Inicialización de ESP-NOW: Se inicializa ESP-NOW y se registra un callback para manejar los mensajes recibidos.
- Recepción del mensaje: Cuando se recibe un mensaje, se reconstruye el string a partir de los bytes recibidos y se imprime en el monitor serial.
- Identificación del emisor: Se utiliza la dirección MAC del emisor para identificar qué dispositivo envió el mensaje.
#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*);