While() = Task watchdog got triggered avec ESP32_Coeur0

Bonjour,

Sur un ESP32, mon programme gère un pas-à-pas sur le Coeur1 et le reste sur le Coeur0. (pour plus d’info voir ce sujet).

Dans le programme principal, j’ai un bouton pour purger manuellement (fait tourner le moteur à une certaine vitesse). Ce bouton d’éclanche une fonction qui bloque tous (while (!digitalRead(bouton)) {}) temps que le bouton est enfoncé.

Le souci que j’ai c’est quand mettant le programme principal sur le Coeur0, «ESP-IDF» ajoute un «watchdog timers» qui après ~5seconde remarque que la loop() n’a pas bougé et il me d’éclanche un reset.

Avec le programme ci-dessous, ci je le laisse sur le Coeur1, pas de soucis. Ci je le mes sur le Coeur0, ça bug…

Les lien que j’ai trouver (en anglais avec google_traduction, pas évident de comprendre) traitant ce problème :
forum Arduino Anglais
forum stackoverflow.com
forum Espressif

Programme test : (dans l’IDE pour verser sur le Coeur0 : Outils /Arduino Runs On : / Core 0)

const uint8_t swMoteurOn = 26;

void setup() {
  Serial.begin(115200);
  pinMode(swMoteurOn, INPUT_PULLUP);
}

void loop() {
  if(digitalRead(swMoteurOn)) {
    moteurOn();
  }
}

void moteurOn(){
  delay(20);    //Anti-rebond bloquant mais suffisant, la fonction s'execute à l'arrêt de la machine
  if (digitalRead(swMoteurOn)) return;

  //Copier les paramètres du moteur

  //Remplacer les paramètre moteur par ceux de la fonction

  Serial.println("Moteur On");

  while (!digitalRead(swMoteurOn)) {}  //Attendre que le bouton soit relâché

  Serial.println("Moteur Off\n");

  //Remettre les paramètres moteur copiés au début de la fonction
}

Erreur afficher sur le port-série :

11:34:43.761 -> Moteur On
11:34:48.748 -> E (1802238) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
11:34:48.748 -> E (1802238) task_wdt:  - IDLE (CPU 0)
11:34:48.748 -> E (1802238) task_wdt: Tasks currently running:
11:34:48.748 -> E (1802238) task_wdt: CPU 0: loopTask
11:34:48.748 -> E (1802238) task_wdt: CPU 1: IDLE
11:34:48.748 -> E (1802238) task_wdt: Aborting.
11:34:48.748 -> 
11:34:48.748 -> abort() was called at PC 0x400d8371 on core 0
11:34:48.748 -> 
11:34:48.748 -> 
11:34:48.748 -> Backtrace:0x400833d1:0x3ffbe78c |<-CORRUPTED
11:34:48.780 -> 
11:34:48.780 -> 
11:34:48.780 -> 
11:34:48.780 -> 
11:34:48.780 -> ELF file SHA256: 0000000000000000
11:34:48.780 -> 
11:34:48.780 -> Rebooting...
11:34:48.780 -> ets Jun  8 2016 00:22:57
11:34:48.780 -> 
11:34:48.780 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
11:34:48.780 -> configsip: 0, SPIWP:0xee
11:34:48.780 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
11:34:48.780 -> mode:DIO, clock div:111:34:48.780 -> load:0x3fff0030,len:1344
11:34:48.780 -> load:0x40078000,len:13864
11:34:48.780 -> load:0x40080400,len:3608
11:34:48.812 -> entry 0x400805f0

Pour resoudre ça, vous me consayé quoi ?

  • Trouver une façon de réinisialiser le watchdog pendant le while()
  • Transformer ma fonction en _vTask FreeRTOS avec un vTaskDelay(...) dans le while()
  • Faire une fonction On est une fonction Off (ce qui implique de sauvegarder les paramètre depuis le On pour les remettre dans le Off)
  • Désactiver les watchdog, pas trop envie, ou alors que temporairement.
  • Ou une autre solution !

