esp-now-comunicacion-muchos-a-muchos-esp32

Comunicación muchos a muchos con ESP-NOW en ESP32

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.

esp32-espnow-many-to-many

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.

  1. Inicialización de ESP-NOW: Se inicializa ESP-NOW y se registra un callback para saber si el mensaje se envió correctamente.
  2. Registro de peers: Se registran las direcciones MAC de los receptores como “peers” en la red ESP-NOW.
  3. 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 utilizando payload.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);
}
  1. Inicialización de ESP-NOW: Se inicializa ESP-NOW y se registra un callback para manejar los mensajes recibidos.
  2. 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.
  3. 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*);