Configurer et connecter le Wi-Fi

L’ensemble des sketchs de notre projet peuvent être téléchargés ici.

Le sketch « wifi_connection.ino » ajoute la connectivité Wi-Fi à « independant_watchdog.ino ». Il montre comment :

  1. démarrer le module ESP-01 connecté sur l’UART4 avec la fonction Initialize_WiFi ;
  2. lister les réseaux Wi-Fi à proximité avec la fonction listWiFiNetworks ;
  3. se connecter sur l’un d’entre eux avec la fonction Connect_WiFi.

Dans les déclarations globales nous avons apporté les modifications suivantes :

  • Définition de deux constantes, DELAY_5S et RESET_RETRY :
028 #define DELAY_5S (5000)// Temps d'attente 5s pour différents usages
029 // Nombre de tentatives avant de forcer un software reset
030 #define RESET_RETRY (2)
  • Déclarations pour le Wi-Fi :
096 // Bibliothèque pour le WiFi
097 #include <WiFiEsp.h>
098 #define WIFI_BDRATE (115200)
099 
100 // L'objet WiFi_Serial sera un port série physique connecté aux broches
101 // PA1 (pour la ligne RX) et PA0 (pour la ligne TX)
102 HardwareSerial WiFi_Serial(PA1, PA0);
103 
104 // Mémorisation des informations de connexion au réseau WiFi
105 #define WIFI_SSID "mySSID"
106 #define WIFI_PASS "myPASS"
107 char ssid[] = WIFI_SSID;
108 char pass[] = WIFI_PASS;

On notera à la ligne 98 la déclaration d’un nouveau port série WiFi_Serial, connecté à l’UART4 grâce à la classe HardwareSerial. Attention, le débit de ce port série (ligne 98) ne sera pas nécessairement égal à 115200 bauds par défaut pour votre module ESP-01 ; vous devrez peut-être préalablement le configurer avec des commandes AT. Si la documentation technique de votre module indique une valeur par défaut différente de son débit, vous pouvez plus simplement la reporter à la place de 115200 dans le sketch.

Les lignes 105 à 108 montrent comment mémoriser l’identifiant WIFI_SSID du réseau Wi-Fi et son mot de passe WIFI_PASS. Pour cet exemple, nous leur avons attribué respectivement les valeurs « mySSID » et « myPASS », mais vous devrez bien sûr remplacer ces valeurs par les véritables informations de connexion de votre réseau.

Cette approche, qui consiste à écrire les informations de connexion « en dur » dans le sketch, présente l’avantage de la simplicité mais hélas deux inconvénients :

  1. Elle les expose en clair dans le code avant sa compilation, ce qui compromet leur confidentialité.
  2. Si vous déplacez votre station météo et avez besoin de la connecter à un autre réseau Wi-Fi, vous devrez éditer votre sketch pour changer ces informations, le recompiler et charger le nouveau firmware dans la carte NUCLEO.

Nous proposerons par la suite donc une solution un peu plus sécurisée et plus pratique pour modifier à volonté WIFI_SSID et WIFI_PASS sans avoir à recompiler. En contrepartie, le code sera nettement plus compliqué et sujet à des bugs potentiellement difficiles à identifier.

Ceci amène à une réflexion concernant les objets connectés : si vous les bricolez dans le cadre de vos loisirs, demandez-vous toujours s’il est indispensable de rajouter une fonction lorsqu’elle apporte un petit confort occasionnel au prix d’un code plus complexe !

Dans setup, nous avons ajouté les appels aux fonctions Initialize_WiFi (ligne 148) et listWiFiNetworks (ligne 151) :

147   // Initialise le module Wi-Fi
148   Initialize_WiFi(&Serial);
149 
150   // Liste les réseaux Wi-Fi à proximité
151   listWiFiNetworks(&Serial);

Et voici les codes de ces deux fonctions, inspirés des exemples de la bibliothèque WiFiESP :

