Affichage en retour de l'envoi d'un formulaire

Bonsoir à toutes et à tous,

Me voilà de retour avec mon routeur photovoltaïque.

Je souhaite pouvoir enregistrer les paramètres (ssid et password) de mon routeur en utilisant le mode access point.

Le principe (que vous connaissez certainement) est de créer un fichier dans lequel je vais pouvoir, grâce à une connexion en access point, enregistrer les (ssid et password) de mon routeur. Dès lors que ce fichier existera, mon logiciel en extraira ces données pour se connecter en mode Station cette fois à mon routeur.

Pour ce faire, je crée un formulaire dans une page Web dans laquelle je peux renseigner ces données. Un bouton Valider fait une requête pour transmettre ces données à mon logiciel.

Mon problème : après avoir renseigné les données et cliqué sur le bouton Valider, j'aimerais afficher dans cette même page Web sous le formulaire le résultat de ma requête sous la forme : Identification du routeur : réussie ou échouée.

Je n'arrive pas à faire le lien entre le résultat de ma requête et la ligne que je veux écrire.

Voilà mon sketch d'essai :

#include <WiFi.h>
#include "routes.h"

const char* ssid     = "ESP32-Access-Point";
const char* password = "123456789";

AsyncWebServer server(80);

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

  Serial.print("Setting AP (Access Point)…");  // Connect to Wi-Fi network with SSID and password
  // Remove the password parameter, if you want the AP (Access Point) to be open
  WiFi.softAP(ssid, password);

  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);

  setupRoutes(server);
  
  server.begin();
}

void loop(){
}

la page HTML :

#ifndef HTML_AP_H
#define HTML_AP_H

String pageHTML_AP() {
  String HTML = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
	<meta content="text/html; charset=UTF-8" http-equiv="content-type">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>ESP32 Routeur photovoltaïque</title>
    <style> body {font-family: Arial, Helvetica, sans-serif;}
    </style>
</head>
<body>
  <h1 style="text-align: center;">Routeur photovoltaïque ESP32</h1>
  <h2 style="text-align: center;">Paramètres du routeur WiFi</h2>
  <form action="/submit" method="get">
    <label for="line1">Identifiant :</label>
    <input type="text" id="line1" name="line1"><br><br>
    <label for="line2">Mot de passe :</label>
    <input type="text" id="line2" name="line2"><br><br>
    <input type="submit" value="Valider">
  </form>
  <p>Identification du routeur WiFi : <span id="cnxWiFi">...</span></p>

</body>
</html>
  )rawliteral";
  return HTML;
}
#endif

et les routes :

#ifndef ROUTES_H
#define ROUTES_H

#include <ESPAsyncWebServer.h>
#include "html_AP.h"

uint32_t t0;
bool WiFiOK;
void setupRoutes(AsyncWebServer &server) {
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { // Affichage de la page HTML   
    request->send(200, "text/html", pageHTML_AP());
  });


  server.on("/submit", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String Ident = request->getParam("line1")->value();
    String MdP = request->getParam("line2")->value();
    Serial.println("Identifiant : " + Ident);
    Serial.println("Mot de passe : " + MdP);

    t0 = millis();
    WiFiOK = true;
    WiFi.begin(Ident, MdP);
    Serial.println("\nConnecting");
    while (WiFi.status() != WL_CONNECTED) {
      Serial.print(".");
      delay(100);
      if (millis()-t0 > 10000) {
        WiFi.softAP("ESP32-Access-Point", "123456789");
        Serial.println("Connection échouée");
        request->send(200, "text/plain", "échouée");
        WiFiOK= false;
      }
    }
    if (WiFiOK) {
      Serial.println("\nConnected to the WiFi network");
      Serial.print("Local ESP32 IP: ");
      Serial.println(WiFi.localIP());
      request->send(200, "text/plain", "réussie");
    }


  });
}
#endif

En l'état actuel des choses, si mon identification est réussie, cela m'affiche Connexion réussie en tout petit dans une nouvelle page et ça plante si ce n'est pas réussi, mais ça, c'est un autre problème que j'aurai à résoudre. Ce réussie, je voudrais le voir affiché sous mon formulaire.

Cordialement.

Pierre.

Le plus simple : utilisez la bibliothèque WiFiManager

