Récupérer données saisies dans formulaire html pour sauvegarde en mémoire flash

Bonsoir,
J'essai de développer une appli sonde T° qui affiche les valeurs dans une page html (OK).
J'ai 2 autres pages pour configurer l'appli (une Wifi, l'autre MQTT).
Mes paramètres initiaux sont stockés dans un fichier "config.json" stocké en flash via SPIFFS.
J'arrive à charger les paramètres pour activer le wifi et le mqtt et pour les afficher dans les 2 pages de config.
Ce que je n'arrive pas à faire , c'est renvoyer les données vers le sketch pour les sauvegarder dans le "config.json".
Je ne trouve aucun exemple assez complet pour me permettre de comprendre.
J'utilise un ESP32 et développe avec platformio.
La librairie est ESPAsyncWebServer.

Je viens de faire une version très très allégée de mon appli pour me concentrer sur ce qui ne fonctionne pas.
J'ai supprimé toutes mes tentatives infructueuses ... un peu honte de ne rien comprendre.

Voici le main.cpp :

#include <Arduino.h>
#include <WiFi.h>
#include <SPIFFS.h>
#include <ArduinoJson.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <config.h>

Config param;
AsyncWebServer server(80);

// ------------------------------------------------
void setup_wifi()
{
  delay(10);
  WiFi.begin(param.ssidName, param.ssidPassword);

  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }

  Serial.print("\nConnecté au Wifi avec l'adresse : ");
  Serial.println(WiFi.localIP());
}


void setup() {
// ------------------------------------------------ Serial
  Serial.begin(115200);
  delay(2000);
  Serial.println();
  // ------------------------------------------------ SPIFFS
  if (!SPIFFS.begin())
  {
    Serial.println("Erreur SPIFFS");
    return;
  }
 
  // ------------------------------------------------ Chargement des paramètres
  Serial.println("Exécution de la fonction loadConfig....");
  loadConfig(param);

  // ------------------------------------------------ WIFI
  setup_wifi();

  // ------------------------------------------------ SERVER
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
  { 
    request->send(SPIFFS, "/index.html", "text/html"); 
  });

  server.on("/getInfosSystem", HTTP_GET, [](AsyncWebServerRequest *request) 
  {
    AsyncResponseStream *response = request->beginResponseStream("application/json");
    DynamicJsonDocument json(512);
    json["ssidName"] = WiFi.SSID();
    json["ssidPassword"] = param.ssidPassword;
    json["mqttServer"] = param.mqttServer;
    json["mqttPort"] = param.mqttPort;
    json["mqttTopic"] = param.mqttTopic;
    json["mqttPeriod"] = param.mqttPeriod;

    serializeJson(json, *response);
    request->send(response);
  });

  // ----- Activation du serveurS
  server.begin();
  Serial.println("Serveur actif");

}

void loop() {
  // put your main code here, to run repeatedly:
}

Voici la page index.html

<head>
    <meta charset="UTF-8">
    <title>configMQTT</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <style>
        form.form-example {
            display: table;
        }

        div.form-example {
            display: table-row;
        }

        label,
        input {
            display: table-cell;
            margin-bottom: 10px;
        }
    </style>
</head>

<body onload="loadInfosMQTT()">
    <form action="check" method=get name="configMQTT">
        <label for="inputMqttServer">Adresse du Serveur MQTT</label>
        <input type="text" id="inputMqttServer" name="inputMqttServer">

        <label for="inputMqttPort">Port du Serveur MQTT</label>
        <input type="number" id="inputMqttPort" name="inputMqttPort">

        <label for="inputMqttTopic">Topic de publication MQTT</label>
        <input type="text" id="inputMqttTopic" name="inputMqttTopic">

        <label for="inputMqttPeriod">Période de rafraichissement en secondes</label>
        <input type="number" id="inputMqttPeriod" name="inputMqttPeriod" required>

        <hr>

        <input type="button" id="btn" value="Enregistrer" />

    </form>

    <script>
        function loadInfosMQTT() {
            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function () {
                if (this.readyState == 4 && this.status == 200) {
                    obj = JSON.parse(this.responseText);
                    document.getElementById("inputMqttServer").setAttribute('value', obj.mqttServer);
                    document.getElementById("inputMqttPort").setAttribute('value', obj.mqttPort);
                    document.getElementById("inputMqttTopic").setAttribute('value', obj.mqttTopic);
                    document.getElementById("inputMqttPeriod").setAttribute('value', obj.mqttPeriod);

                }
            };
            xhttp.open("GET", "getInfosSystem", true);
            xhttp.send();
        }

    </script>

