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
- Ordinateur
- NodeMCU ESP32 x2
- Câble USB A Mâle vers Mini B Mâle x2
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;
Merci pour cet article qui décrit bien l’envoi de données entre 2 ESP32. J’ai testé avec succès. Par contre la distance de communication entre 2 ESP32 sur terrain dégagé n’excède pas 15m. Pour atteindre les 200m indiqués sur l’article il faut peut-être brancher une antenne externe sur chaque ESP. Avez-vous fait ce test ?
Bonjour,
Il est possible de rajouter une antenne externe mais je n’ai pas fait ce test.
Pour atteindre un distance de 200m avec les antennes PCB, il faut peut être modifier le code pour envoyer des données de plus petite taille avec une fréquence moins élevée. Il y a aussi certainement des paramètres de la librairie esp-now qu’il est possible de modifier pour améliorer ces performances.
peut on me dire où est ce que l’on trouve la bibliothéque .Je ne le trouve ni sur gitub ni sur internet ,ni sur les bibliothéque interne
Normalement, elle est installé lorsqu’on installe le gestionnaire pour ESP32
esp_now.h
Bonjour,
Dans certains cas, dont les circonstances me sont totalement inconnues, l’émetteur n’arrive pas à se connecter au récepteur.
Après quelques recherches, j’ai trouvé sur ce forum (https://rntlab.com/question/espnow-peer-interface-is-invalid/) que le problème venait de la déclaration locale de `esp_now_peer_info_t peerInfo;`
Il suffit de le mettre en global pour que ça fonctionne (!?!)
Bonjour,
Merci pour l’article, comment faire si l’on a plusieurs salves et 1 master en mode bidirectionnel?
Comment dans ce cas enregistrer les mc address des slaves?
Merci
Merci beaucoup pour cet article, je réussir à faire communiquer les deux esp32 bidirectionnelle mais comment pourrais-je faire pour plusieurs slaves et un seul maître, les slaves envoi leurs requetés et maître à son tour réponds chaque slave conformément à sa requeté ?
Et comment identifier chaque slave ?
Merci
Bonjour,
Merci pour votre commentaire. Pourriez-vous tester ce tutoriel?
Merci d’avance
Bonjour,
Je vais savoir si je peux modifier la variable uint8_t en loop() comment pourrais-je faire ?
Merci
Vous pouvez modifier la structure myData pour y inclure n’importe quelle variables
typedef struct struct_message {
char a[32];
int b;
float c;
String d;
bool e;
} struct_message;
struct_message myData
Bonjour
là pas de souci je parle de uint8_t ce lui qui porte l’adresse MAC ou l’identifiant ce dès là qui pose le problème,
Parce que je vais commencé à modifier ça chaque fois que je veux changer le Slave
Merci
bonjour,
merci pour cet article..
j’ai testé et ça fonctionne..
je cherche à transmettre le signal d’un bus I2C
je m’explique je voudrais transmettre le signal que l’on trouve sur un bus sur lequel une sonde de température DS18B20 est branchée..
je ne cherche pas à récupérer la value de la température mais à répliquer le signal lui même sur l’autre ESP32…
est-ce possible ?
bien cordialement
Le signal envoyé sur le bus I2C est codés en bytes. Il est tout à fait possible de l’envoyer tel quel via ESP-NOW.
It’s doesn’t work with this code, i don’t know why, but i put after:
esp_now_peer_info_t peerInfo;
this line and it’s work….
memset(&peerInfo, 0, sizeof(peerInfo));
Thanks a lot!
another solution is to declare esp_now_peer_info_t peerInfo; outside loop()
and place it before setup();