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:
}
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;
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);
});
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 car les paramètres récupérés mqttServer, mqttPort, mqttTopic ne sont pas utilisés.