En este tutorial, veremos cómo configurar dos ESP8266 para establecer comunicación utilizando el protocolo ESP-NOW. El ESP8266 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 ESP8266 o Wemos 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,4 GHz, a una distancia de hasta 200 m. Requiere un emparejamiento inicial, pero una vez realizado la comunicación es persistente y se establece cuando se pone en marcha el ESP8266. Una de las grandes ventajas, además de que utiliza una red dedicada, es que una o varias estaciones ESP8266 pueden conectarse al Wifi en paralelo.
EPS-NOW también se puede utilizar para comunicarse entre varias tarjetas ESP8266 y ESP32. Basta con adaptar el código al tipo de tarjeta.
Código
Para probar la comunicación ESP-NOW entre dos ESP8266s, utilizamos la librería espnow.h disponible cuando se instala 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 émetteur
Para que la tarjeta emisora pueda comunicarse con otra tarjeta ESP8266, 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() (Serial.println(WiFi.macAddress());). Vamos a definir una función que se ejecute después de enviar un mensaje para comprobar que la transmisión se ha realizado correctamente.
No olvides establecer el rol de esp a CONTROLADOR en la configuración: esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
#include <espnow.h>//https://github.com/esp8266/Arduino/blob/master/tools/sdk/include/espnow.h #include <ESP8266WiFi.h> uint8_t broadcastAddress[] = {0xDC, 0x4F, 0x22, 0x58, 0xD2, 0xF5};// 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; unsigned long previousTime=0; // callback when data is sent void OnDataSent(uint8_t *mac_addr, uint8_t status) { Serial.print(F("\r\n Master packet sent:\t")); Serial.println(status == 0 ? "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() != 0) { Serial.println(F("Error initializing ESP-NOW")); return; } Serial.print(F("\nReciever initialized : ")); Serial.println(WiFi.macAddress()); // Define Send function esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER); esp_now_register_send_cb(OnDataSent); // Register peer esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0); } void loop() { if((millis() -previousTime)>1000){ // 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 uint8_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData)); if (result == 0) { Serial.println(F("Sent with success")); } else { Serial.println(F("Error sending the data")); } previousTime=millis(); } }
Código récepteur
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.
No olvides definir el rol de esp como ESCLAVO en la configuración: esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
#include <espnow.h>// https://github.com/esp8266/Arduino/blob/master/tools/sdk/include/espnow.h #include <ESP8266WiFi.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(uint8_t * mac, uint8_t *incomingData, uint8_t 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() != 0) { Serial.println("Error initializing ESP-NOW"); return; } Serial.print(F("\nReciever initialized : ")); Serial.println(WiFi.macAddress()); // Define receive function esp_now_set_self_role(ESP_NOW_ROLE_SLAVE); 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 del receptor. Debes introducirla en el código del transmisor para garantizar el emparejamiento y la comunicación correctos.
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 ESP8266s
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 <espnow.h>//https://github.com/esp8266/Arduino/blob/master/tools/sdk/include/espnow.h #include <ESP8266WiFi.h> const char nom[10]="Master"; //or slave uint8_t broadcastAddress[] = {0xDC, 0x4F, 0x22, 0x58, 0xD2, 0xF5};// REPLACE WITH OTHER STATION MAC ADDRESS // Structure example to send data // Must match the receiver structure typedef struct struct_message { char a[32]; int b; bool c; } struct_message; struct_message dataSent; struct_message dataRcv; unsigned long previousTime=0; // callbacks for sending and receiving data void OnDataSent(uint8_t *mac_addr, uint8_t status) { Serial.print("\r\n"+String(nom)+" packet sent:\t"); Serial.println(status == 0 ? "Delivery Success" : "Delivery Fail"); } void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) { memcpy(&dataRcv, incomingData, sizeof(dataRcv)); Serial.print("\r\nBytes received: "); Serial.println(len); Serial.print("From: "); Serial.println(dataRcv.a); Serial.print("Sensor: "); Serial.println(dataRcv.b); Serial.print("Status: "); Serial.println(dataRcv.c); 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() != 0) { Serial.println(F("Error initializing ESP-NOW")); return; } Serial.print(F("Reciever initialized : ")); Serial.println(WiFi.macAddress()); // Define callback functions esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER); //ESP_NOW_ROLE_SLAVE if slave esp_now_register_send_cb(OnDataSent); esp_now_register_recv_cb(OnDataRecv); // Register peer esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0); //ESP_NOW_ROLE_CONTROLLER if slave } void loop() { if((millis() -previousTime)>1000){ // Set values to send strcpy(dataSent.a, nom); dataSent.b = random(100, 200); dataSent.c = false; // Send message via ESP-NOW uint8_t result = esp_now_send(broadcastAddress, (uint8_t *) &dataSent, sizeof(dataSent)); previousTime=millis(); } }
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.
Para pasar de un código MASTEr a un código ESCLAVO, es necesario modificar el código:
- nombre de la estación nombre[10] (Opcional)
- argumento de la función esp_now_set_self_role
- argumento de la función esp_now_add_peer
Resultados
Utilizando el mismo código con unas ligeras modificaciones, es posible establecer una comunicación bidireccional entre dos ESP8266.