Bouton de commande dans une page web ajax

Bonjour a tous, j'ai plusieurs dizaines d'esp8266 qui fonctionne avec une page web pour me retourner des informations(réception wifi, temp, niveau,...). J'utilise ce modèle de page web ajax:

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
ESP8266WiFiMulti wifiMulti;
String HTTP_req;
WiFiServer server(80);
unsigned long previousMillis = 0;
unsigned long i = 0;
void setup()
{
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  WiFi.mode(WIFI_STA);
  wifiMulti.addAP("****", "*******************");
  IPAddress ip(192, 168, 0, 30);
  IPAddress gateway(192, 168, 50, 1);
  IPAddress subnet(255, 255, 0, 0);
  WiFi.config(ip, gateway, subnet);
  while (wifiMulti.run() != WL_CONNECTED) {
    Serial.println("WiFi not connected!");
    delay(1000);
  }
  Serial.println("Connecting Wifi...");
  if (wifiMulti.run() == WL_CONNECTED) {
    Serial.println(WiFi.localIP());
  }
  ArduinoOTA.setHostname("TEST");
  ArduinoOTA.begin();
  server.begin();
}

void loop() {
  ArduinoOTA.handle();
  unsigned long currentMillis = millis();
  if ((currentMillis - previousMillis >= 1000))
  {
    previousMillis = currentMillis;
    i++;
    wifiMulti.run();
  }
  //------------------------------------------------------------------------------------------------------------------------------------------------------ page web
  WiFiClient client = server.available();
  if (client)
  {
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        HTTP_req += c;  // save the HTTP request 1 char at a time
        if (c == '\n' && currentLineIsBlank) {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: keep-alive");
          client.println();
          if (HTTP_req.indexOf("ajax_switch") > -1) {
            client.println("<!DOCTYPE HTML>");
            client.println("<html>");
            client.println("<style> body { background-color: #000000; font-family: Arial, Helvetica, Sans-Serif; Color: #ffffff; }</style>");
            client.print("<head></head><font face=tahoma><body><h1>");

           //DONNEES UTILES ICI
          client.print("Valeur i : ");
          client.print(i);
          client.println("</h1><h4>");


            client.print(__FILENAME__);
            client.println("</html>");
          }

          else
          { // -------------------------------------------------------------------------------------HTTP request for web page
            // -------------------------------------------------------------------------------------send web page - contains JavaScript with AJAX calls

            client.println("<!DOCTYPE html>");
            client.println("<html>");
            client.println("<head>");
            client.println("<title>TEST</title>");
            client.println("<script>");
            client.println("function GetSwitchAnalogData() {");
            client.println(
              "nocache = \"&nocache=\" + Math.random() * 1000000;");
            client.println("var request = new XMLHttpRequest();");
            client.println("request.onreadystatechange = function() {");
            client.println("if (this.readyState == 4) {");
            client.println("if (this.status == 200) {");
            client.println("if (this.responseText != null) {");
            client.println("document.getElementById(\"sw_an_data\")\
.innerHTML = this.responseText;");
            client.println("}}}}");
            client.println(
              "request.open(\"GET\", \"ajax_switch\" + nocache, true);");
            client.println("request.send(null);");
            client.println("setTimeout('GetSwitchAnalogData()', 1000);");
            client.println("}");
            client.println("</script>");
            client.println("</head>");
            client.println("<body onload=\"GetSwitchAnalogData()\">");
            client.println("<div id=\"sw_an_data\">");
            client.println("</div>");
            client.println("</body>");
            client.println("</html>");
          }
          HTTP_req = "";            
          break;
        }
        if (c == '\n') {
          currentLineIsBlank = true;
        } else if (c != '\r') {
          currentLineIsBlank = false;
        }
      }
    }
    delay(1);
    client.stop();
  }
}

Cela fonctionne bien et c'est facile a modifié/améliorer.