Merci pour ce lien. Cette bibliothèque a l'air de faire bien des choses mais ... lorsque je l'inclue dans mon programme, à la compilation j'ai une palanquée d'erreurs qui semblent a priori liées à une incompatibilité avec les bibliothèques de l'ESP32 à la version 3.0.7 (sachant que la dernière version 3.11 n'est pas fonctionnelle) et à la bibliothèque ESPAsyncWebServer :

In file included from C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.7\libraries\WebServer\src/HTTP_Method.h:4,
                 from C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.7\libraries\WebServer\src/WebServer.h:30,
                 from e:\Arduino\libraries\WiFiManager/WiFiManager.h:94,
                 from E:\Arduino\Projets_ChP\Routeur_PV_01_with_OTA\Routeur_PV_01_with_OTA.ino:20:
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:95:6: error: 'HTTP_DELETE' conflicts with a previous declaration
   95 |   XX(0,  DELETE,      DELETE)       \
      |      ^
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:138:45: note: in definition of macro 'XX'
  138 | #define XX(num, name, string) HTTP_##name = num,
      |                                             ^~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:139:3: note: in expansion of macro 'HTTP_METHOD_MAP'
  139 |   HTTP_METHOD_MAP(XX)
      |   ^~~~~~~~~~~~~~~
In file included from E:\Arduino\Projets_ChP\Routeur_PV_01_with_OTA\Routeur_PV_01_with_OTA.ino:10:
e:\Arduino\libraries\ESP_Async_WebServer\src/ESPAsyncWebServer.h:68:3: note: previous declaration 'WebRequestMethod HTTP_DELETE'
   68 |   HTTP_DELETE = 0b00000100,
      |   ^~~~~~~~~~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:96:6: error: 'HTTP_GET' conflicts with a previous declaration
   96 |   XX(1,  GET,         GET)          \
      |      ^
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:138:45: note: in definition of macro 'XX'
  138 | #define XX(num, name, string) HTTP_##name = num,
      |                                             ^~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:139:3: note: in expansion of macro 'HTTP_METHOD_MAP'
  139 |   HTTP_METHOD_MAP(XX)
      |   ^~~~~~~~~~~~~~~
e:\Arduino\libraries\ESP_Async_WebServer\src/ESPAsyncWebServer.h:66:3: note: previous declaration 'WebRequestMethod HTTP_GET'
   66 |   HTTP_GET = 0b00000001,
      |   ^~~~~~~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:97:6: error: 'HTTP_HEAD' conflicts with a previous declaration
   97 |   XX(2,  HEAD,        HEAD)         \
      |      ^
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:138:45: note: in definition of macro 'XX'
  138 | #define XX(num, name, string) HTTP_##name = num,
      |                                             ^~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:139:3: note: in expansion of macro 'HTTP_METHOD_MAP'
  139 |   HTTP_METHOD_MAP(XX)
      |   ^~~~~~~~~~~~~~~
e:\Arduino\libraries\ESP_Async_WebServer\src/ESPAsyncWebServer.h:71:3: note: previous declaration 'WebRequestMethod HTTP_HEAD'
   71 |   HTTP_HEAD = 0b00100000,
      |   ^~~~~~~~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:98:6: error: 'HTTP_POST' conflicts with a previous declaration
   98 |   XX(3,  POST,        POST)         \
      |      ^
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:138:45: note: in definition of macro 'XX'
  138 | #define XX(num, name, string) HTTP_##name = num,
      |                                             ^~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:139:3: note: in expansion of macro 'HTTP_METHOD_MAP'
  139 |   HTTP_METHOD_MAP(XX)
      |   ^~~~~~~~~~~~~~~
e:\Arduino\libraries\ESP_Async_WebServer\src/ESPAsyncWebServer.h:67:3: note: previous declaration 'WebRequestMethod HTTP_POST'
   67 |   HTTP_POST = 0b00000010,
      |   ^~~~~~~~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:99:6: error: 'HTTP_PUT' conflicts with a previous declaration
   99 |   XX(4,  PUT,         PUT)          \
      |      ^
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:138:45: note: in definition of macro 'XX'
  138 | #define XX(num, name, string) HTTP_##name = num,
      |                                             ^~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:139:3: note: in expansion of macro 'HTTP_METHOD_MAP'
  139 |   HTTP_METHOD_MAP(XX)
      |   ^~~~~~~~~~~~~~~
