Follow BRINGUP.md — it is the authority; this file is
the operating summary. Record results per
BOARDS.md and the Method rules in
test/hardware/README.md.
Non-negotiables
Identify before flashing — the chip AND the board it sits on.
arduino-cli board list + system_profiler SPUSBDataType (VID/PID) +
esptool chip_id for ESP32s. Boards displace each other on hubs, ports
renumber after every flash, UF2 bootloader IDs name the bootloader not
the model, and what the user believes is plugged in has been wrong
before. Never identify by port name.
chip_id names the silicon only. Every native-USB ESP32 enumerates as
303a:1001, so VID/PID cannot tell a XIAO from a SuperMini — and the
pin map, LED polarity, populated buses and FQBN defaults all belong to
the carrier. Inferring the board from the chip put the EGG demo on a
XIAO ESP32-C3 on 2026-09-04, driving the wrong pins and reporting a
missing peripheral that was really a wrong address. Ask the user what
the board is and believe them over your own inference; flash the
board-agnostic test/hardware/OscEcho before any pin-driving demo; and
treat a peripheral that "fails to probe" as a suspected wrong pin map
first. See BRINGUP.md Phase 0, The chip is not the board.
The vendor's peripheral list is a work list. Having found the board's
documentation, do not stop at identification. Every peripheral it names
ends up either announced by the sketch as /enq/<capability> or written
into the board's boards.json note as present-but-not-wired. A pin map
copied into BOARDS.md and then ignored is the failure mode: the LilyGO
T-Encoder-Pro was brought up announcing only /enq/btn, its encoder pins
recorded and unused, on a board named after its encoder. See BRINGUP.md,
The documentation is a work list.
Verify, don't assert. No number leaves the session without: trickle
gate passed, 3 repeats, same-day reference board, mechanism named.
Unverified code carries a STATUS comment saying so. If a measurement
surprises you, suspect the instrument first — five "board bugs" in this
repo's history were the apparatus.
Report failures faithfully, including your own instrument errors.
Latch what a human does; never poll for it. A press or a touch is
shorter than an OSC round trip, so count edges in the sketch and expose
the count on /diag, beside whatever instantaneous level the contract
asks for. Three windows on the T-Encoder-Pro (185, 891 and 1308 samples)
each reported "never changed" and none of them was evidence about the
switch. Say the instruction in chat — a background task's stdout is a log
file nobody is watching. And release every documented reset line before
sweeping a bus: a part held in reset looks exactly like a part that is
not there, which made one touch controller appear on one boot and vanish
on the next. See BRINGUP.md Phase 4.
Read the factory flash before overwriting it.
esptool --port PORT read-flash 0 0x1000000 <backup>.bin, then strings
it. Factory firmware names its own board, and this is stronger evidence
than USB descriptors — every native-USB ESP32 is 303a:1001 and the MAC
OUI is Espressif's. It identified the LilyGO T-FPGA outright (Hello T-FPGA-CORE, a /Users/lewis/ PlatformIO path, XPowersAXP2101). The
same command is the backup, which matters when the MCU is the only thing
that configures an FPGA, a display or a radio. See BRINGUP.md Phase 0.
Some peripherals have no power until firmware grants it. A PMU-managed
board (AXP2101 and friends) leaves rails off at reset, so a peripheral that
"does not respond" may simply be unpowered — the T-FPGA's FPGA core and all
four I/O bank voltages come from the AXP2101, and the M5Capsule latches its
own power through GPIO46. Set the rails the vendor documents and do not
improvise the voltages.
Two boards can ship under one name. When a vendor's config is a
build-time switch between hardware revisions, do not pick a branch —
detect at run time by something the parts themselves disagree about (two
touch controllers at different I2C addresses settled the T-Encoder-Pro),
and report what was found. A vendor's default and a vendor's flagship
example have been observed selecting different revisions.
Transport flow (every board)
Phase 0 identify → Phase 1 compile (test/hardware/OscEcho; new cores may
need a rung in SLIPEncodedSerial.h's detection ladder; ESP32 FQBN options
are per-board — read boards.txt, never carry options between boards) →
Phase 2 flash (per-family procedures in BOARDS.md; after every flash,
re-list ports; lsof when "no device") → Phase 3 verify, in order:
echotest.py, widths.py, oscprobe.py, then bench.py
verify / in 50 -1 / out 200 / compound / ring 20. Place the stack in the
family table (NAK-clean, drop-with-byte-ceiling, or pool-starved-compound)
by fingerprint, then add the BOARDS.md row.
Demo flow (boards with built-in peripherals)
Phase 4 sketch: /enq answered by a bundle — the sketch name, then one
/enq/<capability> line per peripheral actually present, carrying its
shape (absence is silence, never a boolean or a sentinel; see
ADDRESSES.md); probe capabilities at runtime by signal (not begin()'s
return — buses exist without parts on them); respect ISR-shaped driver
contracts; uint64 square-accumulators, float division, window-relative
scope normalisation, full-scale wire values, measured gain; pace with
millis() + /rate (0 stops; clamping it to a minimum is a bug — it makes
"be quiet" stream faster). Phase 5 page: serve on localhost with no-store;
decode every OSC tag including blobs; draw the board to scale and mirror
outbound state; dBFS meters with peak-hold and latched clip; hide absent
peripherals via /enq. Hand-written pages get a contract test
(extras/webserial/test/test-cpx-contract.mjs is the pattern).
Record
BOARDS.md row + any new flashing procedure; test/hardware/README.md if the
stack family taught something new; commit messages state what was measured
and what was not.
1---2name: board-bringup3description: Bring up the OSC library on a newly connected Arduino board — identify the actual hardware, compile, flash, verify the SLIP/OSC transport with the test/hardware suites, and optionally build the XxxOscuino demo sketch plus Web Serial page for boards with built-in peripherals. Use when the user plugs in a board, asks to port or validate OSC on new hardware, or asks for a board demo page.4---56Follow [BRINGUP.md](../../../BRINGUP.md) — it is the authority; this file is7the operating summary. Record results per8[BOARDS.md](../../../BOARDS.md) and the Method rules in9[test/hardware/README.md](../../../test/hardware/README.md).1011## Non-negotiables12131. **Identify before flashing — the chip AND the board it sits on.**14 `arduino-cli board list` + `system_profiler SPUSBDataType` (VID/PID) +15 `esptool chip_id` for ESP32s. Boards displace each other on hubs, ports16 renumber after every flash, UF2 bootloader IDs name the bootloader not17 the model, and what the user believes is plugged in has been wrong18 before. Never identify by port name.1920 `chip_id` names the silicon only. Every native-USB ESP32 enumerates as21 `303a:1001`, so VID/PID cannot tell a XIAO from a SuperMini — and the22 pin map, LED polarity, populated buses and FQBN defaults all belong to23 the *carrier*. Inferring the board from the chip put the EGG demo on a24 XIAO ESP32-C3 on 2026-09-04, driving the wrong pins and reporting a25 missing peripheral that was really a wrong address. Ask the user what26 the board is and believe them over your own inference; flash the27 board-agnostic `test/hardware/OscEcho` before any pin-driving demo; and28 treat a peripheral that "fails to probe" as a suspected wrong pin map29 first. See BRINGUP.md Phase 0, *The chip is not the board*.302. **The vendor's peripheral list is a work list.** Having found the board's31 documentation, do not stop at identification. Every peripheral it names32 ends up either announced by the sketch as `/enq/<capability>` or written33 into the board's `boards.json` note as present-but-not-wired. A pin map34 copied into BOARDS.md and then ignored is the failure mode: the LilyGO35 T-Encoder-Pro was brought up announcing only `/enq/btn`, its encoder pins36 recorded and unused, on a board named after its encoder. See BRINGUP.md,37 *The documentation is a work list*.383. **Verify, don't assert.** No number leaves the session without: trickle39 gate passed, 3 repeats, same-day reference board, mechanism named.40 Unverified code carries a STATUS comment saying so. If a measurement41 surprises you, suspect the instrument first — five "board bugs" in this42 repo's history were the apparatus.434. **Report failures faithfully**, including your own instrument errors.445. **Latch what a human does; never poll for it.** A press or a touch is45 shorter than an OSC round trip, so count edges in the sketch and expose46 the count on `/diag`, beside whatever instantaneous level the contract47 asks for. Three windows on the T-Encoder-Pro (185, 891 and 1308 samples)48 each reported "never changed" and none of them was evidence about the49 switch. Say the instruction in chat — a background task's stdout is a log50 file nobody is watching. And release every documented reset line *before*51 sweeping a bus: a part held in reset looks exactly like a part that is52 not there, which made one touch controller appear on one boot and vanish53 on the next. See BRINGUP.md Phase 4.546. **Read the factory flash before overwriting it.**55 `esptool --port PORT read-flash 0 0x1000000 <backup>.bin`, then `strings`56 it. Factory firmware names its own board, and this is stronger evidence57 than USB descriptors — every native-USB ESP32 is `303a:1001` and the MAC58 OUI is Espressif's. It identified the LilyGO T-FPGA outright (`Hello59 T-FPGA-CORE`, a `/Users/lewis/` PlatformIO path, `XPowersAXP2101`). The60 same command is the backup, which matters when the MCU is the only thing61 that configures an FPGA, a display or a radio. See BRINGUP.md Phase 0.627. **Some peripherals have no power until firmware grants it.** A PMU-managed63 board (AXP2101 and friends) leaves rails off at reset, so a peripheral that64 "does not respond" may simply be unpowered — the T-FPGA's FPGA core and all65 four I/O bank voltages come from the AXP2101, and the M5Capsule latches its66 own power through GPIO46. Set the rails the vendor documents and do not67 improvise the voltages.688. **Two boards can ship under one name.** When a vendor's config is a69 build-time switch between hardware revisions, do not pick a branch —70 detect at run time by something the parts themselves disagree about (two71 touch controllers at different I2C addresses settled the T-Encoder-Pro),72 and report what was found. A vendor's default and a vendor's flagship73 example have been observed selecting *different* revisions.7475## Transport flow (every board)7677Phase 0 identify → Phase 1 compile (`test/hardware/OscEcho`; new cores may78need a rung in `SLIPEncodedSerial.h`'s detection ladder; ESP32 FQBN options79are per-board — read `boards.txt`, never carry options between boards) →80Phase 2 flash (per-family procedures in BOARDS.md; after every flash,81re-list ports; `lsof` when "no device") → Phase 3 verify, in order:82`echotest.py`, `widths.py`, `oscprobe.py`, then `bench.py`83verify / in 50 -1 / out 200 / compound / ring 20. Place the stack in the84family table (NAK-clean, drop-with-byte-ceiling, or pool-starved-compound)85by fingerprint, then add the BOARDS.md row.8687## Demo flow (boards with built-in peripherals)8889Phase 4 sketch: `/enq` answered by a bundle — the sketch name, then one90`/enq/<capability>` line per peripheral actually present, carrying its91shape (absence is silence, never a boolean or a sentinel; see92ADDRESSES.md); probe capabilities at runtime by *signal* (not `begin()`'s93return — buses exist without parts on them); respect ISR-shaped driver94contracts; uint64 square-accumulators, float division, window-relative95scope normalisation, full-scale wire values, measured gain; pace with96`millis()` + `/rate` (0 stops; clamping it to a minimum is a bug — it makes97"be quiet" stream faster). Phase 5 page: serve on localhost with no-store;98decode every OSC tag including blobs; draw the board to scale and mirror99outbound state; dBFS meters with peak-hold and latched clip; hide absent100peripherals via `/enq`. Hand-written pages get a contract test101(`extras/webserial/test/test-cpx-contract.mjs` is the pattern).102103## Record104105BOARDS.md row + any new flashing procedure; test/hardware/README.md if the106stack family taught something new; commit messages state what was measured107and what was not.