Skip to content

Repository files navigation

r2p2-installer

Automatically flash RP2040 or RP2350 firmware from a Raspberry Pi Zero on boot.

When an RP2040/RP2350 board is connected in BOOTSEL mode, the Pi Zero detects the USB MSC drive by its volume label and copies the appropriate UF2 file. The newest UF2 file (by modification timestamp) in the target directory is used, so updating firmware is as simple as dropping a new file in. Shorting a GPIO pin to GND activates "nuke mode" which writes flash_nuke.uf2 instead (erases the target flash completely).

Once set up, this system runs fully headless. No display, keyboard, or SSH session is required during operation. Power on the Pi Zero, connect an RP2 board in BOOTSEL mode, and flashing happens automatically. The ACT LED signals progress.

Background

In workshops involving RP2040 or RP2350 boards, participants often encounter difficulties when trying to install UF2 firmware files from their company-issued PCs. Stringent security policies may block USB Mass Storage devices or prevent copying files to external drives.

This system provides a "Firmware Flashing Station" using a Raspberry Pi Zero. Workshop owners can prepare this dedicated installer to ensure all participants can successfully flash the required firmware regardless of their own PC's security settings.


Hardware Requirements

Raspberry Pi Zero W

Pi Zero W (with WiFi) is required. WiFi is used for SSH and internet access, leaving the USB port exclusively for OTG host mode (flashing RP2 boards).

Pi Zero (without WiFi) is not supported. Its single USB port cannot serve both OTG host mode and network access simultaneously.

  • Micro-USB OTG adapter (micro-USB male to USB-A female) for connecting RP2 boards
  • USB-A to micro-USB cable to connect to the RP2040/RP2350 board
  • Optional but recommended: 10 kOhm resistor from GPIO17 to 3.3V (hardware pull-up for nuke pin)

OS Setup

Recommended: Raspberry Pi OS Lite (64-bit or 32-bit), Bookworm release.

  1. Download from https://www.raspberrypi.com/software/operating-systems/
  2. Flash to a microSD card using Raspberry Pi Imager
  3. In Imager's OS Customisation settings:
    • Enable SSH
    • Set username (pi) and password
    • Configure WiFi (SSID and password)
    • Set locale and timezone
  4. Boot the Pi Zero W and SSH in: ssh pi@raspberrypi.local

Pre-installed packages required by this installer (all present on Raspberry Pi OS):

  • util-linux (provides mount, umount, lsblk)
  • systemd
  • raspi-gpio (for GPIO pull-up; install if missing: sudo apt install raspi-gpio)

Installation

This is a one-time setup. UF2 firmware files are deployed separately after installation and can be swapped out at any time without touching the service.

Step 1: Clone this repository on the Pi Zero W

SSH in over WiFi, then:

sudo apt update && sudo apt install -y git
git clone https://github.com/picoruby/r2p2-installer.git
cd r2p2-installer

Step 2: Run setup

sudo bash setup.sh

This script:

  1. Installs flash_installer.sh to /opt/r2p2-installer/
  2. Creates /opt/r2p2-installer/firmware/rp2040/ and firmware/rp2350/
  3. Installs flash_installer.service to /etc/systemd/system/
  4. Enables and starts the service immediately

The service starts polling right away. UF2 files can be added at any time (see Deploying Firmware below).

Step 3: Verify

# Check service status
systemctl status flash_installer.service

# Watch live logs
journalctl -u flash_installer.service -f

You should see:

[r2p2-installer] Polling for RPI-RP2 / RP2350 drives every 2s...

That is all. The Pi Zero is now ready for headless operation.


USB Connection Diagram

The Pi Zero has two micro-USB ports. The one labeled USB (away from the corner, closer to the center of the board) is the OTG port. The other is power.

  Pi Zero["USB" port (PWR IN)] --- [Micro-USB to 5V power (Laptop USB port, mobile battery, or AC adapter)]
  ["USB" port (OTG)]
         |
  [Micro-USB to Type-A *OTG* adapter](*1)
         |
  [Type-A to Micro-B cable](*2)
         |
  [RP2040/RP2350 board in BOOTSEL mode]