e:\Arduino\libraries\ESP_Async_WebServer\src/ESPAsyncWebServer.h:69:3: note: previous declaration 'WebRequestMethod HTTP_PUT'
   69 |   HTTP_PUT = 0b00001000,
      |   ^~~~~~~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:102:6: error: 'HTTP_OPTIONS' conflicts with a previous declaration
  102 |   XX(6,  OPTIONS,     OPTIONS)      \
      |      ^
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:138:45: note: in definition of macro 'XX'
  138 | #define XX(num, name, string) HTTP_##name = num,
      |                                             ^~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:139:3: note: in expansion of macro 'HTTP_METHOD_MAP'
  139 |   HTTP_METHOD_MAP(XX)
      |   ^~~~~~~~~~~~~~~
e:\Arduino\libraries\ESP_Async_WebServer\src/ESPAsyncWebServer.h:72:3: note: previous declaration 'WebRequestMethod HTTP_OPTIONS'
   72 |   HTTP_OPTIONS = 0b01000000,
      |   ^~~~~~~~~~~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:128:6: error: 'HTTP_PATCH' conflicts with a previous declaration
  128 |   XX(28, PATCH,       PATCH)        \
      |      ^~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:138:45: note: in definition of macro 'XX'
  138 | #define XX(num, name, string) HTTP_##name = num,
      |                                             ^~~
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/http_parser/http_parser.h:139:3: note: in expansion of macro 'HTTP_METHOD_MAP'
  139 |   HTTP_METHOD_MAP(XX)
      |   ^~~~~~~~~~~~~~~
e:\Arduino\libraries\ESP_Async_WebServer\src/ESPAsyncWebServer.h:70:3: note: previous declaration 'WebRequestMethod HTTP_PATCH'
   70 |   HTTP_PATCH = 0b00010000,
      |   ^~~~~~~~~~

exit status 1

Compilation error: exit status 1

Cordialement.

Pierre.

Je mets de côté l'affichage dans une même page et je passe au second problème : le plantage si je ne donne pas les bons identifiants et donc que le routeur n'est pas identifié.

La recherche dure 5 secondes (alors que j'autorise 10 secondes) et bout de ce temps, s'affiche un problème de watch dog qui me fait rebooter l'ESP32 :

16:15:24.710 -> ets Jul 29 2019 12:21:46
16:15:24.710 -> 
16:15:24.710 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
16:15:24.753 -> configsip: 0, SPIWP:0xee
16:15:24.753 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
16:15:24.753 -> mode:DIO, clock div:1
16:15:24.753 -> load:0x3fff0030,len:4832
16:15:24.753 -> load:0x40078000,len:16460
16:15:24.753 -> load:0x40080400,len:4
16:15:24.753 -> load:0x40080404,len:3504
16:15:24.753 -> entry 0x400805cc
16:15:25.126 -> Setting AP (Access Point)…AP IP address: 192.168.4.1
16:16:04.202 -> Identifiant : Zrtuoppoooi
16:16:04.202 -> Mot de passe : Ggdegûiiu
16:16:04.202 -> 
16:16:04.202 -> Connecting
16:16:04.202 -> ..................................................E (52517) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
16:16:09.112 -> E (52517) task_wdt:  - async_tcp (CPU 0/1)
16:16:09.146 -> E (52517) task_wdt: Tasks currently running:
16:16:09.146 -> E (52517) task_wdt: CPU 0: tiT
16:16:09.146 -> E (52517) task_wdt: CPU 1: loopTask
16:16:09.146 -> E (52517) task_wdt: Print CPU 0 (current core) backtrace
16:16:09.146 -> 
16:16:09.146 -> 
16:16:09.146 -> Backtrace: 0x400E2B7C:0x3FFC037C |<-CORRUPTED
16:16:09.146 -> 
16:16:09.146 -> E (52517) task_wdt: Aborting.
16:16:09.146 -> E (52517) task_wdt: Print CPU 1 backtrace
16:16:09.146 -> 
16:16:09.146 -> 
16:16:09.146 -> 
16:16:09.146 -> 
16:16:09.146 -> Backtrace: 0x400db214:0x3ffb2250 0x400dda0c:0x3ffb2270 0x4008e8ae:0x3ffb2290
16:16:09.179 -> 
16:16:09.179 -> 
16:16:09.179 -> 
16:16:09.179 -> 
16:16:09.179 -> ELF file SHA256: 6f894b09fc8f765f
16:16:09.179 -> 
16:16:09.329 -> Rebooting...

Une idée du problème ?

Cordialement.

Pierre.

Ah - je ne l’ai jamais testé sur cette nouvelle version…

Pour l’autre bug c’est avec le code posté au début ?

Oui. J'ai vu qu'on pouvait modifier (chatGPT entre autres) la durée du watchdog en écrivant par exemple : esp_task_wdt_init(30, true); sauf que si j'écris cela, je me fais jeter car le premier terme de esp_task_wdt_init n'est pas une constante, mais une structure. J'ai alors écrit :

esp_task_wdt_config_t wdP;

...
    wdP.timeout_ms = 20000;
    esp_task_wdt_init(wdP, true);

mais là, j'ai l'erreur suivante :

In file included from E:\Arduino\Projets_ChP\Routeur_Acces_Point\Routeur_Acces_Point.ino:2:
E:\Arduino\Projets_ChP\Routeur_Acces_Point\routes_AP.h: In lambda function:
E:\Arduino\Projets_ChP\Routeur_Acces_Point\routes_AP.h:28:23: error: cannot convert 'esp_task_wdt_config_t' to 'const esp_task_wdt_config_t*'
   28 |     esp_task_wdt_init(wdP, true);
      |                       ^~~
      |                       |
      |                       esp_task_wdt_config_t
In file included from E:\Arduino\Projets_ChP\Routeur_Acces_Point\routes_AP.h:6:
C:\Users\prcha\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-632e0c2a\esp32/include/esp_system/include/esp_task_wdt.h:47:58: note:   initializing argument 1 of 'esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t*)'
   47 | esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config);
      |                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~

