LabWired The easy way to build hardware
An RP2040 serial session runs in the LabWired playground.

Run your PlatformIO unit tests with no hardware. You install nothing.

Category: Tutorial

A pio test on real hardware needs a board on the desk. The test needs a USB cable. You need a free hand to press reset on push. LabWired needs none of those items.

This guide connects your PlatformIO pio test suite to the LabWired simulator. The LabWired simulator is a deterministic digital twin of your MCU. Your tests do not change. PlatformIO still parses the usual Unity output. Only the board is absent. You can use the GitHub Actions path below. That path installs nothing on your local machine.

LabWired connects to PlatformIO through the same hook as Renode and QEMU. The hook is the built-in test_testing_command. PlatformIO does not need information about LabWired. PlatformIO runs a command. PlatformIO reads the Unity results from the output of the command.

Start in GitHub Actions with no install

Fork w1ne/labwired-core. Put this text in .github/workflows/firmware-tests.yml. Push the file.

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

The GitHub runner installs PlatformIO. The runner builds the firmware. The runner runs the firmware in the simulator. The runner reports the result of each test. This work does not use 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 ==================

You can use a terminal instead. Open the repository in a GitHub Codespace. Run cd examples/platformio/nrf52840-unity && pio test. This path keeps all work off your machine. The Codespace runs in your browser.

How the tests run

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;

Caption: A process does the work of the board. The rest of the pipeline does not change.

The two sides are plain headless processes. A headless process has no display. A pipe connects the two processes. You do not flash a device. You do not enumerate a probe. Board state does not change between runs. A change in board state causes most hardware-in-the-loop flakiness.

The full integration

The integration is 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. pio test then runs this command. pio test parses the output of the command. Unity output must reach the UART that LabWired mirrors to stdout. For the Arduino, Zephyr, or mbed frameworks, that UART is already your normal test serial port.

What you can test today. The simulator is reliable for bare-metal ARM Cortex-M firmware on validated boards. The simulator is reliable for minimal-HAL ARM Cortex-M firmware on validated boards. The validated boards are STM32, nRF52, and RP2040. Full framework stacks become available one board at a time. A complete Arduino-ESP32 + FreeRTOS image now boots to a rendered e-paper panel. Check the supported boards. Do this before you use the loop on a large framework project. The example above is bare-metal nRF52840.

What you get from the tests

The simulator executes your firmware on a full Cortex-M core model. The model includes peripherals. LabWired validates the peripherals against real silicon. The instructions that run in CI are the instructions that would run on the board.

See the example you can run →

Andrii Shylenko
Andrii Shylenko

Founder, LabWired.