ESP8286 - Serveur plante

Bonjour, j'ai un problème avec un ESP8266 simplement au niveau du serveur. Le système fonctionne bien quelques jours, quelques semaines, après quoi, ESP reste connecté au réseau, je suis capable de le "pigner" et les données sont encore envoyées à Thinkspeak...

Cependant, ma page Web ne fonctionne plus normalement... Tant que je ne fais pas un rafraichissement la page fonctionne, mais je ne suis plus capable d'en ouvrir de nouvelle.

J'ai l'impression que c'est le "serveur" de la librairie ESPAsyncWebSrv.h qui est arrêté. Est-ce qu'il y a moyen de le surveiller et de le redémarrer, parce qu'il est dans un SETUP... Genre refaire un nouveau "server.begin();"

J'utilise le SPIFFS et une feuille de style.

Merci de votre aide!

Sans trace de debug et sans le code c'est difficile de dire ce qui plante.

il se peut que vous ayez une fuite mémoire et qu'au bout d'un moment les opérations sur les Strings ne fonctionnent plus par exemple.

Une solution "moche" mais qui a fait ses preuves serait de rebooter l'ESP préventivement à deux heures du matin (où quand il ne risque pas d'être utilisé) tous les jours ...

Merci J-M-L,

Le rebootage effectivement je l'envisage mais en dernier recours pour l'instant. Je met une partie de mon code, celui de ma connexion qui est surement le problème et la page Web principale.

// Pour la connexion de ESP

#include <ESPAsyncWebSrv.h>
#include <FS.h> // si on utilise le SPIFFS

// Replace with your network credentials
const char* ssid = "";
const char* password = "";
String nom_esp ="Serre"; // Pour nommer le serveur (ne marche pas) https://www.raspberryme.com/esp8266-nodemcu-definition-dun-nom-dhote-personnalise-arduino-ide/

// Ajouter les noms feuilles utilisées, ajouter / avant
String nom_html ="/Projet_V1.html"; //"/XXX";
String nom_html2 ="/Projet_V2.html"; //"/XXX";
String nom_css = "/style.css"; //"/yyy";
String nom_java = "/zzz";

// Variable pour envoyer du data XML par exemple
String var_send = "";

//Variables utilisées...
bool bp_eclairage, auto_arrosage;

// Create AsyncWebServer object on port 80
AsyncWebServer server(80); // Port 90 pour accès à l'extérieur du LAN

void connect_ip_setup(){

  // Initialize SPIFFS si on l'utilise
  if(!SPIFFS.begin()){
    //Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }

 // Definition de IP fixe!  // ***************************  Connexion réseau Ethernet ************************************
  IPAddress ip(192, 168, 68, 200);// ************************************************************
  IPAddress dns(192, 168, 68, 1);
  IPAddress gateway(192, 168, 68, 1);
  IPAddress subnet(255, 255, 255, 0);
  WiFi.config(ip, dns, gateway, subnet);
  WiFi.hostname(nom_esp); // Pour nommer notre serveur, mais ne semble pas marcher dans le Router... Possiblement en raison de IP static...

  // Connect to Wi-Fi   *****
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    //Serial.println("Connecting to WiFi..");
  }

  // Print ESP Local IP Address
  //Serial.println(WiFi.localIP());
  //Serial.println(WiFi.getHostname());

// ***********************************************  Un client connecté, on analyse ce qu'il envoie pour lui répondre ....******************** /
// Mettre les informations provenant de la page ici, avant begin (démarrage du serveur)
  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, nom_html);
    //Serial.println("1- envoie de la page...");
  });

  // Route to load style.css file (s'il y a des feuilles java, php... faire la même procédure)
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, nom_css, "text/css");
  });

  server.on("/get", HTTP_GET, [](AsyncWebServerRequest *request){ // vérifier ce que le web envoie lorsqu'il veut mettre à jour le XML
    //Serial.println("2- requête XML envoie");
    if (request->hasParam("valeur1")) {
      String inStr = request->arg("valeur1");
      // ensuite tu fais ce que tu veux de inStr
     // Serial.println("Réception du texte entré: " + inStr); // S'assure que ça fonctionne!
    }
    request->send(SPIFFS, nom_html); // On recharge la page à la nouvelle adresse...
  });
 
 // Réception de la requête
  server.on("/requete", HTTP_GET, [](AsyncWebServerRequest *request){ // 
    request->send(200, "application/xml", var_send);
   // Serial.println("22- Requête XML, envoie");
  });

  // Appuie sur un bouton qui mène à la page on
  server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, nom_html2); // Envoie la page qui va rediriger vers la principale sans /ON
    bp_eclairage = true;
  });
  
  // Appuie sur un bouton qui mène à la page off
  server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, nom_html);
    bp_eclairage = false;
  });

  // Appuie sur un bouton qui mène à la page auto
  server.on("/auto", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, nom_html);
    auto_arrosage = true;
  });
  // Appuie sur un bouton qui mène à la page man
  server.on("/man", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, nom_html);
    auto_arrosage = false;
  });
