OpenProfalux
Piloter mes volets roulants Profalux 868 MHz depuis Home Assistant sans la clé constructeur, en clonant une télécommande existante avec un ESP32 et un module radio CC1101. La leçon du projet - j'ai chassé une clé de chiffrement pendant des jours avant de découvrir que le moteur accepte tout simplement qu'on rejoue une trame captée.
- Contexte
- Le vrai nœud du projet : KeeLoq, et pourquoi je m’en fiche
- Identification hardware
- Câblage pas à pas
- Comment est construite une trame
- Le firmware
- Apprendre une télécommande
- Piloter, et le modèle « appui + stop »
- Suivre la vraie télécommande
- Home Assistant
- Sauvegarde et mises à jour
- Pour les curieux : et si on voulait quand même la clé ?
- La suite
Contexte
J’ai cinq volets roulants Profalux, pilotés par des télécommandes murales et une télécommande multi-canal « Noé ». Ils marchent très bien, mais ils ne parlent qu’à leurs propres télécommandes 868 MHz. Aucune passerelle officielle vers Home Assistant, aucun scénario du genre « ferme tout au coucher du soleil ». Je voulais les intégrer à ma domotique sans changer les moteurs.

Trois chemins existaient :
- Câbler un ESP en filaire derrière chaque interrupteur (=le projet l0ad/profalux2Esphome fait ça). Propre, mais il faut tirer des fils dans chaque coffre de volet, et ça suppose un interrupteur filaire accessible. Chez moi c’est du tout-radio.
- Acheter une passerelle DEVMEL AirSend qui sait officiellement cloner du Profalux. Ça marche, mais c’est une boîte propriétaire à ~150 € qui passe par son propre backend.
- Refaire une télécommande radio avec un ESP32 et un module CC1101. Zéro fil à tirer, ~15 € de matériel, et tout reste local. C’est ce chemin que j’ai pris.

Le vrai nœud du projet : KeeLoq, et pourquoi je m’en fiche
Les télécommandes Profalux utilisent KeeLoq (=le rolling code de Microchip, celui de la plupart des télécommandes de garage). Le principe : à chaque appui, un compteur s’incrémente, il est chiffré avec une clé de chiffrement dérivée d’une clé constructeur, et le résultat (le « hopping code », 32 bits qui changent à chaque fois) part sur l’air. Le récepteur, qui connaît la clé, déchiffre, vérifie que le compteur avance, et bouge le volet. Rejouer une vieille trame est censé échouer, puisque son compteur est « déjà passé ».
J’ai donc fait ce que fait tout le monde : chasser la clé constructeur. Analyse du firmware d’une passerelle, tables de clés, tentatives de déchiffrement sur des trames réelles capturées, lectures sur l’attaque slide/meet-in-the-middle de KeeLoq… Des jours. Rien de exploitable côté hobby - récupérer cette clé demande soit une fuite, soit une attaque physique par canal auxiliaire à plusieurs centaines d’euros.
Puis j’ai testé la chose la plus bête au banc, celle que j’aurais dû tester en premier :
rejouer une trame capturée. J’ai capté une trame « montée » de ma vraie télécommande, je l’ai réémise telle quelle avec le CC1101… et le volet est monté. Mieux : ça marche encore après avoir réutilisé la vraie télécommande entre-temps.
Autrement dit, le moteur Profalux n’applique pas la contrainte anti-rejeu du compteur. Une trame captée reste valable. Toute la saga cryptographique était sans objet pour le pilotage : je n’ai besoin d’aucune clé. Le firmware clone une télécommande par simple capture + rejeu.
C’est le genre de retournement qui fait râler sur le moment (=tout ce temps sur la crypto) et sourire après : le tell-tale, c’est que la solution la plus simple mérite d’être testée avant la plus savante.
Identification hardware
Rien à démonter ici, contrairement à un module à reverser : on construit l’émetteur. Deux composants.
- Un ESP32. N’importe lequel. J’utilise un M5Stack ATOM Lite (=ESP32-PICO en boîtier fini, ~11 €) parce que tout son header du bas est accessible aux fils dupont, sans souder.
- Un module CC1101 868 MHz. C’est l’émetteur-récepteur radio sub-GHz. Il parle en SPI à l’ESP32. J’ai écarté l’EBYTE E07-900M10S (=trop de CMS fin à souder à la main) pour un petit module vert classique. Piège de ces modules : les pastilles sont au pas de 2 mm (=pas le standard 2.54 mm) et même pas régulières (=la rangée côté antenne est à un autre pas). Le header ou le breadboard standard 2.54 ne rentre donc pas. Il existe des pin headers 2 mm (=plus rares), mais j’ai soudé les fils directement.
Un piège dès l’achat : il existe des CC1101 en 433 MHz et en 868 MHz, et les deux se ressemblent comme deux gouttes d’eau. Profalux émet autour de 868, donc prendre la bonne bande : un module 433 ne captera rien. Le marquage de bande est souvent minuscule, d’où la photo ci-dessous du module exact que j’utilise.

