fbpixel
Etiquetas: ,
5
(1)

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.

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.

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;

Fontes

How useful was this post?

Click on a star to rate it!

Average rating 5 / 5. Vote count: 1

No votes so far! Be the first to rate this post.

As you found this post useful...

Follow us on social media!

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?