// ***************************************              Pour le chauffage....   ***************************
  // Appuie sur un bouton qui mène à la page on...
  server.on("/on_chauffage", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, nom_html);
    chauffage_manuel = true;
  });
  
  // Appuie sur un bouton qui mène à la page off
  server.on("/off_chauffage", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, nom_html);
    chauffage_manuel = false;
  });

  // Appuie sur un bouton qui mène à la page auto
  server.on("/auto_chauffage", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, nom_html);
    auto_chauffage = true;
  });
  // Appuie sur un bouton qui mène à la page man
  server.on("/man_chauffage", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, nom_html);
    auto_chauffage = false;
  });

  // ************************ *****************************************  Start server ***********************************
  server.begin();
}
  
  // ***********************************   Consctruction d'une table XML par exemple et construction de la variable ********************
void construction_XML(float temperature_f, float humidite_f, bool arrosage_f, bool auto_f, int8_t heure_f, int8_t minute_f, bool chauffage_f, bool auto_chauffage_f){

  String arrosage_texte, mode_texte, chauffage_texte, auto_chauffage_texte;

  if (arrosage_f){
    arrosage_texte = "Marche";
  }
  else{
    arrosage_texte = "Arret";
  }
  if (auto_f){
    mode_texte = "Auto";
  }
  else{
    mode_texte = "Manuel";
  }
  // Chauffage
  if (chauffage_f){
    chauffage_texte = "Marche";
  }
  else{
    chauffage_texte = "Arret";
  }
  if (auto_chauffage_f){
    auto_chauffage_texte = "Auto";
  }
  else{
    auto_chauffage_texte = "Manuel";
  }  
  var_send = "<variables><temperature>" + String(temperature_f); // 
  var_send += "</temperature><humidite>" + String(humidite_f);
  var_send += "</humidite><arrosage>" + arrosage_texte;
  var_send += "</arrosage><auto>" + mode_texte;
  var_send += "</auto><heure>" + String(heure_f);
  var_send += "</heure><minute>" + String(minute_f);
  var_send += "</minute><chauffage>" + chauffage_texte;
  var_send += "</chauffage><auto_chauffage>" + auto_chauffage_texte;
  var_send += "</auto_chauffage></variables>";
}

HTML principale