exit status 1

Compilation error: cannot convert 'esp_task_wdt_config_t' to 'const esp_task_wdt_config_t*'

Bref, je ne sais pas trop quelle syntaxe il faut employer exactement.

Cordialement.

Pierre.

il veut le pointeur sur la structure ➜ essayez

    esp_task_wdt_init(&wdP, true);

mais attention, comme c'est une structure il y a d'autres champs

Structures

struct esp_task_wdt_config_t

Task Watchdog Timer (TWDT) configuration structure.

Public Members

uint32_t timeout_ms

TWDT timeout duration in milliseconds

uint32_t idle_core_mask

Bitmask of the core whose idle task should be subscribed on initialization where 1 << i means that core i's idle task will be monitored by the TWDT

bool trigger_panic

Trigger panic when timeout occurs

Faudrait peut être configurer les autres champs aussi

Oui, j'ai regardé ça un peu plus en détail. Je me suis basé sur ce site en lui posant cette question : ESP32 comment définir les paramètres de la srtructure esp_task_wdt_config_t.

Ça n'a pas fonctionné.Cela m'affiche :

20:46:12.711 -> E (53050) task_wdt: esp_task_wdt_deinit(706): Tasks/users still subscribed
20:46:12.711 -> E (53050) task_wdt: esp_task_wdt_init(573): TWDT already initialized
20:46:12.758 -> E (53056) task_wdt: add_entry(189): task is already subscribed
20:46:12.758 -> Rés : 259 // ESP_ERR_INVALID_STATE       0x103   /*!< Invalid state */
20:46:12.758 -> 
20:46:12.758 -> Connecting
20:46:12.758 -> ..................................................E (62650) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
20:46:17.711 -> E (62650) task_wdt:  - async_tcp (CPU 0/1)
20:46:17.711 -> E (62650) task_wdt: Tasks currently running:
20:46:17.711 -> E (62650) task_wdt: CPU 0: IDLE0
20:46:17.756 -> E (62650) task_wdt: CPU 1: loopTask
20:46:17.756 -> E (62650) task_wdt: Print CPU 0 (current core) backtrace

Je referai des essais ...

Cordialement.

Pierre.

Je reviens vers vous après moult essais ... tous infructueux.
J'ai essayé la configuration donnée ici. Elle fonctionne.

Si j'en fais un fonction : void modifWD(uint32_t tWD), comme dans le bout de code suivant :

#ifndef ROUTES_AP_H
#define ROUTES_AP_H

#include <ESPAsyncWebServer.h>
#include "html_AP.h"
#include <esp_task_wdt.h>

bool WiFiOK;
uint32_t t0, t1;
esp_err_t ESP32_ERROR;