</body>

</html>

Merci de votre aide.

elle est sans doute trop épurée ou pas assez... on ne sait pas ce que fait loadConfig() par exemple

vous voulez lire les paramètres d'un formulaire web et les écrire en SPIFFS c'est ça?

faites le en 2 temps

  • apprenez à écrire un fichier SPIFFS. Regardez aussi ce que sait faire DynamicJsonDocument pour écrire dans un fichier
  • apprenez à lire les paramètres d'une requête GET avec ESPAsyncWebServer

puis mettez ça en pratique en regroupant les 2

Bonjour,

Je joins la partie chargement/sauvegarde de "config.json".
Pour alléger main.cpp, j'ai créé un deuxième pgm "config.cpp" que voici :

#include <Arduino.h>
#include <SPIFFS.h>
#include <ArduinoJson.h>
#include <config.h>

// ------------------------------------------------
// Loads the configuration from a file
void loadConfig(Config &config)
{
  // Open file for reading
  File configFile = SPIFFS.open("/config.json", "r");

  if (configFile)
  {
    size_t size = configFile.size();
    // ------ Allocate a buffer to store contents of the file.
    std::unique_ptr<char[]> buf(new char[size]);
    configFile.readBytes(buf.get(), size);

    StaticJsonDocument<1024> doc;

    // ------ Deserialize the JSON document
    DeserializationError error = deserializeJson(doc, buf.get());
    if (error)
      Serial.println(F("Failed to read file, using default configuration"));

    // ------ Copy values from the JsonDocument to the Config
    strcpy(config.ssidName, doc["ssidName"]);
    strcpy(config.ssidPassword, doc["ssidPassword"]);
    strcpy(config.mqttServer, doc["mqttServer"]);
    strcpy(config.mqttPort, doc["mqttPort"]);
    strcpy(config.mqttTopic, doc["mqttTopic"]);
    strcpy(config.mqttPeriod, doc["mqttPeriod"]);
  
    // ------ Close the file
    configFile.close();
  }
  else
    Serial.println("Echec ouverture config.json");
}

