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;