Combining these should work for this purpose:


GPIO Nuke Pin (GPIO17, Board Pin 11)

In normal operation the pin is held HIGH internally (no jumper needed). To activate nuke mode, short Pin 11 to GND before connecting the RP2 board.

Pi Zero 40-pin GPIO header (physical pin numbers):

    3.3V  [ 1][ 2]  5V
    SDA1  [ 3][ 4]  5V
    SCL1  [ 5][ 6]  GND
   GPIO4  [ 7][ 8]  TXD0
     GND  [ 9][10]  RXD0
  GPIO17  [11][12]  GPIO18   <-- NUKE PIN (BCM 17)
  GPIO27  [13][14]  GND
  GPIO22  [15][16]  GPIO23
    3.3V  [17][18]  GPIO24
   MOSI   [19][20]  GND
   MISO   [21][22]  GPIO25
   SCLK   [23][24]  CE0
     GND  [25][26]  CE1

To enable nuke mode:
  Connect Pin 11 (GPIO17) <---[jumper]---> Pin 9 or Pin 6 (GND)

To return to normal flash mode:
  Remove the jumper.

Recommended: add a 10 kOhm resistor between Pin 11 (GPIO17) and Pin 1 (3.3V) as a hardware pull-up. This prevents the pin from floating if raspi-gpio is unavailable and the sysfs fallback is used instead.


Usage

Normal flash

  1. Put your RP2040 or RP2350 board into BOOTSEL mode (hold BOOTSEL, connect USB, release BOOTSEL)
  2. Connect the board to the Pi Zero via the OTG adapter
  3. The Pi Zero will:
    • Detect the RPI-RP2 or RP2350 drive
    • Mount it temporarily
    • Copy the appropriate UF2 file
    • Unmount (the RP2 board reboots automatically)
  4. LED goes off when done

Nuke mode (erase flash)

  1. Short GPIO17 (Pin 11) to GND (Pin 9) with a jumper wire
  2. Connect the RP2 board in BOOTSEL mode
  3. flash_nuke.uf2 is written; the board's flash is erased
  4. Remove the jumper to restore normal flash mode

Headless Operation

Once setup.sh has been run, no display, keyboard, or SSH session is needed during normal operation.

Daily workflow:

  1. Power on the Pi Zero (boots in ~20 seconds, service starts automatically)
  2. Connect an RP2 board in BOOTSEL mode via the OTG adapter
  3. Watch the ACT LED: solid on during flash, off when done
  4. Disconnect the board; repeat for the next one

Updating firmware: SSH in over WiFi, drop a new UF2 file into the appropriate directory, disconnect. The next flash uses it automatically. See Deploying Firmware.


LED Behavior

State ACT LED
Booting (before service start) Blinks (SD card activity)
Service running, idle Off (= ready)
Actively copying firmware Solid on
Flash completed successfully Off
Flash failed Blinks 10 s, then off

"Off" after boot means the service is running and waiting for an RP2 board. Wait for the LED to go off before connecting a board.


Configuration

Edit /opt/r2p2-installer/flash_installer.sh to change these variables near the top:

Variable Default Description
GPIO_NUKE_PIN 17 BCM GPIO number for nuke mode pin
POLL_INTERVAL 2 Seconds between drive detection scans
FIRMWARE_DIR /opt/r2p2-installer/firmware Directory containing UF2 files
MOUNT_POINT /tmp/rp2-mount Temporary mount point

After editing, restart the service:

sudo systemctl restart flash_installer.service

Deploying Firmware

Placing and updating UF2 files is the primary day-to-day operation. The service is always running; just drop a file in and the next flash will use it. No service restart, no re-running setup.

The installer always picks the newest file by modification timestamp from each directory, so you can freely accumulate versions or replace them. Filenames do not matter.

Directory layout