J'essai depuis plusieurs jours en gardant ce modèle d'y insérer un ou plusieurs boutons pour pouvoir modifier des variables, mais en vain. Est ce possible simplement (pour éviter de refaire tous le code sur tous mes modules) ? par exemple réinitialiser la variable "i" à 0 avec un bouton dans la page et avec une fonction ?

J'ai trouver des modeles/librairies(embajax) mais je dois refondre le code massivement/completement...

Quelqu'un a une solution (simple)? Merci pour toute aide...

i c'est pas terrible comme nom de variable... ça représente quoi?

un bouton pourrait déclencher un script local dans la page web qui mette l'affichage à 0 puis ce script ferait appel à une URL particulière qui dirait au programme Arduino que la variable a été remise à 0.

i (incrément) représente le compteur de secondes depuis le démarrage, c'est juste un exemple. Cela pourrais être une autre variable. ce code est juste le modèle de base. la partie qui gère l'affichage de la page en ajax, je l'ai trouvée sur le web, mais cela dépasse mes compétences, je m'en sert, je peux afficher plein de chose mais je ne comprends pas tout dans le html.
je pense qu'il doit être possible de rajouter du code pour avoir un/plusieurs boutons sur la page pour agir sur des GPIOs ou des variables tout en conservant la mise a jour chaque seconde, mais je bloque depuis des jours...
le but serai lors d'un appui sur un bouton de déclencher une fonction genre...

void appui() {
  i = 0;
}

Comment rajouter un bouton qui ferai ça ?
Merci

Comme dit plus haut il faut écrire du JavaScript associé au bouton, ce JavaScript déclenche une requête que vous devez reconnaître côté serveur, et c’est alors que vous appellerez votre fonction

(J’ai un petit tuto qui parle d’ Ajax dans la partie tutos du forum)

Bonjour, merci pour l'aide, Je pense avoir trouvé le bon tuto, mais je bloque toujours. J'ai réussi a insérer un bouton sur la page, en ajoutant

<button type=\"button\" onclick=\"appui2()\">TEST</button>"

j'ai mis ce script

            client.println("<script>");
            client.println("function appui2() {");
            client.println("var uniqueURL = 'reqEtatBouton' + '&aleatoire=' + Math.trunc(Math.random() * 1000000);");
            client.println("var request = new XMLHttpRequest();");
            client.println("request.onreadystatechange = function() {");
            client.println("if (this.readyState == 4) {");
            client.println("if (this.status == 200) {");
            client.println("if (this.responseText != null) {");
            client.println("document.getElementById(\"idBouton\").innerHTML = this.responseText;");
            client.println("}}}}");
            client.println("request.open(\"GET\", uniqueURL , true);");
            client.println("request.send(null);");
            client.println("}");
            client.println("</script>");

mais cela ne fonctionne pas.

voici le code complet :

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
ESP8266WiFiMulti wifiMulti;
String HTTP_req;
WiFiServer server(80);
unsigned long previousMillis = 0;
unsigned long i = 0;
int rssi;

