El protocolo esp-now es muy sencillo de utilizar y utiliza la señal wifi, sin embargo tiene algunas limitaciones como por ejemplo en número de bytes que podemos hacer en cada envío, ya que es de solo 250 bytes.
Para enviar o recibir información utilizando el protocolo «esp-now» se necesita la dirección «MAC» del dispositivo del cual recibes la información, y también debes especificar la dirección «MAC» del dispositivo al que quieres enviar la información.
Muestro abajo el código con el cual podrás ver en pantalla, desde el «monitor serie», la dirección «MAC» del dispositivo que ejecuta el código.
Código para conocer la dirección MAC desde monitor serie:
#include <Arduino.h>
#include "WiFi.h"
void setup(){
Serial.begin(9600);
WiFi.mode(WIFI_STA);
Serial.println(WiFi.macAddress());
}
void loop(){
Serial.println(WiFi.macAddress());
delay(5000);
}
El código desde el cual un modulo 1 ESP32 envía un dato a otro modulo ESP y al mismo tiempo recibe un dato del mismo módulo se muestra a continuación.
Lo primero que necesitamos es incluir las librerías que necesitamos, para este proyecto necesitaremos la librería «esp-now», que es el protocolo para enviar y recibir datos desde un módulo ESP32 a otro. Y la librería «wifi», que la utilizaremos para operar el sistema de comunicación wifi de los ESP32.
#include <esp_now.h>
#include <WiFi.h>
Lo siguiente y para poder almacenar la dirección MAC hemos utilizado un array llamado «broadcastAddress», en este caso esta definido como dato «uint8_t» que es un entero de 8 bits sin signo negativo.
// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x34, 0x86, 0x5D, 0xFD, 0x00, 0x00};
En el siguiente código definimos la estructura que tendrán los datos que queremos tanto enviar como recibir. Utilizamos «struct» para asignar al siguiente nombre un tipo de dato «estructura», y lo declaramos como tipo «typedef» para que quede guardado con ese nombre, en este caso se queda guardado como estructura el nombre que definimos, de esta manera no necesitaremos volver a escribir «struct» cada vez que nos refiramos al nombre definido las sucesivas veces.
Un dato tipo «struct» puede tener una o mas variables, en nuestro ejemplo tendra una única variable «int a» declarada la letra «a» como numero entero.
Después declaramos dos variables llamadas myDataRec y myDataSen que contendrán cada una de ellas las variables del tipo de dato «struct» definido.
// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
int a;
} struct_message;
// Create a struct_message called myData
struct_message myDataRec;
struct_message myDataSen;
El siguiente código nombraremos «peerInfo» como tipo de dato estructurado llamado «esp_now_peer_info_t» (descrito en la página oficial de espressif), esta estructura consta de 3 variables, la primera será donde introduciremos la dirección MAC del módulo donde queremos enviar los datos, la segunda variable se utiliza para nombrar una clave para recibir los datos y en la tercera indicaremos el canal con el que queremos transmitir los datos via wifi, en caso de utilizar el canal por defecto pondremos un cero.
La función que utiliza esta estructura de dato «peerInfo» es «esp_now_add_peer» y en este caso nos devuelve un «ESP_OK» si se ha encontrado el módulo con la MAC indicada.
esp_now_peer_info_t peerInfo;
.
.
.
// Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
// Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
La siguiente parte crearemos una función para enviar los datos al otro módulo ESP32, la función se llama «OnDataSent» con dos parámetros, el primer parámetro es la dirección MAC y el segundo parámetro nombra el dato de tipo «enum» llamado «esp_now_send_status_t» definido en espressif. Este dato nos da como respuesta si el envío se ha realizado con éxito o ha habido algún fallo.
// callback when data is sent
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");
}
La siguiente función la utilizaremos para recibir los datos del módulo ESP32 que los envía. La función se llama «OnDataRec», el primer parámetro guarda la dirección MAC en formato array del módulo que esta enviando el mensaje.
// callback function that will be executed when data is received
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) {
memcpy(&myDataRec, incomingData, sizeof(myDataRec));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Int: ");
Serial.println(myDataRec.a);
Serial.println();
}
En el siguiente código ya estamos dentro del setup(), dentro de la sintaxis de Arduino la función setup() la utilizamos para declarar variables, estructuras o datos que solo deben ejecutarse una vez en cada inicio del programa.
En nuestro caso dentro del setup() la primera instrucción que tenemos es Serial.begin, la utilizamos para indicar la velocidad en baudios con la que transmite la información el monitor serial.
// Init Serial Monitor
Serial.begin(9600);
La siguiente instrucción indicaremos en que modo dentro de la tecnología wifi va a trabajar nuestro modulo, pudiendo elegir entre modo «static» (enviar o recibir información via wifi), modo «Acces Point» (conexión a internet) o ambos. En este caso lo configuramos en modo «static».
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
Ahora escribiremos la función esp_now_init() que nos devuelve un valor indicando si el protocolo esp-now se ha iniciado correctamente.
// Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
Cuando hacemos un envio de datos debemos registrar la llamada al envío de datos utilizando, esp_now_register_send_cb :
// Once ESPNow is successfully Init, we will register for Send CB to
// get the status of Trasnmitted packet
esp_now_register_send_cb(OnDataSent);
Igual que arriba si recimos datos debemos registrar la llamada, en este caso utilizaremos esp_now_register_recv_cb :
// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info
esp_now_register_recv_cb(OnDataRecv);
El siguiente extracto de código nos situamos dentro de la función void loop(), que dentro de la sintaxis del código de arduino se ejecuta constantemente como un bucle cerrado. Dentro de void loop() suele estar el programa en cuestión con las tareas que queremos que haga el modulo modulo ESP32, en nuestro caso.
En nuestra función loop() la primera instrucción en la asignación de un numero a la variable «a» definida dentro de la estructura «myDataSen»:
// Set values to send
myDataSen.a = 1;
Para enviar el mensaje utilizamos la función esp_now_send(), esta función envía los datos que hemos especificado, y devuelve ESP_OK en el caso de que no hallan habido errores.
// Send message via ESP-NOW
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myDataSen, sizeof(myDataSen));
if (result == ESP_OK) {
Serial.println("Sent with success");
}
else {
Serial.println("Error sending the data");
}
Código completo del Módulo 1:
#include <esp_now.h>
#include <WiFi.h>
// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x34, 0x86, 0x5D, 0x00, 0x00, 0x00};
// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
int a;
} struct_message;
// Create a struct_message called myData
struct_message myDataRec;
struct_message myDataSen;
esp_now_peer_info_t peerInfo;
// callback when data is sent
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");
}
// callback function that will be executed when data is received
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) {
memcpy(&myDataRec, incomingData, sizeof(myDataRec));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Int: ");
Serial.println(myDataRec.a);
Serial.println();
}
void setup() {
// Init Serial Monitor
Serial.begin(9600);
// 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;
}
// Once ESPNow is successfully Init, we will register for Send CB to
// get the status of Trasnmitted packet
esp_now_register_send_cb(OnDataSent);
// Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
// Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info
esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
// Set values to send
myDataSen.a = 1;
// Send message via ESP-NOW
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myDataSen, sizeof(myDataSen));
if (result == ESP_OK) {
Serial.println("Sent with success");
}
else {
Serial.println("Error sending the data");
}
delay(2000);
}
En nuestro ejemplo el módulo 2 trabaja exactamente igual que el módulo 1, a continuación se muestra el código completo del módulo 2:
#include <esp_now.h>
#include <WiFi.h>
// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0xc0, 0x49, 0xef, 0xce, 0x00, 0x00};
// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
int a;
} struct_message;
// Create a struct_message called myData
struct_message myDataRec;
struct_message myDataSen;
esp_now_peer_info_t peerInfo;
// callback when data is sent
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");
}
// callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
memcpy(&myDataRec, incomingData, sizeof(myDataRec));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Int: ");
Serial.println(myDataRec.a);
Serial.println();
}
void setup() {
// Initialize Serial Monitor
Serial.begin(9600);
// 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;
}
// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info
esp_now_register_recv_cb(OnDataRecv);
// Once ESPNow is successfully Init, we will register for Send CB to
// get the status of Trasnmitted packet
esp_now_register_send_cb(OnDataSent);
// Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
// Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
}
void loop() {
// Set values to send
myDataSen.a = 2;
// Send message via ESP-NOW
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myDataSen, sizeof(myDataSen));
if (result == ESP_OK) {
Serial.println("Sent with success");
}
else {
Serial.println("Error sending the data");
}
delay(2000);
}
Hola, gracias por esta información.
se podría hacer que varios esp32 se comuniquen todos entre si?
saludos!
Hola, si quisiera tener 5 placas que se comuniquen entre si bidireccionalmente, se podria?
Saludos y gracias
Hola Gonzalo, si conectas las placas sin acceso a internet, es decir modo «Acces Point» para comunicar información entre ellas utilizando la señal wifi de la placa, he encontrado información que indica que puedes conectar hasta 20 placas. Yo he podido conectar hasta 5 placas simultaneas trabajando perfectamente. De todas formas por si quieres algo mas de informacion te dejjo el enlace de dos páginas que hablan sobre ello.
Un saludo.
Web oficial de espressif en relación esp-now:
https://www.espressif.com/en/solutions/low-power-solutions/esp-now
Web de «randomnerdtutorials», información relacionadad con esp-now:
https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/
Hola al momento no he tenido éxito en la conexión de los esp32 como transmisor y receptor de datos. Compilan bien, sin error. pero me arrojan en los dos monitores seriales que abro los siguientes mensajes.
Last Packet Send Status: Delivery Fail
Sent with success
Si me pueden dar una mano…les agradecería
Hola Nestor, el fallo en el envio se puede producir por varias cosas. Te indico algunas que para mi son las mas comunes.
Verifica las direcciones MAC, la esp32 que envia el mensaje debe tener la direccion MAC de la esp32 a la que quiere enviar el mensaje, y viceversa.
Cuidado con los consumos, quiero decir que si estas alimentando las placas con usb al ordenador no suele ser un problema pero si por el contrario estas alimentando las placas con alguna fuente de alimentacion, asegurate de que la fuente de alimentacion puede suministrar por lo menos 500 mA, la esp32 con WIFI de espnow cuando arranca o envia suele tener picos de 250 mA, si ademas tienes conectado algun otro dispositivo puedes tener problemas de consumo. cuando envias la informacion intenta enviar un numero entero int de prueba , y si quieres enviar un string te rrecomiendo que mejor utilices un array en vez del string. Si no consigues localizar el fallo intenta enviarnos mas informacion sobre el fallo, tambien puedes intentar implementar algo de codigo para encontrar algun fallo que indique que partes funcionan y donde se esta produciendo el fallo. Espero haberte ayudado algo, un saludo.
Hola Buenas Noches
Cuando hago la compilacion me arroja este error, hay un problema en la linea 52 esp_now_register_recv_cb(OnDataRecv);
Compilation error: invalid conversion from ‘void (*)(const uint8_t*, const uint8_t*, int)’ {aka ‘void (*)(const unsigned char*, const unsigned char*, int)’} to ‘esp_now_recv_cb_t’ {aka ‘void (*)(const esp_now_recv_info*, const unsigned char*, int)’} [-fpermissive]
Si me pudiera ayudar se los agradeceria mucho.
Saludos.
Hola Eduardo, creo que el fallo podría venir por el tipo de datos que pasas como parametro, yo probaría a revisar los 3 parametros de la funcion :typedef void (*esp_now_recv_cb_t)(const esp_now_recv_info_t *esp_now_info, const uint8_t *data, int data_len), sino es eso podrías intentar dar mas informacion del codigo.
Un saludo
Hola a todos. Estoy Queriendo enviar datos de un esp8266 a un esp32, y no puedo. Entre dos esp32no tengo problemas. Pero cuando coloco un esp8266, puedo recibir datos pero no puedo enviarlos. alguien que tenga alguna idea. Gracias
Hola:
tengo un aviso de error: (como el del chico de arriba).
Compilation error: invalid conversion from ‘void (*)(const uint8_t*, const uint8_t*, int)’ {aka ‘void (*)(const unsigned char*, const unsigned char*, int)’} to ‘esp_now_recv_cb_t’ {aka ‘void (*)(const esp_now_recv_info*, const unsigned char*, int)’} [-fpermissive]
¿Se sabe como se solucionó?
Muchas gracias
Es que no se mucho de c++. Programé muy poco, hace 25 años.
Quiero hacer que un módulo esp32 wroom detecte la entrada a través de la interrupción de dos laseres, y mande una señal a otro esp32 wroom.