// ------------------------------------------------
// Save the configuration to a file
void saveConfig(Config &config)
{
// Open file for writing
  File configFile = SPIFFS.open("/config.json", "w");

  if (configFile)
  {
    DynamicJsonDocument doc(512);

    doc["ssidName"] = config.ssidName;
    doc["ssidPassword"] = config.ssidPassword;
    doc["mqttServer"] = config.mqttServer;
    doc["mqttPort"] = config.mqttPort;
    doc["mqttTopic"] = config.mqttTopic;
    doc["mqttPeriod"] = config.mqttPeriod;

    // Serialize JSON to file
    if (serializeJson(doc, configFile) == 0) {
      Serial.println(F("Echec sauvegarde config.json"));
    }
 
    // Close the file
    configFile.close();
 
  }

et le "config.h" associé :

typedef struct 
{
  char ssidName[32];
  char ssidPassword[64];
  char mqttServer[15];
  char mqttPort[6];
  char mqttTopic[32];
  char mqttPeriod[4];
} Config;

// ------------------------------------------------
// Loads the configuration from a file
void loadConfig(Config &config);

// ------------------------------------------------
// Save the configuration to a file
void saveConfig(Config &config);

Cette partie fonctionne.
Ma fonction saverConfig() fonctionne.
C'est effectivement le côté ESPAsyncWebServer que je ne comprends.
J'arrive à lire le contenu de mon config.json pour l'afficher dans la page HTML mais pas l'inverse.

Il faut un handler AsyncWebServer pour l'URL "/check". Ensuite il faut récupérer les arguments inputMqttServer, inputMqttTopic, etc.
variable = server.arg("inputMqttServer");
Si c'est une case à cocher l'argument peut être absent. Il faut vérifier son existence :
variable = server.hasArg("checkbox-name") ? true : false;

Merci pour le retour mais je dois être de ceux qui comprenne vite mais à qui il faut expliquer très très très longtemps :grin:

J'ai essayé ta réponse :

server.on("/check", HTTP_GET, [](AsyncWebServerRequest *request) 
  {
    String variable = server.arg("inputMqttServer");
    
  });

Le compilateur me retourne "AsyncWebServer" n'a pas de membre "arg".

J'ai essayé ça aussi :

server.on("/check", HTTP_GET, [](AsyncWebServerRequest * request) 
  {
    int paramsNr = request->params();
    for (int i = 0; i < paramsNr; i++) 
    {
      AsyncWebParameter* p = request->getParam(i);
      Serial.print("\np->name() : ");
      Serial.println(p->name());

      Serial.print("\np->value() : ");
      Serial.println(p->value());
    }
    request->send(200);
  });

Sans plus de succès

Côté HTM, j'ai essayé cette fonction javascript appelée par le "onclick()" du bouton :

function saveMQTT() 
{
            const mqttServer = document.getElementById('inputMqttServer');
            const mqttPort = document.getElementById('inputMqttPort');
            const mqttTopic = document.getElementById('inputMqttTopic');

            var jsonFormInfo = JSON.stringify({
                mqttServer: mqttServer.value,
                mqttPort: mqttPort.value,
                mqttTopic: mqttTopic.value
            });

            var xhttp = new XMLHttpRequest();
            xhttp.open("GET", "backFromMqtt", true);
            xhttp.send();
        }

Effectivement dans un formulaire le bouton doit être du type "submit".

    <input type="submit" value="Enregistrer">

Voilà où j'en suis :
"Index.html" modifié (extrait):

<body onload="loadInfosMQTT()">
    <form onSubmit="event.preventDefault(); saveMQTT(this);">

        <label for="inputMqttServer">Adresse du Serveur MQTT</label>
        <input type="text" id="inputMqttServer" name="inputMqttServer">
        <label for="inputMqttPort">Port du Serveur MQTT</label>
        <input type="number" id="inputMqttPort" name="inputMqttPort">
        <label for="inputMqttTopic">Topic de publication MQTT</label>
        <input type="text" id="inputMqttTopic" name="inputMqttTopic">
        <label for="inputMqttPeriod">Période de rafraichissement en secondes</label>
        <input type="number" id="inputMqttPeriod" name="inputMqttPeriod" required>

        <button type="submit">Enregistrer</button>

    </form>

    <script>
        function loadInfosMQTT() {
            .... Elle fonctionne, identique au post précédent
        }

        function saveMQTT() {
            // Get the data from each element on the form.
            const mqttServer = document.getElementById('inputMqttServer');
            const mqttPort = document.getElementById('inputMqttPort');
            const mqttTopic = document.getElementById('inputMqttTopic');

            window.alert("Passe par saveMQTT");

            var xhttp = new XMLHttpRequest();
            xhttp.open("GET", "check", true);
            xhttp.send(200);
        }
    </script>
</body>

Côté "main.cpp", 2 versions de check:
Version 1 :

server.on("/check", HTTP_GET, [](AsyncWebServerRequest *request) 
  {
    if(request->hasArg("inputMqttServer"))
    {
        String arg = request->arg("inputMqttServer");
        Serial.print("The argument is: ");
        Serial.println(arg);
    } else
        Serial.println("Ne trouve pas cet argument.");
            
    request->send(200);
  });

Ne trouve pas cet argument

Version 2 :

server.on("/check", HTTP_GET, [](AsyncWebServerRequest * request) 
  {
    int paramsNr = request->params();
    Serial.print("nb param:");
    Serial.println(paramsNr);
    
    for (int i = 0; i < paramsNr; i++) 
    {
      AsyncWebParameter* p = request->getParam(i);
      Serial.print("\np->name() : ");
      Serial.println(p->name());

      Serial.print("\np->value() : ");
      Serial.println(p->value());
    }
    request->send(200);
  });

M'affiche 0 paramètres.

Dans le jeu des 7 erreurs, qui va gagner ?

Merci,

J'ai enfin trouvé la solution.
Ce lien m'a bien aidé : https://github.com/yash-sanghvi/ESP32/tree/master/Captive_Portal_SPIFFS_HTML

Cordialement

Pourquoi ne pas avoir utilisé une forme toute simple :
<form action="/check" method=\"post\">

Pourquoi passer par une fonction JavaScript qui renvoie une requête HTTP ? autant fournir l'URL directement dans la balise < form >.
Surtout que la requête XMLHttpRequest n'envoie aucun paramètre :woozy_face: car les paramètres récupérés mqttServer, mqttPort, mqttTopic ne sont pas utilisés.