Drive a Bus Pirate 5/5XL/6 (buspirate) from its terminal or a host script: transaction syntax, mode selection, IO pinout, programmable power supply and pull-ups, per-pin voltage measurement, logic analyzer, and the BPIO2 binary scripting port. Use when probing, sniffing, scanning, or bit-banging an I2C, SPI, UART, 1-Wire, 2-wire, 3-wire, or JTAG/SWD bus, dumping an EEPROM or flash chip, or figuring out which of the two serial ports is which. Covers Bus Pirate 5 and later only — classic v3.x i...
Scanned 9/10/2026
Install to Claude Code
npx -y skills add curtisgalloway/public-skills --skill bus-pirate --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Bus Pirate?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/curtisgalloway-bus-pirate)More formats (shields.io, HTML) on the badges page.
---
name: bus-pirate
description: >-
Drive a Bus Pirate 5/5XL/6 (buspirate) from its terminal or a host script: transaction syntax,
mode selection, IO pinout, programmable power supply and pull-ups, per-pin voltage measurement,
logic analyzer, and the BPIO2 binary scripting port. Use when probing, sniffing, scanning, or
bit-banging an I2C, SPI, UART, 1-Wire, 2-wire, 3-wire, or JTAG/SWD bus, dumping an EEPROM or
flash chip, or figuring out which of the two serial ports is which. Covers Bus Pirate 5 and
later only — classic v3.x is entirely different firmware.
---
<!--
SPDX-FileCopyrightText: 2026 contributors
SPDX-License-Identifier: Apache-2.0
-->
# Bus Pirate
The Bus Pirate is a USB-attached serial-protocol multi-tool: you type a short
bracket expression at its terminal and it drives I2C/SPI/UART/1-Wire/etc. on
eight buffered IO pins, with a programmable power supply, switchable pull-ups,
per-pin voltage measurement, and a logic analyzer built in. It is the fastest
way to answer "is this chip alive and what does it say" without writing firmware.
This skill covers **Bus Pirate 5, 5XL, and 6** (the RP2040/RP2350 generation).
Detail lives in two companion files, read them when the task needs them:
- `references/modes.md` — every bus mode: pins, config prompts, what `[` `{` `]`
`}` `r` mean *in that mode*, and the mode-specific commands (`scan`, `sniff`,
`eeprom`, `flash`, `ddr5`, `sle4442`, `bluetag`, `bridge`, `glitch`, …).
- `references/automation.md` — driving it from a host script: the BPIO2 binary
port, on-device `script`/`macro` files, binmodes for PulseView/flashrom/
AVRDUDE, and how to cope with the interactive ANSI terminal from pyserial.
## Identify the hardware first
| | Bus Pirate 5 | Bus Pirate 5XL | Bus Pirate 6 | Bus Pirate v3.x (classic) |
|---|---|---|---|---|
| MCU | RP2040, 125 MHz | RP2350A | RP2350B, 133/150 MHz | PIC24FJ64 + FT232R |
| RAM | 264 KB | 520 KB | 512 KB | tiny |
| Follow-along logic analyzer | No | No | **Yes** | No |
| USB | native CDC, 2 ports + mass storage | same | same | FTDI, **one** port |
| Firmware file | `bus_pirate5_rev10.uf2` | (discontinued) | `bus_pirate6_rev2.uf2` | `.hex` via PIC bootloader |
| Status | volume production, primary dev platform | **discontinued** (RP2350 E9 errata; owners swapped to 6) | limited/"luxury" production | legacy, different codebase |
- 5 vs 6 is a small difference: same firmware features today; 6 has more RAM/PIO
and the extra pins needed for the follow-along logic analyzer (a second buffer
that watches the *bus* rather than what the MCU drives).
- **v3.x is not this tool.** It runs unrelated firmware with a different command
set and the old BBIO1 binary protocol, and it appears as an FTDI serial port
(`/dev/ttyUSB*` on Linux, `/dev/cu.usbserial-*` on macOS) — a single port, not
two. If the device you found is one FTDI port, stop and treat it as v3;
nothing on docs.buspirate.com applies verbatim.
- Confirm with `i` at the prompt: it prints e.g. `Bus Pirate 5 REV10`, the
firmware build date, the MCU, the serial number, the available modes, and the
active binmode.
## Quick start: plugged in to a first measurement
```
# 1. find the two CDC ports (macOS)
ls /dev/cu.usbmodem*
# 2. attach to the LOWER-numbered one (the terminal)
screen /dev/cu.usbmodem1234567890121 # or: tio /dev/ttyACM0 (Linux)
```
Then in the terminal:
```
<enter> # wake it; answer the VT100 prompt (y, or n for dumb terminals)
HiZ> i # who am I, what firmware, which modes
HiZ> m i2c # pick a mode by NAME (menu numbers move between builds)
I2C> W 3.3 100 # PSU on: 3.3 V, 100 mA fuse (lowercase w = off)
I2C> P # pull-ups on (10 k to VOUT/VREF) — mandatory for I2C
I2C> v # voltage report: VOUT + all 8 IO pins
I2C> scan # who is on the bus
I2C> [0xa0 0x00 [0xa1 r:8] # write addr+reg, repeated start, read 8 bytes
I2C> w # power down when done
```
`v` is the one-shot answer to "what voltage is on that pin"; `V` repeats it
continuously until a key is pressed. `v.<pin>` does it *inside* a transaction.
## Connecting
Plugging in a Bus Pirate 5/6 presents **three** USB devices, no drivers needed:
| USB function | What it is |
|---|---|
| CDC serial #0 | the **user terminal** (the VT100 command line) |
| CDC serial #1 | the **binary/scripting port** (BPIO2, SUMP, FALA, legacy binmodes) |
| Mass storage | the onboard NAND as a FAT16 disk (config, scripts, dumps) |
Serial settings do not matter — it is USB CDC, the baud rate is ignored.
115200 8N1 is the customary thing to type when a program demands a value.
**Which port is which:**
- **macOS** — `ls /dev/cu.usbmodem*` shows two (plus matching `tty.*`). The name
ends in the USB interface number: the **lower** suffix (e.g. `…121`) is the
terminal, the **higher** (`…123`) is the binary port. Prefer `cu.*` over
`tty.*` (`cu` allows multiple openers, `tty` is exclusive).
- **Linux** — two `/dev/ttyACM*` nodes. `/dev/serial/by-id/` disambiguates
stably; the lower interface number is the terminal. (`by-id` naming is not in
the official docs — verify by pressing enter and looking for a prompt.)
- **Windows** — two COM ports; the lower number is *usually* the terminal, but
the docs explicitly warn Windows is inconsistent. Try both.
- Universal test: open a port and press enter. The terminal answers with a
prompt (`HiZ>`); the binary port stays silent.
Terminal emulators the docs bless: Tera Term (Windows), `tio` (best on Linux —
defaults to 115200 8N1 and reconnects across replugs), `screen`, `minicom`,
`cu`. On macOS only iTerm2 + `cu` renders full color; `screen`/Terminal come up
monochrome (switch to ANSI-256 under `c` → 2 if you want color).
Exit incantations: `screen` → `ctrl-a k y` (or `ctrl-a ctrl-\`); `cu` → newline
then `~.`; `minicom` → `Esc q`; `tio` → `ctrl-t q`.
## The terminal
- **Prompt is the mode**: `HiZ>`, `I2C>`, `SPI>`, `LED-(WS2812)>`,
`INFRARED-(RAW)>`. It always boots into **HiZ** — every output disabled, a
deliberately safe state. Nothing happens on the wires until you `m` into a mode.
- **VT100 vs ASCII**: on reset it asks `VT100 compatible color mode? (Y/n)>`.
Yes gives color plus a live status bar (voltage/current/pin state) redrawn
with ANSI escapes. Say **n** for dumb terminals and for scripted use — the
status bar is exactly the "garbage characters" people report. Changeable later
under `c`.
- **Line editing**: left/right cursor, up/down history, home/end, backspace/delete.
- **Up to 255 characters** per line; press enter to execute.
- **Chaining**: `;`, `&&`, `||` chain commands on one line
(`a 0; a 1; a 2; a 3`).
- **Menus**: defaults appear in `( )` or marked `*` — bare enter accepts them,
`x` + enter exits a menu without changes. Most settings persist to
`bpconfig.bp` on the internal disk and are offered back as
"Use previous settings?" next time.
- **Help is authoritative and newer than any doc**: `?` / `help` (all commands),
`help mode` (current mode's commands), and `<command> -h` (per-command usage,
e.g. `W -h`, `logic -h`, `scan -h`). When this skill and `-h` disagree, trust `-h`.
## Global commands
| Command | Does |
|---|---|
| `i` | version/hardware info; in a mode, also mode info, bit order, display format |
| `c` | config menu: language, ANSI color, ANSI toolbar, LCD screensaver, LED effect/color/brightness → saved to `bpconfig.bp` |
| `m` / `m <mode>` | mode menu / jump straight to a mode (`m i2c`, `m hiz`) |
| `l` / `L` | bit order MSB-first (default) / LSB-first |
| `o` | number display format: Auto (mirrors your input), HEX, DEC, BIN, ASCII |
| `d` | LCD display mode: Default (pin labels + voltages) or Scope |
| `~` | factory self-test — **HiZ only, disconnect everything first** |
| `reboot` | restart (you may need to reattach the terminal) |
| `$` | jump to the UF2 bootloader; prints the exact firmware filename you need |
| `cls` | clear/redraw + re-init the VT100 terminal (use after attaching late) |
| `ovrclk [-m MHz\|-k kHz] [-v mV]` | CPU overclock — disabled unless compiled in |
| `w` / `W [v] [mA] [-u pct]` | power supply off / on |
| `v` / `V` | voltage report once / continuously |
| `p` / `P` | pull-up resistors off / on |
| `g` / `G` | PWM frequency generator off / on |
| `f` / `F` | frequency + duty measurement once / continuously |
| `= <n>` | show `<n>` as HEX/DEC/BIN |
| `\| <n>` | reverse the bits of `<n>` |
| `a <pin>` / `A <pin>` / `@ <pin>` | drive pin low / high / make it an input and read it |
| `logic [start\|stop\|show\|hide\|nav] [-f Hz] [-o N]` | logic analyzer core + in-terminal graph |
| `jep106 <bank> <id>` | JEDEC JEP106 vendor lookup (flash/NAND manufacturer IDs) |
| `binmode` | pick what the *second* USB serial port speaks |
| `ls` `cd` `mkdir` `rm` `cat` `hex` `dump` `label` `format` `image` | onboard flash disk |
| `macro` `script` `button` `pause` | on-device scripting (see `references/automation.md`) |
## Bus transaction syntax
This is the heart of the tool. **A line is a bus transaction only if it starts
with `[`, `{`, or `>`.** Everything else is parsed as a terminal command — a
bare `r` is not a read, `>r` is.
| Token | Meaning |
|---|---|
| `[` | START (I2C start, 1-Wire reset, SPI CS active, …) — mode-specific |
| `{` | **alternate** START (e.g. SPI CS active *and* echo the byte read while writing) |
| `]` | STOP (I2C stop, SPI CS inactive, close UART, …) |
| `}` | alternate STOP |
| `>` | execute the rest of the line as bus commands with **no** START |
| `r` | read one byte |
| `r:N` | read N bytes (1–255) |
| `0b1001` | write binary; padding zeros optional (`0b1` == `0b00000001`) |
| `0x5a` / `0h5a` | write hex, case-insensitive; `0x5` == `0x05` |
| `0`–`255` | write decimal (anything with no `0x`/`0b`/`0h` prefix) |
| `"abc"` | write the ASCII bytes `0x61 0x62 0x63` |
| ` ` (space) | value delimiter — required between numbers, not between other tokens |
| `d` / `d:N` | delay 1 µs / N µs |
| `D` / `D:N` | delay 1 ms / N ms |
| `:N` | repeat the preceding token N times (`0x55:2`, `r:3`, `D:3`) |
| `.N` | operate on N bits instead of 8 (`0x5a.4` writes `0x0a`; `r.4`; `0x5432.12`); combines with repeat: `0x5a.4:2` |
| `v.<pin>` | measure and print the voltage on IO`<pin>` mid-transaction |
| `a.<pin>` / `A.<pin>` / `@.<pin>` | drive pin low / high / input+read, mid-transaction |
| `^` | one clock tick (2WIRE/3WIRE only) |
| `/` `\` | clock pin high / low (2WIRE/3WIRE only) |
| `-` `_` | data (MOSI) pin high / low (2WIRE/3WIRE only) |
| `.` | read the data (MISO) pin (2WIRE/3WIRE only) |
Note the two different `.` uses — as a *suffix* on a value it is a bit count; as
a *standalone token* in 2WIRE/3WIRE it reads the data pin. And note
`a.<pin>` (bus syntax, dot) vs `a <pin>` (terminal command, space).
Worked examples:
```
I2C> [0xa0 0x00 [0xa1 r:8] # write reg addr, repeated START, read 8 — addresses are 8-bit!
SPI> [0x9f r:3] # JEDEC ID: CS low, cmd 0x9F, read 3, CS high
SPI> [ 0x55:2 D:3 r:3] # write 0x55 twice, wait 3 ms, read 3
SPI> >0x5a.4 # write only 4 bits, no chip select change
1WIRE> [0xcc 0x44] # reset, SKIP ROM, start DS18B20 conversion
UART> [ "AT\r" r:16 ] # open, send, read up to 16 bytes, close
DIO> >0b10101010 # drive all free pins in one write
DIO> >r # read all pins as one byte
2WIRE> >^^^ / \ - _ . # hand-clock a non-standard bus
```
**I2C addresses in Bus Pirate syntax are 8-bit** (`0xA0` write / `0xA1` read for
the 7-bit address `0x50`). `scan` prints both forms; datasheets usually print
the 7-bit one. Getting this wrong is the single most common I2C mistake.
## Modes
Select by name (`m i2c`); the numeric menu ordering has changed between firmware
builds, so `m 5` is not portable.
| Mode | Pins used | Notes |
|---|---|---|
| `HiZ` | none | boot state, everything disabled, only mode where `~` self-test runs |
| `1WIRE` | OWD (IO0) | open-drain, **pull-ups mandatory**; `scan` = ROM search |
| `UART` | TX (IO4), RX (IO5) | push-pull; `[`=open+`r` to read, `{`=open+async echo; `bridge`, `gps`, `glitch` |
| `HDUART` | RXTX (IO0), RST (IO2) | half-duplex on one wire, open-drain, **pull-ups mandatory**; SIM/bank cards |
| `I2C` | SDA (IO0), SCL (IO1) | open-drain, **pull-ups mandatory**; `scan`, `sniff`, `i2c dump`, `eeprom`, `ddr4/5`, sensors |
| `SPI` | MOSI(IO5)/MISO(IO6)/CLK(IO4)/CS(IO7) | push-pull, 1–62500 kHz; `flash`, `eeprom` |
| `2WIRE` | SDA (IO0), SCL (IO1), RST (IO2) | I2C-like but no ACK bit; bitwise `^ / \ - _ .`; `sniff`, `sle4442` |
| `3WIRE` | MOSI/MISO/CLK/CS | SPI with the same bitwise primitives, 1–3900 kHz |
| `DIO` | all 8 | no protocol; `>0xff` writes all pins, `>r` reads all pins |
| `LED` | SDO (IO0) [+ SCL (IO1)] | WS2812/SK6812/NeoPixel, APA102/SK9822, or the 18 onboard LEDs |
| `INFRARED` | LERN IO1, BARR IO3, IRTX IO4, 38K IO5, 56K IO7 | RAW (aIR) / NEC / RC5; `tvbgone`, `irtx`, `irrx` |
| `JTAG` | probe pins IO0…IO7 | **not a JTAG engine** — it hosts `bluetag`, a JTAG/SWD pin finder |
Pin assignments above are the BP5 defaults; the LCD and the `i` output print the
live labels for the current mode — trust those over this table if they disagree.
Full per-mode configuration prompts and command flags: `references/modes.md`.
## Pinout
Main connector, 10-pin 2.54 mm keyed (also fine with DuPont jumpers):
| Pin | Name | Description |
|---|---|---|
| 1 | VOUT/VREF | 1–5 V, 300 mA PSU output **or** an external reference voltage that powers the buffers and pull-ups |
| 2–9 | IO0–IO7 | buffered IO, 1.65–5 V, analog voltage measurement, optional 10 kΩ pull-up |
| 10 | GND | signal ground |
Auxiliary connector, 9-pin 1.0 mm: pins 1–8 = IO0–IO7, pin 9 = GND. It is a tap
point for an external logic analyzer, so you don't stack probes on the main header.
Per-pin capability (Bus Pirate 5 / RP2040):
| IO | GPIO | PWM slice | Measure V | Measure freq | Generate freq | UART | I2C | SPI | LED |
|---|---|---|---|---|---|---|---|---|---|
| IO0 | GPIO8 | 4A | yes | no | yes (tied to IO1) | | SDA | | SDO |
| IO1 | GPIO9 | 4B | yes | **yes** | yes (tied to IO0) | | SCL | | SCL |
| IO2 | GPIO10 | 5A | yes | no | yes (tied to IO3) | | | | |
| IO3 | GPIO11 | 5B | yes | **yes** | yes (tied to IO2) | | | | |
| IO4 | GPIO12 | 6A | yes | no | yes (tied to IO5) | TX | | SCLK | |
| IO5 | GPIO13 | 6B | yes | **yes** | yes (tied to IO4) | RX | | MOSI | |
| IO6 | GPIO14 | 7A | yes | no | yes (tied to IO7) | | | MISO | |
| IO7 | GPIO15 | 7B | yes | **yes** | yes (tied to IO6) | | | CS | |
Consequences: **frequency measurement only works on odd pins (1, 3, 5, 7)**, and
a PWM slice's two pins share one frequency (independent duty cycles). Using the
'B' pin of a slice for measurement blocks the 'A' pin as a generator.
A 3-pin SWD header (GND / SWDIO / SWCLK, GND furthest from the board edge) is
exposed on the underside for firmware development.
## Power supply and pull-ups
```
W # interactive: prompts for volts then current limit
W 3.3 # 3.3 V, default 300 mA fuse
W 5 50 # 5 V, 50 mA fuse
W 3.3 0 # 3.3 V, no current limit
W 3.3 100 -u 20 # …with a 20% undervoltage trip instead of the default 10%
w # off
```
- Programmable PSU: 0.8–5.0 V capable, **1–5 V is the usable range** (limited by
the Vgs of the reverse-protection MOSFET); 0–500 mA current sense and digital
fuse; default limit 300 mA (which is also the rated maximum — the extra
headroom is for spikes); adjustable undervoltage trip, default 10%.
- A tripped limit disables the supply, inverts the terminal colors, rings the
bell, prints an error, and **halts command execution**. Re-arm with `W`.
- The VOUT/VREF pin is a one-way valve: you can instead feed the target's own
supply *into* it and the Bus Pirate buffers and pull-ups will run at that
voltage. **5 V DC absolute maximum** — more damages the Bus Pirate and your
computer.
- Pull-ups are 10 kΩ to whatever is on VOUT/VREF. `P` on, `p` off. They are
**required** for I2C, 1-Wire, HDUART, and 2WIRE — those buses only ever pull
low, so without pull-ups there is no logic 1 at all. Classic symptom: the I2C
`scan` reports a device at *every* address.
- With no voltage on VOUT/VREF, `P` warns and does nothing useful. Check with `v`.
## Measurement features
- **Voltage** — `v` (once) / `V` (continuous) reports VOUT and all eight IO pins;
`v.<pin>` reads one pin inside a transaction. The live VT100 status bar and the
LCD show the same data continuously.
- **Frequency counter** — `f <pin>` once, `F <pin>` continuous (odd pins only).
Reports frequency, period, and duty cycle.
- **Frequency generator (PWM)** — `G` (menu) or `G <pin>`; enter a period or
frequency *with units* (`12.4khz`, `1ms`, `500ns`) and a duty cycle *with the
percent sign* (`35%`). `g` / `g <pin>` disables. Shows as `PWM` on the status
bar and LCD.
- **Logic analyzer** — 8 channels, 131 K samples, 62.5 MSPS, trigger on one pin
high or low. Three front ends: `logic` in the terminal (ASCII graph, plus
"follow along" capture that fires on every bus transaction), SUMP for
sigrok/PulseView, and FALA (follow-along) for a patched PulseView. See
`references/automation.md`.
- **Oscilloscope** — `d` → Scope puts a scope on the LCD. 0.5 MSPS, 12-bit, one
channel, any IO pin: fine for audio and slow analog, useless above ~500 kHz.
Commands: `sr <pin> <o|n|a>` run (once/normal/auto), `ss` stop, `x` timebase,
`y` volts, `t` trigger. Those drop into a single-key interactive mode
(`+ - ^ v < > B M E T r s o n a t x y`, enter to leave).
## Gotchas
- **It boots into HiZ.** No mode, no power, no pull-ups — commands "do nothing"
until you `m`, then `W`, then `P`.
- **Pick modes and binmodes by name, not menu number.** The numbering differs
between firmware builds (the docs themselves show several different menus).
- **8-bit I2C addresses** in bus syntax (`0xA0`/`0xA1`), 7-bit in datasheets.
- **A bus line must start with `[`, `{`, or `>`.** Otherwise it is a terminal
command and you will get an error, or worse, something unrelated.
- **Pull-ups need a voltage on VOUT/VREF**, from `W` or from the target.
- **`a`/`A`/`@` refuse pins already owned** by the active mode or by a PWM.
- **Frequency measurement is odd-pins-only**; paired PWM pins share a frequency.
- **The onboard USB disk is read-only while a terminal is connected**, and it
detaches/reattaches whenever the Bus Pirate writes to it (that is deliberate,
to stop corruption). Close the terminal to copy files onto it.
- **Scripts cannot answer menus** — only lines typed at the command prompt.
`W 3.3 50` works; `W` followed by `3.3` on the next line does not.
- **Button script assignments do not survive a reboot.**
- **SUMP and follow-along logic analyzer share one capture buffer** and cannot
run simultaneously (you get a memory error).
- **Before Bus Pirate 6, logic capture is taken behind the IO buffer** — you see
what the Bus Pirate intended to drive, not what is actually on the wire. Fine
when all pins are inputs (HiZ), misleading when driving.
- **Self-test (`~`) requires HiZ and nothing connected**; attached devices can be
damaged and will fail the test.
- **Wrong `.uf2` → menacing red blink.** Hold the underside button while
plugging in to force the bootloader, then load the other file. Bootloader disk
names: `RPI-RP2` (v5), `RP2350` or `BP__BOOT` (v6).
- **The terminal is a full-screen ANSI UI.** The live status bar constantly
rewrites the bottom of the screen, so naive `readline()` scripting sees escape
sequences and cursor moves interleaved with your data. Fixes, in order of
preference: use the BPIO2 binary port instead; answer `n` to the VT100 prompt;
or set `"terminal_ansi_color": 0` and `"terminal_ansi_statusbar": 0` in
`bpconfig.bp`. Details in `references/automation.md`.
- **255 character limit** on a single terminal line.
## Sources
- Command reference (the single biggest page, and the most current):
<https://docs.buspirate.com/docs/command-reference/>
- Quick setup / port identification: <https://docs.buspirate.com/docs/tutorial-basics/quick-setup/>
- IO pin descriptions and the RP2040 pin map: <https://docs.buspirate.com/docs/overview/io-pins/>
- Logic analyzer: <https://docs.buspirate.com/docs/logic-analyzer/logicanalyzer/>
- Oscilloscope: <https://docs.buspirate.com/docs/scope/>
- BPIO2 scripting interface: <https://docs.buspirate.com/docs/binmode-reference/protocol-bpio2/>
- Firmware downloads/upgrade: <https://docs.buspirate.com/docs/downloads/>
The device's own `?`, `help mode`, and `<command> -h` output is generated from
the running firmware and is more current than any of the above — including this
skill. Check it before concluding a documented flag does not exist.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!