
Firmware pour Raspberry Pi Pico W qui crée un adaptateur Wi-Fi USB sans pilote avec pontage transparent de couche 2, authentification WPA2/WPA3 et console de gestion hors bande.
= pico-usb-wifi :toc: macro :toclevels: 3 :idprefix: :idseparator: -
pico-usb-wifi est un firmware pour la Raspberry Pi Pico W qui la transforme en adaptateur USB Wi-Fi sans pilote, apparaissant comme un périphérique USB CDC-NCM.
:figure-caption: AI Slop
.Diagramme de pico-usb-wifi image::images/openrouter-banana2-rpi-pico.png[]
Le firmware fonctionne comme un pont transparent de couche 2 qui fait transiter les trames entre l'interface sans fil de la Pico W et son interface USB. L'interface USB de l'hôte adopte l'adresse MAC de la station Wi-Fi de la Pico W, ce qui fournit une identité MAC et IP unique de bout en bout.
Aucun pilote côté hôte, module noyau ou pile sans fil n'est requis ; voir <<no-host-side-wi-fi-stack,Pas de pile Wi-Fi côté hôte>>.
L'hôte n'a besoin que des pilotes cdc_ncm et cdc_acm intégrés, fournis avec tous les systèmes d'exploitation modernes : Linux, macOS, Windows et les OS mobiles.
== Fonctionnalités
pico-usb-wifi fournit les fonctionnalités suivantes :
.Une situation réelle image::images/slop_2.png[]
== Pourquoi cela existe
J'avais besoin d'un adaptateur USB Wi-Fi pour un projet Linux embarqué à venir. Je n'avais pas de clé USB Wi-Fi bon marché ; alors plutôt que d'aller en acheter une en magasin pour cinq dollars, j'ai passé deux jours d'un long week-end férié et environ un million de jetons Claude Code à développer ce firmware.
白一百, auteur de pico-usb-wifi
Google a dit que ce n'était pas faisable :
.pico-usb-wifi "Non réalisable" image::images/gemini_says_not_possible.png[]
toc::[]
== Pas de pile Wi-Fi côté hôte
Contrairement à une clé USB Wi-Fi, cet adaptateur n'expose à l'hôte qu'une interface de type Ethernet. La Pico W contient toute la partie sans fil : la radio, l'association, le suppliant WPA2/WPA3 et le domaine réglementaire.
Cela permet aux systèmes d'éviter d'installer wpa_supplicant, la pile sans fil cfg80211/mac80211, une base de données réglementaire, ainsi que le firmware du chipset ou un pilote propriétaire.
L'approvisionnement des identifiants Wi-Fi se fait sur le périphérique, via sa console de gestion hors bande, et non via un outillage sans fil côté hôte.
Cela préserve à un hôte contraint, à un appareil, ou à un hôte sans pilotes sans fil (ou dont le noyau du fournisseur en est dépourvu), la capacité de se connecter à des réseaux sans fil en utilisant uniquement des pilotes génériques de classe CDC.
== Comment ça fonctionne
[#fig-topology] .Diagramme de topologie image::images/topology.svg[Topologie du pont de couche 2 transparent,820]
L'interface USB de l'hôte reçoit l'adresse MAC de la station Wi-Fi de la Pico W, de sorte qu'une seule MAC existe de bout en bout et que la Pico W peut transférer les trames Ethernet telles quelles entre USB et Wi-Fi. Une station Wi-Fi ne peut pas relier plusieurs adresses MAC ; c'est donc en réduisant l'hôte et la station à une seule MAC qu'un pont transparent est rendu possible. La justification complète, le chemin de données et la gestion IPv6/multicast sont décrits dans <<architecture,Architecture>>.
== Prérequis côté hôte
L'hôte nécessite les pilotes cdc_ncm et cdc_acm inclus dans l'arborescence du noyau.
Tous deux font partie du noyau Linux principal depuis plus d'une décennie, de sorte que tout noyau actuellement pris en charge les inclut.
Aucun module hors arborescence, blob de firmware ou pilote propriétaire n'est utilisé.
Les mêmes pilotes de classe existent sur macOS, Windows 10 et versions ultérieures, Android et iOS.
[NOTE] Aucun autre système d'exploitation n'a été testé.
== Compilation
Le projet est un projet CMake standard du pico-sdk. Il nécessite la chaîne d'outils embarquée ARM, CMake, un backend de compilation (Ninja ou Make), Python 3, et une copie du pico-sdk avec ses sous-modules. Les TinyUSB et lwIP inclus dans le pico-sdk sont utilisés sans modification.
=== Dépendances
Sur les systèmes basés sur Arch (Arch, CachyOS, Manjaro), la chaîne d'outils provient des dépôts officiels :
arm-none-eabi-newlib fournit la bibliothèque C embarquée et les en-têtes ; sans lui, le compilateur croisé ne trouve pas stdint.h ni les en-têtes similaires.
libusb n'est nécessaire que pour picotool, que le pico-sdk compile depuis les sources lors de la première configuration afin de générer le UF2 ; aucun paquet picotool séparé n'est requis.
=== Étapes de compilation
git clone -b 2.2.0 --recurse-submodules https://github.com/raspberrypi/pico-sdk export PICO_SDK_PATH="$PWD/pico-sdk"
cp src/wifi_config.h.example src/wifi_config.h # then edit SSID/password, or leave blank cmake -S . -B build -G Ninja -DPICO_BOARD=pico_w -DCMAKE_BUILD_TYPE=Release cmake --build build
Le drapeau -G Ninja est facultatif ; omettez-le pour utiliser le générateur Make par défaut (puis cmake --build build -j).
wifi_config.h contient les identifiants par défaut au moment de la compilation et est ignoré par git.
Le laisser vide produit une image sans identifiants intégrés, provisionnée à l'exécution via la console de gestion (<<management-console,Console de gestion>>) ; le remplir intègre un réseau par défaut.
== Écriture du firmware
Les étapes suivantes chargent le firmware sur la carte.
. Maintenez le bouton BOOTSEL enfoncé tout en connectant la carte en USB.
Elle apparaît comme un volume de stockage de masse USB RPI-RP2, généralement sous /run/media/<user>/RPI-RP2 ou /media/<user>/RPI-RP2.
. Copiez pico-usb-wifi.uf2 sur ce volume.
La carte redémarre automatiquement dans le firmware.
. Connectez la carte à l'hôte qui doit recevoir la connectivité Wi-Fi.
== Utilisation sur un hôte Linux
Branchez le périphérique à l'hôte et provisionnez ses identifiants Wi-Fi une fois via la console de gestion (<<management-console,Console de gestion>>). L'interface de l'hôte se comporte ensuite comme n'importe quelle connexion filaire sur le réseau du point d'accès.
Un hôte qui gère les interfaces automatiquement (NetworkManager, systemd-networkd, dhcpcd) n'a besoin d'aucune configuration : il exécute DHCP et SLAAC via le pont et reçoit une seule adresse IPv4, une adresse IPv6, la passerelle du point d'accès et le DNS, exactement comme le ferait un client filaire.
Il n'y a ni adresse ni passerelle à configurer côté périphérique, car la Pico n'en détient aucune.
L'adresse MAC de l'interface est celle de la station Wi-Fi ; c'est ainsi qu'une identité unique est présentée au réseau.
La sortie de la commande ip ci-dessous montre l'interface résultante : un client DHCP/SLAAC ordinaire sur le sous-réseau du point d'accès, avec la MAC de la station et aucune trace de la Pico.
== Console de gestion
La console de gestion est l'interface de configuration, sur la première fonction série CDC-ACM (généralement /dev/ttyACM0).
Elle est accessible dès que le périphérique s'énumère, avant même l'association Wi-Fi, de sorte que le provisionnement ne nécessite jamais de réseau.
Ouvrez-la avec un terminal série tel que picocom ou screen ; le débit en bauds est sans importance pour le CDC USB.
La console renvoie l'écho des saisies et affiche une invite ; chaque commande imprime l'état complet du périphérique.
L'authentification Wi-Fi est soit WPA2-PSK, soit WPA3-SAE (AES).
Le mot de passe est la phrase de passe du réseau, ou le réseau est ouvert quand le mot de passe reste vide.
Un profil protégé par mot de passe utilise le mode de transition WPA2/WPA3, il rejoint donc l'un ou l'autre type de point d'accès.
La console stocke jusqu'à huit profils d'identifiants ; un est le profil actif, et le périphérique s'y associe.
set ssid/set pass modifient le profil actif, list/use/del gèrent l'ensemble, et scan découvre les réseaux à proximité et rejoint l'un d'eux depuis une liste numérotée — pratique quand un SSID contient des caractères difficiles à saisir.
Les mots de commande sont insensibles à la casse ; la console les affiche en minuscules.
La session ci-dessous provisionne un réseau en le scannant.
$ picocom /dev/ttyACM0
Un changement prend effet immédiatement, en ré-associant au profil actif ; aucun redémarrage n'est requis.
Répétez pour enregistrer d'autres réseaux ; list les affiche et use <n> bascule sur celui actif :
save persiste chaque profil dans la flash ; restore abandonne les modifications non enregistrées en rechargeant l'enregistrement sauvegardé.
Le tableau suivant récapitule l'ensemble des commandes.
[#tbl-config-commands] .Commandes de la console de gestion [cols="2,3", options="header"] |=== |Commande |Effet
|set ssid <text>
|Définit le SSID du profil actif (une valeur peut contenir des espaces) et ré-associe ; crée le premier profil s'il n'en existe aucun.
|set pass <text>
|Définit la phrase de passe WPA2/WPA3 du profil actif (vide pour un réseau ouvert) et ré-associe.
|set country <CC\|WORLDWIDE>
|Définit le pays réglementaire (pleinement appliqué au prochain démarrage).
|set debug <on\|off>
|Diffuse des diagnostics sur la console de débogage ; voir <<debug-console,Console de débogage>>.
|list
|Liste les profils enregistrés, en marquant le profil actif.
|use <n>
|Rend le profil n actif et ré-associe.
|del <n>
|Supprime le profil n.
|scan
|Recherche les réseaux à proximité et entre dans le sous-menu de scan (back, join <n>, scan pour répéter, ou live pour un flux continu désassocié). join prépare le réseau choisi comme profil actif, prêt pour set pass.
|save
|Persiste tous les profils et réglages dans la flash.
|restore
|Abandonne les modifications non enregistrées en rechargeant les réglages sauvegardés.
|===
L'adresse attribuée à l'hôte est affichée dans le dump d'état comme host IPv4 et host IPv6, observée passivement depuis le trafic ponté, car la Pico ne détient aucune adresse à rapporter.
Le secteur de configuration se trouve à la fin de la flash, distinct de l'image du programme au début, si bien qu'un simple reflash de pico-usb-wifi.uf2 laisse les profils enregistrés intacts (un effacement complet de la puce les supprime).
L'exception est la mise à niveau vers v1.1.0 : la disposition de l'enregistrement a changé pour contenir plusieurs profils, si bien qu'un enregistrement antérieur à 1.1.0 est ignoré et les réseaux doivent être ressaisis une fois (voir le changelog).
== Console de débogage
La console de débogage est un flux de diagnostics en écriture seule sur la deuxième fonction série CDC-ACM (généralement /dev/ttyACM1).
Elle reste silencieuse jusqu'à ce que set debug on soit émis sur la console de gestion ; elle n'a donc aucun coût lorsqu'elle est désactivée et n'interfère jamais avec la gestion.
Une fois activée, elle rapporte les changements d'association et une ligne périodique de statistiques du pont, comme dans la session ci-dessous.
Une compilation avec -DTRACE_FRAMES=1 ajoute un résumé d'une ligne pour chaque trame pontée, mais elle inonde la console en charge ; elle est donc désactivée par défaut.
Les champs de statistiques sont décrits dans le tableau suivant.
[#tbl-debug-stats] .Champs des statistiques de débogage [cols="1,3", options="header"] |=== |Champ |Signification
|->wifi
|Trames transférées de l'hôte vers le Wi-Fi.
|->host
|Trames transférées du Wi-Fi vers l'hôte.
|txdrop
|Trames hôte vers Wi-Fi abandonnées car la station n'était pas encore associée (l'hôte réessaie).
|rxdrop
|Trames Wi-Fi vers hôte abandonnées car le côté USB ne pouvait pas les drainer assez rapidement.
|refl
|Trames Wi-Fi vers hôte abandonnées car il s'agissait de la propre transmission de l'hôte, réfléchie par le point d'accès.
|poolfail
|Trames hôte vers Wi-Fi abandonnées car le pool de pbuf de lwIP était momentanément épuisé.
|ringpk
|Profondeur maximale de la file USB-TX du Wi-Fi vers l'hôte (sur 32) depuis la ligne de statistiques précédente, puis remise à zéro -- une jauge en direct, où une valeur proche de 32 signifie que l'USB ne draine pas aussi vite que le Wi-Fi fournit. (Contrairement à un maximum absolu, elle retombe dès qu'une rafale passe.)
|link
|État du lien Wi-Fi de la station : up (associé), join/down (association en cours), ou une raison d'échec -- badauth (mauvaise phrase de passe), nonet (SSID introuvable), fail.
|hangs
|Nombre de fois où le chien de garde a récupéré le firmware d'un blocage depuis la dernière mise sous tension à froid ; voir <<automatic-recovery,Récupération automatique>>.
|faults
|Fautes matérielles dont le firmware s'est remis depuis la dernière mise sous tension à froid.
|faultpc
|Adresse de la faute matérielle la plus récente (0x00000000 si aucune), à mettre en correspondance avec addr2line.
|freeram
|RAM libre en octets, pour évaluer la marge lors du réglage des tailles de tampons.
|===
Le traçage par trame partage le lien USB Full-Speed avec le trafic ponté ; il réduit donc le débit tout en inondant la console ; c'est une option de compilation (-DTRACE_FRAMES=1) destinée uniquement au débogage approfondi.
== Récupération automatique
Un chien de garde matériel redémarre le périphérique si le firmware cesse de servir sa boucle principale — blocage ou interblocage de pilote — il se ré-énumère donc tout seul en quelques secondes, sans avoir besoin d'être débranché. Un gestionnaire de faute matérielle distinct intercepte immédiatement une faute CPU et enregistre l'adresse fautive.
Les compteurs du pont précédant le crash survivent au redémarrage dans la RAM non initialisée.
Au moment de la récupération, le périphérique imprime un rapport sur une ligne RECOVERED from ... sur la console de débogage, avec ces compteurs (et l'adresse fautive en cas de faute matérielle), et la ligne stats: en cours porte les totaux hangs, faults et faultpc ; un crash laisse donc une trace de diagnostic même s'il s'est auto-effacé.
== États de la LED de la carte
Le tableau suivant répertorie les motifs de la LED de la carte et leur signification.
[#tbl-led] .Motifs de la LED de la carte [cols="1,3", options="header"] |=== |Motif |Signification
|Fixe |Associé à un point d'accès - l'état de fonctionnement normal.
|Clignotement lent - 1 Hz |Wi-Fi configuré, en cours d'association ou pas encore associé.
|Clignotement rapide - 5 Hz |Aucun Wi-Fi configuré ; provisionnez-le via la console de gestion.
|Double flash - deux impulsions rapides, puis une pause |Scan en direct (continu) en cours ; le périphérique est désassocié et diffuse les points d'accès voisins vers la console de gestion jusqu'à ce qu'une touche soit pressée.
|Éteinte |USB non prêt. |===
== Travaux futurs
Le pont fonctionne sur l'USB Full-Speed natif du RP2040 (12 Mbit/s) ; le débit plafonne donc autour de 4 à 5 Mbit/s de charge utile TCP — ample pour un tableau de bord ou une surface de contrôle, mais un plafond dur. Le goulot d'étranglement est le lien USB, pas la radio Wi-Fi. Quelques pistes pour l'augmenter, en ordre approximatif d'effort :
Aucune de ces pistes n'est requise pour l'usage prévu du firmware ; ce sont des points de départ pour qui veut plus de débit.
== Bibliothèques amont et crédits
Ce firmware est assemblé à partir de plusieurs projets amont, répertoriés dans le tableau suivant.
[#tbl-upstream] .Composants amont [cols="1,2,1,4", options="header"] |=== |Composant (fichiers dans l'arborescence) |Amont |Licence |Rôle
|USBNet |https://github.com/mattmyne/usbnet[mattmyne/usbnet] |MIT a|Module USB-réseau de base, descripteurs USB et squelette principal, étendus ici en pont Wi-Fi.
usb_network.c, usb_network.h - réécrits en tant que pont L2usb_descriptors.c - modifié pour inclure un CDC-NCM composite + double CDC-ACMtusb_config.h - modifié|TinyUSB |https://github.com/hathach/tinyusb[hathach/tinyusb] |MIT a|Pile de périphériques USB CDC-NCM et CDC-ACM, utilisée telle que fournie dans le pico-sdk. +
|pico-sdk 2.2.0 |https://github.com/raspberrypi/pico-sdk[raspberrypi/pico-sdk] |BSD-3-Clause a|Support de la carte, système de compilation, et TinyUSB, lwIP et cyw43-driver fournis.
pico_sdk_import.cmake - copie exactelwipopts.h - exemple pico_w réduit|Exemple TinyUSB net_lwip_webserver
|Peter Lawrence et Ha Thach, via https://github.com/hathach/tinyusb[hathach/tinyusb]
|MIT
a|Base originale du collage USB-réseau ; réduit au chemin CDC-NCM. +
|lrndis |https://github.com/fetisov/lrndis[fetisov/lrndis] |MIT a|Influence de conception sur l'approche USB-réseau +
Les sources restantes sont originales à ce projet :
main.cconfig.cconfig.hconfig_proto.cconfig_proto.hserial_console.cserial_console.hwifi_scan.cwifi_scan.hdebug_console.cdebug_console.h== Licence
Ce projet est sous licence MIT ; voir link:LICENSE[LICENSE]. Les composants amont conservent leurs propres licences, comme indiqué dans <<upstream-libraries-and-credits,Bibliothèques amont et crédits>>.
== Architecture
=== Vue d'ensemble
Le périphérique est un périphérique USB CDC-NCM qui relie un hôte au Wi-Fi.
La Pico W exécute la station Wi-Fi et fait circuler les trames Ethernet entre le lien USB et la radio.
L'hôte exécute sa propre pile IP et détient l'identité réseau unique ; la Pico ne détient aucune adresse IP propre.
L'hôte n'a besoin de rien d'autre que des pilotes cdc_ncm et cdc_acm intégrés.
=== Pourquoi un pont de couche 2 par adoption de MAC
L'objectif est que l'hôte apparaisse sur le réseau Wi-Fi comme un périphérique ordinaire avec une seule adresse, tandis que la Pico reste invisible. Une contrainte physique de couche 1 détermine la manière d'y parvenir.
Une station Wi-Fi ne peut pas relier de manière transparente plusieurs adresses MAC. Lorsque l'Infineon CYW43 s'associe à un point d'accès en mode station, l'association accorde exactement une adresse MAC, et les trames de données 802.11 qu'elle émet sont liées à cette MAC de station. Sans les trames à quatre adresses (WDS), que le point d'accès doit également prendre en charge et autoriser, la radio ne peut pas transporter de trames pour le compte d'autres adresses MAC situées derrière elle. C'est la limitation bien connue selon laquelle un client Wi-Fi ne peut pas être ponté.Cette firmware ne contourne pas cette contrainte ; elle la supprime. L'interface USB de l'hôte est configurée pour adopter l'adresse MAC de la station Wi-Fi, de sorte qu'il n'existe qu'une seule MAC de bout en bout. L'hôte et la station partageant une même identité, le Pico est un simple pont de niveau 2 : il transmet les trames Ethernet telles quelles entre l'USB et le Wi-Fi, sans toucher à rien au-dessus de la couche 2. Le point d'accès voit une station unique et ordinaire ; l'hôte exécute lui-même DHCP, SLAAC et Neighbor Discovery et conserve les adresses qui en résultent.
Les conséquences sont énumérées dans le tableau ci-dessous.
[#tbl-bridge-effects] .Propriétés du pont d'adoption MAC [cols="1,3", options="header"] |=== |Propriété |Raison
|Une seule IP, détenue par l'hôte |Le Pico ne s'attribue aucune adresse : il n'y a donc qu'une seule identité, sur le sous-réseau même du point d'accès — et non un sous-réseau de partage privé.
|IPv4 comme IPv6 |La transmission s'effectue en couche 2, si bien que SLAAC, DHCPv6, les annonces de routeur et Neighbor Discovery traversent sans modification, sans code spécifique à une version.
|Pas de NAT ni de redirections de ports |Rien n'est réécrit : les connexions entrantes atteignent directement l'hôte ; il n'y a rien à masquer ni à mapper.
|Pas de pile Wi-Fi côté hôte
|Le Pico gère l'association et le supplicant ; l'hôte n'a donc besoin ni de wpa_supplicant, ni de base de données réglementaire, ni de pilote sans fil — seulement des pilotes de classe CDC.
|===
=== Chemin des données
Côté USB, TinyUSB fournit le périphérique CDC-NCM, et la MAC de l'interface hôte est définie à la MAC de la station au démarrage (usb_network_set_host_mac, avant l'énumération).
Hôte vers Wi-Fi : une trame arrive via tud_network_recv_cb, est mise en attente, puis, dans la boucle principale, est transmise sur la radio avec cyw43_send_ethernet.
Wi-Fi vers hôte : le gestionnaire input de la netif de la station est remplacé, de sorte que chaque trame reçue par le pilote cyw43 est confiée au pont au lieu de lwIP, mise en file, puis transmise à l'hôte avec tud_network_xmit.
Aucune interface IP lwIP n'existe côté USB, et la netif de la station ne porte aucune IP ; lwIP ne sert qu'à l'état de liaison de la netif cyw43 et au pool de pbuf.
=== Modèle de concurrence
Le firmware utilise pico_cyw43_arch_lwip_threadsafe_background.
Le Wi-Fi est pris en charge dans une IRQ d'arrière-plan et un contexte asynchrone, afin de ne jamais affamer l'USB, dont tud_task() s'exécute dans la boucle principale.
Cet agencement a une conséquence stricte pour le pont.
TinyUSB ne doit être touché que depuis la boucle principale, or les trames Wi-Fi sont reçues dans le contexte d'arrière-plan.
Le gestionnaire de réception Wi-Fi ne fait donc qu'insérer chaque trame dans un tampon circulaire, et la boucle principale vide ce tampon vers tud_network_xmit.
La violation de cette règle s'est manifestée côté hôte par NETDEV WATCHDOG: transmit queue timed out, avec une déconnexion de l'USB.
L'envoi des trames de l'hôte vers le Wi-Fi a lieu dans la boucle principale et maintient cyw43_arch_lwip_begin()/cyw43_arch_lwip_end() autour de l'appel cyw43.
=== Multicast et IPv6
Par défaut, une station ne reçoit que les groupes multicast auxquels elle s'est jointe. Le pont n'exécute aucune pile IP qui lui soit propre et ne rejoint rien ; sans intervention, la radio laisserait donc tomber le multicast dont dépend IPv6, et l'IPv6 de l'hôte ne fonctionnerait pas à travers le pont. Les annonces de routeur, la détection d'adresses dupliquées et la résolution d'adresses reposent toutes sur le multicast.
À chaque association, le firmware définit l'iovar CYW43 allmulti, si bien que la station délivre toutes les trames multicast, quel que soit le filtre.
Ceci est réalisé avec l'cyw43_ioctl publique sur WLC_SET_VAR, et non en passant en mode monitor, de sorte que le format des trames Ethernet est inchangé.
Un réseau placé derrière un commutateur à snooping IGMP ou MLD peut encore élaguer une partie du multicast que l'hôte n'a jamais sollicité ; un point d'accès domestique plat le diffuse en inondation.
=== Filtre d'auto-réflexion
Parce que l'hôte partage la MAC de la station, un multicast ou un broadcast envoyé par l'hôte est réinondé par le point d'accès vers la station, qui reçoit désormais tout le multicast, et serait renvoyé à l'hôte comme sa propre trame.
Le gestionnaire de réception abandonne toute trame Wi-Fi vers hôte dont la MAC source est la MAC de la station, car une telle trame ne peut être que la propre transmission de l'hôte réfléchie par le point d'accès.
Un pont ne doit pas renvoyer à une station ses propres trames ; l'abandon est compté comme refl dans les statistiques de débogage.
=== Les deux consoles série
La gestion et les diagnostics sont hors bande, sur deux fonctions CDC-ACM du même périphérique USB composite, plutôt que sur le réseau. C'est une conséquence délibérée de la transparence : le côté réseau ne transporte que le trafic de l'hôte et ne possède aucune adresse sur laquelle le Pico pourrait répondre.
La console de gestion (/dev/ttyACM0) exécute le protocole de configuration en ligne et est joignable dès que l'USB énumère, avant même que le Wi-Fi soit actif ; le dispositif est donc toujours provisionnable sans aucune IP.
La console de débogage (/dev/ttyACM1) est un flux de diagnostic en écriture seule — événements d'association, compteurs périodiques du pont et résumés facultatifs de paquets par trame — émis uniquement lorsqu'elle est activée ; elle ne coûte donc rien quand elle est désactivée et n'encombre jamais la console de gestion.
Aucune des deux consoles ne touche au réseau, donc aucune ne génère de trafic qu'un pare-feu hôte journaliserait.
=== Stockage de la configuration
Les paramètres d'exécution — jusqu'à huit profils d'identifiants Wi-Fi, l'index du profil actif, le pays réglementaire et l'indicateur de débogage — résident dans un enregistrement unique du dernier secteur de la flash, à l'écart de l'image du programme au début de la flash.
L'enregistrement s'étend sur plusieurs pages flash ; un save programme donc tout le secteur d'un coup (l'effacement est de toute façon à l'échelle du secteur).
Au démarrage, l'enregistrement n'est accepté que si son nombre magique et un CRC-32 sur le reste de la structure correspondent exactement ; toute discordance charge à la place les valeurs par défaut de compilation.
Il n'y a pas de migration versionnée : un changement de disposition de l'enregistrement échoue simplement au contrôle magique/CRC et revient aux valeurs par défaut, ce qui est acceptable car une valeur par défaut compilée en dur initialise encore le premier profil. (C'est pourquoi la mise à niveau vers v1.1.0, qui a élargi l'enregistrement en une liste de profils, abandonne un enregistrement antérieur à la v1.1.0.)
Un save réécrit l'enregistrement via flash_safe_execute, qui coordonne l'effacement et la programmation avec l'autre cœur et désactive les interruptions pendant les quelques millisecondes que cela prend.
Cette écriture s'exécute depuis le gestionnaire de console pendant que le verrou lwIP est tenu ; la brève fenêtre d'interruptions désactivées suspend le service Wi-Fi d'arrière-plan, ce qui est acceptable pour une sauvegarde peu fréquente initiée par l'hôte.
=== Particularités du matériel et de la chaîne d'outils
Ces comportements du RP2040, de l'Infineon CYW43 et du pico-sdk coûtent un temps de débogage réel et sont faciles à réintroduire.
==== Le service d'arrière-plan exige un TinyUSB limité à la boucle principale
C'est la règle de concurrence énoncée dans <<concurrency-model,Modèle de concurrence>>. Avec le service d'arrière-plan, la réception Wi-Fi s'exécute dans un contexte qui ne peut pas appeler TinyUSB ; c'est pourquoi l'anneau de transmission différée existe.
==== L'association n'est pas une IP
L'helper cyw43 cyw43_tcpip_link_status ne signale CYW43_LINK_UP qu'une fois que la station détient une adresse IP, et cette station n'en prend délibérément jamais.
L'état d'association est donc lu depuis l'indicateur de liaison de la netif (netif_is_link_up), que le pont utilise également pour décider de transmettre ou non.
==== Une identité modifiée nécessite un nouvel identifiant de produit
Un périphérique composite qui modifie son ensemble d'interfaces en conservant le même identifiant de vendeur et de produit USB peut se voir servir un descripteur en cache par l'hôte.
L'identifiant de produit est dérivé des classes activées ; l'ajout de chaque fonction CDC-ACM le décale donc (cafe:4020 vers cafe:4022) et l'hôte relit la nouvelle disposition.
==== Les compilations Release transforment assert en no-op
Un CMAKE_BUILD_TYPE=Release définit NDEBUG, ce qui compile assert() en rien ; une allocation protégée uniquement par une assertion passe donc outre un échec et déréférence NULL.
Un crash du firmware avant l'exécution de tud_task apparaît côté hôte comme une erreur d'énumération USB -110, un délai de lecture du descripteur de périphérique dépassé.
De véritables gardes NULL sont utilisées plutôt que des assertions sur le chemin de démarrage.
=== Environnement vérifié
La configuration confirmée comme fonctionnelle utilise pico-sdk 2.2.0, la chaîne d'outils GCC arm-none-eabi, ainsi que TinyUSB et lwIP fournis avec le pico-sdk, sans modification.
La carte de référence est la Pico W (RP2040).
La Pico 2 W (RP2350) devrait fonctionner.
Une compilation produit un build/pico-usb-wifi.uf2 d'environ 670 Ko.