En este tutorial, veremos cómo configurar dos ESP32 para establecer comunicación utilizando el protocolo ESP-NOW. El ESP32 es una placa de desarrollo que integra Bluetooth y WiFi. Por lo tanto, puede conectarse e intercambiar datos con dispositivos conectados a la misma red.
Hardware
- Ordenador
- NodeMCU ESP32 x2
- Cable USB A macho a Mini B macho x2
Descripción ESP-NOW
ESP-NOW es un protocolo de comunicación desarrollado por Espressif que permite la comunicación inalámbrica entre varios dispositivos sin necesidad de una red específica. Permite intercambiar pequeños paquetes de datos a alta velocidad a través de las bandas de frecuencia de 2,4GHz, con una separación de hasta 200m. Requiere un emparejamiento inicial, pero una vez realizado, la comunicación es persistente y se establece cuando se pone en marcha el ESP32. Una de las grandes ventajas, además de que utiliza una red dedicada, es que una o varias estaciones ESP32 pueden conectarse al Wifi en paralelo.
EPS-NOW también se puede utilizar para comunicarse entre varias tarjetas ESP32 y ESP8266. Basta con adaptar el código al tipo de tarjeta.
Código
Para probar la comunicación ESP-NOW entre dos ESP32, utilizamos la librería esp_now.h disponible al instalar el gestor de tarjetas. Vamos a definir y enviar una estructura de datos entre la tarjeta maestra y la tarjeta esclava. Esta estructura debe ser idéntica entre las dos tarjetas para que los datos se entiendan correctamente.
Código transmisor
Para que la tarjeta emisora se comunique con otra tarjeta ESP32, necesita su dirección MAC. Puede recuperar esta dirección cuando la tarjeta receptora se inicia a partir de los mensajes enviados mediante la función setup(). Vamos a definir una función que se ejecuta después de enviar un mensaje para comprobar si la transmisión se ha realizado correctamente.
#include <esp_now.h>// https://github.com/espressif/esp-idf/blob/master/components/esp_wifi/include/esp_now.h #include <WiFi.h> uint8_t broadcastAddress[] = {0xA4, 0xCF, 0x12, 0x9A, 0x19, 0xAC};// REPLACE WITH RECEIVER MAC ADDRESS // Structure example to send data // Must match the receiver structure typedef struct struct_message { char a[32]; int b; float c; String d; bool e; } struct_message; struct_message myData; // callback when data is sent void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { Serial.print(F("\r\n Master packet sent:\t")); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); } void setup() { // Init Serial Monitor Serial.begin(115200); // Set device as a Wi-Fi Station WiFi.mode(WIFI_STA); // Init ESP-NOW if (esp_now_init() != ESP_OK) { Serial.println(F("Error initializing ESP-NOW")); return; } Serial.print(F("Transmitter initialized : ")); Serial.println(WiFi.macAddress()); // Define Send function esp_now_register_send_cb(OnDataSent); // Register peer esp_now_peer_info_t peerInfo; memcpy(peerInfo.peer_addr, broadcastAddress, 6); peerInfo.channel = 0; peerInfo.encrypt = false; // Add peer if (esp_now_add_peer(&peerInfo) != ESP_OK) { Serial.println(F("Failed to add peer")); return; } } void loop() { // Set values to send strcpy(myData.a, "data type char"); myData.b = random(1, 20); myData.c = 1.2; myData.d = "hello"; myData.e = false; // Send message via ESP-NOW esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData)); if (result == ESP_OK) { Serial.println(F("Sent with success")); } else { Serial.println(F("Error sending the data")); } delay(1000); }
N.B.: En algunas versiones, este código no funciona. Hay dos soluciones posibles si se encuentra con un problema: o
- colocar memset(&peerInfo, 0, sizeof(peerInfo)); después de la declaración peerInfo
- declarar esp_now_peer_info_t peerInfo antes de loop()
Código receptor
En el código del receptor, crearemos una función que se ejecuta cuando se recibe un mensaje. Esta función se utiliza para procesar la información recibida. En este ejemplo, mostramos los datos contenidos en la estructura.
#include <esp_now.h>// https://github.com/espressif/esp-idf/blob/master/components/esp_wifi/include/esp_now.h #include <WiFi.h> // Structure example to receive data // Must match the sender structure typedef struct struct_message { char a[32]; int b; float c; String d; bool e; } struct_message; struct_message myData; // callback when data is received 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() { // Initialize Serial Monitor Serial.begin(115200); // Set device as a Wi-Fi Station WiFi.mode(WIFI_STA); // Init ESP-NOW if (esp_now_init() != ESP_OK) { Serial.println("Error initializing ESP-NOW"); return; } Serial.print(F("Receiver initialized : ")); Serial.println(WiFi.macAddress()); // Define receive function esp_now_register_recv_cb(OnDataRecv); } void loop() { }
Cuando cargues el código por primera vez, fíjate bien en el mensaje de configuración que contiene la dirección MAC. Debe introducirla en el código del transmisor para que el emparejamiento se realice correctamente y la comunicación funcione.
Resultados
Una vez definida la dirección MAC de la tarjeta receptora y cargados los códigos en cada tarjeta, se establece la comunicación y la estructura se envía y descifra correctamente.
Bonus: Comunicación bidireccional entre dos ESP32s
Hemos visto cómo enviar datos de una tarjeta a otra utilizando el protocolo ESP-NOW. Ahora vamos a ver cómo, modificando ligeramente los códigos, podemos obtener una comunicación bidireccional.
Código Transceiver
#include <esp_now.h> #include <WiFi.h> uint8_t broadcastAddress[] = {0xA4, 0xCF, 0x12, 0x9A, 0x19, 0xAC};// REPLACE WITH THE OTHER TRANSCEIVER MAC ADDRESS // Structure example to send data // Must match the receiver structure typedef struct struct_message { char a[32]; int b; float c; String d; bool e; } struct_message; struct_message myData; char dataRcv[15]; // callbacks for sending and receiving data void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { Serial.print("\r\nMaster packet sent:\t"); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); } void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { memcpy(&dataRcv, incomingData, sizeof(dataRcv)); Serial.print("\r\nBytes received: "); Serial.println(len); Serial.print("From slave: "); Serial.println(dataRcv); Serial.println(); } void setup() { // Init Serial Monitor Serial.begin(115200); // Set device as a Wi-Fi Station WiFi.mode(WIFI_STA); // Init ESP-NOW if (esp_now_init() != ESP_OK) { Serial.println(F("Error initializing ESP-NOW")); return; } Serial.print(F("Transceiver initialized : ")); Serial.println(WiFi.macAddress()); // Define callback functions esp_now_register_send_cb(OnDataSent); esp_now_register_recv_cb(OnDataRecv); // Register peer esp_now_peer_info_t peerInfo; memcpy(peerInfo.peer_addr, broadcastAddress, 6); peerInfo.channel = 0; peerInfo.encrypt = false; // Add peer if (esp_now_add_peer(&peerInfo) != ESP_OK) { Serial.println(F("Failed to add peer")); return; } } void loop() { // Set values to send strcpy(myData.a, "from Master"); myData.b = random(1, 20); myData.c = 1.2; myData.d = "hello"; myData.e = false; // Send message via ESP-NOW esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData)); delay(1000); }
N.B.: Para obtener el mismo código para el emisor y el receptor, utilizamos la misma estructura de datos para los mensajes enviados y recibidos. Es posible definir estructuras diferentes en función del origen del mensaje. No obstante, hay que tener en cuenta que la tarjeta receptora debe conocer la estructura del mensaje recibido para poder descifrarlo.
Resultados
Para la estación 0, definimos el mensaje
strcpy(dataSent.a, "Master"); dataSent.b = random(100, 200); dataSent.c = false;
Y para la estación 1:
strcpy(dataSent.a, "Slave"); dataSent.b = random(10, 20); dataSent.c = false;
Hola, queremos realizar la comunicación de dos ESP32 pero no hemos tenido exito debido a que el código arroja «Failed to add peer» para ese caso como es posible resolverlo?.
Hola, ¿has probado alguna de estas dos soluciones?
– añade la línea memset(&peerInfo, 0, sizeof(peerInfo)); después de la declaración de peerInfo
– declarar esp_now_peer_info_t peerInfo; antes de definir setup()
Me gusta mucho su contribución para la comunicación de ESP´s pero tengo problemas:
Tambien quiero realizar comunicación unidireccional entre dos ESP32, pero en el Monitor serial donde debe imprimir la dir.MAC del receptor sale la MAC del emisor con el consiguiente fallo de que error:
Reciever initialized : 40:22:D8:3D:79:B4 //esto es la MAC del emisor!!!!!
Error sending the data
He probado insertar la linea de memset sin exito. Hay alguna solución?
Hola, ¡gracias por tu comentario!
La dirección MAC del ESP32 es normalmente única. ¿Estás seguro de que estás enviando el código correcto al dispositivo correcto?
Si realmente hay un problema de direccionamiento, puedes cambiar la tarjeta o intentar cambiar la dirección MAC
uint8_t newMACAddress[] = {0x32, 0xAE, 0xA4, 0x07, 0x0D, 0x66};
esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress[0]);
Gracias por su rápida respuesta.
Seguramente no me he expresado bien. He introducido mi dir. MAC de mi tarjeta receptora en la pos:
uint8_t broadcastAddress[] = {0xA4, 0xCF, 0x12, 0x9A, 0x19, 0xAC};// REPLACE WITH RECEIVER MAC ADDRESS
para aparejar (peer) las dos tarjetas, pero en lugar de intentar aparejar con esta dirección se utiliza la dir. MAC de la tarjeta del emisor.
Se entiende lo que pretendo explicar?
Hola,
Entiendo.
Si el código receptor devuelve la dirección MAC del emisor, significa que el código receptor está cargado en el dispositivo emisor.
Gracias por su respuesta.
Cree que puede haber una solución para este problema?
Para probar, he cargado el programa de comunicación bidireccional y tengo el mismo problema con la dirección MAC.
Para evitar confusión entre emisor y receptor y las corresspondientes dir. MAC he instalado ambas tarjetas en PC´s diferentes.
Utilizo el Arduino 1.8.19 porque en la versión 2.3.2 no consigo activar el Serial Monitor.
Uno de los PC´s funciona con Windows 7, el otro con W 11.
Que me puede sugerir para instalarlo con éxito?
La versión 1.8.19 es una buena opción para la programación de ESP32. Cualquiera que sea el código cargado, la función WiFi.macAddress() debe devolver una dirección única dependiendo del ESP32.
– Conecta la tarjeta 1 al PC1 y carga el código del receptor. Observa la dirección MAC del «receptor
– conecta la tarjeta 2 a PC2 y carga el código del transmisor. Observe la dirección MAC del «emisor
Deberías tener dos direcciones MAC diferentes
– sustituya broadcastAddress por la dirección del «receptor» en el código del emisor
Exactamente esto estaba haciendo multitud de veces. Por si acaso lo he repetido con plena concentración a las dir. MAC pero el reultado es el mismo:
Transmitter initialized : 40:22:D8:3D:79:B4
E (94) ESPNOW: Peer interface is invalid
Failed to add peer
Error sending the data
Estoy desesperado.
Le podria documentarlo con los correspondientes «screen shot» si mi descripción no fuese suficiente.
¡Ánimo!
¿Cuáles son las direcciones MAC de las dos tarjetas ESP32?
¿cuál es el valor de uint8_t broadcastAddress[]?
¿has probado a añadir el comando memset?
esp_now_peer_info_t peerInfo;
memset(&peerInfo, 0, sizeof(peerInfo));
Emisor: 40:22:D8:3D:79:B4;
Receptor: 40:22:D8:3E:66:C0;
Broadcast Addr: {0x40, 0x22, 0xD8, 0x3E, 0x66, 0xC0};
He probado insertar la linea de memset sin exito. Lo que no sé si hay que sustituir algún parametro.
Nota: si yo utilizo el mismo sketch intuyo que probablemente hay un problema con mi entorno de funcionamiento. SO? versión arduino? alguna versión de biblioteca? etc.
P.S.: He estado buscando sketch en Espressif y he encontrado una biblioteca en github de WifiEspNow.h de «yoursunny»
https://github.com/yoursunny/WifiEspNow/tree/main/src
a lo mejor es de tu interés.
En el emisor me aparece el mensaje de «Error sending the data» mientra que en el receptor no aparece nada en el serial monitor, sabe por qué podría ser?
¿ha incluido la dirección MAC del receptor en el código del remitente?
Buenas tardes. Una consulta: funcionaría también de la misma forma el protocolo ESP-NOW para comunicar dos placas de diferente modelos. Ejemplo:
– ESP32-cam (Esclavo)
– ESPWrover-cam (Maestro)
El ESP32-cam debería enviar una señal al ESPWrover-cam para activar un relevador (muy simple).
Gracias.
Hola, no se me ocurre ninguna razón por la que no funcionaría.
esto ya no sirve, tienes que cambiar la libreria esp_now ya no está
¿Por qué dice que ya no existe? ¿Qué debería sustituirla?
Las bibliotecas en ESP-IDF y Arduino IDE me parece que se mantienen.
https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP_NOW
https://github.com/espressif/esp-now
https://demo-dijiudu.readthedocs.io/en/latest/api-reference/wifi/esp_now.html