Petite confession en passant : je ne suis pas le roi de la soudure, et mes pâtés d’étain sur les pastilles du CC1101 sont franchement dégueulasses. Une bonne partie de l’excuse tient au pas de 2 mm non standard du module (=voir plus haut) : le 2.54 classique ne rentre pas, et faute d’avoir un header 2 mm sous la main, j’ai étamé les fils un par un directement sur les pastilles, à la main. Ça finit rarement joli, mais ça conduit, et c’est tout ce qu’on demande à une soudure.
Câblage pas à pas
Le CC1101 se relie à l’ESP32 par six fils : les quatre du bus SPI (MISO, MOSI, SCK, CS), plus GDO0 (=la broche qui sort la donnée radio, indispensable), et l’alimentation.
⚠️ Le CC1101 est un composant 3.3 V. Jamais 5 V. L’alimenter en 5 V le détruit. Sur l’ESP32 on prend donc le rail 3V3, pas le 5V/VBUS.
Sur le M5Stack ATOM Lite, tous les signaux sont sur le header du bas, ce qui rend le
câblage sans soudure. Voici le mapping que le firmware attend (=cible m5stack), avec un
code couleur suggéré pour ne pas s’emmêler :
| Signal CC1101 | Broche ATOM Lite | Couleur du fil |
|---|---|---|
MISO | G33 | 🟢 |
MOSI | G23 | 🟡 |
SCK | G19 | 🔵 |
CS (=CSN) | G22 | ⚪ |
GDO0 | G25 | 🟠 |
GDO2 (=optionnel) | G21 | 🟣 |
3V3 | 3V3 | 🔴 |
GND | GND | ⚫ |
Si tu utilises un ESP32 DevKit classique à la place (=cible external), le firmware
attend le bus VSPI : MISO 19, MOSI 23, SCK 18, CS 5, GDO0 4, GDO2 2.
Piège classique : sur beaucoup de breakouts CC1101, GDO0 n’est pas aligné avec
la sérigraphie évidente. C’est la broche data - si rien ne se capture, c’est le premier
fil à re-vérifier. GDO2 est optionnel (=debug), on peut le laisser non branché au début.
Pour l’antenne, un bout de fil rigide de 8.6 cm (=quart d’onde à 868 MHz) soudé sur la pastille antenne suffit pour tester sur le bureau. Pour couvrir la maison, une vraie antenne 868 sur connecteur améliore beaucoup la portée.
Comment est construite une trame
Une trame Profalux, c’est du KeeLoq HCS301 pur. Au SDR, ma porteuse est à 868.425 MHz, modulation OOK (=on/off keying : la porteuse est présente ou absente), avec un élément de base T_E d’environ 455 µs. Sur l’air, dans l’ordre :
- Un préambule : une vingtaine d’alternances courtes (=~455 µs chacune) qui réveillent le récepteur et calent son horloge.
- Un en-tête : un long silence d’environ 4.4 ms qui marque le début des données.
- Le mot de code : 66 bits, chaque bit codé par la durée du niveau haut/bas (=période de bit ~1365 µs, soit 3·T_E).
Les 66 bits se découpent ainsi :
| Champ | Bits | Rôle |
|---|---|---|
| Hopping code | 32 | la partie chiffrée, change à chaque appui |
| Numéro de série | 28 | identifiant de la télécommande, en clair |
| Bouton | 4 | descente 0x01, stop 0x02, montée 0x04 |
| État | 2 | batterie faible (VLOW) + répétition (RPT) |
La seule partie chiffrée, c’est le hopping code (=32 bits). À l’intérieur, une fois déchiffré avec la clé, on trouve : un compteur de 16 bits qui s’incrémente à chaque appui, une partie du numéro de série (=10 bits, la « discrimination »), le code bouton et 2 bits de dépassement. C’est ce compteur chiffré qui fait le « rolling » : impossible à prédire sans la clé. Le reste de la trame (=série, bouton, état) est en clair.
Détail utile pour la position : le bit RPT distingue un maintien. Quand tu gardes le doigt appuyé, la télécommande répète la même trame (=même hopping code, compteur figé, RPT à 1). Le compteur ne s’incrémente qu’au relâchement suivi d’un nouvel appui.
Rien de tout ça n’est nécessaire pour utiliser le firmware (=il capte et rejoue les bits tels quels), mais c’est utile pour comprendre ce qui passe et pour la section suivante.
Le firmware
Le code est sur github.com/Shad107/OpenProfalux,
dossier firmware/. C’est de l’ESP-IDF (=le SDK natif Espressif, pas Arduino ni ESPHome).
Compiler (=via Docker, sans rien installer localement) :
docker run --rm -e IDF_TARGET=esp32 -v "$PWD":/project/firmware -w /project/firmware \
espressif/idf:v5.2.2 bash -c "idf.py set-target esp32 && idf.py build"
Flasher + moniteur série :
idf.py -p /dev/ttyUSB0 flash monitor
Premier démarrage. Le module n’a pas encore de Wi-Fi configuré, donc il ouvre son propre point d’accès :
- Un réseau Wi-Fi
OpenProfalux_XXXXapparaît (=XXXX= fin de la MAC), ouvert (=pas de mot de passe, on se connecte directement). - S’y connecter, ouvrir
http://192.168.4.1, onglet Système : nom de l’appareil, SSID de la box (=laisse le mot de passe vide si ton réseau est ouvert, par exemple un SSID en authentification par MAC), adresse du broker MQTT (=un bouton « Détecter le broker » cherche Mosquitto en mDNS et remplit le champ tout seul). - Enregistrer + redémarrer. Le module rejoint la box en Wi-Fi et se connecte à MQTT.

