Breadboard
docs
10 · Hardware tests in your pipeline

Breadboard CI

Run your circuits as tests on every push: deterministic emulated-hardware runs on hosted workers, GitHub-annotated failures, and replayable flight recordings.

Breadboard CI runs a circuit plus a small test spec on Breadboard's servers — the same engine and emulated boards as the bench, headless — and turns the result into a pass/fail your pipeline can gate on. Deadlines in a spec are simulated time, never wall clock: the engine is deterministic on its 5 ms grid, so a verdict can never flake because a runner was slow. When a spec fails, the run comes back with a flight recording you can replay bit-exactly on your own bench. CI is part of the CI plan ($12/month or $96/year — everything in Pro, plus the hosted runners).

Quickstart: GitHub Actions

  1. 1Mint a token: in the app, account menu → CI tokens… → name it and Create token. The ci_… secret is shown exactly once — copy it into a repository secret named BREADBOARD_CI_TOKEN. Revoke it any time from the same dialog.
  2. 2Commit your circuit: save the bench to a JSON file in the repo (File → Export → Project, e.g. circuit.bb.json).
  3. 3Write a spec: a .bbtest.json file (format below) next to it, e.g. in tests/.
  4. 4Add the workflow:
# .github/workflows/breadboard.yml
name: circuit-tests
on: [push, pull_request]
jobs:
  breadboard:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: Dollis-Hill/breadboard-ci-action@v1
        with:
          token: ${{ secrets.BREADBOARD_CI_TOKEN }}
          doc: circuit.bb.json
          specs: tests/
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: breadboard-ci-recordings
          path: breadboard-ci-recordings/

Failures land as error annotations on the spec file, with the failing step, the sim time it missed its deadline, and a tail of the serial output. The action wraps a small CLI, breadboard-ci: breadboard-ci run --doc circuit.bb.json tests/ submits and polls (token from BREADBOARD_CI_TOKEN), breadboard-ci ping checks a token. Exit codes: 0 every spec passed, 1 a spec failed, 2 setup or service trouble (bad token, missing plan, spent minutes).

The .bbtest.json spec format

{
  "version": 1,
  "name": "thermostat kicks in",
  "boot": { "bootTimeoutSimS": 20, "settleSimS": 5 },
  "wallBudgetMs": 120000,
  "steps": [
    { "type": "expect-serial", "component": "esp1",
      "pattern": "ready", "withinSimS": 10 },
    { "type": "set-param", "component": "tmp1", "params": { "temperatureC": 41 } },
    { "type": "expect-visual", "component": "led1",
      "brightness": { "min": 0.5 }, "withinSimS": 5 }
  ]
}

A spec boots the document (MicroPython banner → REPL on every board → the Code tab's script injected), runs the steps in order, then settles for settleSimS more simulated seconds. Every run also implicitly asserts that no firmware error or error-level diagnostic ever fires — a crashing program fails even if every explicit step passed. A spec must contain at least one expect-* step, and steps target components by their id (select a part on the bench to see it in the Inspector).

  • expect-serial — a regex must match the board's serial output within withinSimS simulated seconds (output after your script started, and after the latest stimulus step). expect-serial-absent is its dual, checked once at the end of the run.
  • interact — press the hardware: buttonDown/buttonUp, switchSet, dialChange, toggleDip, keypadDown/keypadUp, pirTrigger, turn (rotary encoder).
  • set-param — set component parameters directly (sensor values, slider positions): { "params": { "temperatureC": 41 } }.
  • write-serial — type into a board's serial console (omit component to broadcast to every serial-capable board).
  • wait — advance the simulation by simS seconds.
  • expect-visual — poll a component's visible state until every given predicate holds: brightness (0..1 min/max), textContains (character displays), pixelsLit (LED strips), rpm, toneHz, fault.
  • expect-voltage — a net voltage at a component pin, e.g. { "component": "esp1", "pin": "g4", "min": 3.0 }.
Firmware scope: MicroPython on the ESP32 family and Pico, analog/digital circuits, and prebuilt firmware blobs already referenced by the document. Compiling Arduino sketches inside a CI run is coming later.

Failure recordings

A failing run returns the whole session as a .bbrec.json flight recording — every simulation input at its exact sim time. The action writes recordings to breadboard-ci-recordings/ (keep them with an upload-artifact step, as in the quickstart). To debug one, download the artifact and open breadboard.build/app/?open-recording=1 — pick the file and the failure replays on the bench with the full time-travel deck: pause, scrub to the moment it went wrong, single-step, or resume live from any instant and take over.

Allowances and limits

  • The CI plan includes 1500 simulated minutes a month (240 a day) and 2 runs in parallel. A run charges its simulated duration, rounded up to whole minutes.
  • One run is one document plus one spec, up to 2 MB together; up to 100 steps per spec; results (verdict + recording) are kept for 24 hours.
  • wallBudgetMs (default 120 s, max 600 s) is a real-time safety valve for runaway programs, not a test input — a healthy run never hits it.