Running pio test on real hardware means a board on the desk, a USB
cable, and a free hand to press reset on push. With LabWired, none of that is necessary.
This guide wires your PlatformIO pio test suite into the
LabWired simulator, a deterministic digital twin of your MCU.
Your tests don't change and PlatformIO still parses the usual Unity output;
the only thing missing is the board. If you take the GitHub Actions path below,
you won't install anything locally either.
It plugs into PlatformIO the same way
Renode and QEMU do:
through the built-in test_testing_command hook. PlatformIO doesn't
need to know anything about LabWired; it runs a command and reads the Unity
results from its output.
Fork w1ne/labwired-core, drop this
into .github/workflows/firmware-tests.yml, and push:
name: Firmware tests (LabWired simulator)
on: [push, pull_request]
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install PlatformIO
run: pipx install platformio
- uses: dtolnay/rust-toolchain@stable
- name: Build the LabWired CLI and put it on PATH
run: |
cargo build --release -p labwired-cli
echo "$PWD/target/release" >> "$GITHUB_PATH"
- name: Run unit tests in the simulator
working-directory: examples/platformio/nrf52840-unity
run: pio test -e nrf52840_dk
GitHub's runner installs PlatformIO, builds the firmware, runs it in the simulator, and reports per-test results, all without touching your machine:
test/test_smoke/test_main.c:50: test_addition [PASSED]
test/test_smoke/test_main.c:51: test_uart_is_enabled [PASSED]
test/test_smoke/test_main.c:52: test_string_length [PASSED]
================== 3 test cases: 3 succeeded in 00:00:01.4 ==================
If you'd rather poke at it from a terminal, open the repo in a
GitHub Codespace and run
cd examples/platformio/nrf52840-unity && pio test. That still
keeps everything off your machine, since the Codespace runs in your browser.
flowchart LR
A["pio test<br/>builds firmware.elf"] -- ELF --> B["LabWired simulator<br/>boots the ELF · deterministic"]
B -- "UART → stdout" --> C["PlatformIO<br/>parses Unity PASS/FAIL"]
C --> D(("✓"))
classDef ok fill:#27c93f,stroke:#1a1a1a,stroke-width:2px,color:#0d2b12;
class D ok;
Both sides are plain headless processes connected by a pipe. There is nothing to flash, no probe to enumerate, and no board state to drift between runs — which is where most hardware-in-the-loop flakiness comes from.
It's a few lines of platformio.ini:
[platformio] ; so the ${platformio.*} vars resolve
[env:nrf52840_dk]
platform = nordicnrf52
board = nrf52840_dk
test_testing_command =
labwired test --script labwired.test.yaml
--firmware ${platformio.build_dir}/${this.__env__}/firmware.elf
pio test builds firmware.elf, then runs this command and
parses its output. The only firmware-side requirement is that Unity's output
reaches the UART that LabWired mirrors to stdout. With the Arduino, Zephyr, or
mbed frameworks, that is your normal test serial already.
result.json.The simulator executes your firmware on a full Cortex-M core model with peripherals validated against real silicon, so the instructions that run in CI are the ones that would run on the board.