2. Couper le Wi-Fi et retrouver les mesures manquantes

Télécharger le projet, les exercices et les corrections (ZIP, v0.2.2)

Leçon 2 Couper le Wi-Fi et retrouver les mesures manquantes

Une interruption sépare les mesures émises par un ESP32 de leur récepteur.
Une reconnexion ne dit pas quelles mesures sont arrivées. Les traces de production et de réception permettent de le vérifier.

La carte publie une valeur par seconde. Coupez sa connexion pendant trente secondes : le récepteur cesse d’afficher des valeurs, puis reprend. À première vue, le système s’est rétabli. Que sont devenues les mesures produites entre les deux ?

Nous allons provoquer cette coupure sur l’ESP32 seulement, puis rapprocher les observations de la carte et du récepteur. Le but est de situer ce que nous savons, sans attribuer toutes les absences au réseau.

Gardez le projet et le poste de la leçon 1. Le broker et le récepteur doivent rester en marche pendant l’essai. N’éteignez pas le routeur et ne désactivez pas le Wi-Fi de l’ordinateur : cela changerait plusieurs éléments à la fois.

Prévoir ce qui devrait se passer

Dans main/main.c, la tâche producer_task fabrique les mesures. Elle n’attend pas le retour de chaque publication MQTT. En mode initial, si le programme sait que la connexion est indisponible, il écrit dropped_offline et abandonne la mesure. Il ne la conserve pas pour plus tard.

La file FreeRTOS de huit éléments sert seulement à transmettre le travail entre deux tâches lorsque la connexion paraît disponible. Le producteur refuse d’y ajouter une mesure s’il se sait hors connexion. Cette file ne constitue donc pas une réserve pour la coupure.

Écrivez votre prévision avant de lancer le test : la production continuera-t-elle ? Le compteur sautera-t-il à la réception ? Les numéros absents arriveront-ils ensuite ? Vous allez vérifier chaque réponse avec les fichiers.

Préparer une coupure reproductible

Ouvrez idf.py menuconfig, puis ParlezVousTech MQTT lab. Utilisez ces réglages :

Réglage Valeur
Keep unsent samples in a bounded RAM buffer (lesson 3) Désactivé
Sample period in milliseconds 1000
Number of samples per run 60
Disconnect Wi-Fi this many seconds after sampling starts 15
Duration of the deliberate Wi-Fi disconnection in seconds 30

Recompilez et flashez depuis le terminal ESP-IDF :

idf.py build
idf.py -p PORT flash

Lancez une nouvelle capture côté ordinateur. Remplacez l’adresse par celle de votre broker :

python tools/capture_mqtt.py --host 192.168.1.10 --out captures/outage-baseline.jsonl

Attendez READY. Ouvrez ensuite le moniteur série, activez son fichier de log avec Ctrl+T puis Ctrl+L, et appuyez sur EN/RESET pour capturer un démarrage complet :

idf.py -p PORT monitor

Quinze secondes après le début de la production, la tâche d’essai annonce OUTAGE_BEGIN et demande la déconnexion du Wi-Fi. Elle empêche temporairement la reconnexion automatique. Trente secondes plus tard, OUTAGE_END annonce une demande de reconnexion. Il faut encore que le Wi-Fi, l’adresse IP et MQTT soient rétablis.

Laissez l’essai atteindre PRODUCTION_DONE, puis attendez dix secondes. Arrêtez le récepteur avec Ctrl+C, puis le moniteur avec Ctrl+]. Notez le fichier série et le boot de l’exécution complète.

Lire la chronologie

Cherchez OUTAGE_BEGIN dans la capture série. Les lignes produced doivent continuer. Repérez une de ces mesures suivie de dropped_offline : le programme indique ici explicitement qu’il l’a abandonnée.

Après OUTAGE_END, cherchez MQTT connected. La durée demandée n’est pas la durée exacte de l’indisponibilité MQTT. Les tâches ne s’exécutent pas toutes au même instant et la reconnexion prend du temps. N’attendez donc pas exactement trente numéros absents sur la carte réelle.

