Gestion de préférences mémorisées sur ESP32 avec WiFiManager

Voici un exemple pour expliquer comment présenter à l'utilisateur une page web qui permet de rentrer des paramètres pour son application ESP32 avec WiFiManager et de les sauvegarder en mémoire Flash en utilisant l'API Preferences de Espressif

L'idée est de créer un portail captif qui affiche automatiquement une page de configuration quand on se connecte sur le réseau ouvert de l'ESP32.

WiFiManager est généralement utilisé pour choisir un Wi-Fi et fournir le mot de passe afin de configurer un ESP32 dans un environnement particulier, mais WiFiManager ne se limite pas qu'à cela et on peut ne pas demander le Wi-Fi et juste fonctionner en portail local pour fournir des paramètres de configuration / calibration d'un appareil.

C'est ce que je fais dans l'exemple qui suit, avec 3 paramètres "longueur", "largeur", et "hauteur" :

Quand vous lancez le code vous voyez dans le moniteur série (à 115200 bauds)

Système prêt. Accédez au réseau Wi-Fi AirPressure

---------------
Configuration Enregistrée:
longueur -> "10" -> 10
largeur -> "20" -> 20
hauteur -> "30" -> 30

une fois que vous rejoignez le réseau Wi-Fi AirPressure défini dans le code par

const char * wifiSSID = "AirPressure";

Vous verrez cela sur votre téléphone :

C'est mon code qui créé ce portail et rajoute un menu Setup qui donne accès aux paramètres, un séparateur et les boutons Restart Exit avec cette ligne de code:

  std::vector<const char *> menu = {"param", "sep", "restart", "exit"};
  wm.setMenu(menu);

En cliquant sur Setup vous accédez aux paramètres que j'ai défini dans un tableau

const char* parametresDesc[] = {"longueur", "largeur", "hauteur"};

la valeur par défaut (textuelle) des paramètres est stockée dans

char parametresTxt[nbParams][tailleMaxTexte] = {"10", "20", "30"};

donc ici on a bien les 3 paramètres "longueur", "largeur", et "hauteur"


vous pouvez éditer le contenu dans les champs textuels, par exemple ici je rajoute un 0 à chaque variable.

le code essaye d'être un peu intelligent et de faire une analyse de ce qui a été saisi pour extraire le nombre. Si vous tapez "coucou" à la place d'un nombre, ce sera ignoré (et il y aura un message dans le moniteur série).


Quand vous cliquez sur Save, ça déclenche l'appel du callback callbackParametres () dans le code qui se charge de relire la valeur de chacun des champs de la page web, et d'affecter sa conversion en long dans le tableau

long parametres[nbParams];

et de les stocker en mémoire permanente (Flash) de l'ESP32 en utilisant l'API Preferences de Espressif.

vous verrez dans le moniteur série que c'est bien extrait avec l'affichage

---------------
Configuration Enregistrée:
longueur -> "100" -> 100
largeur -> "200" -> 200
hauteur -> "300" -> 300

Vous pouvez donc utiliser ensuite ce tableau de paramètres dans votre programme, il correspond aux préférences de l'utilisateur et si vous coupez le courant de votre ESP32 et le remettez, vous verrez dans la console

Système prêt. Accédez au réseau Wi-Fi AirPressure

---------------
Configuration Enregistrée:
longueur -> "100" -> 100
largeur -> "200" -> 200
hauteur -> "300" -> 300

➜ on voit que les valeurs ont bien été sauvegardées.


voilà il ne reste plus qu'à intégrer cela à votre code, la seule dépendance c'est que la loop() soit non bloquante et que la première ligne soit l'appel à la gestion du wifimanager

void loop() {
  wm.process();
  // ici le reste du code en non bloquant

}

Simple et efficace.

le code
/* ============================================
  code is placed under the MIT license
  Copyright (c) 2024 J-M-L
  For the Arduino Forum : https://forum.arduino.cc/u/j-m-l

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  ===============================================
*/


#include <WiFiManager.h>                  // https://github.com/tzapu/WiFiManager
#include <Preferences.h>

Preferences preferences;

const char * wifiSSID = "AirPressure";

constexpr size_t tailleMaxTexte = 10;
const char* parametresDesc[] = {"longueur", "largeur", "hauteur"};
const size_t nbParams = sizeof parametresDesc / sizeof * parametresDesc;
char parametresTxt[nbParams][tailleMaxTexte] = {"10", "20", "30"};
long parametres[nbParams];

WiFiManager wm; // global wm instance
WiFiManagerParameter wmParameters[nbParams];


// ----------------------------------------------------------------
// WiFi Manager et Préférences
// ----------------------------------------------------------------

void printConfig() {
  Serial.println("\n---------------\nConfiguration Enregistrée:");
  for (size_t i = 0; i < nbParams; i++) {
    Serial.print(parametresDesc[i]); Serial.print(" -> \""); Serial.print(parametresTxt[i]); Serial.print("\" -> "); Serial.println(parametres[i]);
  }
  Serial.println();
}

void callbackParametres () {
  if (preferences.begin("parametres", false)) {
    for (size_t i = 0; i < nbParams; i++) {
      const char * vTxt = wmParameters[i].getValue();
      char * ptr = nullptr;
      long v = strtol(vTxt, &ptr, 10); // base 10
      if (ptr && *ptr == '\0') {
        // le parsing s'est bien passé on définit la valeur textuelle et numérique
        parametres[i] = v;
        snprintf(parametresTxt[i], tailleMaxTexte, "%s", vTxt);
        preferences.putString(parametresDesc[i], parametresTxt[i]); // on sauve pour la prochaine fois
      } else {
        // ça s'est mal passsé on ne modifie pas
        Serial.print("erreur format pour ");
        Serial.println(parametresTxt[i]);
      }
    }
    preferences.end();
  } else {
    Serial.println("Error writing Prefernces");
  }
  printConfig();
}

// ----------------------------------------------------------------

void setup() {
  Serial.begin(115200); Serial.println();

  // on lit les valeurs de configuration
  if (preferences.begin("parametres", false)) {

    for (size_t i = 0; i < nbParams; i++) {
      char * ptr = nullptr;
      char vTxt[tailleMaxTexte];
      snprintf(vTxt, tailleMaxTexte, "%s", preferences.getString(parametresDesc[i], parametresTxt[i]).c_str());
      long v = strtol(vTxt, &ptr, 10); // base 10

      if (ptr && *ptr == '\0') {
        // le parsing s'est bien passé on définit la valeur textuelle et numérique
        parametres[i] = v;
        snprintf(parametresTxt[i], tailleMaxTexte, "%s", vTxt);
      } else {
        // ça s'est mal passsé
        Serial.println("erreur parsing");
        parametres[i] = atoi(parametresTxt[i]); // on prend la valeur par défaut
        preferences.putString(parametresDesc[i], parametresTxt[i]); // on sauve pour la prochaine fois
      }
    }
    preferences.end();
  } else {
    Serial.println("Erreur Preferences");
  }

  // gestion portail config
  WiFi.mode(WIFI_STA); //  esp par défaut sur STA+AP (ou mettre WIFI_AP)

  wm.setDebugOutput(false);

  for (size_t i = 0; i < nbParams; i++) { // on créee les champs spécifiques
    new (&(wmParameters[i])) WiFiManagerParameter(parametresDesc[i], parametresDesc[i], parametresTxt[i], tailleMaxTexte);
    wm.addParameter(&(wmParameters[i]));
  }
  wm.setConfigPortalBlocking(false);
  wm.setSaveParamsCallback(callbackParametres);

  std::vector<const char *> menu = {"param", "sep", "restart", "exit"};
  wm.setMenu(menu);
  wm.startConfigPortal(wifiSSID);
  Serial.print("\nSystème prêt. Accédez au réseau Wi-Fi "); Serial.println(wifiSSID);
  printConfig();
}

void loop() {
  wm.process();
  // ici le reste du code en non bloquant

}
1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.