Site icon AranaCorp

Communication entre deux ESP32 avec ESP-NOW

Dans ce tutoriel, nous allons voir comment configurer deux ESP32 afin d’établir une communication avec le protocole ESP-NOW. L’ESP32 est une carte de développement intégrant le Bluetooth et le Wifi. Elle peut donc se connecter et échanger des données avec des appareils connectés à ce même réseau.

Matériel

Description ESP-NOW

ESP-NOW est un protocole de communication, développé par Espressif, permettant la communication sans fil de plusieurs appareils sans utiliser de réseau particulier. Il permet l’échange à haute vitesse de petits paquets de données sur les bandes de fréquence 2.4GHz et ce jusqu’à 200m de distance. Il nécessite un appairage initiale mais une fois cela fait la communication est persistante et s’établit d’elle même au démarrage de l’ESP32. Un des gros intérêts, mise à part qu’il utilise un réseau dédié, est que l’un ou plusieurs des stations ESP32 peuvent se connecter au Wifi en parallèle.

EPS-NOW permet aussi de communiquer entre plusieurs cartes ESP32 et ESP8266. Il suffit pour cela d’adapter le code au type de carte.

Code

Pour tester la communication ESP-NOW entre deux ESP32, nous utilisons la librairie esp_now.h disponible à l’installation du gestionnaire de carte. Nous allons définir et envoyer une structure de données entre la carte maître et la carte esclave. Cette structure doit être identique entre les deux cartes pour que les données soient comprises correctement.

Code émetteur

Pour que la carte émettrice puisse communiquer avec une autre carte ESP32, elle a besoin de son adresse MAC. Vous pouvez récupérer cette adresse au démarrage de la carte réceptrice dans les messages envoyés à partir de la fonction setup(). Nous allons définir une fonction qui s’exécute après l’envoie d’un message pour vérifier si la transmission s’est bien passée.

#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.: Dans certaines version, ce code ne fonctionne pas. Deux solutions possibles si vous rencontrez un problème: ou

  • placer memset(&peerInfo, 0, sizeof(peerInfo)); après la déclaration de peerInfo
  • déclarer esp_now_peer_info_t peerInfo avant le loop()

Code récepteur

Dans le code récepteur, nous allons créer une fonction qui s’exécute à la réception d’un message. Cette fonction permet de traiter les informations reçues. Dans cet exemple, nous affichons les données contenues dans la structure.

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

Lorsque vous téléversez pour la première fois le code, observez bien le message du setup contenant l’adresse MAC. Vous devez la placer dans le code de l’émetteur pour réussir l’appairage et que la communication fonctionne.

Résultat

Une fois l’adresse MAC de la carte réceptrice définie et les codes téléversés sur chaque carte, la communication s’établit et la structure est envoyée et déchiffrée correctement.

Bonus: Communication bidirectionnelle entre deux ESP32

Nous avons vu comment envoyer des données d’une carte à une autre en utilisant le protocole ESP-NOW. Nous allons voir, maintenant, comment, en modifiant légèrement les codes, on peut obtenir une communication dans les deux sens.

Code 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.: Pour obtenir le même code pour l’émetteur et le récepteur, nous utilisons la même structure de données pour les messages envoyés et reçu. Il est possible de définir des structures différentes en fonction de la provenance du message. Tout en gardant à l’esprit que la carte qui réceptionne doit connaître la structure du message reçu pour pouvoir le déchiffrer..

Résultat

Pour la station 0, nous définissons le message

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

Et pour la station 1:

  strcpy(dataSent.a, "Slave");
  dataSent.b = random(10, 20);
  dataSent.c = false;

Sources

Quitter la version mobile