Quelques exemples de ce qui est possible avec l’ESP32 et comment le faire :
Note : Les parties de code utilisées dans cet article sont à intégrer dans un programme, le code ne fonctionne pas seul (sauf le premier exemple pour l’initialisation OTA qui est complet).
Les mises à jour Over The Air (à distance)
La connexion avec un câble USB, c’est pas mal pour préparer et tester son code; mais lorsque l’ESP32 est installé dans une boite de dérivation peu accessible, comment faire pour adapter son programme ?
Pas de problème 🙂 , nous allons travailler sans fil et sans rien avoir à démonter.
Tout d’abord, il faut implanter dans votre ESP32 un petit programme qui va attendre patiemment l’arrivée de vos mises à jour. Il suffit de mettre vos propres valeurs dans les 3 zones : « votre_box« , « votre_motdepasse » et « votre_esp » puis d’envoyer le programme sur votre ESP32. Le programme va s’installer dans une zone OTA spécifique et il ne réagira qu’à l’arrivée d’une nouvelle version de votre programme principal.
Il faudra installer les quatre bibliothèques déclarées dans les #include en début de programme avec le Gestionnaire de Bibliothèques de l’IDE Arduino.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
// Librairies à utiliser #include <WiFi.h> // Librairie WiFi.h (gestion du wifi) #include <WiFiUdp.h> // Librairie WiFiUdp.h (gestion du protocole UDP) #include <ESPmDNS.h> // Librairie ESPmDNS.h (gestion mDNS - UDP sur le port 5353) #include <ArduinoOTA.h> // Librairie ArduinoOTA.h (mise à jour via wifi) const char* ssid = "votre_box"; const char* password = "votre_motdepasse"; void setup() { Serial.begin(115200); Serial.println("Booting"); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("Connection Failed! Rebooting..."); delay(5000); ESP.restart(); } // Port defaults to 3232 // ArduinoOTA.setPort(3232); // Hostname defaults to esp3232-[MAC] ArduinoOTA.setHostname("votre_esp"); // No authentication by default // ArduinoOTA.setPassword("admin"); // Password can be set with it's md5 value as well // ArduinoOTA.setPasswordHash("DCD9938D2E394A4F85918D477079232C"); ArduinoOTA .onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() Serial.println("Start updating " + type); }) .onEnd([]() { Serial.println("\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }) .onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); Serial.println("Ready"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } void loop() { ArduinoOTA.handle(); } |
Je n’ai pas réussi à faire fonctionner correctement l’authentification, alors j’ai laissé les lignes en commentaire (26 à 30).
Après l’ajout de ce module OTA sur votre ESP32, un nouveau port de connexion apparaîtra avec le nom que vous avez donné à votre ESP et son adresse IP. C’est ce port réseau qu’il faudra sélectionner pour les prochaines mises à jour en OTA.

