Etiquetas: ,

Neste tutorial, veremos como configurar dois ESP32s para estabelecer comunicação usando o protocolo ESP-NOW. O ESP32 é uma placa de desenvolvimento que integra Bluetooth e WiFi. Pode, portanto, ligar-se e trocar dados com dispositivos ligados à mesma rede.

Hardware

  • Computador
  • NodeMCU ESP32 x2
  • Cabo USB A macho para Mini B macho x2

Descrição ESP-NOW

O ESP-NOW é um protocolo de comunicação desenvolvido pela Espressif, que permite a comunicação sem fios entre vários dispositivos sem a necessidade de uma rede específica. Permite a troca de pequenos pacotes de dados a alta velocidade nas bandas de frequência de 2,4 GHz, até 200 m de distância. Requer um emparelhamento inicial, mas uma vez efectuado, a comunicação é persistente e estabelece-se quando o ESP32 é ligado. Uma das grandes vantagens, para além do facto de utilizar uma rede dedicada, é que uma ou mais estações ESP32 podem ligar-se à rede Wifi em paralelo.

O EPS-NOW também pode ser utilizado para comunicar entre várias placas ESP32 e ESP8266. Só precisa de adaptar o código ao tipo de placa.

Código

Para testar a comunicação ESP-NOW entre dois ESP32, utilizamos a biblioteca esp_now.h disponível quando instala o gestor de cartões. Vamos definir e enviar uma estrutura de dados entre o cartão mestre e o cartão escravo. Esta estrutura deve ser idêntica entre os dois cartões para que os dados sejam compreendidos corretamente.

Código émetteur

Para que a placa emissora possa comunicar com outra placa ESP32, necessita do seu endereço MAC. Este endereço pode ser obtido quando a placa recetora é iniciada a partir de mensagens enviadas através da função setup(). Vamos definir uma função que é executada depois de uma mensagem ter sido enviada para verificar se a transmissão foi bem sucedida.

#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.: Em algumas versões, este código não funciona. Existem duas soluções possíveis se encontrar um problema: ou

  • colocar memset(&peerInfo, 0, sizeof(peerInfo)); depois da declaração peerInfo
  • declare esp_now_peer_info_t peerInfo antes de loop()

Código récepteur

No código do recetor, vamos criar uma função que é executada quando uma mensagem é recebida. Esta função é utilizada para processar a informação recebida. Neste exemplo, exibimos os dados contidos na estrutura.

#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() {
}

Quando carregar o código pela primeira vez, observe atentamente a mensagem de configuração que contém o endereço MAC. É necessário introduzi-lo no código do transmissor para que o emparelhamento seja bem sucedido e a comunicação funcione.

espnow-esp32-get-mac-address Comunicação entre dois ESP32 com ESP-NOW

Resultados

Uma vez definido o endereço MAC do cartão recetor e carregados os códigos em cada cartão, a comunicação é estabelecida e a estrutura é enviada e desencriptada corretamente.

espnow-esp32-master-results Comunicação entre dois ESP32 com ESP-NOW
espnow-esp32-slave-results Comunicação entre dois ESP32 com ESP-NOW

Bónus: Comunicação bidirecional entre dois ESP32s

Vimos como enviar dados de um cartão para outro utilizando o protocolo ESP-NOW. Vamos agora ver como, modificando ligeiramente os códigos, podemos obter uma comunicação bidirecional.

Código Transceiver

#include <esp_now.h>
#include <WiFi.h>

uint8_t broadcastAddress[] = {0xA4, 0xCF, 0x12, 0x9A, 0x19, 0xAC};// REPLACE WITH 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 obter o mesmo código para o emissor e o recetor, utilizamos a mesma estrutura de dados para as mensagens enviadas e recebidas. É possível definir estruturas diferentes consoante a origem da mensagem. No entanto, é necessário ter em conta que o cartão recetor deve conhecer a estrutura da mensagem recebida para a poder decifrar.

Resultados

Para a estação 0, definimos a mensagem

  strcpy(dataSent.a, "Master");
  dataSent.b = random(100, 200);
  dataSent.c = false;

E para a estação 1:

  strcpy(dataSent.a, "Slave");
  dataSent.b = random(10, 20);
  dataSent.c = false;
espnow-esp32-transceiver0-results Comunicação entre dois ESP32 com ESP-NOW
espnow-esp32-transceiver1-results Comunicação entre dois ESP32 com ESP-NOW

Fontes