<html>
  <head>
    <meta charset="utf-8">    <!-- avec les accents -->
    <!--  <meta http-equiv="refresh" content="0; url=/"> -->
    <title>Hydroponie</title>
    <!-- Favicon -->
    <link rel="icon" type="image/x-icon" href=https://cdn-icons-png.flaticon.com/512/3227/3227707.png>
    <link rel="stylesheet" href="./style.css"> <!-- Feuille de styles-->
     
        <!-- Pour les gages -->
    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> 
    <script type="text/javascript">
      google.charts.load('current', {'packages':['gauge']});
      google.charts.setOnLoadCallback(drawChart);

      var temperature //= 22; // Valeur à afficher
      var humidite //= 33; // Valeur à afficher
      var niveau //= Math.random()*100; //80; // Valeur à afficher
            // Variables globales!
      var data;
      var option;
      var option2;
      var heure; // à remplacer... à enlever...
      var minute; // à remplacer...
      var comm;  // à remplacer...

      var chart;
      var chart2;
      var chart3;
      var Temperature_data;
      var humidite_data;
      var niveau_data;

      function drawChart() {

        Temperature_data = google.visualization.arrayToDataTable([ 
          ['Label', 'Value'], // 
          ['Température', 50], // Affichage gage affiché et valeur du début
        ]);
        humidite_data = google.visualization.arrayToDataTable([ 
          ['Label', 'Value'], // 
          ['Humidité', 55], // Affichage gage et valeur du début
        ]);
        niveau_data = google.visualization.arrayToDataTable([ 
          ['Label', 'Value'], // 
          ['Niveau %', 77], // Affichage gage et valeur du début
        ]);

        options = {
          width: 300, height: 200,  // dimention du gage
          redFrom: 90, redTo: 100,
          yellowFrom:75, yellowTo: 90,
          minorTicks: 5,
        };
        options2 = {
          width: 300, height: 200,  // dimention du gage
          redFrom: 45, redTo: 50,
          yellowFrom:40, yellowTo: 45,
          minorTicks: 10, max:50, min:-5
        };

        obtenirVariables(); // appel la fonction pour mettre à jour les données
      }
    
      function obtenirVariables(){
		    var uniqueURL = "reqEtatVariables" + "&aleatoire=" + Math.trunc(Math.random() * 1000000);
		    var request = new XMLHttpRequest(); // http://www.toutjavascript.com/reference/ref-xmlhttprequest.php

		    // la fonction à appeler lors d'un changement d'avancement de la requête AJAX
		    request.onreadystatechange = function(){
			    if (this.readyState == 4) {
				    // Indicateur de l'avancement de l'appel AJAX == 4 => Données complètement accessibles 
				      if (this.status == 200) {
					      // Code retour du serveur après l'appel AJAX == 200 => OK, tout s'est bien passé
					      if (this.responseXML != null) {
						      // si on a bien obtenu une réponse non nulle
                  // alors on va extraire du XML les éléments qui nous intéressent
                  temperature = this.responseXML.getElementsByTagName('temperature')[0].childNodes[0].nodeValue;
                  //document.getElementById("humidite").innerHTML =
                  humidite = this.responseXML.getElementsByTagName('humidite')[0].childNodes[0].nodeValue;
                  //document.getElementById("niveau").innerHTML =
                  //niveau = this.responseXML.getElementsByTagName('niveau')[0].childNodes[0].nodeValue;
                  //document.getElementById("niveau").innerHTML = niveau; Il semble y avoir problème si on n'utilise pas id dans le code...
                  document.getElementById("arrosage").innerHTML =this.responseXML.getElementsByTagName('arrosage')[0].childNodes[0].nodeValue;
                  //arrosage = this.responseXML.getElementsByTagName('arrosage')[0].childNodes[0].nodeValue;

                  document.getElementById("auto").innerHTML =this.responseXML.getElementsByTagName('auto')[0].childNodes[0].nodeValue;

                  document.getElementById("heure").innerHTML =this.responseXML.getElementsByTagName('heure')[0].childNodes[0].nodeValue;
                  document.getElementById("minute").innerHTML =this.responseXML.getElementsByTagName('minute')[0].childNodes[0].nodeValue;
                  //document.getElementById("comm").innerHTML =this.responseXML.getElementsByTagName('comm')[0].childNodes[0].nodeValue;
                  document.getElementById("chauffage").innerHTML =this.responseXML.getElementsByTagName('chauffage')[0].childNodes[0].nodeValue;
                  document.getElementById("auto_chauffage").innerHTML =this.responseXML.getElementsByTagName('auto_chauffage')[0].childNodes[0].nodeValue;
               
              
                }
				      }
			      }
		      }
        
         // Pour le 1er gage température
        chart = new google.visualization.Gauge(document.getElementById('chart_div'));
        Temperature_data.setValue(0, 1, temperature);
        chart.draw(Temperature_data, options2); // affichage du gage
        
        //Pour le 2e gage - Humidité
        chart2 = new google.visualization.Gauge(document.getElementById('chart_div2'));
        humidite_data.setValue(0, 1, humidite);
        chart2.draw(humidite_data, options); // affichage du gage 2        
        
        //Pour le 3e gage - niveau...
        /* chart3 = new google.visualization.Gauge(document.getElementById('chart_div3'));
        niveau_data.setValue(0, 1, niveau);
        chart3.draw(niveau_data, options); // affichage du gage 3           
        */
        //fonction_bouton1();

		    request.open("GET", "/requete" , true); // ici on envoie la requête GET sur l'URL /reqEtatVariables
		    request.send(null);
		    setTimeout("obtenirVariables()", 2000); // Temps de rafraichissement
  	  }    
      /*
      function fonction_bouton1(){ // pour mettre à jour le bouton...
        if (arrosage){
          arrosage_texte= "Arrêt";
        }
        else{
          arrosage_texte= "Marche";
        }
        document.getElementById("arrosage").innerHTML = arrosage_texte;
      }
      function submitMessage() {
        alert("Valeur tramis à ESP");
        setTimeout(function(){ document.location.reload(false); }, 500);   
      }*/
      // Pour éviter des erreurs, éléments non définis: https://isotropic.co/how-to-fix-cannot-read-property-style-of-null-in-javascript/#:~:text=This%20error%20boils%20down%20to,you%20attempted%20to%20access%20it.
      document.addEventListener("DOMContentLoaded", function () {
        let Bouton = document.getElementById("bouton");
        let marche = document.getElementById("marche");
        let togg2 = document.getElementById("togg2");
     
        function marche1(){
          if (true){
            if(getComputedStyle(Bouton).display != "none"){
              Bouton.style.display = "none";
            }
            else {
              Bouton.style.display = "block";
            }
          }
        }
        togg2.onclick=marche1; 
      });

      


    </script>
  </head>
  <body >
    <div style= "position: absolute; top:0px; left:230px; ">
    <h1>Hydroponie</h1></p>
    </div>
    <!-- Lien vers la page aquaponie -->
    <div style= "position: absolute; top:0px; left:800px; ">
      <a href="http://XXXX"> <h1>Hydroponie</h1></p></a>
      <a href="https://thingspeak.com/login?skipSSOCheck=true"> <h1>Thingspeak.com</h1></p></a>
    </div>


    <div id="chart_div" style=" position: absolute; top:60px; left:100px; "></div> <!-- Température -->
    <div id="chart_div2" style=" position: absolute; top:60px; left:400px; "></div> <!-- Humidite -->
    <div id="chart_div3" style=" position: absolute; top:270px; left:250px; "></div><!-- Niveau -->

    <!-- Pour le niveau 
    <div id='chart-container' style= " position: absolute; top:270px; left:200px;"> 
      Reload Page if you don't see animation
    </div>
    <div style= " position: absolute; top:545px; left:330px;"> 
      <h3><span id="niveau">...</span> %</h3>
    -->
    
    <!-- Affichage de l'heure et...! -->
    <div style= " position: absolute; top:250px; left:100px;"> 
    <h3> Il est <span id="heure"> ... </span> heure(s) et <span id="minute"> ... </span> minute(s). <!-- lire l'heure de ESP... -->
    <br><br> L'éclairage est en <span id="arrosage"> ... </span> <span id="auto"> </span> <!-- Probablement en raison du XML, si on enleve id="auto", l'heure ne s'affiche plus -->
    
    <!-- Formulaire pour entrer des données 
    <br><br><form action="/get"> 
      Entrer une valeur: <input type="text" name="valeur1" style="width: 50px;"> -->
      <!-- <input type="submit" value="Envoie" onclick="submitMessage()">  On peut enlever la fonction submitMessage si on recharge la page dans ESP
      <input type="submit" value="Envoie">
    </form><br>
    </h3></div> -->
  </div>  
    <!-- Bouton -->

       
    <div id="bouton" style= " position: absolute; top:330px; left:100px;"> 
    <h3>Éclairage: 
    <a href="/on"><button id="marche">Éclairage</button></a>
    
    <!--
    <a href="/off"><button>Arrêt</button></a></p>
    Mode:
    <a href="/auto"><button>Auto</button></a>
    <a href="/man"><button>Manuel</button></a></p>
    </h3>
    <h3>Le chauffage est en <span id="chauffage"> ... </span> et le mode est <span id="auto_chauffage"> ... </span> Vérifier pourquoi ça change de ligne, trop long
    Chauffage: 
    <a href="/on_chauffage"><button>Marche</button></a>
    <a href="/off_chauffage"><button>Arrêt</button></a></p>
     Mode:
    <a href="/auto_chauffage"><button>Auto</button></a>
    <a href="/man_chauffage"><button>Manuel</button></a></p>
      -->

    </h3></div>
   

    <!-- les gros boutons...
    <p><a href="/on"><button class="button">ON</button></a></p>
    <p><a href="/off"><button class="button button2">OFF</button></a></p>
    -->
    <!-- Les 2 graphiques -->
    <div style= " position: absolute; top:540px; left:100px;"> 
      <iframe width="450" height="240" style="border: 1px solid #cccccc;" src="https://thingspeak.com/channels/2096527/charts/8?bgcolor=%23ffffff&color=%23d62020&days=2&dynamic=true&title=Serre+humidit%C3%A9&type=line&yaxis=%25"></iframe>
    </div>
    <div style= " position: absolute; top:540px; left:700px;"> 
      <iframe width="450" height="240" style="border: 1px solid #cccccc;" src="https://thingspeak.com/channels/2096527/charts/7?bgcolor=%23ffffff&color=%23d62020&days=2&dynamic=true&title=Serre+Temperature&type=line&yaxis=%2ACelcius"></iframe>  
    </div>
    <div style= " position: absolute; top:200px; left:700px;"> 
      <iframe width="450" height="240" style="border: 1px solid #cccccc;" src="https://thingspeak.com/channels/2096527/charts/6?bgcolor=%23ffffff&color=%23d62020&days=2&dynamic=true&title=Arrosage+serre&type=line&yaxis=On"></iframe>
    </div>    

    <button id="togg2">Connexion</button>

  </body>

