Bus Pirate 6 · Volume 4
Bus Pirate 6 Volume 4 — The Syntax Language and the VT100 / On-Screen UI
Bracket transactions, repeat/delay/partial-byte operators, macros, on-board files, the live status bar
Contents
1. About this volume
This volume documents everything you can type at the BP6 prompt — the top-level commands valid in any mode, the syntax language used inside a mode, the macro system, the on-board NAND filesystem commands, and the on-screen UI elements that mirror what the terminal sees. Read this first if you’re going to use the BP6 every day. Vols 6-7 walk specific protocol modes on top of this foundation; Vol 9 walks end-to-end workflows that combine multiple commands into recipes.
The syntax language is the Bus Pirate’s defining UX. It’s compact, regular, and protocol-agnostic — the same [ ] brackets delimit a transaction whether you’re in I²C, SPI, or 1-Wire mode; the same r:N operator reads N bytes regardless of which protocol does the actual reading. Once it’s in your fingers, you stop noticing it.
Documentation source: firmware.buspirate.com/command-reference/syntax is the canonical syntax reference; docs.buspirate.com/docs/command-reference/ is the canonical command reference. Where the docs lag the firmware, the firmware source (src/syntax_compile.c and src/commands/) is the source of truth.
2. Top-level commands (any mode)
These commands are valid at any prompt — they don’t depend on the current protocol mode. Many of them are single-character mnemonics; the uppercase / lowercase distinction matters for some pairs (on / off, set / read).
2.1 The help system: ? and ??
?— context-sensitive help. Lists the commands valid in the current mode.??— verbose help. Same list, plus per-command summaries.
Both render to the terminal. The LCD doesn’t mirror help — you need a serial console for the full text.
2.2 Device info, mode select, config: i, m, c
i— device info dump. Firmware version, build hash, RP2350B unique chip ID, free heap, free NAND space, ARM-vs-RISC-V architecture, current mode. The chip ID is what shows up as the unit’s S/N (see Vol 1 § 7).m— mode select. Prints the mode list and prompts for a choice. Each mode then runs its own setup wizard (pin assignment, speed, parity, etc.). Cancelling at any point returns you to the prompt without changing the active mode.c— config. Sub-menus for color palette, language (i18n), LED brightness, and miscellaneous device-wide settings. Persistent across reboots (settings stored on the on-board NAND).
2.3 # reset and # line-comment (the dual meaning)
Historically (pre-2026-04-07): # at column 0 was a soft-reset command. Type it at the prompt and the firmware re-initializes — equivalent to power-cycling without a USB unplug.
After commit 93aefde (2026-04-07): # is also a line-comment character in the syntax parser. Anywhere in a syntax line, # and everything after it (to the line end) is discarded. The reset behavior is preserved only when # is the first character of input and the rest of the line is empty — i.e., bare # followed by Enter.
Practical consequence: you can now annotate pasted command sequences:
W 3.3 # set PSU to 3.3 V
P # enable pull-ups on configured pins
[0x90 r:4] # I²C read 4 bytes from address 0x48
Previously each # would have triggered a reset — not great for pasted scripts.
2.4 $ jump to bootloader
$ at the prompt makes the firmware report the expected UF2 filename for the current board, then reboots into the RP2350’s BOOTSEL mass-storage mode. The RPI-RP2 USB drive appears on the host; drop the UF2 onto it.
This is the canonical firmware-update path. Hardware fallback (hold BOOTSEL button while plugging in USB) still works if firmware is unresponsive, but $ is faster for normal updates.
2.5 ~ self-test
~ runs the built-in self-test. Walks per-pin output drive (forces each IO high, then low, verifies readback via the look-behind buffer or per-pin ADC), checks the PSU output rail at several voltages, exercises the LCD, blinks the LED chain, and reports pass/fail.
Useful after a firmware update to confirm nothing regressed, and as a sanity check before believing a “the BP6 seems broken” symptom (often a probe-cable issue, not the unit).
2.6 Power and pull-ups: W, w, V, v, P, p
| Command | Action |
|---|---|
W <voltage> | Set PSU output voltage (1.0-5.0 V) and enable the rail |
w | Disable PSU output |
V | Read PSU output voltage (live ADC sample) — continuous-update display |
v | Read PSU output voltage — single snapshot |
P | Enable pull-ups on configured pins |
p | Disable pull-ups |
W and w are output control; V and v are measurement; P and p are pull-up control. The pull-up state is global (all configured pins together), not per-pin from the prompt — for per-pin control, use the mode’s own pin-config wizard.
The V continuous display is useful: type V, walk around the bench, see how the PSU droops when you connect a target — the LCD updates in real time.
2.7 Aux pin: a, A, @
The BP6 has a single auxiliary pin (the AUX header’s GPIO, not the parallel-tap of IO0-7). Commands:
A— drive AUX pin higha— drive AUX pin low@— read AUX pin (one-shot)
Use cases: trigger an oscilloscope, sync to an external event, drive an enable line that doesn’t fit on the 8 IO pins.
2.8 Frequency / PWM: f, F, g, G
| Command | Action |
|---|---|
f | Measure frequency on the current frequency-input pin (one-shot) |
F | Measure frequency continuously |
g | Generate PWM (prompts for frequency and duty cycle) |
G | Stop PWM generation |
Frequency measurement uses a PIO state machine to count edges over a known interval. Resolution: roughly ±0.1% at frequencies above 1 kHz; below that the measurement window grows.
PWM generation uses another PIO state machine. Frequency range: ~1 Hz to a few hundred kHz; duty cycle 0-100% in 1% steps.
2.9 Display, bit order, conversion: o, l, L, =, |
o— output format. Sub-menu picks binary / decimal / hex / ASCII / mixed for syntax-output formatting.l/L— bit-order set.l= LSB-first,L= MSB-first (the default). Affects how bytes are clocked out / in for protocols where it matters (SPI mostly; UART is fixed-format).=— convert a number between bases. Prompts for the value and renders it in binary / decimal / hex / ASCII.|— reverse bits of a number. Useful for hand-crafting bit patterns when an SPI device expects LSB-first but the syntax language defaults to MSB.
2.10 On-board NAND filesystem: ls, cd, mkdir, rm, cat
The BP6’s 1 Gbit NAND is mounted as a FatFS filesystem (Vol 2 § 8). Standard Unix-ish commands operate on it:
ls— list files in current directorycd <dir>— change directorymkdir <dir>— create directoryrm <file>— delete filecat <file>— print file contents to terminal
Use cases:
- Store macro scripts as text files;
catplays them back through the parser. - Store wordlists, captured pcap, WiGLE CSVs.
- Drop files onto the BP via USB MSC from the host;
lsandcatfrom the BP to verify.
format re-initializes the filesystem (destructive — loses all stored data). Used when the FatFS corrupts (rare, but possible after an unclean unplug).
3. The syntax language
The syntax language operates inside a mode. After you m into I²C or SPI (etc.), what you type at the prompt is syntax-language input, not top-level commands. Same prompt; different parser.
3.1 Transaction delimiters: [ and ]
[ opens a transaction; ] closes it. What “transaction” means depends on the mode:
- I²C:
[= START condition;]= STOP condition. - SPI:
[= CS asserted (low);]= CS deasserted (high). - 1-Wire:
[= reset pulse + presence detect;]= idle. - UART:
[= open the port for streaming;]= close it (mostly a no-op since UART is async). - JTAG:
[= TAP state machine to TEST-LOGIC-RESET;]= release.
Brackets must balance per line. Mixed brackets — like [0x90 r:4 with no ] — produce a parse error.
You can have multiple bracketed transactions per line: [0x90 r:4] [0x91 r:4] is two separate I²C transactions, each with its own START/STOP, on one input line.
3.2 Continuation: >
> is “execute without START.” Used inside a transaction to send a partial sequence without a new START condition. Most useful for repeated I²C transactions that share an address:
[0x90 r:1 > r:1 > r:1]
This is: START, write 0x90, read 1, continue (no repeated START) read 1, continue read 1, STOP. Equivalent to a single 3-byte read after one address write.
For most workflows you don’t need > — the simpler [0x90 r:3] works the same way. > matters when a device’s protocol requires explicit “no repeated START” semantics.
3.3 Numeric literals: hex, binary, decimal, ASCII
Four literal formats:
0x<hex>— hexadecimal.0x55,0xff,0x1234.0b<binary>— binary.0b10101010,0b11110000.<decimal>— decimal.123,255."<ascii>"— ASCII string."hello"writes the bytes 0x68 0x65 0x6c 0x6c 0x6f.
Type doesn’t matter to the bus — all become bytes — but the literal format is preserved in the readback for clarity. 0xFF reads back as 0xFF not 255.
Multi-byte values: 0x1234 writes two bytes (0x12 then 0x34, MSB-first by default — flip with l).
3.4 Read operators: r and r:N
r— read one byte (or one bit, depending on mode).r:N— read N bytes.r:4reads 4 bytes.r.B— read B bits (partial byte). See § 3.7.
Reads return ACKed bytes for I²C, MOSI-quiet-MISO-active bytes for SPI, etc. The mode decides what “reading” means.
r:N is the most common operator after numeric literals — used in every flash dump, every EEPROM read, every JTAG IDCODE check.
3.5 Repeat: :N
:N after any token repeats it N times. Works on literals, reads, and delays:
0x55:2writes0x55 0x55(two bytes of the same value).0:8writes eight zero bytes.r:4reads 4 bytes (this is ther:Nform — same operator, generalized).d:10delays 10 µs.D:5delays 5 ms.
The repeat range is bounded by the 255-character line limit (§ 3.10), not by an explicit max-N.
3.6 Delays: d, d:N, D, D:N
d— delay 1 microsecond.d:N— delay N microseconds.D— delay 1 millisecond.D:N— delay N milliseconds.
Used inside transactions to give a slow device time to process. Example: writing to an I²C EEPROM:
[0x90 0x00 0x00 0x55 0xAA] D:5
Write address 0x0000 with bytes 0x55, 0xAA, then close transaction, then wait 5 ms (the typical EEPROM write cycle time) before issuing the next operation.
Delays are busy-wait inside the syntax runner — they pause the CPU but don’t disable interrupts, so USB and the display keep working.
3.7 Partial-byte write/read: 0x5a.4, r.4
The .B suffix sends or reads exactly B bits, not a full byte:
0x5a.4— write the low 4 bits of 0x5a (which are0b1010). Useful when a protocol needs a 4-bit or 12-bit field.0x5432.12— write the low 12 bits of 0x5432 (which are0b010000110010).r.4— read 4 bits, return as a value 0-15.
Used heavily for non-byte-aligned protocols like Microwire (3-Wire), some JTAG state machines, and a few custom protocols. The mode has to support arbitrary-width transfers — most do.
3.8 Whitespace as numeric separator
Whitespace separates tokens. 0x55 0xAA writes two bytes (0x55 then 0xAA). 0x55,0xAA is a parse error (no comma operator).
Whitespace inside a token is illegal: 0x 55 is a parse error.
3.9 # line comment (commit 93aefde)
Added 2026-04-07 in commit 93aefde. Anywhere on a syntax line, # and everything after it is discarded. This lets pasted command sequences carry inline annotations:
W 3.3 # set PSU to 3.3 V for this target
[0x48 r:1] # read 1 byte from I²C address 0x48 (typical temperature sensor)
This is also why the # reset command is now restricted to bare # at column 0 with nothing else on the line — the parser distinguishes by context. The comment behavior is non-destructive; you can’t accidentally reset by leaving a # in a command line.
If you need a comment at the start of a line: any non-empty content before the # (even a single space) treats it as a comment, not a reset. Bare # Enter is reset; # Enter (note leading space) is a no-op comment.
3.10 The 255-character line limit
BC_MAX_LINE is 255. A syntax line longer than that is rejected with a “line too long” error. This bounds the worst-case bytecode array size and avoids the parser allocating dynamically.
Workaround for long sequences: store them as a file on the NAND and cat them — the parser reads the file line by line. Or break a long line into multiple lines; bracket transactions don’t have to be on one line (a [ on one line and ] on the next is fine, though unusual).
4. Worked syntax examples per protocol
4.1 I²C: [0x90 r:4 0x91 r:4]
I²C address 0x48 (the 7-bit address). The 8-bit form with R/W bit is 0x90 (write) or 0x91 (read).
Sequence:
[— I²C START0x90— write address byte with W=0 (write mode)r:4— read 4 bytes (the device’s response, ACKed by master)0x91— write address byte with W=1 (read mode; this triggers a repeated START internally)r:4— read 4 more bytes]— I²C STOP
A typical use: address 0x48 is a TI TMP102 temperature sensor. The first 4 bytes read are the temperature register (after a pointer write); the second 4 are configuration register data.
4.2 SPI: [0x90 r:4]
SPI doesn’t have an address byte — the chip-select line picks the chip. The 0x90 here is a command byte to whatever the SPI target is. For a 25-series flash chip, 0x90 is the “Read Manufacturer/Device ID” command.
Sequence:
[— assert CS (low)0x90— clock out the byte 0x90 over MOSIr:4— clock out 4 dummy bytes on MOSI, capture MISO into the read buffer]— deassert CS (high)
For a Winbond W25Q-class chip, this returns the manufacturer ID (0xEF) and the device code (varies by capacity).
4.3 UART: streaming write and read
UART is asynchronous; brackets are mostly a no-op. Typical usage:
[ "HELLO\r\n" r:8 ]
This writes “HELLO\r\n” out the TX line, then reads up to 8 bytes from RX (with a default timeout if fewer arrive). For interactive UART work, the BP6 also has a “transparent UART bridge” mode where the BP6 acts as a USB-to-UART pass-through — useful for opening a console on an embedded board without leaving the BP6’s syntax-language framing in the way.
4.4 1-Wire: [0x55 r:8]
1-Wire’s “transaction” is the reset pulse + presence detect.
Sequence:
[— 1-Wire reset pulse + presence detect (the device responds with a presence pulse; the BP6 detects it)0x55— ROM command “Match ROM” (followed by 8 bytes of ROM ID — though here we’re being lazy and reading; for a real match you’d write 8 bytes)r:8— read 8 bytes (typical ROM ID readback for a Skip-ROM + Read-ROM sequence)]— idle
For a DS18B20 temperature sensor on the wire: the first 8 bytes you read after a Skip-ROM (0xCC not 0x55) and Read-Scratchpad (0xBE) sequence are the temperature, alarm thresholds, configuration, and CRC.
5. Macros
Macros let you replay a stored command sequence with a single short token.
5.1 Listing and running stored macros: (0) and (n)
(0)— list all stored macros for the current mode.(n)— run macro numbern. The macro’s stored text is fed through the syntax parser as if you typed it.
Each mode has its own macro namespace. Macros stored in I²C mode don’t appear in SPI mode’s list.
5.2 Storing macros on the NAND
Macros are stored as files in the BP6’s on-board NAND under a per-mode directory (e.g., /macros/i2c/). Each file is a text file with the macro’s syntax-language contents. Create them by dropping files onto the USB MSC mount, or use the BP6’s cat > file syntax (limited; see firmware docs for the canonical workflow).
This is one of the BP6’s underused features — most users discover the macro system only after wanting it. Once you’ve established a “scan, dump, verify” recipe for a chip family, parking it as a macro makes the next session faster.
6. The on-board NAND filesystem
The NAND mounts as a USB Mass Storage device when the BP6 enumerates. From the host’s side it looks like a small removable drive — drop files on, drag files off. From the BP6’s side, use ls, cd, mkdir, rm, cat (§ 2.10).
Practical directory layout (firmware default, can be customized):
/ ← FatFS root
├── config/ ← persistent settings
├── macros/
│ ├── i2c/ ← I²C macros
│ ├── spi/ ← SPI macros
│ └── jtag/ ← JTAG macros
├── captures/ ← PCAP files written by sniffers
├── wardrive/ ← WiGLE-format CSVs (if you're doing GPS work)
├── portals/ ← Evil Portal HTML payloads
└── scripts/ ← .bp6 scripts for `cat`-replay
Files appearing here from the host’s USB MSC are immediately visible to BP6 commands without a reboot. Same the other way: a BP6 mkdir shows up as a new folder on the host’s drive.
7. The VT100 status bar
Bottom-of-terminal display, color-coded, updated at roughly 10 Hz.
7.1 What it shows
Live values, all the time:
- Current mode (HiZ, UART, I²C, SPI, etc.) and its key config (baud, freq, address bits)
- Per-pin labels for IO0-IO7: each pin shows its name in the current mode (e.g., in I²C:
SDASCLfor IO0 and IO1;--for unconfigured pins) - Live voltage per pin (post-1T45 buffer, from the CD4067 mux — Vol 2 § 7)
- Pull-up state per pin (a small indicator next to each label)
- PSU on/off + output voltage + measured current draw
- Frequency counter / PWM channel state if active (
FREQ/PWMindicators) - Aux pin state if explicitly set
7.2 Color coding and the c palette
The default color scheme:
- Red — output, driving high
- Blue — input, reading low
- Yellow — input, reading high
- Gray — input, unconnected / floating
- Accent color (orange or green, mode-dependent) — active in current mode
- White — pull-up enabled
Type c at the prompt for a sub-menu that tunes colors per-element. The settings persist in NAND /config/.
For high-contrast / accessibility, the c menu includes a “monochrome” theme that drops to black-on-white.
8. The on-screen ST7789V mirror
The 240×320 IPS LCD shows roughly the same content as the VT100 status bar (§ 7.1). It’s a visual mirror — when you don’t have a terminal connected, the LCD is your status display. When you do have a terminal, you can ignore the LCD.
What the LCD adds beyond the VT100 mirror:
- Per-mode visual schematic — small icon showing which pin is doing what. Helpful as a quick visual check before connecting probes.
- Live waveform / level traces for some modes (PWM duty cycle, frequency counter trace). Limited resolution (240×320 isn’t a scope), but useful for “is it doing anything?” sanity checks.
- Capture progress — when a long workflow runs (
flash dump,wardrive), the LCD shows a progress bar.
The LCD updates at ~10 Hz from the same render loop that drives the VT100 status bar. Both come from the same in-firmware state machine; they don’t drift.
9. Menu-bar + F-keys (firmware Mar 2026+)
Added in March 2026 (see Vol 3 § 10): a top-of-terminal menu bar with F-key shortcuts. The menu bar shows command categories (Mode / Power / Display / NAND / etc.) and each is associated with a function-key shortcut (F1-F8).
Use case: discoverability. New users can press F1 and see what’s available without reading the help. Power users mostly ignore the menu bar and stay in the syntax language.
The menu bar is on by default; turn it off with the c config sub-menu.
10. Differences from the classic BP3.6 syntax language
For anyone coming from a BP3.6 (the long-lived 2010-2018 PIC-based generation):
| Aspect | BP3.6 | BP5/6 |
|---|---|---|
| Number of I/O pins | 5 | 8 |
| Voltage range | 3.3 V only (with weak 5 V tolerance) | 1.2-5.5 V at the target side; 1.0-5.0 V programmable PSU |
| LCD on-screen UI | none | 240×320 IPS with full status mirror |
| On-board storage | none | 1 Gbit NAND mounted as USB MSC |
| PIO state machines | no | 8 (BP5) / 12 (BP5XL/BP6) |
| Bracket syntax | same | same |
r:N, :N repeat, delays | same | same |
0x5a.4 partial byte | same | same |
[ and ] semantics | same per protocol | same per protocol |
$ bootloader | new | present |
# line comment | absent | present (Apr 2026+) |
| BBIO host protocol | present | present (compat) |
| BPIO2 host protocol | absent | new (FlatBuffers + COBS) |
| Smart card / DDR5 SPD / blueTag | absent | present |
The syntax language is largely backward-compatible — a BP3.6 user can type the same I²C and SPI bracket sequences and they work as expected. The new operators (# comment, partial-byte reads, the extended PSU range) are additive.
Macros and the NAND filesystem are the big workflow change. On a BP3.6 you couldn’t store sequences across reboots. On the BP6 you can build up a personal macro library and have it survive forever.
11. Cheatsheet updates for Vol 12
Items from this volume that belong on the laminate cheatsheet:
- The top-level command alphabet:
i m c # $ ~ W w V v P p a A @ f F g G o l L = | ls cd mkdir rm cat ?. Single line. - Bracket transactions:
[= START (or CS-low / 1-Wire-reset depending on mode);]= STOP (or CS-high / idle). - Numeric literals:
0xhex,0bbinary,<n>decimal,"..."ASCII. r:Nreads N bytes;:Nrepeats the previous token N times;d:N/D:Nfor µs/ms delay.0x5a.4writes the low 4 bits;r.4reads 4 bits.#is a line comment (Apr 2026+); bare#at start of line still resets.(0)lists macros;(n)runs macro n.- Worked examples for I²C / SPI / 1-Wire / UART — one bracket sequence each.
- The 255-character line limit.
- VT100 color key: red=out-high, blue=in-low, yellow=in-high, gray=floating, accent=mode-active, white=pull-up-on.
End of Volume 4. Volume 5 picks up with power and probing — the PSU set/measure workflow, pull-up management, per-pin voltage reads, the frequency counter and PWM generator, and the blueTag JTAG/SWD pin-finder.