fbpixel
Etiquetas: ,

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.

espnow-esp32-get-mac-address Comunicación entre dos ESP32 con ESP-NOW

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.

espnow-esp32-master-results Comunicación entre dos ESP32 con ESP-NOW
espnow-esp32-slave-results Comunicación entre dos ESP32 con ESP-NOW

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;
espnow-esp32-transceiver0-results Comunicación entre dos ESP32 con ESP-NOW
espnow-esp32-transceiver1-results Comunicación entre dos ESP32 con ESP-NOW

Fuentes