Je crois que j'ai trouvé :blush:

while (!digitalRead(bouton)) { delay(1);}

En mettant 1 mili-seconde de délais dans le while(), ça doit permettre au watchdog de ce réinisialiser. Enfin c'est ce-que je pences. En tous cas dans le programme test, j'ai tenu le bouton plus de 5 minuttes enfoncé et il n'a pas RESETER.

Salutations, bonne journée

Si les timings sont importants, Plutôt que de perdre 1ms pour juste réinitialiser le watchdog, utilisez yield() au lieu de delay

while (digitalRead(bouton) == LOW) yield();

(Et c’est pas plus mal de tester avec LOW pour le respect des APIs)

Merci,
Mais avec le programme test ci-dessous celà ne marche pas !
En recherchant ce qu'est yield(), je suis tombé là-dessus:

J'ai du mal à imaginer comment yield() sais combien de temps il doit laisser pour les autres tâches!

Pardon, erreurs faitent par un débutant, j'ai corrigé.

Le programme test corrigé fait avec yield(), qui ne marche pas :sleepy:

const uint8_t swMoteurOn = 26;

void setup() {
  Serial.begin(115200);
  pinMode(swMoteurOn, INPUT_PULLUP);
}

void loop() {
  if(digitalRead(swMoteurOn) == HIGH) {
    moteurOn();
  }
}

void moteurOn(){
  delay(20);    //Anti-rebond bloquant mais suffisant, la fonction s'execute à l'arrêt de la machine
  if (digitalRead(swMoteurOn) == LOW) return;

  //Copier les paramètres du moteur

  //Remplacer les paramètre moteur par ceux de la fonction

  Serial.println("Moteur On");

  // while (digitalRead(swMoteurOn )== LOW) {  //Attendre que le bouton soit relâché
  //   //delay(1);
  //   yield();
  // }
  while (digitalRead(swMoteurOn) == LOW)  yield();

  Serial.println("Moteur Off\n");

  //Remettre les paramètres moteur copiés au début de la fonction
}

Il ne le sait pas. Yield dit juste, "je ne fais rien pour le moment, allez voir s'il n'y a pas une autre tâche qui pourrait avoir besoin du processeur et au passage dites au chien de garde de rester dans sa niche"). Vous avez un scheduler qui se charge de donner la main aux tâches qui s'exécutent sur un coeur.

le code que vous avez posté devrait fonctionner si vous mettez LOW pour l'appui. je l'écrirais comme cela

const uint8_t swMoteurOn = 26;

void moteurOn() {
  Serial.println("Moteur On");
  delay(20);    // Anti-rebond bloquant du pauvre
  while (digitalRead(swMoteurOn) == LOW)  yield(); // on attend le relâchement 
  delay(20);    // Anti-rebond bloquant du pauvre
  Serial.println("Moteur Off");
}

void setup() {
  pinMode(swMoteurOn, INPUT_PULLUP);
  Serial.begin(115200);
}

void loop() {
  if (digitalRead(swMoteurOn) == LOW) moteurOn();
}

la réponse est plus sans doute dans le code que vous n'avez pas posté..

Plus j'avence, plus je recule. Maintenant ça le fait quand je presse le bouton mais aussi quand je le presse pas.

J'ai un ESP32 ou je mais que ce code de test, juste un switch branché dessus.
Au cas ou des restes de Watchdog resterait dans la mémoire, j'ai même aissayé avec le dernier ESP neuf qui me reste de copier coller le code que vous avez fait, c'est la même chose !

Je suis très loin de mettre en doute votre parole, mais il ne fait pas rien, le while() tourne toujours !
Ca serais plus du genre, je te laise faire, si tu as autre-chose à faire!

Comment est branché le switch?
Vous pouvez nous en dire plus sur le circuit ?

Quand vous dites c’est la même chose - c’est à dire ?

un fil GND vers switch, un fil 26 vers switch. USB entre ESP32 et PC.

Avec un ESP neuf, j'ai le même résultat qu'avec un ESP déjà utilisé ("Occasion"). Mais toujours avec le même montage; juste un switch.

J'ai fait le test, c'est juste qu'il y a quelques temps, dans un autre projet, j'ai u une erreur qui m'empaichait de téléverser un programme, et après recherche, il c'est avéré que c'était des erreurs rémanante qui incrustais des watchdogs. La solution étais de reflacher l'ESP en micro-Pyton avec ESPTOOL.py
Ca n'a rien à voir avec ce projet, mais pour confirmer, j'ai essayé avec un ESP que je n'ai encors jamais utilisé et ça change rien.

Vous avez choisi le bon ESP32 comme target ?

Désolé, je ne suis pas sûr de comprendre:
vous voulez dire le choix de la carte dans l'IDE ?

Ci c'est ça, j'ai essayé avec "ESP32 dev Module" et "ESP-WROOM-DA Module". J'ai même essayé avec l'ancien Arduino-IDE version1.8.15 ou le nouveau version 2.1.0
Mais toujours le même résultat !
Ci c'est le port COM, je n'ai qu'un câble USB. par de risque de ce tromper de ce côté là.

Quand au code pour lequel je fais cette recherche, je veux bien le partager, il y a le programme principal dans le sujet cité dans mon premier poste. Mais je pence que le bout de programme test suffit pour représenter le problème.
Mais ci ça peut aider je veux bien le mettre au complet ici.

Ce que je comprend pas c'est qu'avec delay() ça marche, si je regarde la fonction delay() détailler par hbachetti ci-dessous, c'est ce que l'on essaye de faire !

Je ne comprends pas… vous dites que ce code tout seul,


const uint8_t swMoteurOn = 26;

void moteurOn() {
  Serial.println("Moteur On");
  delay(20);    // Anti-rebond bloquant du pauvre
  while (digitalRead(swMoteurOn) == LOW)  yield(); // on attend le relâchement 
  delay(20);    // Anti-rebond bloquant du pauvre
  Serial.println("Moteur Off");
}

void setup() {
  pinMode(swMoteurOn, INPUT_PULLUP);
  Serial.begin(115200);
}

void loop() {
  if (digitalRead(swMoteurOn) == LOW) moteurOn();
}

sur un ESP32 avec un switch dont une patte va à la pin 26 et l’autre à GND (en faisant attention de ne pas créer un court circuit en en prenant des pins diagonalement opposées si c’est un de ces push buttons) avec rien d’autre fait planter votre ESP32? C’est ça ?

Oui que ça !
Si je le fait tourner sur le coeur1 pas de soucis, dès que je le fais tourner sur le coeur0 ça plante.
PS: pour choisir le coeur dans l'IDE: "Outils" >> "Arduino Runs On:" >> "Core 0" ou" Core 1"

Bouton deux pattes:
10PCS-Touch-schalter-8x8x5-Pin-5H-Silica-Gel-Stille-Taste-Zwei-F-e-Pin-Leitf-higen.jpg_50x50.jpg_1

:bulb: ah j’avais oublié cette partie !! Yield() n’est pas la bonne solution dans ce cas.

Vous avez effectivement très peu de marge de manœuvre sur le core0.

Yield permet aux tâches ayant une plus grande priorité de s’exécuter mais la tâche qui s’occupe du watchdog et de la tâche idle a une moindre priorité que la votre dans le code ce qui fait qu’appeler yield ne suffit pas. Il faudrait appeler esp_task_wdt_feed() éventuellement mais en faisant cela vous enregistrez votre tâche arduino comme étant surveillée par le watchdog en plus de la tâche idle. Il faudra donc continuer d’appeler la fonction dans la loop.

L’usage de vTaskDelay() permet à toutes les tâches sous contrôle du watchdog de s’exécuter et donc la tâche idle sera activée. Cette fonction est appelée par delay() et donc c’est pour cela que ça fonctionne sans doute dans ce cas,

Cela dit, En pratique il faudrait faire une petite machine à etat et ne pas bloquer la loop

On dirait qu'il apprend comment il peut m'en merder GRRR!!!

Maintenant, il considère même la loop comme bloquante.
un simple "Hello Word" avec que le câble Usb de branché sur ESP32 et ça RESET

uint32_t oldtime = 0;

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

void loop() {
  if(oldtime + 1000 <= millis()){
    Serial.println("Hello word");
    oldtime = millis();
  }
}

Le prot-série:

07:17:30.916 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
07:17:30.976 -> configsip: 0, SPIWP:0xee
07:17:30.976 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
07:17:30.977 -> mode:DIO, clock div:1
07:17:30.977 -> load:0x3fff0030,len:1344
07:17:30.977 -> load:0x40078000,len:13864
07:17:30.977 -> load:0x40080400,len:3608
07:17:30.977 -> entry 0x400805f0
07:17:32.057 -> Hello word
07:17:33.042 -> Hello word
07:17:34.069 -> Hello word
07:17:35.056 -> Hello word
07:17:36.053 -> Hello word
07:17:36.053 -> E (10077) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
07:17:36.053 -> E (10077) task_wdt:  - IDLE (CPU 0)
07:17:36.053 -> E (10077) task_wdt: Tasks currently running:
07:17:36.053 -> E (10077) task_wdt: CPU 0: loopTask
07:17:36.053 -> E (10077) task_wdt: CPU 1: IDLE
07:17:36.053 -> E (10077) task_wdt: Aborting.
07:17:36.053 -> 
07:17:36.053 -> abort() was called at PC 0x400d7eb9 on core 0
07:17:36.085 -> 
07:17:36.085 -> 
07:17:36.085 -> Backtrace:0x400833d1:0x3ffbe78c |<-CORRUPTED
07:17:36.085 -> 
07:17:36.085 -> 
07:17:36.085 -> 
07:17:36.085 -> 
07:17:36.085 -> ELF file SHA256: 0000000000000000
07:17:36.085 -> 
07:17:36.085 -> Rebooting...
07:17:36.085 -> ets Jun  8 2016 00:22:57
07:17:36.085 -> 
07:17:36.085 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
07:17:36.085 -> configsip: 0, SPIWP:0xee
07:17:36.085 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
07:17:36.085 -> mode:DIO, clock div:1
07:17:36.085 -> load:0x3fff0030,len:1344
07:17:36.085 -> load:0x40078000,len:13864
07:17:36.153 -> load:0x40080400,len:3608
07:17:36.153 -> entry 0x400805f0
07:17:37.210 -> Hello word
07:17:38.191 -> Hello word
07:17:39.221 -> Hello word
07:17:40.178 -> Hello word
07:17:41.200 -> Hello word
07:17:41.200 -> E (10077) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:

:man_mage: Je vais finir par croire au mauvais oeil :dotted_line_face:
Au début c'étais que quand je gardais le bouton enfoncé, après c'est quand je changais pas d'état au bouton (bouton reste à ON ou alors reste à OFF. Et maintenant juste la loop suffis.
Et c'est pas des traces dans l'ESP32, vue que l'ESP neuf me l'a directement fait à ON {while} et à OFF {loop}

c'est quand je changais pas d'état au bouton
Ca me fais pencer et si j'appelais une fonction vide: void VasTuerLeChien(){}
:sleepy: et non, ça marche pas.

vTaskDelay(1); fonctionne, par contre à quoi correspond le "1" ?
Un tick d'horloge ? c'est pas longtemps si c'est ça ?

C’est dans la doc

Parameters:

xTicksToDelay The amount of time, in tick periods, that the calling task should block.