void modifWD(uint32_t tWD) { // Fonction qui fonctionne seulement si je la lance dans le setup()
  Serial.println("Configuring WDT...");
  Serial.print("Watchdog Timeout (in seconds) set to : ");
  Serial.println(tWD);
  esp_task_wdt_deinit();
  // Task Watchdog configuration
  esp_task_wdt_config_t wdt_config = {
    .timeout_ms = tWD,
    .idle_core_mask = (1 << portNUM_PROCESSORS) - 1,
    .trigger_panic = true // Enable panic to restart ESP32
  };
  // WDT Init
  ESP32_ERROR = esp_task_wdt_init(&wdt_config);
  Serial.println("Last Reset : " + String(esp_err_to_name(ESP32_ERROR)));
  esp_task_wdt_add(NULL);  //add current thread to WDT watch
}

void setupRoutes(AsyncWebServer &server) {
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { // Affichage de la page HTML   
    request->send(200, "text/html", pageHTML_AP());
  });


  server.on("/submit", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String Ident = request->getParam("line1")->value();
    String MdP = request->getParam("line2")->value();
    Serial.println("Identifiant : " + Ident);
    Serial.println("Mot de passe : " + MdP);

    //    modifWD(60000); // ne fonctionne pas ici, mais c'est ici que je voudrais laisser 10 secondes à la recherche du routeur

    WiFiOK = true;
    WiFi.disconnect(); // On se déconnecte du mode Access Point
    WiFi.mode(WIFI_STA); // On passe en mode Station
    WiFi.begin(Ident, MdP); // Recherche du routeur avec les identifiantet mot de passe donnés
    Serial.println("\nConnection ");
    t0 = millis();
    while (WiFi.status() != WL_CONNECTED) { // Recherce du routeur 
      t1 = millis();
      Serial.print("."); Serial.println(t1-t0);
      delay(100);
      if (t1-t0 > 10000) { // Routeur pas trouvé
        WiFiOK = false;
        break;
      }
    }
    if (!WiFiOK) {
      Serial.println("Connection échouée");
      //      request->send(200, "text/plain", "Connection échouée");
    } else {
      Serial.println("\nConnecté au réseau WiFi");
      Serial.print("Local ESP32 IP: ");
      Serial.println(WiFi.localIP());
      //      request->send(200, "text/plain", "Connection réussie");
    }
  });
}
#endif

Cela fonctionne si je l'appelle dans le setup().

J'ai essayé de l'appeler dans la loop() suite à une réponse clavier par exemple ou bien dans ma fonction de recherche du routeur, cela ne fonctionne plus. J'ai le même code d'erreur :

20:46:12.711 -> E (53050) task_wdt: esp_task_wdt_deinit(706): Tasks/users still subscribed
20:46:12.711 -> E (53050) task_wdt: esp_task_wdt_init(573): TWDT already initialized
20:46:12.758 -> E (53056) task_wdt: add_entry(189): task is already subscribed
20:46:12.758 -> Rés : 259 // ESP_ERR_INVALID_STATE       0x103   /*!< Invalid state */

Je pense que je n'ai rien compris à la philosophie de gestion du WatchDog et je ne sais pas comment l'appliquer.

Par ailleurs, ai-je vraiment besoin de modifier ce WatchDog ? Tout ce que souhaite, est que l'ESP32 ne reboot pas au bout de 5secondes alors qu'il mouline à la recherche du routeur. D'après ce que j'ai compris, le WatchDog n'aime pas les fonction inactive. Main quoi la fonction de recherche suivante est-elle inactive :

    while (WiFi.status() != WL_CONNECTED) { // Recherce du routeur 
      t1 = millis();
      Serial.print("."); Serial.println(t1-t0);
      delay(100);
      if (t1-t0 > 10000) { // Routeur pas trouvé
        WiFiOK = false;
        break;
      }
    }

Merci de votre aide.

Pierre.

Bonjour Pierre,

Tout d'abord, comme je prends votre discussion en cours de route, pardonnez moi si je mets en dehors de la plaque.

Sur le watchdog, vous l'armez comme vous avez fait dans le setup, et ensuite, dans la loop, vous le remettez à zéro ("nourrir le chien") à chaque appel de loop avec un esp_task_wdt_reset();

Ce watchdog n'est rien d'autre qu'un timer qui redémarre l'ESP au bout d'un certain temps. On doit remettre le compteur à zéro ("nourrir le chien") périodiquement pour éviter qu'il ne "morde"...

