Program your microcontrollers from WSL2 with USB support

USB is one of the most important computer interfaces for an embedded developer. It’s how we flash, test and debug our hardware. It is supported on the 3 major desktop operating systems allowing developers to use the OS they are most productive in. Yet, despite having “universal” in its name, USB support isn’t the same everywhere. A device programmer may only have a Windows binary, while a compiler may only work in Linux.

When Microsoft released Windows Subsystem for Linux (WSL), Windows developers were excited about the potential of using both Windows and Linux tools from one machine. At launch, it mostly delivered on that promise with one big caveat: no USB support.

Of course there were hacks with copy/pasting and custom network drivers, but none of them worked reliably. But late last year Microsoft announced that they’ve added USB support to WSL! I was certainly keen to try it out and over the past few months I’ve been testing it as part of my Windows development workflow. It’s now in a state where I can recommend for use to other developers, though there are a few gotchas to make note of (we’ll discuss that later.) Today I will walk you through the basic steps of programming an embedded device over USB using WSL.

What you need to get started

Windows 10 or 11

Obviously to do development on Windows…you need a copy of Windows. What’s less obvious is that WSL & USB are both supported by Windows 10. You just need Windows 10 version 2004 or the latest build of Windows 11.

WSL

There are a few ways to install WSL and you may have WSL already installed on your machine. But before we can get to using USB we need to check which Linux kernel is currently installed. It needs to be a kernel version of 5.10.60.1 or later. You can check by running uname -a.

If you installed WSL using the command line (see docs), then you may have a recent-enough kernel via Windows Update. However, I recommend installing Windows Subsystem for Linux Preview from the Microsoft Store. It’ll keep your WSL kernel up to date without having to wait for a service pack.

You also need a Linux distribution and you can use any distribution you like from the Microsoft Store. I prefer to keep things easy and use Ubuntu.

usbipd-win

This is what makes all the magic happen. The instructions are pretty straightforward. It’s mostly a matter of installing the usbipd-win program on the Windows side and a little bit of configuration on the Linux side.

(If you’re curious how this works, make sure to check out the underlying USB/IP protocol. It’s been part of the Linux kernel for years!)

Windows Terminal (optional)

Strictly not needed to work with WSL or USB, Windows Terminal is a much more modern terminal than what comes stock with Windows or PowerShell. It also offers a tabbed interface which is especially useful when needing to switch between Windows and WSL while managing your USB devices.

Using USB from WSL

With the dependencies out of the way, we can start interacting with a USB device.

Attaching a device to WSL from Windows

You’ll need to get comfortable with usbipd as it’s how we’ll enable USB to be used with WSL. The basic steps are:

  1. Launch Windows Terminal as an Administrator
  2. Run usbipd wsl list to list all the devices on your machine, noting the BUS-ID for the device you want to use
  3. Run usbipd wsl attach --busid={BUS-ID} to use the device from WSL
  4. Run usbipd wsl list again to verify that the device is now being used by WSL

Accessing a USB device from WSL

At this point you have a real USB device in Linux. To quickly verify that everything works:

  1. Open Ubuntu (doesn’t need to be as an Administrator)
  2. Run lsusb and note the USB device
  3. Start interacting with the device like you normally would

I followed the Golioth Getting Started Guide for the ESP32 using the Linux instructions and it worked like a treat. But I’ve also tested the mainline Zephyr, Arduino CLI & an STM32 with dfu-util.

Running the west flash command in WSL2

What to keep in mind

If you’re new to Linux, you’ll need to get a little familiar with tools like udev and how to find your device paths, but there are a ton of resources online. Some of the devices I tried already had the udev rules defined in my release of OpenOCD but one didn’t. It took me awhile to figure out the ancient incantation to make udev happy. Usually If you’re using an open source tool they provide pretty good documentation for usage on Linux, so check the manual.

One challenge with this approach is that usbipd tries to re-attach a device if it reboots. This can happen when you flash firmware. It doesn’t always recover, especially if the Bus ID changes, so you may need to re-attach the device. It’s a little annoying if you forget, so make sure you keep that admin console open.

Lastly, while many USB, USB/Serial & HID devices are included with the current WSL kernel, some may not be by default. Leave a comment on this issue if you want to request additional drivers.

USB all the things!

I’m grateful that Microsoft has added support for USB in WSL. It’s not only helpful for Golioth developers but anyone else doing embedded development on Windows. I didn’t go into every bit of detail in this post so if you have any questions or run into issues, feel free to reach out to me on Twitter or join our Discord.

Talk with an Expert

Implementing an IoT project takes a team of people, and we want to help out as part of your team. If you want to troubleshoot a current problem or talk through a new project idea, we're here for you.