Le conseil que je lis un peu partout c’est que si vous commencez à jouer avec les cœurs alors il faut sauter le pas et programmer directement en utilisant les API de freeRTOS et gérer les tasks vous même

un tick par défaut c’est 1ms

Code test :

void NourrirLeChien() {
   Serial.println("start");
   vTaskDelay(10000); //celle là semble la plus propre, après je sais par à quoi correspond 1click !
   Serial.print("stop");
   Serial.print("\t||\tconfigTICK_RATE_HZ ");
   Serial.println(configTICK_RATE_HZ);
}

Résultat : ~10 seconde pour faire 10’000 click, ce qui correspond à configTICK_RATE_HZ = 1’000

12:17:08.507 -> start 
12:17:18.526 -> stop || configTICK_RATE_HZ 1000 
12:17:18.526 -> start 
12:17:28.493 -> stop || configTICK_RATE_HZ 1000 
12:17:28.527 -> start 
12:17:38.532 -> stop || configTICK_RATE_HZ 1000

=====================================================================

Mais je crois que je tiens quelques chose :
:blush: :blush: :blush:

esp_task_wdt_reset(); Voir ce lien

Code test :

void NourrirLeChien() {
   Serial.println("start");
   for(uint16_t i = 0; i <= 10000; i++){
       esp_err_t esp_task_wdt_reset(void); //Voir: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/wdts.html#_CPPv418esp_task_wdt_resetv
   }
   Serial.print("stop");
   Serial.print("\t||\tconfigTICK_RATE_HZ ");
   Serial.println(configTICK_RATE_HZ);
}

Résultat : moins d’une ms pour l’exécuter 10’000 fois

12:29:16.921 -> start 
12:29:16.921 -> stop || configTICK_RATE_HZ 1000 
12:29:16.953 -> start 
12:29:16.953 -> stop || configTICK_RATE_HZ 1000 
12:29:16.953 -> start 
12:29:16.953 -> stop || configTICK_RATE_HZ 1000

:blush:

Le code de test complet:

const uint8_t swMoteurOn = 26;

void moteurOn() {
  Serial.println("Moteur On");
  delay(20);                                                // Anti-rebond bloquant du pauvre
  while (digitalRead(swMoteurOn) == LOW) NourrirLeChien();  // on attend le relâchement
  delay(20);                                                // Anti-rebond bloquant du pauvre
  Serial.println("Moteur Off");
}

void NourrirLeChien() {
  /*Utilisable pour réinitaliser les watchdogs */
  Serial.println("start");
    //delay(1);     //Mettre un delay d'une millis dans la loop = pas térriblex
    //vTaskDelay(10000);  //celle là semble la plus, mais 1click Correspond à 1 millis
    for(uint16_t i = 0; i <= 10000; i++){
      esp_err_t esp_task_wdt_reset(void);   //Voir: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/wdts.html#_CPPv418esp_task_wdt_resetv
    }
  Serial.print("stop");
  Serial.print("\t||\tconfigTICK_RATE_HZ ");
  Serial.println(configTICK_RATE_HZ);
  /*Ne fonctionne pas pour faire taire le chien */
    //delayMicroseconds(2000);  //J'ai essayé de remplacer delay par delayMicroseconds, ça marche pas
    /* J'ai essayé de piocher dans les vTask au bol, mais ça n'a rein donné */
    //vTaskSwitchContext();     //Ne fait apparament rein.
    //vTaskDelay(vTaskSwitchContext()); //Par désespoir, mais ça compile pas
    //vTaskEndScheduler();    //Très mauvaise idée, la ça bug non-stop
    //vTaskMissedYield();     //Ne fonctionne pas
}

void setup() {
  pinMode(swMoteurOn, INPUT_PULLUP);
  Serial.begin(115200);
}

void loop() {
  if (digitalRead(swMoteurOn) == LOW) moteurOn();
  NourrirLeChien();
}

:cold_sweat:
Je me suis réjoui trop vite, ça continue à bugger.
:sob:

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