</html>

Bonjour

Je ne vois rien de bien méchant dans ce code qui pourrait faire planter. Il y a bien des Strings mais ça n'a pas l'air très gros et elles s'autodétruisent...

Avez vous essayé en passant en DHCP au lieu d'IP fixe et en réservant 192, 168, 68, 200 dans votre routeur ?

Allô,

Non, mais si je met en DHCP il pourra choisir n'importe quelle adresse... Je peux par exemple mettre le DHCP entre 1 et 150 et réserver les adresses 151 à 255, mais mes ESP pourront avoir les adresses 151 à 155 ce qui me pose un problème sur mon réseau local pour le trouver et ensuite ouvrir la bonne adresse pour le Forwarding... Il y a surement quelque chose qui m'échappe sur le routeur..

Pour les String, j'imagine que c'est toujours préférable d'utiliser char*? Je ne suis par certain si c'est toujours possible par contre...

Bonne journée!

➜ non avec une réservation DHCP

dans votre box vous pouvez associer à la Mac address de votre ESP une adresse IP fixe. Cela permet de réserver cette adresse IP spécifique, tout en utilisant le mécanisme du DHCP et attribuer automatiquement cette IP chaque fois que l'appareil se connecte au réseau.

Cool, je ne connaissais pas, je vais regarder si mon routeur me permet de faire cette action. Il va falloir que je fasse attention pour déterminer quel ESP est en place lors de mes tests!