446 void Initialize_WiFi(HardwareSerial *serial) {
447 
448   // Initialise le port série du WiFi
449   WiFi_Serial.begin(WIFI_BDRATE);
450   while (!WiFi_Serial) delay(100);
451  
452   serial->println("WiFi serial baud rate set to " + String(WIFI_BDRATE));
453 
454   // Initialise le module ESP-01
455   WiFi.init(&WiFi_Serial);
456 
457   if (WiFi.status() == WL_NO_SHIELD) {
458     serial->println("WiFi module not present");
459     while (true);
460   }
461   serial->println("WiFi module available");
462 }
490 void listWiFiNetworks(HardwareSerial *serial) {
491 
492   // Cherche les réseaux Wifi à proximité
493   int numSsid = WiFi.scanNetworks();
494 
495   if (numSsid == -1) {
496     serial->println("Couldn't get a wifi connection");
497     while (true);
498   }
499  
500  // Renvoie la liste des réseaux trouvés sur le port série serial
501   serial->println("Available WiFi networks :");
502  
503   for (int thisNet = 0; thisNet < numSsid; thisNet++) {
504 
505     if (strlen(WiFi.SSID(thisNet))) { // Si des réseaux on été trouvés 
506  
507       serial->print(thisNet + ") ");
508       serial->print(WiFi.SSID(thisNet));
509       serial->print("\tSignal: ");
510       serial->print(WiFi.RSSI(thisNet) + " dBm");
511       serial->print("\tEncryption: ");
512 
513       int thisType = WiFi.encryptionType(thisNet);
514 
515       switch (thisType) {
516         case ENC_TYPE_WEP:
517           serial->println("WEP");
518           break;
519         case ENC_TYPE_WPA_PSK:
520           serial->println("WPA_PSK");
521           break;
522         case ENC_TYPE_WPA2_PSK:
523           serial->println("WPA2_PSK");
524           break;
525         case ENC_TYPE_WPA_WPA2_PSK:
526           serial->println("WPA_WPA2_PSK");
527           break;
528         case ENC_TYPE_NONE:
529           serial->println("None");
530           break;
531       }
532     }
533   }
534 }

Le code de loop est également peu modifié, avec l’ajout de l’appel à la fonction Connect_WiFi en ligne 175 :

165 void loop() {
166 
167   if (rec_mod == SET_REC_MOD) { // Toutes les SET_REC_MOD itérations...
168 
169     rec_mod = 0;
170 
171     // Allume la LED utilisateur
172     digitalWrite(LED_BUILTIN, HIGH);
173 
174     // Connexion du module Wi-Fi au réseau spécifié
175     Connect_WiFi(&Serial);
176 
177     // Lecture des capteurs environnementaux
178     Read_Sensors(&Serial);
179 
180     // Construit un objet "String" nommé "payload" à partir des mesures
181     String payload = String(measure.temperature) + ";" + String(measure.pressure)
182                      + ";" + String(measure.humidity);
183 
184     // Envoie le contenu de payload dans le tableau de caractères Record_Payload
185     payload.toCharArray( Record_Payload, RECORD_PAYLOAD_SIZE );
186 
187     // Ecris le contenu de Record_Payload comme nouvelle ligne dans logfilename
188     SD_Write_Record(logfilename, Record_Payload);
189 
190     // Affichage des mesures sur le port série du ST-LINK
191     Serial.println("Payload : " + payload);
192 
193     // Eteint la LED utilisateur
194     digitalWrite(LED_BUILTIN, LOW);
195 
196   } // Clôture de if (rec_mod == SET_REC_MOD)
197 
198   rec_mod++;
199 


208   // Mise en veille (mode "STOP") avant l'itération suivante
209   delay(5);
210   LowPower.deepSleep(main_loop_delay);
211 }

Ci-dessous le code de la fonction Connect_WiFi lui aussi construit à partir des exemples de la bibliothèque WiFiESP :

467 void Connect_WiFi(HardwareSerial *serial) {
468
469   uint8_t status = WL_IDLE_STATUS;
470   uint8_t n = 0;
471 
472   while ( status != WL_CONNECTED) {
473
474     serial->println("\nConnecting to WPA SSID: " + (String)ssid);
475     status = WiFi.begin(ssid, pass);
476     n++;
477
478     // Rechargement du watchdog
479     IWatchdog.reload();
480
481     // Si trop de tentatives de reconnexion infructueuses, RESET !
482     if (n > RESET_RETRY) while (true);
483     delay(DELAY_5S);
484   }
485 }

On constate que le sketch tente de connecter le module ESP-01 au réseau Wi-Fi toutes les DELAY_5S = 5 secondes (ligne 483) mais qu’après RESET_RETRY = 2 tentatives infructueuses il bloque l’exécution dans une boucle sans clause de fin (ligne 482). Le cas échéant, compte tenu du fait que le compte à rebours de l’IDWG ne sera plus rechargé avant qu’il atteigne zéro, la station redémarrera IWDG_TIMEOUT_MAX = 32 secondes plus tard par suite d’un software reset.