Ceci dit, cela implique qu'il ne faut pas bloquer la loop avec des delay() ou des attentes... sinon le chien va mordre ! Je n'ai pas tout saisi de votre cas, mais si vous lancez une connexion dans un tour de loop, il ne faut pas rester à attendre dans le même tour... Il vous faut faire une machine à états:

  1. Lancer la demande de connexion dans un tour,
  2. dans les tours suivants, tester où ça en est,
  3.         si c'est connecté passer à la suite,
    
  4.         sinon passer le tour,
    
  5.         on peut rajouter un délai au bout duquel on abandonne...
    

Une machine à états peut faire le job. Dans chaque tour de loop() on nourrit le chien, et comme il n'y a pas d'attente dans chaque tour il ne mord pas, sauf imprévu... pour lequel il est là.

A+ et bonne bidouille.

Microquettas

En fait un appel à delay va « nourrir le chien » comme vous dites, ce n’est pas un souci.

Ce sont les attentes actives sans rendre la main au système par un appel à yield() ou delay() par exemple qui sont problématiques.

Merci "MicroQuettas" pour ces précisions. J'ai réussi à "armer" le WatchDod dans le setup() puis le "nourrir" périodiquement pour qu'il ne se déclenche pas.

Pour autant, j'ai deux questions :

  1. Comme je l'avais expliqué précédemment, armer le WatchDog ailleurs que dans le setup() ne fonctionnait pas. Pourquoi ?

  2. Mis à part ma demande de recherche du routeur qui déclenchait le WatchDog, je n'ai jamais eu de problème avec le reste de mon programme (ni avec d'autres programmes). Je n'ai donc pas besoin de m'en servir. D'où ma deuxième question, comment annuler (se désinscrire ?) le WatchDog ? J'ai bien vu la fonction esp_task_wdt_delete(task_handle);, mais je ne sais pas où trouver ce task_handle.

Cordialement.

Pierre.

@J-M-L
Bonjour,

Merci pour la précision.
Pour yield() c'est évident, mais pour delay() ça l'est moins.
Après reflexion ça se tient parfaitement, le timer s 'arrête dès que la loop() rend la main au système.

@ChPr
Bonjour Pierre,

Pour répondre à vos questions, je ne vois pas raison pour que l'on ne puisse pas armer le watchdog depuis la loop. Il faut simplement être prudent et ne l'armer qu'une seule fois...

Pour votre deuxième question, je ne sais pas.

D'après la description que vous donnez, je continue de penser qu'une machine à états avec timer serait plus adaptée à votre problème de gestion de connexion. @J-M-L a publié il y a déjà qq temps un très bon tuto sur le sujet.

En gros, on passe d'un état à un autre:

  • si une condition se réalise (par ex. la réponse d'un serveur), ou
  • par déclenchement d'un timer (par ex. si le serveur ne répond pas dans le temps imparti).

Il est intéressant de noter que les deux états "de destination" ne sont pas les mêmes. On peut aussi avoir plusieurs états de destination selon la réalisation de conditions...
Cette technique est simple et permet de gérer des actions de manière souple et fluide sans jamais rien bloquer, et sans usine à gaz...
Vous pouvez utiliser plusieurs de ces machines à états pour les différentes parties de votre application.

Bonne bidouille,

MicroQuettas

oui, en fait sur un ESP, l'appel à delay() met la tâche en sommeil, ce n'est pas une attente active en regardant millis() donc c'est l'OS qui a la main.

J'ai réalisé un petit sketch où, tout se passe bien, je peux inscrire une tâche dans le setup(), puis la désinscrire et la désinitialiser dans la loop(). Je peux la réinscrire dans la loop() et ainsi de suite. Tout fonctionne bien.

#include <esp_task_wdt.h>

bool wdActif;
esp_err_t ESP32_ERROR;
uint32_t t0, t1, t2;

bool modifWD(uint32_t tWD) {
  Serial.println("Configuring WDT...");
  Serial.print("Watchdog Timeout (in seconds) set to : ");
  Serial.println(tWD);
  esp_task_wdt_deinit(); // Désinscrit et dé-initialise les tâches inactives

  esp_task_wdt_config_t wdt_config = { // Task Watchdog configuration
    .timeout_ms = tWD,
    .idle_core_mask = (1 << portNUM_PROCESSORS) - 1, // Tous les coeurs
//    .idle_core_mask =  (1 << portNUM_PROCESSORS) - 2, // le coeur 1
    .trigger_panic = true // Enable panic to restart ESP32
  };

  ESP32_ERROR = esp_task_wdt_init(&wdt_config); // WDT Init
  Serial.println("Last Reset : " + String(esp_err_to_name(ESP32_ERROR)));
  esp_task_wdt_add(NULL);  // add current thread to WDT watch
  if (ESP32_ERROR == ESP_OK)
    return true;
  else
    return false;
}

void setup() {
  Serial.begin(115200);
  ESP32_ERROR = esp_task_wdt_status(NULL);
  Serial.println("Statut : " + String(esp_err_to_name(ESP32_ERROR)));
  wdActif = modifWD(10000);
  t2 = millis();
}

void loop() {
  if (Serial.available()) {
    String R = Serial.readString();
    if (R == "R") {
      ESP32_ERROR = esp_task_wdt_delete(NULL);
      Serial.println("Désinscription : " + String(esp_err_to_name(ESP32_ERROR)));
      ESP32_ERROR = esp_task_wdt_deinit();
      Serial.println("Déinitialisation : " + String(esp_err_to_name(ESP32_ERROR)));
      wdActif = false;
    }
    if (R == "I") {
      wdActif = modifWD(10000);
      t2 = millis();
    }
  }
  if (millis()- t2 > 3000) {
    t2 = millis();
    if (wdActif) {
      Serial.println("WD actif");
      esp_task_wdt_reset();
      delay(1);
    } else
      Serial.println("WD inactif");
  }
}

Avec mon application, si je l'inscris dans le setup() et que je laisse les choses en l'état, tout se passe bien. Comme j'ai mis des timeout pour les temps de connexion, le WatchDog ne se déclenche jamais. Même si mon sketch ne ressemble pas à une machine d'état, c'en est une.

server.on("/submit", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String Ident = request->getParam("line1")->value();
    String MdP = request->getParam("line2")->value();
    Serial.println("Identifiant : " + Ident);
    Serial.println("Mot de passe : " + MdP);
    WiFiOK = true;
//    WiFi.disconnect(); // On se déconnecte du mode Access Point
//    WiFi.mode(WIFI_STA); // On passe en mode Station
    WiFi.begin(Ident, MdP); // Recherche du routeur avec les identifiant et mot de passe donnés
    Serial.println("\nConnection ");
    t0 = millis();
    while (WiFi.status() != WL_CONNECTED) { // Recherce du routeur 
      t1 = millis();
      Serial.print(".");
      delay(100);
      if (t1-t0 > 10000) { // Routeur pas trouvé
        WiFiOK = false;
        break;
      }
    }
    if (!WiFiOK) {
      Serial.println("Connection échouée");
      request->send(200, "text/plain", "Connection échouée");
    } else {
      Serial.println("\nConnecté au réseau WiFi");
      Serial.print("Local ESP32 IP: ");
      Serial.println(WiFi.localIP());
      request->send(200, "text/plain", "Connection réussie");
    }
    ESP32_ERROR = esp_task_wdt_delete(NULL);
    Serial.println("Désinscription : " + String(esp_err_to_name(ESP32_ERROR)));
    ESP32_ERROR = esp_task_wdt_deinit();
    Serial.println("Déinitialisation : " + String(esp_err_to_name(ESP32_ERROR)));
    wdActif = false;
  });

Dans la loop(), le code est le suivant afin d'activer ou non le reset du WatchDog :

void loop(){
  if (millis()- t2 > 3000) {
    t2 = millis();
    if (wdActif) { // mis à vrai lors de l'inscription et à faux à la sortie de la fonction de recherche de réseau WiFi
      Serial.println("WD actif");
      esp_task_wdt_reset();
      delay(1);
    } else
      Serial.println("WD inactif");
  }
}

Là où ça ne fonctionne plus, c'est lorsque je veux désinscrire et désinitialiser la tâche. Suite à ces commandes, j'obtiens les messages suivants :

16:05:13.272 -> Connecté au réseau WiFi
16:05:13.272 -> Local ESP32 IP: 192.168.0.4
16:05:13.272 -> Désinscription : ESP_OK
16:05:13.272 -> E (49427) task_wdt: esp_task_wdt_deinit(706): Tasks/users still subscribed
16:05:13.272 -> Déinitialisation : ESP_ERR_INVALID_STATE
16:05:13.272 -> E (49440) task_wdt: esp_task_wdt_reset(763): task not found
16:05:13.272 -> E (49441) task_wdt: esp_task_wdt_reset(763): task not found
16:05:13.272 -> E (49444) task_wdt: esp_task_wdt_reset(763): task not found
16:05:13.272 -> E (49449) task_wdt: esp_task_wdt_reset(763): task not found
...

