Construire son propre Thread Border Router avec des ESP32
Pourquoi construire son propre Thread Border Router alors que de nombreux appareils en intègrent déjà un ? Parti d’un simple besoin domotique, ce projet m’a conduit beaucoup plus loin que prévu : Thread, OpenThread, ESP32-S3, ESP32-C6, RCP, antennes externes, diagnostic réseau... Retour sur un Border Router que j’ai construit, modifié et cassé plusieurs fois avant d’aboutir à une solution que je peux réellement comprendre, observer et maintenir.
Tout a commencé avec une serrure.
Plus précisément, une Nuki Smart Lock Pro que je voulais intégrer proprement à Home Assistant.
J'aurais pu choisir la solution la plus évidente : le Wi-Fi. La serrure le supporte, Home Assistant aussi, et cela aurait probablement réglé le problème très rapidement.
Mais une serrure fonctionne sur batterie.
Maintenir une connexion Wi-Fi a un coût énergétique. Thread a précisément été conçu pour les objets connectés contraints : faible consommation, réseau maillé IPv6 et possibilité pour certains appareils de passer une grande partie de leur temps en sommeil.
Dans le cas d'une serrure, le raisonnement était donc assez simple : moins d'énergie consacrée à la connectivité signifie potentiellement davantage d'autonomie entre deux recharges.
Le choix de Thread avait du sens.
Mais il introduisait une nouvelle pièce dans l'équation.
Home Assistant vit sur mon réseau IP. La serrure, elle, allait vivre sur un réseau Thread. Pour faire communiquer les deux, il me fallait un Thread Border Router.
J'aurais pu en acheter un.
Évidemment, ce n'est pas ce que j'ai fait.
Pourquoi construire son propre Thread Border Router ?
C'est probablement la première question à se poser.
Les Thread Border Routers existent déjà. Ils sont intégrés à de nombreux produits Apple, Google, Amazon, Home Assistant et autres passerelles domotiques.
Si votre seul objectif est de connecter quelques appareils Matter over Thread et de ne plus jamais vous préoccuper de ce qui se passe dessous, construire le vôtre n'a probablement aucun intérêt.
Mais si vous voulez comprendre, observer ou modifier votre réseau Thread, les choses deviennent beaucoup plus intéressantes.
Comprendre
Construire le Border Router oblige rapidement à comprendre ce qui se passe réellement sous Matter et Thread.
On découvre notamment la séparation entre :
- le réseau IP classique ;
- le réseau Thread ;
- le Border Router ;
- le processeur hôte ;
- la radio IEEE 802.15.4 ;
- le RCP --- Radio Co-Processor ;
- Spinel ;
- et l'Operational Dataset qui définit l'identité du réseau Thread.
Thread devient beaucoup moins magique.
Observer et diagnostiquer
Un Border Router que l'on contrôle donne également accès aux outils OpenThread.
Quelques commandes suffisent déjà à révéler beaucoup de choses :
ot state
ot dataset active
ot child table
ot neighbor table
ot router table
On peut voir les enfants du Border Router, ses voisins, la qualité des liens, le RSSI, les RLOC16 ou encore l'état du réseau.
Ce qui ressemble initialement à une simple passerelle devient alors un véritable outil de diagnostic Thread.
Maîtriser la radio
C'est également l'un des gros intérêts matériels de mon montage.
En utilisant un ESP32-C6 comme coprocesseur radio, je contrôle son firmware mais également son implantation RF.
Dans mon cas, le XIAO ESP32-C6 permet d'utiliser une antenne externe pour la radio IEEE 802.15.4.
Cela ne signifie évidemment pas qu'une antenne externe transforme magiquement la portée d'un réseau Thread : les communications restent bidirectionnelles et les appareils distants doivent eux aussi être capables de répondre.
Mais cela permet d'optimiser le placement de l'antenne, de l'éloigner d'un boîtier ou d'une masse métallique et, plus généralement, de maîtriser beaucoup mieux la partie radio du Border Router.
Maîtriser le firmware
Le RCP n'est pas une boîte noire.
Je peux construire son firmware, le modifier et l'instrumenter.
Je montrerai même plus loin que le processeur hôte peut ensuite maintenir automatiquement le firmware de ce coprocesseur radio.
Le prix
Enfin, l'investissement matériel est presque anecdotique.
Deux petites cartes ESP32, quelques fils, une alimentation et éventuellement deux antennes externes suffisent.
Pour quelques dizaines d'euros, on obtient non seulement un Thread Border Router, mais également une plateforme d'expérimentation et d'analyse du réseau.
Première idée : un ESP32-C6 devrait suffire
Après quelques recherches, mon premier réflexe a été de tenter la construction du Thread Border Router autour d'un seul ESP32-C6.
Le choix semblait évident.
Le C6 dispose à la fois du Wi-Fi et d'une radio IEEE 802.15.4 compatible Thread.
Sur le papier :
Pourquoi utiliser deux microcontrôleurs lorsqu'un seul semble disposer de toutes les interfaces nécessaires ?
Et techniquement, ça a fonctionné.
J'ai obtenu un Thread Border Router opérationnel.
Il serait donc incorrect d'affirmer qu'un Border Router construit uniquement autour d'un ESP32-C6 ne fonctionne pas.
En revanche, l'expérience ne m'a pas totalement convaincu.
J'observais notamment un problème presque quotidien que je n'arrivais pas à expliquer. L'ensemble me semblait également manquer de réactivité, même si ce dernier point reste une observation totalement empirique : je n'avais effectué aucune mesure permettant de le quantifier.
Surtout, plus j'explorais l'écosystème OpenThread et Espressif, plus la séparation entre un processeur hôte et un coprocesseur radio dédié semblait être l'architecture à privilégier.
Et il restait un problème plus fondamental.
Le système était une grosse boîte noire.
Quand quelque chose se passait mal, déterminer si le problème venait du Wi-Fi, de Thread, de la coexistence radio ou de mon implémentation devenait rapidement compliqué.
Pour réellement investiguer, il fallait revenir au câble USB, connecter le C6 à un PC et observer sa console.
Très pratique sur un établi.
Beaucoup moins pour un équipement destiné à fonctionner 24 heures sur 24 dans une maison.
Le C6 avait pourtant été condamné un peu vite
J'ai découvert plus tard l'origine de mon fameux problème quotidien.
Mon point d'accès Wi-Fi redémarre volontairement toutes les nuits, vers 03:00.
L'implémentation de base que j'utilisais ne gérait manifestement pas correctement ce scénario : après la disparition temporaire du point d'accès, la connexion n'était pas rétablie comme je m'y attendais.
Le C6 n'avait donc pas réellement « crashé ».
Il avait perdu son backhaul Wi-Fi et ne l'avait pas correctement récupéré.
Cette découverte est arrivée plus tard et je ne suis pas revenu à l'architecture C6 seul pour autant.
Car entre-temps, je ne cherchais déjà plus simplement un Thread Border Router qui fonctionne.
Je voulais un Thread Border Router dont je puisse comprendre le fonctionnement lorsqu'il ne fonctionne pas.
Deux puces plutôt qu'une
C'est à ce moment que j'ai décidé de séparer les rôles.
Le choix du matériel a été extrêmement pragmatique : j'avais un ESP32-S3 et un XIAO ESP32-C6 sous la main.
Ce n'est pas nécessairement la combinaison la plus classique pour construire un Thread Border Router.
Mais elle est techniquement supportée et il n'y avait aucune raison de ne pas essayer.
Le principe devient alors :
Le S3 prend en charge le réseau IP et les fonctions du Border Router.
Le C6 devient beaucoup plus spécialisé : il s'occupe de la radio IEEE 802.15.4 et communique avec l'hôte à travers Spinel.
Avant de construire le Border Router, il fallait donc commencer par transformer le C6 en RCP.
Construire le firmware RCP de l'ESP32-C6
Avant d'aller plus loin, un mot sur ESP-IDF. L'Espressif IoT
Development Framework est le framework officiel de développement
d'Espressif pour les microcontrôleurs ESP32. Il fournit notamment les
composants OpenThread et l'exemple ot_rcp utilisé ici.
Le framework peut être récupéré depuis le dépôt officiel : https://github.com/espressif/esp-idf.
Pour ce projet, j'ai utilisé ESP-IDF 5.5.5 sous Windows 11 avec
PowerShell. Je précise volontairement la version : ESP-IDF, OpenThread
et esp-thread-br évoluent encore rapidement, et une version différente
peut présenter des menus ou des options légèrement différents.
L'exemple RCP se trouve directement dans ESP-IDF :
examples\openthread\ot_rcp
Sous Windows, je commence par charger l'environnement ESP-IDF :
cd C:\esp\esp-idf
.\export.ps1
Puis :
cd examples\openthread\ot_rcp
idf.py set-target esp32c6
idf.py menuconfig
Configuration du RCP
La configuration nécessaire est finalement assez courte.
Dans OpenThread RCP Example, je choisis de définir manuellement les broches UART :
[*] Configure RCP UART pin manually
(17) The number of RX pin
(16) The number of TX pin
Ce qui correspond dans mon sdkconfig à :
CONFIG_OPENTHREAD_UART_PIN_MANUAL=y
CONFIG_OPENTHREAD_UART_RX_PIN=17
CONFIG_OPENTHREAD_UART_TX_PIN=16
Le C6 communiquera donc avec son host via :
C6 RX = GPIO17 = D7 sur le XIAO ESP32-C6
C6 TX = GPIO16 = D6 sur le XIAO ESP32-C6
C'est un détail utile au moment du câblage : menuconfig utilise les
numéros de GPIO, tandis que le brochage du XIAO identifie ces mêmes
broches comme D7 et D6.
L'exemple est configuré pour exposer le RCP en UART :
CONFIG_OPENTHREAD_RCP_UART=y
# CONFIG_OPENTHREAD_RCP_SPI is not set
Le C6 utilise directement sa propre radio IEEE 802.15.4 :
CONFIG_OPENTHREAD_RADIO=y
CONFIG_OPENTHREAD_RADIO_NATIVE=y
Garder la console indépendante du RCP
Je configure également la console sur le contrôleur USB Serial/JTAG :
Component config → ESP System Settings → Channel for console output
(X) USB Serial/JTAG Controller
Ce qui donne :
CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y
Cette séparation est particulièrement pratique :
GPIO16 / GPIO17
│
└── Spinel / communication avec le S3
USB Serial/JTAG
│
└── logs et diagnostics du C6
Je peux donc instrumenter et observer le C6 sans jamais polluer la liaison utilisée par le RCP.
Configuration flash
Sur mon XIAO ESP32-C6, la configuration utilisée est :
Flash SPI mode : DIO
Flash SPI speed : 80 MHz
Flash size : 2 MB
Ces valeurs correspondent à ma carte. Elles ne doivent pas être considérées comme une configuration universelle pour toutes les cartes basées sur un ESP32-C6.
Les captures ci-dessous reprennent les principaux écrans de menuconfig utilisés pour construire ce firmware RCP. Elles permettent de vérifier visuellement la configuration avant de lancer le build.
Utiliser l'antenne externe du XIAO ESP32-C6
Cette étape est entièrement optionnelle. Elle ne sert que si vous branchez réellement une antenne externe sur le connecteur du XIAO ESP32-C6.
Si vous utilisez l'antenne intégrée de la carte, aucune modification du firmware n'est nécessaire : vous pouvez directement passer à la section Construire et flasher.
Dans mon cas, j'ai choisi d'utiliser une antenne externe, ce qui nécessite de sélectionner explicitement le chemin RF correspondant dans le firmware.
À ce stade, je pourrais déjà construire un RCP fonctionnel.
Mais le XIAO ESP32-C6 possède une caractéristique particulièrement intéressante pour mon projet : un connecteur pour antenne externe.
Brancher une antenne sur le connecteur ne suffit cependant pas.
Il faut également sélectionner le chemin RF par logiciel.
Dans :
examples\openthread\ot_rcp\main\esp_ot_rcp.c
j'ajoute :
#include "driver/gpio.h"
Puis :
/*
* XIAO ESP32-C6 RF switch
*
* GPIO3 = RF switch control
* GPIO14 = antenna selection
*
* GPIO14 HIGH selects the external antenna.
*/
static void xiao_use_external_antenna(void)
{
gpio_set_direction(GPIO_NUM_3, GPIO_MODE_OUTPUT);
gpio_set_level(GPIO_NUM_3, 0);
gpio_set_direction(GPIO_NUM_14, GPIO_MODE_OUTPUT);
gpio_set_level(GPIO_NUM_14, 1);
ESP_LOGI(TAG, "XIAO ESP32-C6 external antenna selected");
}
Et j'appelle cette fonction immédiatement au début de app_main() :
void app_main(void)
{
/*
* Select XIAO external antenna first.
*/
xiao_use_external_antenna();
/* ... */
}
Le résultat est donc :
GPIO3 = LOW
GPIO14 = HIGH
│
└── antenne externe sélectionnée
Construire et flasher
Je peux maintenant construire le RCP :
idf.py build
Puis, pour cette première installation, le flasher directement.
Sous Windows, je peux d'abord lister les ports série disponibles afin d'identifier celui du XIAO :
Get-CimInstance Win32_SerialPort |
Select-Object DeviceID, Name
Par exemple :
DeviceID Name
-------- ----
COM4 USB Serial Device (COM4)
Il suffit alors d'utiliser le port correspondant :
idf.py -p COM4 flash monitor
Autre possibilité : ouvrir le Gestionnaire de périphériques Windows → Ports (COM et LPT). En cas de doute, débrancher puis rebrancher le XIAO permet généralement d'identifier immédiatement le port qui apparaît.
Au démarrage, ma modification permet notamment de vérifier :
XIAO ESP32-C6 external antenna selected
Le RCP est prêt.
Et il faut retenir l'emplacement de son build :
C:\esp\esp-idf\examples\openthread\ot_rcp\build
Je montrerai plus tard que ce répertoire joue un rôle important dans mon architecture finale.
Instrumenter le RCP
Cette étape n'est pas nécessaire au fonctionnement du Border Router.
Durant mes investigations, j'avais cependant ajouté quelques diagnostics au firmware du C6.
J'enregistrais notamment la raison du dernier reset et une tâche produisait toutes les 60 secondes un heartbeat contenant l'uptime, la mémoire libre et le minimum de mémoire libre observé.
Les diagnostics étaient volontairement envoyés sur la console ESP-IDF USB et jamais sur l'UART du RCP.
Cette instrumentation n'est donc pas nécessaire à la procédure finale, mais elle illustre assez bien l'un des intérêts de construire son propre système : lorsque quelque chose se comporte bizarrement, je peux instrumenter jusqu'au coprocesseur radio.
Ironiquement, c'est aussi ce qui m'a aidé à innocenter progressivement le C6 dans mon problème de déconnexion quotidienne.
Première version S3 + C6
Le firmware du C6 étant prêt, il fallait maintenant lui donner un host.
Pour cette première version, je suis parti de l'exemple ot_br fourni
avec ESP-IDF et l'ai construit pour l'ESP32-S3.
Après quelques ajustements de configuration et de câblage, le résultat était fonctionnel.
Le S3 gérait le Wi-Fi, le réseau IP et OpenThread côté host. Le C6 s'occupait exclusivement de la radio 802.15.4.
J'ai alors commencé à utiliser quelques appareils IKEA sur batterie comme cobayes pour expérimenter avec le réseau Thread.
Je pouvais désormais interroger OpenThread, observer les enfants du Border Router, les voisins, le dataset ou encore la qualité des liens.
Thread devenait progressivement beaucoup moins opaque.
Le montage fonctionnait.
J'aurais pu m'arrêter là.
Évidemment, ce n'est pas ce qui s'est passé.
Je voulais simplement une page Web...
Il restait un problème très pratique.
Même avec ma nouvelle architecture, investiguer profondément le Border Router impliquait encore trop facilement de revenir à une console série.
Or ce système était destiné à devenir une composante permanente de l'infrastructure de la maison.
Je ne voulais pas devoir aller chercher un câble USB et connecter un ordinateur chaque fois que je souhaitais savoir ce que faisait mon réseau Thread.
L'objectif suivant était donc extrêmement modeste : ajouter une petite page Web de statut.
Quelques informations sur l'état du Border Router, le réseau Thread et suffisamment de données pour effectuer un premier diagnostic depuis un navigateur.
C'est en cherchant comment intégrer cette interface que je suis revenu
sur le projet esp-thread-br d'Espressif.
Et là, surprise.
Ce que je pensais devoir développer existait déjà.
Et sous une forme nettement plus complète.
esp-thread-br : j'étais en train de réinventer quelque chose qui existait déjà
Le projet avait manifestement beaucoup évolué.
L'exemple :
examples/basic_thread_border_router
proposait notamment :
- une interface Web pour configurer et interroger Thread ;
- le démarrage automatique du Border Router ;
- la gestion du Wi-Fi, avec possibilité de configurer le réseau au moment du déploiement ;
- le support de RCP externes ;
- les standalone development kits ;
- le choix d'un ESP32-C6 comme RCP ;
- et même la mise à jour du firmware du RCP par le host.
Autre possibilité intéressante : le firmware ne doit pas nécessairement être préparé avec les identifiants du réseau Wi-Fi définitif. Il peut être construit et flashé à l'avance, puis configuré une fois installé sur son réseau de destination.
Ce n'est pas l'option que j'ai choisie ici, puisque ce Border Router est destiné à mon propre réseau. Mais le scénario est intéressant : il devient possible de préparer complètement un Border Router pour quelqu'un d'autre et de lui fournir un appareil qu'il pourra déployer chez lui sans installer ESP-IDF ni recompiler le firmware. Cela rapproche nettement le projet d'un appareil autonome réellement déployable in-place.
Plus intéressant encore : mon architecture S3 + C6 pouvait parfaitement s'inscrire dans cette implémentation.
Ma combinaison n'était peut-être pas la configuration la plus classique, mais elle était supportée.
À ce stade, esp-thread-br m'a semblé suffisamment mature pour
envisager d'en faire la base de mon Border Router permanent.
Il y avait simplement un petit problème.
Mon Border Router fonctionnait déjà.
Mon réseau Thread existait. Mes appareils IKEA y étaient connectés.
Et remplacer le firmware du S3 revenait à casser volontairement quelque chose que j'avais fini par faire fonctionner correctement.
Encore une fois.
Peut-on remplacer le Border Router sans reconstruire le réseau Thread ?
La réponse se trouve dans une notion que j'avais progressivement appris à connaître : l'Operational Dataset.
Un réseau Thread n'est pas simplement défini par son nom.
Son dataset contient notamment le canal, le PAN ID, l'Extended PAN ID, le Mesh Local Prefix, la Network Key, le PSKc, la politique de sécurité et les autres paramètres nécessaires pour identifier le réseau.
Avant de toucher au Border Router fonctionnel, j'ai donc commencé par sauvegarder son dataset :
ot dataset active -x
Mais également sa représentation lisible :
ot dataset active
Dans mon cas, j'avais notamment :
Channel: 11
Ext PAN ID: dead00beef00cafe
Mesh Local Prefix: fdde:ad00:beef:0::/64
Network Name: OpenThread
PAN ID: 0x37dd
Et surtout la représentation hexadécimale complète retournée par
ot dataset active -x.
Cette petite ligne allait me permettre de reconstruire le nouveau Border Router sans créer un nouveau réseau Thread.
Construire le Border Router final
esp-thread-br est un projet Espressif distinct d'ESP-IDF. Il faut donc
le récupérer séparément. Le dépôt officiel se trouve ici :
https://github.com/espressif/esp-thread-br.
Dans mon installation, je l'ai placé directement sous C:\esp :
cd C:\esp
git clone --recursive https://github.com/espressif/esp-thread-br.git
L'arborescence utile devient donc :
C:\esp\
├── esp-idf\
│ └── examples\openthread\ot_rcp\build\
│
└── esp-thread-br\
└── examples\basic_thread_border_router\
Après avoir chargé l'environnement ESP-IDF :
cd C:\esp\esp-idf
.\export.ps1
je peux rejoindre l'exemple du Border Router :
cd C:\esp\esp-thread-br\examples\basic_thread_border_router
et cibler l'ESP32-S3.
Ma configuration finale utilise :
CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESP_BR_BOARD_STANDALONE=y
CONFIG_ESP_BR_C6_TARGET=y
CONFIG_OPENTHREAD_BR_AUTO_START=y
CONFIG_OPENTHREAD_BR_START_WEB=y
Le S3 peut donc démarrer automatiquement le Border Router et exposer son interface Web.
UART entre le S3 et le C6
Ma liaison Spinel utilise :
CONFIG_PIN_TO_RCP_TX=4
CONFIG_PIN_TO_RCP_RX=5
Dans la configuration OpenThread du host :
.rx_pin = CONFIG_PIN_TO_RCP_TX,
.tx_pin = CONFIG_PIN_TO_RCP_RX,
Mon câblage est donc :
Laisser le S3 mettre à jour le C6
basic_thread_border_router possède un mécanisme particulièrement
intéressant : le host peut mettre à jour le firmware du RCP.
Pour cela, il doit non seulement communiquer avec le C6 en UART, mais également pouvoir contrôler son reset et son mode bootloader.
Cela implique deux connexions supplémentaires entre le S3 et le C6. Contrairement à la liaison UART, cette partie demande quelques petites soudures sur le XIAO ESP32-C6 afin de récupérer les signaux nécessaires sur les pads de la carte. Ce n'est pas particulièrement compliqué avec une panne fine, mais c'est probablement la partie matérielle la plus délicate du montage.
J'ajoute donc :
S3 GPIO7 ──────────> C6 EN
S3 GPIO8 ──────────> C6 GPIO9 / BOOT
Ces deux connexions ne transportent aucune donnée Thread. Elles servent uniquement au S3 à piloter la séquence BOOT/RESET nécessaire lorsqu'il doit reflasher le firmware du RCP.
Ma liaison complète devient :
Et j'active :
CONFIG_AUTO_UPDATE_RCP=y
D'où vient le firmware du C6 ?
Ma configuration contient :
CONFIG_RCP_SRC_DIR="$ENV{IDF_PATH}/examples/openthread/ot_rcp/build"
CONFIG_RCP_PARTITION_NAME="rcp_fw"
CONFIG_RCP_PATH_NAME="rcp"
Le Border Router utilise donc les artefacts déjà présents dans :
C:\esp\esp-idf\examples\openthread\ot_rcp\build
La chaîne devient :
Cela signifie surtout que ma personnalisation du firmware C6 est conservée.
Lors de ma première migration, aucune mise à jour du C6 ne s'est produite. La raison était finalement très simple : le firmware RCP embarqué par le S3 provenait du même build que celui que j'avais déjà flashé manuellement dans le C6.
Les versions étaient identiques. Le mécanisme d'auto-update n'avait donc tout simplement rien à faire.
Les captures suivantes reprennent les principaux réglages menuconfig utilisés sur l'ESP32-S3 pour cette configuration : choix du RCP, câblage avec le C6, connexion et reconnexion Wi-Fi, mécanisme de mise à jour du RCP et console de diagnostic USB.
Flasher le nouveau S3
Le partitionnement utilisé prévoit notamment :
nvs 24K
otadata 8K
ota_0 2M
ota_1 2M
web_storage 200K
rcp_fw 640K
La partition rcp_fw contient les éléments nécessaires à la mise à jour
du coprocesseur radio.
Après démarrage, le S3 retrouve correctement son C6 et le Border Router peut fonctionner.
Il reste donc à lui rendre son ancien réseau Thread.
Restaurer le réseau Thread existant
Je réinjecte l'Operational Dataset sauvegardé avant la migration.
Après activation, je peux vérifier :
ot dataset active
Je retrouve les paramètres de mon réseau original, puis :
ot state
retourne :
leader
Le nouveau Border Router a donc recréé le même réseau Thread.
Mais quelque chose manque encore : mes appareils IKEA.
Où sont passés les appareils ?
La première vérification donne :
ot child table
ot neighbor table
Les deux sont vides.
J'avais pourtant restauré le bon dataset.
La réponse était beaucoup plus simple que prévu : mes appareils de test fonctionnent sur batterie. Ils dormaient.
Après avoir retiré puis remis les piles de l'un des appareils, les nœuds ont commencé à réapparaître.
Quelques instants plus tard, ot neighbor table affichait notamment :
RLOC16 0xd801 Avg RSSI -55 LQ 3
RLOC16 0xd802 Avg RSSI -58 LQ 3
Et ot child table retournait :
0xd801
0xd802
Je venais donc de remplacer l'implémentation complète de mon Thread Border Router sans recréer le réseau et sans devoir réinstaller mes appareils de test.
Ce que j'ai cassé en chemin
Vu depuis la fin, le montage semble presque trivial : deux ESP, quelques fils, deux antennes, un firmware RCP et un firmware Border Router.
Mais pour arriver à cette version, j'ai cassé plusieurs fois quelque chose qui fonctionnait. Et presque chaque casse m'a appris quelque chose.
Le « crash » quotidien
J'ai soupçonné le C6 avant de découvrir que mon point d'accès Wi-Fi redémarrait chaque nuit et que ma première implémentation gérait mal son retour.
Le C6 impossible à observer
J'ai ajouté un heartbeat et la raison des resets au firmware pour déterminer ce qu'il faisait réellement.
La migration vers un nouveau Border Router
J'ai appris que l'identité du réseau pouvait être conservée grâce à l'Operational Dataset.
Les appareils disparus après migration
Ils n'avaient pas oublié le réseau. Ils dormaient simplement.
L'auto-update qui ne mettait rien à jour
Il fonctionnait parfaitement. Le firmware proposé était simplement le même que celui déjà installé.
L'antenne externe
J'ai également réalisé qu'il ne suffisait pas de penser à l'antenne actuellement utilisée par le C6.
Il fallait s'assurer que le firmware RCP embarqué pour les futures mises à jour sélectionne lui aussi cette antenne.
C'est ce qui m'a amené à comprendre précisément la relation entre
ot_rcp/build, rcp_fw.bin et le mécanisme d'auto-update.
Si je devais le refaire aujourd'hui
Toute cette expérimentation était utile.
Mais elle n'est absolument pas nécessaire pour construire le même Border Router aujourd'hui.
La procédure peut désormais être résumée ainsi :
- installer ESP-IDF et
esp-thread-br; - construire
ot_rcppour l'ESP32-C6 ; - configurer son UART ;
- sélectionner l'antenne externe si vous utilisez un XIAO ESP32-C6 avec une antenne externe ;
- construire
basic_thread_border_routerpour le S3 ; - sélectionner le C6 comme RCP ;
- configurer la liaison UART ;
- optionnellement, configurer EN et BOOT pour activer l'auto-update du RCP ;
- activer la Web UI et l'auto-start, puis flasher le S3 et le C6 ;
- créer un nouveau dataset ou importer celui d'un réseau existant.
Le résultat matériel final est simplement :
Et maintenant que j'ai terminé la modification RF du S3, celui-ci dispose lui aussi de son antenne externe, cette fois pour le Wi-Fi.
Les deux antennes ont donc des fonctions complètement indépendantes :
antenne S3 → Wi-Fi / réseau IP
antenne C6 → IEEE 802.15.4 / Thread
Finalement, pourquoi l'avoir construit ?
Au départ, je voulais simplement connecter une serrure en Thread plutôt qu'en Wi-Fi.
Cela m'a conduit à construire un Border Router sur un ESP32-C6.
Puis à le remplacer par un S3 accompagné d'un C6.
Puis à instrumenter le C6.
Puis à vouloir ajouter une petite page Web.
Puis à découvrir qu'Espressif proposait désormais une implémentation beaucoup plus complète de ce que j'étais progressivement en train de construire.
Et finalement à casser une nouvelle fois mon Border Router pour migrer vers cette nouvelle architecture.
Le résultat est pourtant beaucoup plus intéressant que le besoin initial.
J'ai maintenant un Thread Border Router dont je contrôle le firmware, avec une radio 802.15.4 dédiée, des antennes externes pour le Wi-Fi et Thread, une interface d'observation du réseau et la possibilité de mettre à jour automatiquement son coprocesseur radio.
Surtout, Thread n'est plus vraiment une boîte noire.
Je peux regarder les enfants du Border Router, ses voisins, la qualité de leurs liens, leur RSSI, leurs rôles et l'état du réseau.
C'est probablement là que se trouve la véritable réponse à la question posée au début.
Pourquoi construire son propre Thread Border Router ?
Pas parce qu'il est impossible d'en acheter un.
Pas nécessairement parce qu'il sera meilleur qu'un produit du commerce.
Mais parce qu'un Border Router grand public cherche généralement à vous faire oublier que Thread existe.
Celui-ci fait exactement l'inverse.
Il permet de regarder ce qui se passe dessous.
Et pour quelqu'un qui aime comprendre comment fonctionnent les choses, c'est probablement la meilleure raison de le construire.
Mises à jour
21 septembre 2026 — Ajout d'une note sur la reconnexion Wi-Fi et passage de CONFIG_EXAMPLE_WIFI_CONN_MAX_RETRY de 6 à -1 pour assurer une reconnexion persistante après une indisponibilité prolongée du point d'accès.

Commentaires
Aucun commentaire pour le moment.
Ajouter un commentaire