Bonjour à tous,
Je partage aujourd’hui un projet d’intégration complet d’un spa KL8300 (contrôleur très répandu en France) dans Home Assistant, réalisé entièrement en reverse engineering.
L’objectif était simple : pouvoir contrôler et superviser mon spa depuis Home Assistant, sans modifier le contrôleur d’origine, sans fil supplémentaire visible, et avec un retour d’état en temps réel.
Le résultat : 8 fonctions contrôlables :
- Pompe 1,
- Pompe 2,
- Chauffage,
- Blower, LED,
- Filtration,
- Panneau,
- Température et lecture de la température de l’eau, le tout via une platine ESP32 maison connectée au bus interne du spa.
Bus LCD (lecture passive) :
Le spa pilote un afficheur LCD via le protocole TM1668 SPI (LSB-first, rising edge, STB = signal de framing). En sniffant ce bus, l’ESP32 lit en temps réel l’état de tous les équipements sans intervenir dans la communication.
-
Protocole : SPI LSB-first, rising edge, STB = CS/framing
-
Trame : 64 octets / 24 sous-trames, cycle ~500 ms
-
Décodage : SF20=Pompe1, SF21=Pompe2/Chauffage, SF23=Panneau, SF15=Blower, SF14=LED, SF11/SF12=Température
-
Protection ESP32 : diviseurs 10kΩ/20kΩ (5V→3.33V) + résistances série 330Ω + buffers 74HC125
Bus KEY (injection active) :
Le spa utilise une matrice clavier pour détecter les appuis boutons. En insérant des switches CD4066 en série sur les lignes de la matrice, l’ESP32 simule des appuis pour contrôler les équipements.
-
8 switches CD4066 (2× HCF4066BE) pilotés par 8 transistors 2N2222
-
Logique : GPIO LOW → transistor bloqué → CTRL=+5V → switch CD4066 fermé → commande active
-
Commandes avec timing précis (séquences multi-pulses pour le chauffage, LED intelligente, réglage température)
Matériel :
ESP32-38PinWide (NodeMCU-32S), HLK-PM01, 2× 74HC125 DIP14, 2× CD4066 (HCF4066BE) DIP14, 8× 2N2222 TO-92, PCB custom (Fritzing)
Point critique : les rails +5V et +3V3 ne doivent jamais être reliés. Un court-circuit entre les deux provoque une panne WiFi de l’ESP32 — vécu lors du développement !
Mapping bus KEY :
Firmware et ESPHome :
Un fichier header C++ inclus dans ESPHome, tournant en tâche FreeRTOS sur le core 0 pour ne pas bloquer le WiFi. Il gère :
-
Lecture LCD : décodage complet de la trame TM1668 toutes les 500 ms
-
Décodage température : table de correspondance 7 segments (dizaines SF11 + unités SF12)
-
Injection commandes : système de flags thread-safe entre les lambdas ESPHome et la tâche FreeRTOS
-
LED intelligente : 1 pulse si éteinte (→ animé), 2 pulses si allumée (→ fixe → off)
-
Chauffage : séquence 5 pulses avec timing précis
Côté Home Assistant :
-
switch avec retour d’état réel (vérifie l’état avant d’envoyer)
-
binary sensorpour chaque équipement
-
sensor température + signal WiFi + uptime
-
number slider 1-10 pour Temp+ (1 pulse = +1°C)
Outils utilisés :
- Saleae Logic - capture et décodage du protocole TM1668 (absolument indispensable)
- ESPHome - framework firmware ESP32
- Fritzing - conception du PCB maison
Les erreurs à ne PAS reproduire :
C’est la partie la plus utile du retour d’expérience. J’en ai fait quelques-unes bien mémorables :
1. Court-circuit 5V -3.3V-panne WiFi immédiate
Le circuit utilise deux tensions : +5V pour les switches et +3.3V pour les buffers. Un cavalier de soudure mal placé les a reliés ensemble. Résultat : le WiFi de l’ESP32 ne fonctionnait plus du tout. Pas de message d’erreur, juste… silence. Vérifier l’isolation entre les deux rails avant toute mise sous tension.
2. GPIO12 — la broche piégée de l’ESP32
GPIO12 contrôle la tension du flash au démarrage de l’ESP32. Si elle est HIGH au boot, l’ESP32 refuse de démarrer correctement.
Sur mon premier ESP32, j’ai dû brûler un eFuse pour fixer ça définitivement (opération irréversible via espefuse.py).
Quand cet ESP32 est tombé en panne et que je l’ai remplacé, le nouvel ESP32 est vierge, sans eFuse. La solution propre sur le nouveau : s’assurer que GPIO12 est maintenu LOW au démarrage (diviseur 10k/20k vers GND), ce qui satisfait le strapping sans toucher aux eFuses.
À retenir : ne jamais connecter GPIO12 avec un pull-up externe. Préférer un pull-down ou utiliser un autre GPIO si possible.
3. La résistance de trop sur le signal CLK
Une résistance 4.7kΩ traînait sur le signal CLK (GPIO5) vestige d’un câblage antérieur. Elle chargeait suffisamment le signal pour que le buffer 74HC125 ne le lise plus. Des heures de debug pour trouver ça. Vérifier qu’il n’y a pas de résistance parasite sur les lignes de signal.
4. La broche OE du buffer 74HC125 laissée flottante
Le 74HC125 a une broche « Output Enable » active LOW. Si elle est flottante, le buffer ne laisse rien passer, le signal CLK restait bloqué à HIGH, GPIO5 ne voyait rien. Toujours relier les broches /OE à GND ou à un GPIO de contrôle.
5. CS et STB confondus faux signal capturé
Sur le bus LCD, deux signaux ressemblent à un chip select. J’ai pris le mauvais pendant un moment. La clé : compter les transitions. Le vrai signal de framing fait 238 transitions là où l’autre n’en fait que 4. Analyser les compteurs de transitions dans Saleae avant de configurer le décodeur.
6. GPIO13 et GPIO14 déjà câblés sur le PCB
Quand j’ai voulu ajouter GPIO22 et GPIO23 pour le chauffage et le Temp+, j’ai réalisé que GPIO13 et GPIO14 étaient déjà câblés ailleurs sur ma platine. Impossible de les réutiliser. Solution : fils volants ajoutés à la main sur le PCB. Cartographier tous les GPIOs utilisés AVANT de concevoir le PCB.
7. Alimenter l’ESP32 par USB ET par le circuit en même temps
Pendant les tests, j’avais l’USB branché pour les logs ET la platine alimentée par le HLK-PM01. Les deux alimentations se battent via le régulateur interne de l’ESP32. Une seule source d’alimentation à la fois.
8. Tester l’injection sans valider le passthrough d’abord
La platine est intercalée dans le câble du spa. Si la platine plante, le spa ne répond plus du tout. J’ai toujours validé que les signaux passaient correctement en lecture seule AVANT d’activer l’injection. Tester le passthrough en premier, l’injection en dernier.
Pour les électroniciens curieux :
Le protocole LCD est du SPI TM1668 : LSB-first, rising edge, STB=framing, 64 octets par trame, 24 sous-trames. Le décodage de la température passe par une table de correspondance 7 segments par chiffre.
L’injection KEY utilise deux CD4066 (HCF4066BE ou 74HC4066 — identiques en brochage) pilotés par des 2N2222 en collecteur commun. La logique est inversée : GPIO LOW = commande active.
Photos des connecteurs ou viens s’incérer le montage (1 key - 2 lcd) :
Toutes les infos sont disponibles dans les fichiers partagés sur mon lien Github (code, typon, matrice, gerber, bom, etc.) :
https://github.com/francki85/kl8300-spa
N’étant pas équipé pour pouvoir réaliser des montages double face, celui-ci est en simple face (10cmx15cm) mais tiens dans la boite étanche du panneau de contrôle. Je dois aussi préciser que je ne suis pas compétent dans la réalisation d’un schéma pro mais tout les détails fourni permette a celui qui le désir de réaliser un PCB plus petit tenant sur un double face.
Si d’autres ont un spa KL8300 ou un contrôleur similaire utilisant le TM1668, Le protocole LCD devrait être identique.
Je vous joindrait des photos du montage une fois celui-ci remis au propre sans tout les fils volants ![]()