Apprendre une télécommande
C’est le cœur du clone. Onglet Apprentissage de l’UI :
- On choisit l’action à enregistrer (=montée, stop, ou descente).
- On clique Écouter, puis on appuie une fois sur le bouton correspondant de la vraie télécommande physique.
- La trame captée s’affiche (=numéro de série, bouton, force du signal). On donne un nom à la télécommande (« Murale chambre »), et on affecte la trame au volet.
On répète pour les trois actions. Un volet peut avoir plusieurs télécommandes (=la murale et la Noé multi-canal) : il suffit de capturer chacune, le firmware mémorise tous les numéros de série qui pilotent ce volet.
Piloter, et le modèle « appui + stop »
Une subtilité Profalux qui change la logique de position : ce n’est pas un maintien. On tape montée, le moteur part tout seul vers le haut, et on tape stop quand on veut qu’il s’arrête. La position d’un volet à mi-course, c’est donc le temps entre l’appui montée et l’appui stop.
Le firmware reproduit exactement ça :
- Les boutons montée / stop / descente rejouent la rafale captée correspondante.
- Un curseur de position (=0 à 100 %) demande une position : le firmware connaît le temps d’une course complète (=qu’on a chronométré dans l’onglet Calibration), lance le moteur et envoie stop tout seul au bon moment.
Comme il n’y a aucun retour du moteur (=on émet, on n’écoute pas sa position réelle), le pourcentage est une estimation par le temps. Elle dérive un peu et se recale automatiquement dès que le volet va à fond (0 % ou 100 %). Pour un volet, c’est largement suffisant au quotidien.
Suivre la vraie télécommande
Si quelqu’un utilise la télécommande murale physique, le firmware écoute aussi et met à jour la position dans Home Assistant : il voit passer la trame montée (=le volet part en haut), et la trame stop (=il fige la position estimée là où on s’est arrêté). Il faut pour ça que la télécommande ait été apprise (=son numéro de série est connu du volet).
Home Assistant
Une fois le MQTT configuré, chaque volet appris apparaît tout seul dans Home Assistant comme une entité cover (=via MQTT discovery) : ouvrir, fermer, stop, et une position en pourcentage. Pas de YAML à écrire côté HA.
Sauvegarde et mises à jour
Deux filets de sécurité que je tenais à avoir :
- Sauvegarde : l’onglet Système exporte un fichier JSON avec les noms des télécommandes, les trames de référence et la calibration. Si l’ESP32 est réinitialisé ou remplacé, on restaure tout en un clic. Vu que le pilotage repose entièrement sur ces trames captées, c’est la chose à ne pas perdre.
- OTA : mise à jour du firmware par le navigateur (=upload du
.bin), ou par une URL, avec retour arrière automatique si le nouveau firmware ne démarre pas.
Pour les curieux : et si on voulait quand même la clé ?
Le rejeu suffit largement pour piloter, mais la question crypto reste amusante. Deux vraies pistes existent pour récupérer la clé de chiffrement d’une télécommande, si un jour l’envie prend.
1. L’attaque par canal auxiliaire (DPA / CPA). À chaque trame reçue, le récepteur KeeLoq déchiffre. Pendant ce calcul, sa consommation électrique fuit de l’information sur la clé. En mesurant finement cette consommation pendant le déchiffrement, puis en corrélant sur toutes les hypothèses de sous-clé, on remonte la clé complète en quelques centaines de traces (=Differential / Correlation Power Analysis). Le matériel de référence pour ça est un ChipWhisperer (le Husky, ou le Nano plus abordable pour débuter). C’est la méthode « propre » et fiable, mais elle demande un accès physique au circuit à sonder.