Le tableau suivant décrit un exemple illustratif, pas un relevé matériel :

Observation Interprétation
produced, numéro 20, puis dropped_offline Le programme a créé puis abandonné cette mesure.
produced, numéro 46, puis publish_returned L’appel de publication a signalé un succès local.
received, même carte, même boot, numéro 46 Notre récepteur a effectivement observé cette mesure.

Si un publish_returned reste sans réception correspondante, ces deux traces ne suffisent pas à localiser l’absence. Le message peut manquer après l’appel, le récepteur peut avoir été indisponible, ou la capture peut être incomplète.

Compter sans masquer les doublons

Comparez l’exécution entière, sans la fenêtre de séquences utilisée pour la leçon 1 :

python tools/compare.py --serial SERIAL_FILE --received captures/outage-baseline.jsonl

Remplacez SERIAL_FILE par votre fichier. Dans la session qui correspond à votre boot, relevez produced_unique, matched_unique, missing_at_receiver et duplicate_receptions.

Pour une production entièrement capturée, produced_unique doit être 60. missing_at_receiver contient les numéros dont la création apparaît dans le log série mais qui n’apparaissent pas dans cette capture du récepteur. Comparez-les aux lignes dropped_offline, dropped_busy et publish_failed.

matched_unique compte les identités communes aux deux fichiers. Vingt messages reçus ne prouvent pas que vingt mesures distinctes sont arrivées : un doublon peut cacher une absence si l’on regarde seulement un total. Vérifiez aussi changed_payload_sequences ; retrouver le numéro avec une valeur différente n’est pas un résultat correct.

receiver_interruptions compte les événements receiver_disconnected enregistrés par notre récepteur lors d’une déconnexion signalée comme un échec. Un arrêt normal par Ctrl+C n’ajoute pas cet événement. Repérez son emplacement dans la capture : une interruption pendant l’essai peut expliquer des absences. Ce compteur ne décrit pas la connexion Wi-Fi de l’ESP32 et ne détecte pas tous les arrêts brutaux du programme.

Si received_without_production_log n’est pas vide, vérifiez le début du log série, les resets et les fichiers choisis. Le comparateur ne reconstitue pas une production qu’il n’a pas observée.

Exercice et correction

Pour une même carte et un même boot, supposons que le log contient les productions 1 à 6. Le récepteur contient les numéros suivants, avec des valeurs inchangées :

2, 4, 3, 3, 5

Calculez les mesures absentes, le nombre de réceptions en double et le numéro arrivé pour la première fois après un numéro plus élevé. Expliquez ensuite pourquoi changer seulement le QoS ne récupérerait pas une mesure abandonnée avant l’appel MQTT.

Correction. Les mesures 1 et 6 sont absentes. Le numéro 3 apparaît deux fois, soit une réception supplémentaire. Sa première arrivée suit celle de 4 : il est donc signalé comme arrivée tardive. Les quatre identités reçues sont 2, 3, 4 et 5, pour cinq messages. Le QoS s’applique aux échanges pris en charge par MQTT ; il ne peut pas récupérer une valeur que notre application n’a jamais confiée au client.

Vous pouvez exécuter cet exemple sans carte :

python tools/compare.py --serial examples/synthetic-serial.log --received examples/synthetic-received.jsonl

Ces fichiers sont synthétiques. Ils permettent d’apprendre à interpréter le rapport, pas de valider la connexion de votre ESP32.

Ce que vous devez pouvoir expliquer

Conservez votre prévision et le rapport. Choisissez une mesure manquante et expliquez ce que les traces permettent d’affirmer à son sujet. Pour une autre absence située à la frontière de la coupure, indiquez ce qui reste incertain.

Dans la leçon 3, nous garderons les mesures pendant l’indisponibilité connue. Il faudra aussi décider lesquelles abandonner lorsque la mémoire sera pleine.