De retour après une petite absence je reviens avec un problème surprenant.
Le code posté ci-dessous fonctionne correctement avec une carte ESP32-S2 mini (wemos.cc) à savoir : clignotement de la LED cohérent avec l'état de la connexion, messages sur la console série corrects
Par contre avec une autre carte, le même code (modulo le changement d'IO pour la led) ne fonctionne pas bien :
connexion wifi OK, serveur web fonctionnel
mais...
la led clignote irrégulièrement et très vite
la console se rempli de messages illisibles
D'où cela peut-il provenir ?
J'ai bien sûr testé blick.ino sur la carte en question, RAS
vous voulez dire ESP32-S2 mini versus ESP32-WROOM ?
L’ESP32-S2 est monocœur. Les callbacks WiFi et loop s’exécutent de façon séquentielle sur le même cœur, ce qui masque les accès concurrents aux variables globales. Modifier ledInterval depuis un callback WiFi et l’utiliser dans loop ne pose pas de problème visible dans ce cas.
L’ESP32-WROOM est bicœur. Les callbacks WiFi s’exécutent sur un autre cœur que celui de loop. ledInterval, previousMillis et ledState sont donc accédées et modifiées simultanément sans synchronisation.
Je serai tenté de dire que cela provoque des valeurs incohérentes lues dans loop.
Oui, je précise alors (désolé de manquer parfois de clarté) : l'ESP32-S2 mini fonctionne comme attendu et l'ESP32-WROOM à un clignotement bizarre.
L'explication est (comme toujours) claire et cohérente.
Mais ce que je ne comprend pas c'est que previousMillis et ledState ne sont modifiés que dans le loop() et ledInterval ne devrait pas changer, sauf défaut du wifi, ce qui n'est pas le cas et temps normal (et clairement pas pendant mes tests où le wifi était stable)
Est-ce que les déclarer en volatile suffirait ? (pas le matéril sous la main pour tester tout de suite)
Volatile force la lecture ou écriture en RAM a l’adresse de la variable mais ne garantit pas l’atomicité de l’opération. Il faudrait un mutex / sémaphore.
Une autre piste serait d'interdire l'utilisation du 2nd cœur, mais c'est un peu plus délicat et même dangereux car cela risquerait de tout mettre en vrac en dehors de l'utilisation d'un OS multitâche
NB: Sauf erreur de ma part, je ne vois que la variable globale ledInterval en accès concurent si effectivement accédée depuis les 2 cœurs
Sur ESP32, noInterrupts() désactive uniquement les interruptions au niveau du cœur sur lequel le code Arduino s’exécute, en pratique le plus souvent le core 1. Les interruptions matérielles du second cœur continuent de fonctionner normalement donc vaut mieux prendre l’approche mutex ou atomique si c’est approprié.
L’ajout de volatile est inutile, car std::atomic fournit déjà toutes les garanties de visibilité mémoire et d’ordonnancement. Les opérations de lecture, écriture et read-modify-write sur cet atomic sont sûres entre les deux cœurs.
Cela ne protège pas une séquence logique de plusieurs opérations distinctes, qui nécessiterait alors un mécanisme de synchronisation supplémentaire mais ce n’est pas le cas ici .
Encore plus bizarre que je ne le pensais : j'ai connecté une LED externe au GPIO23 et modifié le code en conséquence.
Avec blick.ino, c'est OK, la LED externe clignote
Avec mon code... la LED externe ne clignote pas mais la LED de la carte reprend son clignotement bizarre.
Autre chose, probablement liée mais inexpliquée pour moi : la console série affiche des caractères (très nombreux) incongrus (vitesse de transmission correcte) :