2. L’attaque slide / meet-in-the-middle (=Indesteege et al., 2008). Purement mathématique, sur les trames radio, sans toucher au matériel. Elle exploite la structure de KeeLoq (=528 tours d’une fonction très simple) mais réclame beaucoup de couples à clair connu, de l’ordre de 2^16 trames d’une même télécommande, pour une complexité finale autour de 2^44.5. Faisable sur un bon GPU, mais il faut d’abord collecter énormément de hopping codes distincts.
Et c’est exactement pourquoi le firmware a une option « capturer toutes les trames ». Quand on l’active, chaque trame distincte reçue est publiée en MQTT et dédupliquée par numéro de série. Sur des semaines, ça accumule tranquillement le jeu de données de hopping codes qui alimenterait une attaque slide. C’est une porte laissée ouverte pour plus tard, désactivée par défaut, et totalement inutile au pilotage.
La suite
Ce qui est fait : le firmware compile (=ESP-IDF v5.2.2, ~1.2 Mo), et le rejeu est validé au banc (=le moteur suit une trame rejouée, y compris après usage de la vraie télécommande). Reste à valider sur l’installation complète :
- calibration fine des temps de course sur mes cinq volets,
- portée de l’antenne depuis l’emplacement de l’ESP,
- comportement avec la télécommande Noé multi-canal.
Matériel
- ◨
N'importe quel ESP32 fait l'affaire. Le M5Stack ATOM Lite est pratique parce que tout le header du bas est accessible aux fils dupont, sans soudure, et qu'il tient dans un tout petit boîtier fini.
- ◨Module radio CC1101 868 MHz ~3-5 €
Émetteur-récepteur sub-GHz. Prendre impérativement une variante 868 MHz (=pas 433, sinon rien ne capte). Attention, sur ces petits modules verts les pastilles sont au pas de 2 mm (=pas le standard 2.54 mm) et même pas uniformes (=la rangée côté antenne est à un autre pas). Les headers et breadboards standard 2.54 ne rentrent donc pas ; il existe bien des pin headers 2 mm (plus rares), mais j'ai fini par souder les fils directement. J'ai laissé tomber l'EBYTE E07-900M10S en CMS, trop fin à souder à la main. 3.3 V uniquement.
- ◨Antenne 868 MHz ~2 €
Un simple bout de fil de 8.6 cm (=quart d'onde à 868 MHz) suffit pour tester à courte portée. Une antenne hélicoïdale à connecteur SMA ou u.FL améliore nettement la portée dans la maison.
AliExpress - ◨Fils dupont femelle-femelle ~3 €
Six fils pour relier le CC1101 à l'ESP32 en SPI, sans soudure sur le M5Stack. Kit 40 fils.
AliExpress / Amazon
Prix indicatifs relevés au moment du projet. Aucun lien affilié - je ne touche rien.