Merci beaucoup, je vais essayer cela éventuellement! Sinon, ça sera le reboot à une certaine fréquence!

Bonsoir les amis,

Je suis votre discussion avec beaucoup d'intérêt car elle va peut-être m'apporter la réponse à une question que je me pose depuis déjà un certain temps sur la stabilité de la classe ESPAsyncWebSrv.

Elle a de super fonctionnalités, est facile à utiliser... mais est entièrement écrite avec des String... Est-elle stable au delà de quelques heures ou jours ?

Bon courage et je suis preneur de vos résultats quels qu'ils soient.

Bonne bidouille,

MicroQuettas

Allô,

Bien que j'ai des problèmes occasionnelles, elle a déjà roulé pendant quelques mois et le problème n'est pas nécessairement la bibliothèque. Je vais tenter prochainement d'y aller avec un IP en DHCP avec les suggestions de J-M-L. Je pourrais essayer un nouveau ESP également. Le ESP32 est p-e plus stable que le ESP8286 aussi... Dans mon cas un redémarrage ne me poserait pas de grand problème non plus, c'est juste le principe qui m'agace! :slight_smile:

L’usage des Strings dans cette classe est raisonnable et le risque de morcellement de la mémoire est faible car il semble que tout soit bien libéré dans le bon ordre et bien sûr il y a beaucoup plus de mémoire que sur un UNO :slight_smile:

Il se peut cependant que le code des utilisateurs soit lui mal fichu et abuse des Strings…

Un ESP32 sera mieux avec ses deux cœurs et une stack IP plus robuste à mon sens que dans les 8266. L’idée de tester en DHCP est de voir si ça change quelque chose.

Capturer aussi par callback les événements qui arrivent sur le WiFi comme la déconnexion etc pourrait donner des indices

Merci bien J-M-L.
Je continue de suivre attentivement la discussion.
Bonne journée et bonne bidouille

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