what? judgy? Im not the best at naming ok?
Turn a Raspberry Pi Pico 2 W into a USB WiFi dongle using MicroPython. Plug it into any computer and get internet access over USB. Yes, you could just buy a WiFi dongle for less than the cost of the Pico. No, that didn't stop us.
The Pico pretends to be a USB Ethernet adapter (CDC-ECM), connects to WiFi, and bridges traffic between the two using a hand-rolled network stack written in MicroPython -- a language famous for its speed in the same way that sloths are famous for their agility.
- Host computer sees a USB Ethernet adapter and gets an IP via our homebrew DHCP server.
- The Pico connects to WiFi using credentials you hardcode into a Python file like an animal.
- TCP and UDP traffic gets NAT'd through an application-level network stack that was definitely not written while reading RFC 793 with increasing despair.
- DNS requests are proxied so you can actually resolve domain names, which feels like a miracle every time it works.
101.9 KB/s. That is not a typo. We reimplemented networking in an interpreted language running on a microcontroller clocked at 150 MHz. You will feel the mass of every electron.
And well? I can load my own website. So I'd say it's actually usable for things. Unlike most of my things.
- Raspberry Pi Pico 2 W -- the $6 board doing $6 worth of work
- MicroPython firmware (v1.23+ recommended)
usb-devicepackage installed viamip- A willingness to accept that this exists
Download the latest MicroPython .uf2 for the Pico 2 W from micropython.org. Hold BOOTSEL while plugging in the Pico, then drag the .uf2 file to the mounted drive.
Connect to the Pico's REPL (e.g., via Thonny or mpremote) and run:
import mip
mip.install("usb-device")mpremote cp config.py :config.py
mpremote cp ecm_interface.py :ecm_interface.py
mpremote cp netstack.py :netstack.py
mpremote cp boot.py :boot.py
mpremote cp main.py :main.pyOr use the included flash.sh script, which will auto-detect your Pico, fight ModemManager for control of the serial port, and upload everything in one go. It works surprisingly often.
Open config.py and set your WiFi network name and password:
WIFI_SSID = "YourNetworkName"
WIFI_PASS = "YourPassword"Yes, plaintext credentials on an unencrypted filesystem. The security model here is "don't lose the Pico."
Unplug and re-plug the Pico. It will boot, connect to WiFi, and appear as a USB Ethernet adapter. If all goes well, which historically is a big "if."
[Host Computer] --USB--> [Pico 2 W: CDC-ECM] --WiFi--> [The Internet]
^ ^
| |
Fake Ethernet Application-level NAT
adapter that written in a language
actually works that shouldn't be doing
(somehow) this (but is)
| File | What It's Doing Down There |
|---|---|
config.py |
WiFi credentials in plaintext, network settings, and connection limits that exist because we ran out of RAM, not because we planned ahead |
ecm_interface.py |
Convinces your OS that this Pico is a real Ethernet adapter. USB descriptor wizardry. Contains dead code that survived review because the reviewer was also the author |
netstack.py |
571 lines of hand-rolled Ethernet/IP/TCP/UDP parsing, DHCP server, DNS proxy, and NAT. Every networking course project's final boss |
boot.py |
Runs before anything else to hijack the USB bus. 12 lines that can brick your REPL access if you look at them wrong |
main.py |
Connects to WiFi, starts the bridge loop, and garbage-collects every 100 iterations because MicroPython will eat your RAM like it's at a buffet |
flash.sh |
Deployment script that kills ModemManager, probes serial ports, and uploads files. Enterprise-grade DevOps for a $6 board |
- Throughput: 50-100 KB/s. You can load web pages. You probably can't stream video. You definitely can't stream video.
- Connections: 128 TCP, 256 UDP at a time. Configurable, but the Pico has 520 KB of RAM, so go ahead and raise those limits -- see what happens.
- No ICMP forwarding: You can ping the Pico itself (it responds to prove it's alive), but pings to the internet go nowhere.
tracerouteis right out. - Single client: One host computer at a time. If you need to share WiFi with multiple devices, you want an actual router.
- TCP state machine: Written by someone who learned TCP by writing this TCP implementation. Connection edge cases may produce outcomes that are, charitably, creative.
| OS | Status |
|---|---|
| Linux | Works out of the box. CDC-ECM is natively supported. The one time Linux's driver model paid off. |
| macOS | Works out of the box. Apple supporting open standards -- mark the calendar. |
| Windows 10+ | Requires registry tweaks, sacrificial offerings, and driver configuration. So, par for the course. |
boot.py takes over the USB interface at startup. If you need to get back in:
- Hold BOOTSEL during reset to enter USB mass-storage mode, then re-flash MicroPython.
- Or use
mpremotefrom another machine:mpremote rm :boot.py mpremote reset
Because "I could just buy a WiFi dongle" is the coward's path, and we wanted to understand networking at a level that makes us mass uncomfortable at parties.
Do whatever you want with this. If you find a way to make money from a MicroPython WiFi dongle that maxes out at dial-up speeds, you deserve it.