Watches for a UF2 bootloader drive to appear, copies a firmware file to it, waits for the device to reset and disappear, then goes back to waiting. Repeats until you cancel it.
No drag-and-drop for every device! Just run the script, plug a board in, it flashes, unplug it, plug the next one in.
Built for short production runs: batch-programming a stack of boards with the same firmware, where you want to plug, wait for the LED/prompt, unplug, and immediately plug the next one in without touching a file browser or a keyboard each time.
Also useful for:
- Bench iteration where you rebuild and reflash the same board repeatedly during development.
- Test-jig or panel-testing workflows where a free hand and no GUI interaction matters.
- Polls for a removable volume with the target label (default
RPI-RP2). - Confirms it's actually the expected device, not just something else that happens to share the label, by checking the underlying device's USB vendor:product ID against a known list, and that the filesystem is what's expected. If it doesn't match, the volume is ignored and the script keeps waiting.
- Copies the nominated file across.
- Waits for the device to disappear. UF2 bootloaders reset themselves once the file lands, which unmounts the drive.
- Repeats from step 1.
Ctrl+C exits cleanly at any point in the loop.
Three independent, single-file scripts, no shared library between them. Copy the one for your OS.
| File | Platform | Status |
|---|---|---|
batch-uf2 |
Linux | ✅ Tested and working |
batch-uf2-macos |
macOS | |
batch-uf2.ps1 |
Windows (PowerShell) |
Out of the box, all three are configured for RP2040 and RP2350 (Raspberry Pi Pico / Pico 2 and boards built on those chips) in BOOTSEL mode. See Adding target devices to point it at something else.
- Linux: bash,
findmnt,lsblk,udevadm. All standard on any systemd-based distro (Ubuntu, Mint, Debian, Fedora, etc). - macOS: bash and
system_profiler, both ship with macOS. This variant is the least tested of the three, see Known limitations. - Windows: PowerShell 5.1 or later (ships with Windows 10/11), the in-box
PnpDevicemodule.
chmod +x batch-uf2
./batch-uf2 /path/to/firmware.uf2To make it available everywhere:
sudo cp batch-uf2 /usr/local/bin/
sudo chmod +x /usr/local/bin/batch-uf2
batch-uf2 /path/to/firmware.uf2chmod +x batch-uf2-macos
./batch-uf2-macos /path/to/firmware.uf2.\batch-uf2.ps1 -FirmwarePath C:\path\to\firmware.uf2If script execution is blocked, allow it for the current session only:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy BypassLeave it running and connect boards one after another. Press Ctrl+C to stop.
Every variant is driven by two settings near the top of the file:
- Volume label — the name the drive mounts under. Linux/macOS:
LABEL. Windows:$Label. - Known boot IDs — an allowlist of accepted USB vendor:product ID pairs. Linux/macOS:
KNOWN_BOOT_IDS, a bash array of"vid:pid"strings (lowercase hex, no0x). Windows:$KnownBootIds, an array of"VID_xxxx&PID_xxxx"strings.
To target a different UF2 board:
- Put it into its bootloader mode and note the volume name it mounts as. That's your new
LABEL/$Label(or add a second label check if you need to support two different boards, the current scripts assume one label at a time). - Find its USB vendor and product ID:
- Linux:
lsusbwhile it's connected, orudevadm info -a -p $(udevadm info -q path -n /dev/sdX)for the exact attributes the script reads. - macOS:
system_profiler SPUSBDataTypewhile it's connected, look forVendor ID:andProduct ID:under the device's entry. - Windows: Device Manager → the disk drive's Properties → Details → Hardware Ids, or
Get-PnpDevice -Class DiskDrive | Select-Object InstanceId.
- Linux:
- Add the pair to
KNOWN_BOOT_IDS/$KnownBootIdsin the matching format for that platform.
Boards from the same vendor with a different chip (like RP2040 → RP2350) usually just need a new product ID added alongside the existing ones. A board from an entirely different vendor needs its own full vendor:product pair, since the list holds complete pairs rather than a fixed vendor ID, adding one doesn't affect matching for any other entry.
- The vendor:product ID check protects against a coincidental label collision (some unrelated drive happens to be named the same thing).
- The macOS variant parses
system_profiler's text output for the USB device fields. It hasn't been verified against a real Mac. If the guardrail doesn't seem to trigger, runsystem_profiler SPUSBDataType | grep -B8 "<your label>"and check the field names in the script'sget_usb_idsfunction still match. - The Windows variant walks up to four levels of the PnP device tree looking for the vendor:product ID. That covers the layouts this normally takes, but hasn't been verified against real hardware either. If it's not matching, check
Get-PnpDevice -Class DiskDrive | Select-Object InstanceIdagainst whatTest-IsRP2Driveis walking. - One nominated file per run. The script doesn't inspect the connected board and pick firmware for it, thought that might be possible if you point it at a wrapper script instead of a raw
.uf2.