/opt/r2p2-installer/firmware/
  flash_nuke.uf2        (bundled - do not remove)
  rp2040/               <- drop RP2040 UF2 files here
  rp2350/               <- drop RP2350 UF2 files here

Download on the Pi Zero W

SSH in over WiFi and run:

# RP2040 firmware
sudo bash download_firmware.sh rp2040 \
  https://github.com/picoruby/picoruby/releases/download/3.4.2/R2P2-for-RP2040.uf2.gz

# RP2350 firmware
sudo bash download_firmware.sh rp2350 \
  https://github.com/picoruby/picoruby/releases/download/3.4.2/R2P2-for-RP2350.uf2.gz

Checking which file will be used

# Shows newest file first
ls -lt /opt/r2p2-installer/firmware/rp2040/
ls -lt /opt/r2p2-installer/firmware/rp2350/

Removing old files

Old files are ignored automatically, but can be deleted to keep things tidy:

sudo rm /opt/r2p2-installer/firmware/rp2040/old_firmware.uf2

Troubleshooting

Drive not detected

# Check if the drive label appears
ls -la /dev/disk/by-label/

# List USB block devices
lsblk -rno NAME,LABEL,TRAN | grep usb

If no label appears, the board may not be in BOOTSEL mode or the OTG cable/adapter may be faulty.

Mount fails

# Check recent kernel USB messages
dmesg | tail -30

# Check if another process holds the device
fuser /dev/sda 2>/dev/null || echo "not in use"

GPIO pin not working

# Verify raspi-gpio is installed
which raspi-gpio || echo "not installed"

# Read current pin state
raspi-gpio get 17
# Normal (HIGH): "GPIO 17: level=1 fsel=0 alt=0 func=INPUT pull=UP"
# Nuke   (LOW):  "GPIO 17: level=0 ..."

If raspi-gpio is missing: sudo apt install raspi-gpio

Service not starting

journalctl -u flash_installer.service --no-pager -n 50
systemctl status flash_installer.service

A common cause is missing firmware files. Check:

ls -la /opt/r2p2-installer/firmware/
ls -la /opt/r2p2-installer/firmware/rp2040/
ls -la /opt/r2p2-installer/firmware/rp2350/
# rp2040/ and rp2350/ must each contain at least one .uf2 file
# flash_nuke.uf2 must be present at the firmware/ level

Same device reflashed on every boot

Flashed devices are tracked in /tmp/r2p2-flashed/ (cleared on reboot). If you need to reflash without rebooting:

sudo rm -rf /tmp/r2p2-flashed/

Checking service logs after a flash

journalctl -u flash_installer.service --since "10 minutes ago"

File Layout After Installation

/opt/r2p2-installer/
  flash_installer.sh           Main script
  firmware/
    flash_nuke.uf2             Erase firmware (bundled)
    rp2040/
      R2P2-for-RP2040.uf2      RP2040 firmware (newest file is used)
    rp2350/
      R2P2-for-RP2350.uf2      RP2350 firmware (newest file is used)

/etc/systemd/system/
  flash_installer.service

How It Works

  1. On boot, flash_installer.service launches flash_installer.sh as root.
  2. The script polls /dev/disk/by-label/ every 2 seconds for RPI-RP2 or RP2350 symlinks.
  3. When found, it reads GPIO17 to decide normal flash or nuke mode.
  4. For normal mode, it selects the newest .uf2 file (by mtime) from the matching subdirectory (firmware/rp2040/ or firmware/rp2350/).
  5. It mounts the drive to /tmp/rp2-mount and waits for INFO_UF2.TXT to appear.
  6. It copies the selected UF2 file. The RP2 bootloader writes the firmware and reboots.
  7. The Pi Zero unmounts, turns off the LED, and resumes polling.
  8. The Pi Zero resumes polling, ready to flash the same or a different device again.

License

Copyright © 2026 HASUMI Hitoshi. See MIT-LICENSE for further details.

flash_nuke.uf2 is provided by Raspberry Pi Ltd under the BSD 3-Clause License.

About

No description, website, or topics provided.

Resources

Contributing

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages