
Firmware for Raspberry Pi Pico W that creates a driverless USB Wi-Fi adapter with transparent layer-2 bridging, WPA2/WPA3 authentication, and out-of-band management console.
= pico-usb-wifi :toc: macro :toclevels: 3 :idprefix: :idseparator: -
pico-usb-wifi is firmware for the Raspberry Pi Pico W that turns it into a driverless USB Wi-Fi adapter, enumerating as a USB CDC-NCM device.
:figure-caption: AI Slop
.pico-usb-wifi Diagram image::images/openrouter-banana2-rpi-pico.png[]
The firmware works as a transparent layer-2 bridge that forwards frames between the Pico W's wireless interface and its USB interface. The host's USB interface adopts the Pico W Wi-Fi station's MAC address, which provides a single MAC and IP identity end to end.
No host-side driver, kernel module, or wireless stack is required; see <<no-host-side-wi-fi-stack,No Host-Side Wi-Fi Stack>>.
The host only needs the in-box cdc_ncm and cdc_acm drivers that ship with every modern Linux, macOS, Windows, and mobile OS.
== Features
pico-usb-wifi provides these features:
.A Real World Situation image::images/slop_2.png[]
== Why This Exists
I needed a USB Wi-Fi adapter to use in an upcoming embedded Linux project. I did not have a cheap USB Wi-Fi dongle, so instead of going and buying one from a brick-and-mortar store for five USD, I spent two days of a long holiday weekend and about one million Claude Code tokens building this firmware.
白一百, Author of pico-usb-wifi
Google said it was not feasible:
.pico-usb-wifi "Not Feasible" image::images/gemini_says_not_possible.png[]
toc::[]
== No Host-Side Wi-Fi Stack
Unlike a USB Wi-Fi dongle, this adapter exposes only an Ethernet-like interface to the host. The Pico W contains the entire wireless side: the radio, the association, the WPA2/WPA3 supplicant, and the regulatory domain.
This allows systems to avoid installing wpa_supplicant, the cfg80211/mac80211 wireless stack, a regulatory database, and chipset firmware or a vendor driver.
Provisioning the Wi-Fi credentials happens on the device, over its out-of-band management console, not through any host-side wireless tooling.
This keeps a constrained or appliance host or one without wireless drivers, or whose vendor kernel lacks them, the ability to connect to wireless networks using only universal CDC class drivers.
== How It Works
[#fig-topology] .Topology Diagram image::images/topology.svg[Transparent layer-2 bridge topology,820]
The host's USB interface is given the Pico W's Wi-Fi station's MAC address, so a single MAC exists end to end and the Pico W can forward Ethernet frames verbatim between USB and Wi-Fi. A Wi-Fi station cannot bridge several MAC addresses, so collapsing the host and the station onto one MAC is what makes a transparent bridge possible at all. The full rationale, data path, and IPv6/multicast handling are described in <<architecture,Architecture>>.
== Host Requirements
The host requires the in-tree cdc_ncm and cdc_acm drivers.
Both have been part of mainline Linux for well over a decade, so any currently supported kernel includes them.
No out-of-tree module, firmware blob, or vendor driver is involved.
The same class drivers exist on macOS, Windows 10 and later, Android, and iOS.
[NOTE] No other operating systems were tested.
== Building
The project is a standard pico-sdk CMake project. It needs the ARM embedded toolchain, CMake, a build backend (Ninja or Make), Python 3, and a checkout of the pico-sdk with its submodules. The TinyUSB and lwIP bundled in the pico-sdk are used unmodified.
=== Dependencies
On Arch-based systems (Arch, CachyOS, Manjaro), the toolchain comes from the official repositories:
arm-none-eabi-newlib supplies the embedded C library and headers; without it the cross-compiler cannot find stdint.h and similar headers.
libusb is only needed for picotool, which the pico-sdk builds from source during the first configure to generate the UF2; no separate picotool package is required.
=== Build Steps
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
The -G Ninja flag is optional; omit it to use the default Make generator (then cmake --build build -j).
wifi_config.h holds the compile-time default credentials and is gitignored.
Leaving it blank produces an image with no baked credentials that is provisioned at runtime over the management console (<<management-console,Management Console>>); filling it in bakes a default network.
== Writing the Firmware
The steps here load the firmware onto the board.
. Hold the BOOTSEL button while connecting the board over USB.
It mounts as an RPI-RP2 USB mass-storage volume, commonly under /run/media/<user>/RPI-RP2 or /media/<user>/RPI-RP2.
. Copy pico-usb-wifi.uf2 onto that volume.
The board reboots into the firmware automatically.
. Connect the board to the host that is to receive Wi-Fi connectivity.
== Using On A Linux Host
Plug the device into the host and provision its Wi-Fi credentials once over the management console (<<management-console,Management Console>>). The host's interface then behaves like any wired connection on the access point's network.
A host that manages interfaces automatically (NetworkManager, systemd-networkd, dhcpcd) needs no setup: it runs DHCP and SLAAC over the bridge and receives a single IPv4 address, an IPv6 address, the access point's gateway, and DNS, exactly as a wired client would.
There is no device-side address or gateway to configure, because the Pico holds none.
The interface's MAC address is the Wi-Fi station's MAC, which is how one identity is presented to the network.
The ip command output here shows the resulting interface: an ordinary DHCP/SLAAC client on the access point's own subnet, with the station's MAC and no trace of the Pico.
== Management Console
The management console is the configuration front end, on the first CDC-ACM serial function (commonly /dev/ttyACM0).
It is reachable as soon as the device enumerates, before Wi-Fi is associated, so provisioning never requires a network.
Open it with a serial terminal such as picocom or screen; the baud rate is irrelevant for USB CDC.
The console echoes input and shows a prompt, and every command prints the full device state.
Wi-Fi authentication is either WPA2-PSK or WPA3-SAE (AES).
The password is the network's passphrase or open when the password is left blank.
A password-protected profile uses WPA2/WPA3 transition mode, so it joins either kind of access point.