void setup()
{
  Serial.begin(115200);
  delay(1000);

  WiFi.mode(WIFI_STA);

  wifiMulti.addAP("******", "**********************");

  IPAddress ip(192, 168, 2, 30);
  IPAddress gateway(192, 168, 50, 1);
  IPAddress subnet(255, 255, 0, 0);
  WiFi.config(ip, gateway, subnet);

  while (wifiMulti.run() != WL_CONNECTED) {
    Serial.println("WiFi not connected!");
    delay(1000);
  }
  Serial.println("Connecting Wifi...");
  if (wifiMulti.run() == WL_CONNECTED) {
    Serial.print("WiFi connected to ");
    Serial.print(WiFi.SSID());
    Serial.print(" ----> ");
    Serial.print(WiFi.RSSI());
    Serial.println("dbm ");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
  ArduinoOTA.setHostname("test");
  ArduinoOTA.begin();
  server.begin();
}

void loop() {
  ArduinoOTA.handle();
  unsigned long currentMillis = millis();
  if ((currentMillis - previousMillis >= 1000))
  {
    previousMillis = currentMillis;
    i++;
    rssi = WiFi.RSSI();
    wifiMulti.run();
  }
  //------------------------------------------------------------------------------------------------------------------------------------------------------ page web
  WiFiClient client = server.available();
  if (client)
  {
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        HTTP_req += c;
        if (c == '\n' && currentLineIsBlank) {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: keep-alive");
          client.println();
          if (HTTP_req.indexOf("ajax_switch") > -1) {
            client.println("<!DOCTYPE HTML>");
            client.println("<html>");
            client.println("<style> body { background-color: #000000; font-family: Arial, Helvetica, Sans-Serif; Color: #ffffff; }</style>");
            client.print("<head></head><font face=tahoma><body><h1>");
            //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

            client.println("<button type=\"button\" onclick=\"appui()\">TEST</button>");

            //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            client.println("<h5>");

            client.print("Niveau ");
            client.print( WiFi.SSID());
            client.print(" : ");
            client.print(rssi);
            client.println(" dBm<br />");

            client.print("Cycles : ");
            client.print(i);
            client.print("<br />");

            client.print("Reset : ");
            client.print(ESP.getResetReason());
            client.println("<br />");

            client.println("Built on "); client.print(__DATE__); client.print(" at "); client.print(__TIME__); client.println("<br />");
            client.print(__FILENAME__);
            client.println("</html>");
          }
          else
          {
            client.println("<!DOCTYPE html>");
            client.println("<html>");
            client.println("<head>");
            client.println("<title>TEST</title>");      //------------------------------------------------------------------------------------------------------------------------------------------8888888888888888888888888888

            client.println("<script>");
            client.println("function GetSwitchAnalogData() {");
            client.println("nocache = \"&nocache=\" + Math.random() * 1000000;");
            client.println("var request = new XMLHttpRequest();");
            client.println("request.onreadystatechange = function() {");
            client.println("if (this.readyState == 4) {");
            client.println("if (this.status == 200) {");
            client.println("if (this.responseText != null) {");
            client.println("document.getElementById(\"sw_an_data\").innerHTML = this.responseText;");
            client.println("}}}}");
            client.println("request.open(\"GET\", \"ajax_switch\" + nocache, true);");
            client.println("request.send(null);");
            client.println("setTimeout('GetSwitchAnalogData()', 1000);");
            client.println("}");
            client.println("</script>");

            //--------------------------------------------------------------------Bouton
            client.println("<script>");
            client.println("function appui() {");
            client.println("var uniqueURL = 'reqEtatBouton' + '&aleatoire=' + Math.trunc(Math.random() * 1000000);");
            client.println("var request = new XMLHttpRequest();");
            client.println("request.onreadystatechange = appui2() {");
            client.println("if (this.readyState == 4) {");
            client.println("if (this.status == 200) {");
            client.println("if (this.responseText != null) {");
            client.println("document.getElementById(\"idBouton\").innerHTML = this.responseText;");
            client.println("}}}}");
            client.println("request.open(\"GET\", uniqueURL , true);");
            client.println("request.send(null);");
            client.println("}");
            client.println("</script>");


            client.println("</head>");
            client.println("<body onload=\"GetSwitchAnalogData()\">");
            client.println("<div id=\"sw_an_data\">");
            client.println("</div>");
            client.println("</body>");
            client.println("</html>");
          }
          HTTP_req = "";
          break;
        }
        if (c == '\n') currentLineIsBlank = true;
        else if (c != '\r') currentLineIsBlank = false;
      }
    }
    delay(1);
    client.stop();
  }
}
void appui2()
{
  i = 0;
  Serial.println("appui bouton web");
}

Je pense que le script n'est pas bon/adapté. Ca me dépasse, mon html est proche du niveau 0. Auriez vous un exemple ?
Merci d'avance

Petite remarque : pourquoi passer par des appels à client.println() ?
Cela rend le code HTML / JS particulièrement illisible.
Il serait si simple de stocker les pages HTML dans le filesystem de l'ESP, ou même directement en FLASH :

const char page[] = R"(<!DOCTYPE HTML>
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>TITLE</title>
<body>
</body>
</html>
)";

Ensuite s'il y a des parties variables, on peut les traiter avec des balises du genre template :

<body>
Niveau %SSID% : %RSSI% dBm<br />
</body>

Dans le code, il suffit de remplacer %SSID% et %RSSI% par leur valeur à l'aide de String.replace(), et d'envoyer.

je n'ai pas d'exemple tout fait et je n'ai pas mes arduino sous la main en ce moment... Sans connaissance de HTML et Javascript ce n'est pas simple..

En gros votre script envoie une requête de type GET, il faut la capturer de l'autre côté... chose que vous ne faites pas.

Le tuto en question c'était Techniques "avancées" de serveur web sur ESP8266 et Vous verrez que le tuto extrait les requêtes GET

Une fois la requête HTTP entièrement reçue on va analyser le contenu de l'URL pour décider quoi renvoyer. Si on nous demande la page de garde du site "/" alors on renvoie le code de la page que l'on va lire dans la mémoire flash en SPIFFS comme vu précédemment, par contre si la requête (l'URL) est différente alors on va envoyer "quelque chose d'autre" et c'est là que la magie Javascript va rentrer en ligne de compte.

je vous suggère mettre votre projet de côté un moment, de lire les fondamentaux sur HTML et Javascript (il y a quelques liens dans le tuto) puis de pratiquer ce qu'il y a dans le tuto. Vous aurez ainsi les outils pour votre projet et vous comprendrez ce que vous faites

Merci je vais essayer ça mais lorsqu'il y a beaucoup de variables (plus de 50) il faut toutes les convertir en string ? cela alourdi considérablement le code ? Surtout que mes page ne contiennent que du texte/variables.

Oui bien entendu.
Comme expliqué ici : Pages Web multiples - #8 by hbachetti
Mais il y a de multiples avantages :

  • clarté du code, en particulier JS
  • essai possible sous forme de fichier HTML sur PC

Ce code est très peu souple.
Pour ma part je travaille avec ESPAsyncWebServer.

Ok merci je vais regarder tout ça depuis le début et tenter de comprendre... C'est une fonction que je veux implanter et que je cherche depuis un bon moment. Merci encore

+1 sur ce point

Ca simplifierait la gestion des GET

Surtout qu’apparemment des arguments sont prévus.
Sans une librairie serveur un peu cossue (WebServer ou EspAsyncWebServer), c'est la misère ...

Là ce n’est pas un paramètre, L’URL est générée avec de l’aléatoire pour éviter le traitement en cache des serveurs

aleatoire est tout de même un argument d'une URL reqEtatBouton.

oui bien sûr, mais on n'a pas à extraire ce paramètre côté serveur, juste tester la présence de reqEtatBouton dans le GET, donc c'est statique, pas d'analyse des paramètres pour gérer cet appel.

Dans mon tuto (qui faisait "tout à la main" pour expliquer ce qu'on reçoit dans la trame HTTP), je testais donc avec

if (strstr(urlRequest, "/reqEtatBouton")) { 
 ...

mais bien sûr avec ESPAsyncWebServer l'affectation des callbacks aux requêtes est encore plus simple.

OK. Mais il est assez rare qu'un serveur WEB n'ait pas besoin à un moment donné d'extraire des paramètres d'une URL. Si l'on s'en passe cela revient en général à multiplier les URLs.

J'ai fait ça aussi avec ARDUINO+Ethernet, avec parsing des arguments et accroche de CallBacks à la sauce WebServer ESP. Plutôt difficile pour un débutant.

Merci de m'avoir répondu, le html est trop compliqué pour moi, j'ai réussi avec la lib Embajax j'espère seulement que cette lib sera tenu a jour. Merci de votre aide