Ensuite, il va falloir adapter légèrement votre programme principal pour que la mise à jour OTA fonctionne. Voici les éléments qu’il faut rajouter :
- les 4 librairies utilisées dans le programme d’initialisation OTA,
- les 3 variables (wifi_ssid, wifi_password et host_name),
- dans le setup, un appel à la fonction setup_ota (en plus de vos autres éléments),
- la fonction setup_ota elle même,
- dans la boucle loop, l’attente d’une demande de mise à jour OTA (en plus du code du programme).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
// Librairies à utiliser #include <WiFi.h> // Librairie WiFi.h (gestion du wifi) #include <WiFiUdp.h> // Librairie WiFiUdp.h (gestion du protocole UDP) #include <ESPmDNS.h> // Librairie ESPmDNS.h (gestion mDNS - UDP sur le port 5353) #include <ArduinoOTA.h> // Librairie ArduinoOTA.h (mise à jour via wifi) // Paramétres WIFI à modifier en fonction des paramètres locaux #define wifi_ssid "votre_box" #define wifi_password "votre_motdepasse" #define host_name "votre_esp" void setup() { // Connexion au réseau WiFi setup_wifi(); // appel de la fonction setup_wifi sans paramètre // Connexion OTA pour les mises à jour en wifi setup_ota(); // appel de la fonction setup_ota sans paramètre } // Gestion OTA pour les mises à jour distantes en wifi void setup_ota() { // Port defaults to 3232 // ArduinoOTA.setPort(3232); // Hostname defaults to esp3232-[MAC] ArduinoOTA.setHostname(host_name); // No authentication by default // ArduinoOTA.setPassword("admin"); // Password can be set with it's md5 value as well // ArduinoOTA.setPasswordHash("DCD9938D2E394A4F85918D477079232C"); ArduinoOTA .onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() Serial.println("Start updating " + type); }) .onEnd([]() { Serial.println("\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }) .onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); Serial.println("Ready"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } void loop() { ArduinoOTA.handle(); // Attente d'une demande de MàJ OTA } |
Il faudra donc rajouter ces éléments dans tous les programmes pour lesquels vous souhaitez une mise à jour OTA. Pour moi, c’est systématique dans tous mes programmes.
Bien, tout ça c’est parfait, mais comment faire du debug dans cette configuration ? En effet, le moniteur série ne fonctionne plus puisque nous ne sommes plus en connexion série !
Débugger à distance
Il y a aussi une solution, certes un peu différente des « Serial.println » habituels mais tout à fait fonctionnelle. Nous allons ajouter 2 librairies, puis instancier (ou initialiser/activer) RemoteDebug, rajouter une ligne dans la boucle loop pour l’attente d’une demande de débug et modifier le serveur web pour rajouter l’option « debug ». Et oui, il est aussi possible d’installer un serveur web sur un ESP32 😀 , on y reviendra plus loin.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include "RemoteDebug.h" // Librairie RemoteDebug.h (pour mise au point en wifi) #include "RemoteDebugger.h" // Librairie RemoteDebugger.h (pour mise au point en wifi) // à mettre en début de programme avec les variables // Instanciation de RemoteDebug (ou initialisation/activation) RemoteDebug Debug; // ligne de création de la page web /debug à mettre dans le setup avec les autres lignes du serveur web server.on("/debug", page_debug); // page de debug // Fonction page_debug dans laquelle on indique ce que l'on souhaite debugger // ici je veux vérifier l'initialisation des sondes de température void page_debug() { setup_sondes(); Debug.println("Commande OK"); server.send(200, "text/plain","Nombre de sondes : "+String(nbsonde)+" => messages de debug via Putty"); } // à mettre dans la boucle loop Debug.handle(); // Attente d'une demande de debug |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
void setup_sondes() { // Initialisation pour les sondes de température sensors.begin(); // locate devices on the bus Debug.print("Recherche des Sondes..."); Debug.print("Trouvé "); Debug.print(sensors.getDeviceCount(), DEC); nbsonde = sensors.getDeviceCount(); Debug.println(" sondes."); // report parasite power requirements Debug.print("Parasite power is: "); if (sensors.isParasitePowerMode()) Debug.println("ON"); else Debug.println("OFF"); // On vérifie si les sondes sont connectées for (i = 0; i < 6; i = i + 1) { if (!sensors.getAddress(Sond[i], 0)) { Debug.print("Adresse de la Sonde "); Debug.print(i); Debug.println(" non trouvée ! "); } // set the resolution to 9 bit per device sensors.setResolution(Sond[i], TEMPERATURE_PRECISION); // On vérifie que la sonde est correctement configurée Debug.print("Résolution Sonde "); Debug.print(i); Debug.print(" : "); Debug.println(sensors.getResolution(Sond[i]), DEC); } } |
Dans la fonction à débugger, j’ai simplement remplacer les « Serial » par « Debug ».
Voyons maintenant ce que cela donne :
Je me connecte en telnet (port 23) sur l’ESP avec Putty et dans mon navigateur je vais sur la page /debug comme sur les copies d’écran ci-dessous.
A chaque activation de la page web /debug le programme va exécuter la fonction setup_sondes qui se trouve dans la description de la page. Si je souhaite analyser le déroulement d’une autre partie du programme, je modifie le contenu de cette page web.
On peut aussi voir dans la connexion telnet l’adresse IP, l’adresse Mac, la mémoire disponible …
Voici un lien (en anglais) pour plus d’informations : RemoteDebug sur github
Comme je suis loin d’avoir vu tout ce qui est possible avec RemoteDebug, si vous trouvez des choses intéressantes, indiquez les dans les commentaires.
Utiliser une adresse IP fixe
Ce serait quand même mieux si on avait toujours la même adresse IP pour un ESP32 ! Pas de soucis, on va fixer l’adresse IP de notre ESP.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// à ajouter dans les variables au début du code et en mettant vos adresses à vous // Définition de l'adresse IP fixe pour l'ESP32 IPAddress local_IP(192, 168, 1, 200); // Indication de l'adresse de la passerelle (la box) IPAddress gateway(192, 168, 1, 1); // Précision du subnet du réseau local IPAddress subnet(255, 255, 255, 0); // Indication du serveur DNS primaire à utiliser (ici DNS de Google) IPAddress primaryDNS(8, 8, 8, 8); // facultatif // Indication du serveur DNS secondaire à utiliser ici DNS de Google) IPAddress secondaryDNS(8, 8, 4, 4); // facultatif // à mettre dans le set-up après l'initialisation du wifi // Configuration de l'adresse IP fixe (DNS facultatif) if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) { Serial.println("Erreur de configuration pour l'IP fixe ! "); } |
Voilà c’est tout pour l’adresse IP fixe ! Simple non 😉
Comment éviter la boucle loop ?
Cette boucle loop, c’est compliqué car elle fonctionne en permanence et à toute vitesse 😥 Si l’on souhaite mettre des temporisations, on bloque tout le programme et en plus il faut gérer le temps, calculer les délais, … bref c’est pas idéal.
Heureusement, il y a une bibliothèque parfaite (encore une !) pour améliorer le fonctionnement de notre ESP32 : SoftTimer. Le principe, c’est de définir des tâches qui vont exécuter des fonctions avec une périodicité donnée. Dans l’exemple ci-dessous, la tâche t1 va exécuter la fonction PHMesure toutes les 20 secondes, la tâche t2 exécutera PHCalcul toutes les 60 secondes et la tâche t0 qui remplace la boucle loop s’exécutera en permanence. On voit que dans t0, il n’y a que des attentes d’interruptions.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <SoftTimer.h> // Librairie SoftTimer.h (pour le lancement périodique de taches) // Paramètres pour SoftTimer à mettre dans les variables // Périodicité pour la boucle principale (ex loop) Task t0(0, Boucle); // Boucle permanente // Périodicité pour la Mesure du Ph Task t1(20000, PHMesure); // PHMesure toutes les 20 secondes // Périodicité pour le Calcul du Ph et l'envoi à Domoticz Task t2(60000, PHCalcul); // PHCalcul toutes les minutes // à ajouter dans le setup SoftTimer.add(&t0); SoftTimer.add(&t1); SoftTimer.add(&t2); // Fonction PHMesure void PHMesure(Task* me) { // mesure du PH et stockage de l'information dans une table ph_sensor_value = analogRead(PinPH); Mesures_Ph.add(ph_sensor_value); Serial.print("Valeur lue sur la sonde PH en mVolts = "); Serial.println(ph_sensor_value); } // Boucle du programme qui s'exécute perpétuellement void Boucle(Task* me){ server.handleClient(); // Attente d'une requête HTTP ArduinoOTA.handle(); // Attente d'une demande de MàJ OTA Debug.handle(); // Attente d'une demande de debug } |
C’est beaucoup plus souple, et on supprime la boucle loop et ses inconvénients.
Un serveur web dans mon ESP32
Et oui, on va mettre un serveur web dans l’ESP32, c’est incroyable … Il y aura un exemple plus complet dans l’article sur les sondes de température du garage, on ne verra ici que les points principaux.
Dans le code ci-dessous, on intègre 2 librairies, on active le protocole http, on créé un serveur en écoute sur le port 80. Dans le setup, on définit 3 pages (/, /led_on et /led_off) + une page d’erreur et on démarre le serveur. Ensuite, on décrit une fonction pour chaque page html créée, en indiquant les actions à réaliser si la commande html est reçue. Et enfin, dans la boucle loop (ou dans la tâche permanente), on met le serveur en attente d’une commande.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <HTTPClient.h> // Librairie HTTPClient.h (et pas HttpClient.h !) (traitement des requetes http) #include <WebServer.h> // Librairie WebServer.h (serveur http) // Activation du protocole http HTTPClient http; // Création d’une instance serveur WebServer server(80); // PORT d’écoute 80 // à mettre dans la partie setup // Initialisation du serveur web server.on("/", page_html); // page d'accueil server.on("/led_on", page_html_led_on); // page /led_on et traitement associé server.on("/led_off", page_html_led_off); // page /led_off et traitement associé server.onNotFound(page_html_non_trouve); // page non trouvée et traitement de l'erreur server.begin(); // démarre le serveur HTTP // Définition du traitement pour les pages html void page_html() { server.send(200, "text/plain"," Allume (/led_on) ou eteint (/led_off) la LED interne "); // affiche un message } void page_html_led_on() { server.send(200, "text/plain"," LED on "); digitalWrite(ledPinBleu, HIGH); // allume la LED Bleue Serial.println("LED Bleue ON"); } void page_html_led_off() { server.send(200, "text/plain"," LED off "); digitalWrite(ledPinBleu, LOW); // éteint la LED Bleue Serial.println("LED Bleue OFF"); } void page_html_non_trouve() { server.send(404, "text/plain","404: Not found"); } // à mettre dans la boucle loop ou dans la tâche en exécution permanente server.handleClient(); // attend une requête HTTP |
Quelques bibliothèques intéressantes
Illustration de l’apport des bibliothèques dans l’IDE Arduino.
|
1 2 3 4 5 6 7 8 9 10 |
#include <RunningMedian.h> # Librairie RunningMedian.h (pour le calcul de moyenne sur des séries de mesure) // Table de stockage des 12 dernières mesures de PH RunningMedian Mesures_Ph = RunningMedian(12); // Ajout d'une valeur dans la table Mesures_Ph.add(ph_sensor_value); // Récupération de la valeur médiane depuis la table ph_median = Mesures_Ph.getMedian(); |
Un exemple d’utilisation de la bibliothèque RunningMedian qui permet aussi d’obtenir la valeur la plus petite, la plus grande ou la moyenne.
Globalement il y a des bibliothèques pour chaque protocole utilisé ou pour chaque composant électronique (ou famille de composants) à rajouter :
- Pour connecter des écrans I2C :
|
1 2 |
#include <Wire.h> #include <LiquidCrystal_I2C.h> |
- Pour connecter des composants en SPI :
|
1 |
#include <SPI.h> // bibliothèque SPI |
Dans le prochain article, il y aura le programme complet pour la gestion des appareils ménagers du garage et le pilotage des prises pour les pompes. Pratiquement tous les points abordés dans cet article seront utilisés dans ce programme qui pourra servir de base pour d’autres projets.
Bons tests et partagez vos réalisations.



