Hey everyone! If you’ve been working on embedded Rust projects with probe-rs on Windows Subsystem for Linux 2 (WSL2), you’ve probably run into some hurdles getting everything to play nicely. I’ve spent a fair bit of time figuring this out, and I’m excited to share the simplest solution I’ve found to make probe-run work seamlessly. I’ll also touch on a couple of other approaches for those who want a more native WSL2 setup. Let’s get into it.

The Best Approach: Using the Windows Binary from WSL2

I came across this method thanks to the brilliant advice from @tgross35 in the Rust embedded community, who provided this solution in its entirety. The easiest and most reliable way to get probe-run working in WSL2 is to directly call the Windows version of the tool. WSL2 has this great ability to run Windows executables without much hassle, even handling path translations between Windows and Linux automatically. This means you can skip the headache of USB forwarding or complex configurations.

Here’s how to set it up:

  1. Install probe-run on Windows: Make sure probe-run is installed on the Windows side of your system. If you’ve got Rust set up there, it’s as simple as running:
    cargo install probe-run
  2. Configure your project in WSL2: In your Rust project within WSL2, update (or create) the .cargo/config.toml file. Set the runner to probe-run.exe to explicitly use the Windows binary. Here’s a sample configuration for a target like the nRF9160:
    [target.'cfg(all(target_arch = "arm", target_os = "none"))']
    runner = "probe-run.exe --chip nRF9160_xxAA"
     
    rustflags = [
      "-C", "linker=flip-link",
      "-C", "link-arg=-Tlink.x",
      "-C", "link-arg=-Tdefmt.x",
      # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
      # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
      "-C", "link-arg=--nmagic",
    ]
     
    [build]
    target = "thumbv8m.main-none-eabihf"
     
    [alias]
    rb = "run --bin"
    rrb = "run --release --bin"
  3. Run your project normally: From WSL2, just execute cargo run, and it will leverage the Windows-installed probe-run.exe to interact with your hardware. That’s it!

What makes this so effective is WSL2’s seamless integration with Windows. It runs the binary directly and takes care of the details, like path mapping. Since the Windows executable handles communication with the debug probe, there’s no need to wrestle with USB access in WSL2. Honestly, this has been a lifesaver for my workflow, especially after struggling with other methods.

Another Path: Forwarding USB Ports to WSL2

If you’d rather run probe-rs or probe-run natively within WSL2 instead of relying on a Windows binary, you can forward USB ports from Windows to WSL2. I’ve tried this approach using usbipd, and while it does work, I found it far more cumbersome and less reliable than the Windows binary method. I like the simplicity of the first solution way better, but I’ll walk you through this one in case it fits your needs.

Here’s how it works:

  1. Install usbipd on Windows: This tool allows you to share USB devices between Windows and WSL2. You can install it with:
    winget install usbipd
  2. Identify your USB device: Open a Windows terminal (with Administrator privileges) and list connected USB devices:
    usbipd list
  3. Bind the device: Select the bus ID of your debug probe (e.g., a J-Link) and bind it for forwarding:
    usbipd bind --busid <BUSID>
  4. Attach it to WSL2: In your WSL2 terminal, you may need to install USB tools first (on Ubuntu, use sudo apt install linux-tools-common linux-tools-$(uname -r)). Then attach the device:
    sudo usbip attach -r 127.0.0.1 -b <BUSID>
  5. Verify it’s accessible: Check if the device appears in WSL2 with lsusb, and you should be able to use probe-rs natively.

A heads-up: this method isn’t without its frustrations. You often have to re-bind the device after a reboot or disconnection, and it sometimes just doesn’t connect properly. Plus, you’ll likely need admin rights on Windows to manage USB devices. I gave it a shot, but it felt like more trouble than it was worth compared to the Windows binary approach.

A More Involved Option: Debug Servers

Some developers opt to set up a debug server, like a J-Link server, on the Windows side and connect to it from WSL2. It’s a workable solution, but in my opinion, it adds unnecessary complexity. There are extra steps and potential points of failure, especially when the Windows binary method is so straightforward. I’ve experimented with similar setups for other tools in the past, but for probe-rs, I didn’t see the benefit.

My Takeaway

If you’re looking for the path of least resistance, I strongly recommend sticking with the first method—using probe-run.exe directly from WSL2. It’s been incredibly reliable for me, and I don’t have to deal with the quirks of USB forwarding or additional setups. Having tried the usbipd route, I can confidently say the Windows binary approach is far superior in terms of simplicity and consistency. However, if you have a specific need to run everything natively in WSL2—perhaps to avoid Windows dependencies altogether—the USB forwarding option might be worth exploring despite the extra effort.

Have you encountered similar challenges with WSL2 in embedded development? Or do you have a different solution that’s worked for you? I’d be curious to hear your thoughts—feel free to share in the comments or reach out. Here’s to smoother debugging and more time spent coding!