On voit que le désinscription se passe bien, mais pas la déinitialisation qui me dit que : Tasks/users still subscribed alors que la désinscription me dit le contraire ?
Suite à cela, j'ai une flopée de esp_task_wdt_reset(763): task not found alors que je les ai interdits dans mon sketch. Qui envoie cette commande de reset ?

Est-ce que de son côté, cette fonction WiFi ne crée-t-elle pas son propre WatchDog ?

Cordialement.

Pierre.

Votre code s'execute dans le contexte d'un callback web et ESPAsyncWebServer utilise un thread (souvent le core 0, celui du WiFi ) pour gérer les requêtes HTTP entrantes, et dans ce thread, des callbacks sont exécutés pour répondre aux requêtes.

Le message Tasks/users still subscribed vous indique que certaines tâches (probablement la tâche principale du serveur web ou d'autres tâches système) sont encore inscrites au watchdog , et donc la déinitialisation ne peut pas être effectuée.

(sinon pour la question, Le WiFi sur l'ESP32 utilise un watchdog interne pour surveiller les connexions réseau)

Donc la WiFi a son propre WatchDog. De ce fait, je ne configure rien - la WiFi l'a fait - et je ne fais que esp_task_wdt_reset(); dans la boucle de recherche de réseau ... et ça roule tout seul, sans problème. Je n'ai pas à donner de durée. Je n'ai rien à inscrire, à désinscrire ni à déinitialiser.

C'est trop beau pour être vrai. Mais c'est !

Mon skectch de recherche du réseau se résume alors :

  server.on("/submit", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String Ident = request->getParam("line1")->value();
    String MdP = request->getParam("line2")->value();
    Serial.println("Identifiant : " + Ident);
    Serial.println("Mot de passe : " + MdP);
    WiFiOK = true;
    Serial.println("\nConnection ");
    t0 = millis();
    while (WiFi.status() != WL_CONNECTED) { // Recherce du routeur 
      t1 = millis();
      Serial.print(".");
      esp_task_wdt_reset(); // remise à zéro du WatchDog.
      delay(100);
      if (t1-t0 > 10000) { // Routeur pas trouvé
        WiFiOK = false;
        break;
      }
    }
    if (!WiFiOK) {
      Serial.println("Connection échouée");
      request->send(200, "text/plain", "Connection échouée");
    } else {
      Serial.println("\nConnecté au réseau WiFi");
      Serial.print("Local ESP32 IP: ");
      Serial.println(WiFi.localIP());
      request->send(200, "text/plain", "Connection réussie");
    }
  });

Voyons dans la durée ...

Cordialement.

Pierre.

que demande le peuple

:wink:

personnellement je ne démarrerais pas le WiFi dans le callback, je laisserais cela à la loop. le callback noterait l'information et dirait à la loop que c'est prêt pour la connexion. Au moins ce serait fait dans la tâche principale de votre code arduino

Le WiFi - en mode Access Point n'est pas démarré dans le callback, il est démarré dans le setup() :

void setup() {
  Serial.begin(115200);
  Serial.print("Setting AP (Access Point)…");  // Connect to Wi-Fi network with SSID and password
  WiFi.softAP(ssid, password);
  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);
  setupRoutes(server);  // là, je fais appel aux callback en cherchant lapage 192.164.1.4 avec mon smartphone
  server.begin();
  t2 = millis();
}

Après, effectivement, c'est dans le callback que je vais voir, avec les paramètres d'identification, si je peux atteindre/ou pas le routeur.

Cette procédure n'a lieu qu'une seule fois. Une fois les paramètres enregistrés, mon logiciel reconnaitra ce fichier au lancement de l'application et démarrera le routeur dans le setup().

Cordialement.

Pierre

Bonsoir,

Heureux de voir que cela fonctionne. Félicitations !

De toutes façons, je ne vous aurais pas suivi...
Votre utilisation du watchdog est trop pointue pour moi. J'en reste aux fondamentaux, armement en fin de setup(), rafraichissement dans tous les tours de loop().

Pour les temporisations, j'utilise millis() ou ticker qui n'est pas mal non plus.

J'en étais également resté à la minimisation des actions dans les callback, mais ce sont peut-être des pratiques de vieux... prudent.

Bonne soirée et bonne bidouille

MicroQuettas