Dans ce tutoriel, nous allons voir comment configurer un réseau de plusieurs ESP32 avec le protocole ESP-NOW. L’ESP32 est une carte de développement intégrant 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 ou NodeMCU ESP8266 ou Wemos D1 Mini x3
- Câble USB A Mâle vers Mini B Mâle x3
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. 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 ou ESP8266 peuvent se connecter au Wifi en parallèle.
Il est possible grâce à ce protocole de créer un réseau de NodeMCU qui communiquent entre elles.
Code
Nous allons établir une communication bidirectionnelle entre plusieurs ESP32 à l’aide la librairie espnow.h disponible à l’installation du gestionnaire de carte. Nous allons définir et envoyer une structure de données identique pour toute les cartes afin de simplifier le code.
Code Master
Pour que la carte émettrice puisse communiquer avec une autre carte, 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() (Serial.println(WiFi.macAddress());). 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.
N’oubliez pas de modifier les adresses des cartes esclaves correspondant aux cartes que vous utilisez.
#include <esp_now.h> #include <WiFi.h> const char nom[10]="Master"; uint8_t broadcastAddress[2][6] = { {0x2C, 0xF4, 0x32, 0x15, 0x52, 0x22}, //station0 {0xA0, 0x20, 0xA6, 0x08, 0x20, 0xD9} //station1 };// 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; struct_message dataRcv; unsigned long previousTime=0; // callbacks for sending and receiving data void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { Serial.print(F("\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.a); 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("Reciever initilized : ")); 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; peerInfo.channel = 0; peerInfo.encrypt = false; memcpy(peerInfo.peer_addr, broadcastAddress[0], 6); if (esp_now_add_peer(&peerInfo) != ESP_OK){ Serial.println("Failed to add peer"); return; } memcpy(peerInfo.peer_addr, broadcastAddress[1], 6); if (esp_now_add_peer(&peerInfo) != ESP_OK){ Serial.println("Failed to add peer"); return; } } void loop() { if((millis() -previousTime)>5000){ // Set values to send strcpy(myData.a, nom); myData.b = random(1, 20); myData.c = 1.2; myData.e = false; // Send message via ESP-NOW myData.d = "Slave0"; esp_err_t result0 = esp_now_send(broadcastAddress[0], (uint8_t *) &myData, sizeof(myData)); myData.d = "Slave1"; esp_err_t result1 = esp_now_send(broadcastAddress[1], (uint8_t *) &myData, sizeof(myData)); previousTime=millis(); } }
Code Slave
Dans le code SLAVE, 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.
Il faudra modifier le code Slave pour chaque carte esclave afin que les données et identifiant soit différent.
#include <esp_now.h> #include <WiFi.h> const char nom[10]="Slave0"; uint8_t broadcastAddress[] = {0x3C, 0x61, 0x05, 0x30, 0x0A, 0x28};// REPLACE WITH MASTER MAC ADDRESS //{0xDC, 0x4F, 0x22, 0x58, 0xD2, 0xF5} //station0 // 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 dataSent; struct_message dataRcv; unsigned long previousTime=0; // callbacks for sending and receiving data void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { Serial.print("\r\n"+String(nom)+" 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: "); Serial.println(dataRcv.a); Serial.print("To: "); Serial.println(dataRcv.d); Serial.print("Sensor: "); Serial.println(dataRcv.b); Serial.print("Status: "); Serial.println(dataRcv.c); 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("Reciever 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() { if((millis() -previousTime)>1500){ // Set values to send strcpy(dataSent.a, nom); dataSent.b = random(100, 200); dataSent.c = false; // Send message via ESP-NOW esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &dataSent, sizeof(dataSent)); previousTime=millis(); } }
(Code pour ESP8266 ci-dessous)
Lorsque vous téléversez pour la première fois le code, observez bien le message du setup contenant l’adresse MAC du récepteur. 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.
- Master
- Slave0
- Slave1
ATTENTION: lors de la réalisation de ce tutoriel, une des cartes semblait défectueuse et n’envoyait pas de message au maitre alors qu’elle en recevait. A tester et valider avec une autre cartes
Bonus: Communication entre ESP32 et ESP8266
Pour intégrer des cartes ESP8266 dans votre réseau ESP-NOW, il suffit de modifier quelque ligne de code.
Je vous invite à revoir le tutoriel sur ESP-NOW et ESP8266
Pour passer d’un code ESP32 à un code ESP8266, vous devez modifier:
- Les includes en début de code
- Rajouter une fonction esp_now_set_self_role avec le bon rôle défini dans chaque cas
- Modifier les aguments de la fonction esp_now_add_peer
- Modifier les types des arguments des fonctions OnDataRecv et OnDataSent
#include <espnow.h>//https://github.com/esp8266/Arduino/blob/master/tools/sdk/include/espnow.h #include <ESP8266WiFi.h> const char nom[10]="Slave0"; uint8_t broadcastAddress[] = {0x3C, 0x61, 0x05, 0x30, 0x0A, 0x28};// REPLACE WITH MASTER MAC ADDRESS //{0xDC, 0x4F, 0x22, 0x58, 0xD2, 0xF5} //station0 // 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 dataSent; struct_message dataRcv; unsigned long previousTime=0; // callbacks for sending and receiving data void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { Serial.print("\r\n"+String(nom)+" packet sent:\t"); Serial.println(status == 0 ? "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: "); Serial.println(dataRcv.a); Serial.print("To: "); Serial.println(dataRcv.d); Serial.print("Sensor: "); Serial.println(dataRcv.b); Serial.print("Status: "); Serial.println(dataRcv.c); 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() != 0) { Serial.println(F("Error initializing ESP-NOW")); return; } Serial.print(F("Reciever initialized : ")); Serial.println(WiFi.macAddress()); // Define callback functions esp_now_set_self_role(ESP_NOW_ROLE_SLAVE); esp_now_register_send_cb(OnDataSent); esp_now_register_recv_cb(OnDataRecv); // Register peer esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_CONTROLLER, 1, NULL, 0); } void loop() { if((millis() -previousTime)>1500){ // Set values to send strcpy(dataSent.a, nom); dataSent.b = random(100, 200); dataSent.c = false; // Send message via ESP-NOW uint8_t result = esp_now_send(broadcastAddress, (uint8_t *) &dataSent, sizeof(dataSent)); previousTime=millis(); } }
Bonus: Afficher l’adresse MAC pour la copier
Il est possible d’afficher l’adresse MAC du Master ou des Slaves dans un format qui permet de le copier directement dans broadcastAddress.
void getMacAdress(const uint8_t * mac){ /*for (int i=0; i<6; i++){ if (mac[i]<10) Serial.print(0,HEX),Serial.print(mac[i],HEX); // FF:FF:FF:FF:FF:FF else Serial.print(mac[i],HEX); if(i<5) Serial.print(","); } */ Serial.print("{"); for (int i=0; i<6; i++){ Serial.print("0x"); if (mac[i]<10) Serial.print(0,HEX),Serial.print(mac[i],HEX); // {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF} else Serial.print(mac[i],HEX); if(i<5) Serial.print(","); } Serial.print("}"); }