[résolu] Reverse engineering protocole thermopompe hayward

Bonjour,

La structure de ton acquisition est semblable à la mienne : Par défaut, un cycle de 16 trames, 14 de 12 bytes et 2 de 9 bytes.
Toutes tes trames se finissent par un silence de 100, 150 ms ou 1s. Les long silence (1s) sont toutes les 4 trames

Pour les trames avec erreur, c'est probablement du au fait que j'ai été trop strict sur les durée.
Par exemple :

data3[32310:32320,]
time level t2 delay type
32310 109504480 0 109505680 1200
32311 109505680 1 109508480 2800
32312 109508480 0 109509680 1200
32313 109509680 1 109512480 2800
32314 109512480 0 109513680 1200
32315 109513680 1 109516480 2800
32316 109516480 0 109517680 1200
32317 109517680 1 109518640 960 1ms
32318 109518640 0 109519640 1000 1ms
32319 109519640 1 109520480 840
32320 109520480 0 109521640 1160

il y a des NA dans la colonne type par ce que 1200us a été jugé trop loin de 1000us ...
Pour résoudre cela, il suffit d'être plus tolérant sur les délais.

A suivre ...
Laurent

Ha oui aussi:

Hier, en regardant le graphique (celui de la capture où je n'ai pas touché au clavier), et en faisant un zoom arrière, je me suis aussi apercu qu'il y avait une trame différente exactement toutes les minutes.

Chose que je n'avais pas remaqué dans le temps ou mes captures faisait seulement quelques dizaine de seconde.
Peut etre qu'il s'agit de la trame qui a des timings differents.

Peut etre que c'est tout simplement lié au l'horloge sur l'ecran. Je n'ai pas pensé a verifier quand arrivait le changement de minute par rapport à ma capture.

A la fin de mon script, j'affichais aussi une la totalité des timings que j'avais ansi que leur occurence

La c'est pour la capture ou je n'ai touché a rien:

Sort by duration // [duration] => occurence
Array
(
    [80] => 1
    [55] => 1
    [6] => 1
    [101] => 1
    [12] => 1
    [19] => 1
    [2002] => 2
    [7] => 2
    [10] => 3
    [2003] => 3
    [4] => 3
    [102] => 29
    [1003] => 100
    [0] => 231
    [2] => 282
    [152] => 307
    [5] => 445
    [9] => 450
    [3] => 24542
    [1] => 59164
)

Sort by occurence // [duration] => occurence
Array
(
    [0] => 231
    [1] => 59164
    [2] => 282
    [3] => 24542
    [4] => 3
    [5] => 445
    [6] => 1
    [7] => 2
    [9] => 450
    [10] => 3
    [12] => 1
    [19] => 1
    [55] => 1
    [80] => 1
    [101] => 1
    [102] => 29
    [152] => 307
    [1003] => 100
    [2002] => 2
    [2003] => 3
)

et là lorsque je suis passé à ON / OFF plusieurs fois

Sort by duration // [duration] => occurence
Array
(
    [53] => 1
    [178] => 1
    [770] => 1
    [119] => 1
    [29] => 1
    [10] => 3
    [2002] => 3
    [2003] => 3
    [102] => 38
    [1003] => 39
    [0] => 44
    [2] => 64
    [152] => 124
    [5] => 212
    [9] => 218
    [3] => 11700
    [1] => 29102
)

Sort by occurence // [duration] => occurence
Array
(
    [0] => 44
    [1] => 29102
    [2] => 64
    [3] => 11700
    [5] => 212
    [9] => 218
    [10] => 3
    [29] => 1
    [53] => 1
    [102] => 38
    [119] => 1
    [152] => 124
    [178] => 1
    [770] => 1
    [1003] => 39
    [2002] => 3
    [2003] => 3
)

Moi je pense qu'il y a de temps en temps des colisions de frame lorsque la carte PC1000 envoit de l'info, et qu'en meme temps, on change la température via le clavier.

Ok, j'ai poursuivis l'étude des captures ce soir.
Je pense avoir fait d'ÉNORMES progrès sur la compréhension du protocole.

Selon moi, il y a deux familles de trames:

Famille A = Trames envoyés par la carte PC1000 (celles qui contiennent les infos de la température actuelle, la température programmé, le mode etc...)

Famille B = Trames envoyés par le module clavier / ecran de la thermopompe.

Specs de A:
Envoyé par groupe de 4.
Chaque trame est séparé par 152ms
Chaque GROUPE de ses 4 trames est séparé par 1s

Specs de B:
Envoyé par groupe de 8 (je crois)
Chaque trame est séparé par 101ms
Une fois le groupe envoyé, il y a une pause de 2 secondes avant que les les groupes de type A reprennent leur cycle.
Même si on ne touche pas au clavier, ce groupe de trame est envoyé exactement 1 fois toutes les minutes (comme je disais un peu plus haut, il s'agit peut etre de l'horloge de l'ecran qui change de minute ? Mais pourquoi envoyé cette info à la carte PC1000...)


Je confirme ma théorie du conflit. Il arrive très souvent que les deux familles de trame soient un peu mélangé.
Quand on change la température au clavier, la majorité du temps, le groupe de trame B est envoyé alors qu'un groupe de trame A est deja commencé.
C'est là que les trames se superposent et qu'on obtient des timings bizarre qui ne veulent plus rien dire.

Sauf que, étant donné que le groupe B est composé de 8 trames (et qu'il est donc plus long que le groupe A qui n'en a que 4), la fin du groupe B est toujours affiché sans superposition (vu que le groupe A est terminé depuis un bout)
De plus, je pense que comme le groupe A est terminé, il est rendu à sa pause de 1 seconde.
Selon moi, cette pause permet à la carte PC1000 "d'écouter" si justement il n'y aurait pas une trame B en train d'envoyer un signal du clavier.
Ça doit être le cas, car les trames A ne repartent pas tout de suite. À la place, il y a une pause de 2 secondes avant qu'elles reprennent.

A cause de la superposition, il y a donc quelques trames qui sont inutilisable par le module clavier le module PC1000.
Dans les 8 trames, il y a donc surement une répétition du data pour être certain que l'info est bien passé.

En sachant tout cela, je vais pouvoir facilement maintenant ignoré toutes les trames superposé.
C'est assez facile a detecter.
Après je pense que le travail va etre beaucoup plus facile! il ne restera plus qu'a decoder les trames de la famille B vu que celle de A sont deja faites, puis tester la théorie avec un petit programme.

Bonne nouvelle, je suis repassé sur mon script pour analyser les trames type B valides et:
Cela confirme encore un peu ce que je disais au dessus car quand le groupe de 8 trames est envoyé, les 8 trames sont toutes identiques.
Ça doit bien pour être sur qu'une des 8 sera bien lu malgré les superpositions qui rendront les autres invalides.

Une trame de type B = 12 bytes.

Super.
De mon côté, rien de nouveau. Je suis en train de faire un script coté serveur (avec node-red que je découvre ...) pour mettre en base de donnée les trames de la PAC (que ce soit les trames de la carte mère (je n'ai pas la même que la tienne) ou les trames du clavier).

Peux donner ton modèle de carte contrôleur et ton modèle de thermopompe?

Node-red c'est super :slight_smile:

Ma PAC est une Hayward EcoPac Powerline
La carte mère, de mémoire est une PIC1001

Je pense que on est sur les mêmes protocoles.

Sinon, j'ai commencé a récupérer toutes les trames (mêmes celles qui semble avoir des erreurs) sur mon serveur et à les stocker pour les analyser.

J'ai l'impression que le début de la trame contient une information "d'adresse" (quelle registre ?) et une information de fonction (?)
(un peu comme le protocole modbus)

de mon côté (sur 12h sans interventions) :
Les 4 octets / 2 bytes les plus fréquent sont d1b1 (22% des trames)
Les entêtes les plus fréquentes sont

signif(table(data$entete)[freq_adress] / nrow(data)*100,3)

12 304e 3535 81b1 82b1 83b1 84b1 85b1 86b1 d1b1 d21b d2b1
0.0379 6.2200 6.1600 10.7000 10.9000 5.6800 5.4500 5.3600 5.6700 22.1000 5.3100 11.3000
dd1d dd1e
1.3700 3.7600

a noter la suite d'entêtes : 81b1, 82b1, 83b1, 84b1, 85b1

A bientôt

Je n'ai pas encore eu le temps de regarder pour les collisions.
J'ai le même constat que toi sur les checkSum : Si on les calcule classiquement ( somme des n-1 bytes vs dernier bytes) on obtient un écart de 1, 2 ou 3 la plus part du temps.
Cependant on a un pattern particulier qui est peut être en relation avec tes collisions ?

Ci-joint deux images, en ordonnée la valeur du checksum, et abscisse, l'indice d'acquisition de la trame.
La première est pour les 1000 premières trames, la deuxième pour les 100 premières.

Le motif est trop régulier pour que ce soit une erreur de transmission, mais plutôt que on ne connait pas le protocol du calcul de checksum de Hayward ...

A suivre

checkSum100.png

checkSum1000.png

Ha c'est bien que tu trouves la meme chose que moi pour le checksum. Je n'avais pas approfondie la chose.
Par contre tu me perds quand tu expliques le prototcole qui semble etre du modbus (?) je ne connais pas du tout.

De mon coté, j'ai continué mon script.
J'ai trouver ou setter la température, le mode et le power on/off sur les trames de type B.
Mon but étant de verifier sur le calcul du checksum des trames B est le meme que les A.

En travaillant sur cela, j'ai trouvé quelque chose de bizarre:

Sur les trames A,
0 binaire = 3ms
1 binaire = 1ms

Mais sur les trames B c'est l'inverse:
0 binaire = 1ms
1 binaire = 3ms

Je continue

Pour les checkSum, en fait, c'est un peu normal, car je me suis basé sur ton code. Ca montrait que mes trames étaient proches des tiennes.

Par contre, je viens de modifier ton code de CheckSum et maintenant j'obtiens assez souvent des 0, ce qui est sympathique.
J'ai fait modulo 256 et pas modulo 255
(sous R : sum(bt[1:(n-1)])%% 256 - bt[n] : bt la trame en byte, et n le nombre de byte.

Si dessous les 100 premiere trames de mon acquisition.

J'ai par contre des checksum non null pour certaines trames. C'est peut être les trames "B" dont tu parles.

Sinon le protocole modbus, c'est un protocole assez classique, dont la trame va contenir 4 parties :

Une adresse, (sur un byte )
Une fonction, (sur un byte )
Les informations a transmettre sur x bytes
Un checkSum (sur un byte)

Si on était sur une telle structure de trame, une entête de la forme "d1b1" pourrait vouloir dire
appliquer la fonction b1 (et on ne sait pas ce que c'est) sur le registre d1( et on ne sait pas nom plus), avec les données qui suivent (byte de 3 à n-1)

A suivre ...
Best regards
Laurent

data[1:100,]
t mess entete checkSum
1 1.566661e+12 d1b100000f000078607917f9 d1b1 0
2 1.566661e+12 83b146230a23234c825a7c91 83b1 0
3 1.566661e+12 d21b1d2d020da0eafe d21b -46
4 1.566661e+12 dd1e1e16121e000082 dd1e -35
5 1.566661e+12 d1b1050000000078607917ef d1b1 0
6 1.566661e+12 d2b1115e796978000000004c d2b1 0
7 1.566661e+12 82b11638575a103c020164e5 82b1 0
8 1.566661e+12 81b11b7277723d3d3d3d3cd8 81b1 0
9 1.566661e+12 d1b100000f000078607917f9 d1b1 0
10 1.566661e+12 85b142064a1600080000cdb3 85b1 0
11 1.566661e+12 d1b1050000000078607917ef d1b1 0
12 1.566661e+12 d2b1115e796978000000004c d2b1 0
13 1.566661e+12 82b11638575a103c020164e5 82b1 0
14 1.566661e+12 83b146230a23234c825a7c91 83b1 0
15 1.566661e+12 d21b1d2d020da0eafe d21b -46
16 1.566661e+12 dd1e1e16121e000082 dd1e -35
17 1.566661e+12 d1b1050000000078607917ef d1b1 0
18 1.566661e+12 d2b1115e796978000000004c d2b1 0
19 1.566661e+12 82b11638575a103c020164e5 82b1 0
20 1.566661e+12 81b11b7277723d3d3d3d3cd8 81b1 0
21 1.566661e+12 d1b100000f000078607917f9 d1b1 0
22 1.566661e+12 86b100000000000000000037 86b1 0
23 1.566661e+12 84b18c5a505064780006009d 84b1 0
24 1.566661e+12 85b142064a1600080000cdb3 85b1 0
25 1.566661e+12 d1b1050000000078607917ef d1b1 0
26 1.566661e+12 d2b1115e796978000000004c d2b1 0
27 1.566661e+12 82b11638575a103c020164e5 82b1 0
28 1.566661e+12 81b11b7277723d3d3d3d3cd8 81b1 0
29 1.566661e+12 d1b100000f000078607917f9 d1b1 0
30 1.566661e+12 83b146230a23234c825a7c91 83b1 0
31 1.566661e+12 d21b1d2d020da0eafe d21b -46
32 1.566661e+12 dd1e1e16121e000082 dd1e -35
33 1.566661e+12 d1b1050000000078607917ef d1b1 0
34 1.566661e+12 d2b1115e796978000000004c d2b1 0
35 1.566661e+12 82b11638575a103c020164e5 82b1 0
36 1.566661e+12 81b11b7277723d3d3d3d3cd8 81b1 0
37 1.566661e+12 d1b100000f000078607917f9 d1b1 0
38 1.566661e+12 86b100000000000000000037 86b1 0
39 1.566661e+12 84b18c5a505064780006009d 84b1 0
40 1.566661e+12 85b142064a1600080000cdb3 85b1 0
41 1.566661e+12 d1b1050000000078607917ef d1b1 0
42 1.566661e+12 d2b1115e796978000000004c d2b1 0
43 1.566661e+12 82b11638575a103c020164e5 82b1 0
44 1.566661e+12 81b11b7277723d3d3d3d3cd8 81b1 0
45 1.566661e+12 d1b100000f000078607917f9 d1b1 0
46 1.566661e+12 83b146230a23234c825a7c91 83b1 0
47 1.566661e+12 353535353535353535353551 3535 -10
48 1.566661e+12 304effffa7fdfdeed5ffffe8 304e -10
49 1.566661e+12 353535353535353535353551 3535 -10
50 1.566661e+12 304effffa7fdfdeed5ffffe8 304e -10
51 1.566661e+12 353535353535353535353551 3535 -10
52 1.566661e+12 304effffa7fdfdeed5ffffe8 304e -10
53 1.566661e+12 353535353535353535353551 3535 -10
54 1.566661e+12 304effffa7fdfdeed5ffffe8 304e -10
55 1.566661e+12 353535353535353535353551 3535 -10
56 1.566661e+12 304effffa7fdfdeed5ffffe8 304e -10
57 1.566661e+12 353535353535353535353551 3535 -10
58 1.566661e+12 304effffa7fdfdeed5ffffe8 304e -10
59 1.566661e+12 d21b1d2d020da0eafe d21b -46
60 1.566661e+12 dd1e1e16121e000082 dd1e -35
61 1.566661e+12 d1b1050000000078607917ef d1b1 0
62 1.566661e+12 d2b1115e796978000000004c d2b1 0
63 1.566661e+12 82b11638575a103c020164e5 82b1 0
64 1.566661e+12 81b11b7277723d3d3d3d3cd8 81b1 0
65 1.566661e+12 d1b100000f000078607917f9 d1b1 0
66 1.566661e+12 86b100000000000000000037 86b1 0
67 1.566661e+12 84b18c5a505064780006009d 84b1 0
68 1.566661e+12 85b142064a1600080000cdb3 85b1 0
69 1.566661e+12 d1b1050000000078607917ef d1b1 0
70 1.566661e+12 d2b1115e796978000000004c d2b1 0
71 1.566661e+12 82b11638575a103c020164e5 82b1 0
72 1.566661e+12 81b11b7277723d3d3d3d3cd8 81b1 0
73 1.566661e+12 d1b100000f000078607917f9 d1b1 0
74 1.566661e+12 83b146230a23234c825a7c91 83b1 0
75 1.566661e+12 d21b1d2d020da0eafe d21b -46
76 1.566661e+12 dd1e1e16121e000082 dd1e -35
77 1.566661e+12 d1b1050000000078607917ef d1b1 0
78 1.566661e+12 d2b1115e796978000000004c d2b1 0
79 1.566661e+12 82b11638575a103c020164e5 82b1 0
80 1.566661e+12 81b11b7277723d3d3d3d3cd8 81b1 0
81 1.566661e+12 d1b100000f000078607917f9 d1b1 0
82 1.566661e+12 86b100000000000000000037 86b1 0
83 1.566661e+12 84b18c5a505064780006009d 84b1 0
84 1.566661e+12 85b142064a1600080000cdb3 85b1 0
85 1.566661e+12 d1b1050000000078607917ef d1b1 0
86 1.566661e+12 d2b1115e796978000000004c d2b1 0
87 1.566661e+12 82b11638575a103c020164e5 82b1 0
88 1.566661e+12 81b11b7277723d3d3d3d3cd8 81b1 0
89 1.566661e+12 d1b100000f000078607917f9 d1b1 0
90 1.566661e+12 83b146230a23234c825a7c91 83b1 0
91 1.566661e+12 d21b1d2d020da0eafe d21b -46
92 1.566661e+12 dd1e1e16121e000082 dd1e -35
93 1.566661e+12 d1b1050000000078607917ef d1b1 0
94 1.566661e+12 d2b1115e796978000000004c d2b1 0
95 1.566661e+12 82b11638575a103c020164e5 82b1 0
96 1.566661e+12 81b11b7277723d3d3d3d3cd8 81b1 0
97 1.566661e+12 d1b100000f000078607917f9 d1b1 0
98 1.566661e+12 86b100000000000000000037 86b1 0
99 1.566661e+12 84b18c5a505064780006009d 84b1 0
100 1.566661e+12 85b142064a1600080000cdb3 85b1 0

Juste une précision : Avec le checkSum calculé avec un modulo 256, il y a 77% des trames qui ont le checksum à 0.

Hoo, interessant. Merci pour l'info.

De mon coté je viens de finir mes tests, et le calcul du checksum des trames B utilise le même que celui des trames A.
J'ai juste testé 4 ou 5 frames, ca m'a donné 0 à chaque fois. A voir pour le module 256 vs 255 pour les trames B également.

J'ai donc toutes les pieces du puzzle dans les mains.

Je vais passer a un petit prototype arduino

pour l'inversion : Idem, de mon côté (mais basé sur les entêtes et pas les délais entre trame) :

En inversant les trames (complement à 0xff ), quasiment toutes les trames qui n'étaient pas ok en checksum deviennent ok en checksum.

En particulier, il y a les trames qui commencent par 0x304e en non inversée et 0xcfb1 en inversé, on a l'horloge / date. sur les bytes de 2 à 9 (exemple de trame : cfb10000580202113100001e : 00005802021131 semble être un compteur de temps.

J'ai deux autres entêtes / trames qui, inversée, donnent un checksum nul :
(trame origine, trame inversée, checksum trame inversée) :

353535353535353535353551 cacacacacacacacacacacaae 0
82fffffefe7effff 7d00000101810000 0

Bon, j'arrête pour la journée, mais voici le debut du programme arduino qui ne fait pour le moment, seulement générer la trame dans une variable.

sketch arduino

Bonjour à tous,

par curiosité j'ai extrait les mots (suite de caractères ASCII (>=32 && <127)) des trames du post de plode #19 :

0N
Z#L
xN
yN
Pd
`rx====
Btnn
IVZ
xqt
xrt
ZPPdx

Je ne sais pas ci cela vous sera utile.

Si vous voulez je poste les 422 lignes de la représentation ASCII des trames.

Bonjour à tous.

Merci @supercc
Effectivement la conversion en ascii des trames peut permettre
de décoder les trames, et c'est une bonne idée.
Surtout que le décodage que tu montre semble être porteur de sens.
Donc oui, il faut regarder ce que donne les trames en ascii.

Sinon j'ai avancé sur les checksum pour les trames issuent de ma PAC
(il peut y avoir des règles différentes pour d'autres versions de PAC Hayward ?)

J'ai 3 types de trames.

Les trames courtes de 9 bytes.
Dans ce cas, le CS est la somme des 7 bytes, du deuxième au 8ème, modulo 256 qui doit être égale au dernier et 9eme byte. Le premier byte ne participe pas au CS (ce qui est étonnant ...)

Les trames longues de 12 bytes, décodée avec la règle bit 1 si durée de HIGH = 1ms, 0 si 3ms.
Dans ce cas, le CS est la somme des 11 bits, incluant le 1er, modulo 256 qui doit être égale au dernier byte

Les trames longues de 12 bytes "inverses", ( bit 1 si 3ms, 0 si 1ms). On les obtiens en calculant
le complément à 255 (ie trame = 255-trame*. Le CS se calcul comme le précédant.*
Avec cette règle de checksum, quasiment toutes mes trames ( > 99,9%) sont ok. C'est rassurant.
Du coup, pour la conversion en ascii, il faut inverser certaines trames avant de faire la conversion.
A suivre...

Du coup, pour la conversion en ascii, il faut inverser certaines trames avant de faire la conversion.

Je n'ai pas trop compris mais ce n'est pas grave, t'embête pas à m'expliquer, reste concentré, je ne compte pas rentrer dans les subtilités du protocole :wink:

Voici le script shell utilisé : il accepte en argument le nom du ficher contenant les trames au format de ton post #19. (il commence par éliminer les lignes contenant "not a correct trame" et utilise 2 fichiers temporaires : /tmp/test-data.txt et /tmp/test-out.txt).

#!/bin/bash

toAscii() {
	local l
	while read l; do 
		set - $l
		printf "      " 
		for i; do 
			d=$((16#$i))
			if [ $d -ge 32 -a $d -lt 127 ]; then
				printf "%b" \\x$i
			else 
				printf " "
			fi
		done
		echo
	done
}

convert() {
	local l
	while read l; do 
		echo $l | sed -re 's/(..)/\1 /g'
	done | toAscii
}

# Argument ou entrée standard ?

IN=""
[ -n "$1" ] && IN=$1;

cat $IN | grep -v "not a correct trame" > /tmp/test-data.txt

cat /tmp/test-data.txt | sed -re 's/^.* "(.*)"/\1/' | convert > /tmp/test-out.txt

paste -d '\n' /tmp/test-data.txt /tmp/test-out.txt 

echo
echo Words :

cat /tmp/test-out.txt | grep -v '\[' | sed -re 's/\ /\r/g' | sort -u

Merci pour le décodage en ascii.
J'en apprend tous les jours!

Aussi, je comprend maintenant le modulo 256. Ca explique pourquoi avec mon 255, je devais rajouter -3 et que ca ne fonctionnait pas tout le temps :slight_smile:

Je pense pouvoir essayer mon prototype aujourd'hui si tout va bien. Je vous donne des nouvelles.

Si joint la fonction R (a faire en C pour l'arduino) qui test les 3 cas de trames pour le checkSum
(nb, en R, les tableaux commencent à l'indice 1. Le langage est vectoriel, donc sum(bt[1:n] fait la somme des bytes de 1 à n.
%% est l'opérateur modulo, <- l'affectation.

Renvois True si le checksum est correct.

checkSumOk <- function(bt)
{
n <- length(bt)
if( n == 12) { # trame longue
cs <- sum(bt[1:(n-1)])%% 256 - bt[n];
if(bt[1] > 0x40) # les trames "directes" semblent commencer par un byte qui est supérieur à 0x40,
return(cs == 0)
else # les trames inverses.. Attention, la séparation trame directe et trame inverse basée sur 0x40 est peut être fausse.
return( (cs + 10) %% 256 == 0)

en bit inverse, on ajoute 11 fois 255 et on retranche 1 fois 255,

soit un bilan de (11-1)*-1 en modulo 256

c'est à dire un cs de -10 par rapport au calcul du cs direct.

}
if( n == 9) { # trame courte
cs <- sum(bt[2:(n-1)])%% 256 - bt[n]
return(cs == 0)
}
return(F)
}

Avec cette fonction, j'ai 99,95% des trames qui sont ok.
Les trames de 12 bytes directes seraient envoyées par la carte mere de la PAC ?
Les trames de 12 bytes indirectes seraient envoyées par la carte contrôleur (le clavier de contrôle) ?
Les trames de 9 bytes sont envoyées par ???
Pour savoir, il faudrait déconnecter le bus "Net" et regarder les trames ?

A bientôt ... et dans l'attente du programme de atlas2003

Bon, je n'ai pas terminé mais le bilan est quand même positif.

J'ai terminé toute la partie qui génère la trame de commande (les trames B)
Je n'ai pas pu la tester la sur la thermopompe directement mais j'ai pu la recapturer avec mon analyseur logic, puis la repasser la capture dans le même script.

Résultat des courses: j'ai exactement les même résultats entre les vrai captures et ma version qui génère la trame à partir de commande MQTT.

Par contre c'est instable, car je me rend compte que j'ai du reset du watchdog de manière aléatoire du fait qu'il n'y a pas assez de yield dans mon programme et que j'utilise delayMicrosecond.

Bref, ca avance :slight_smile: