End-to-End Example: STM32 + GitHub Actions + BenchCI Cloud

This page shows the full path from firmware build to real hardware validation.

STM32 firmware
    ↓
GitHub Actions build
    ↓
BenchCI Cloud run
    ↓
Cloud-connected Agent
    ↓
real hardware bench
    ↓
results in CLI + dashboard

Goal

By the end of this example, a CI workflow will:

  1. build firmware

  2. upload the firmware artifact

  3. ask BenchCI to run a hardware test

  4. flash the board through a registered Agent

  5. collect results and logs

  6. show the run in the BenchCI dashboard


Prerequisites

You need:

  • a BenchCI account and active workspace

  • a hardware machine connected to the DUT

  • a working bench.yaml

  • a working suite.yaml

  • a registered cloud Agent

  • a GitHub repository containing your firmware project

Verify the bench is visible:

benchci benches list

Example result:

my-bench    online    idle

1. Verify local hardware execution

On the hardware-connected machine:

benchci run \
  --bench bench.yaml \
  --suite suite.yaml \
  --artifact build/fw.elf \
  --verbose

Do not continue until this works locally.

This prevents CI debugging from hiding basic bench problems such as wrong UART ports, missing flash tools, invalid GPIO lines, or firmware that does not print the expected output.


2. Start the cloud Agent

On the hardware-connected machine:

benchci agent cloud \
  --backend https://api.benchci.dev \
  --token YOUR_AGENT_TOKEN \
  --bench bench.yaml \
  --bench-id my-bench \
  --agent-name "STM32 Lab Agent"

The Agent makes outbound requests to the BenchCI backend.

Your lab machine does not need a public inbound port.


3. Add GitHub secrets

In GitHub:

Settings → Secrets and variables → Actions → New repository secret

Add:

BENCHCI_EMAIL=engineer@company.com
BENCHCI_PASSWORD=your-password
BENCHCI_API_URL=https://api.benchci.dev
BENCHCI_BENCH_ID=my-bench

Use a dedicated CI account when possible.


4. Add GitHub Actions workflow

Create:

.github/workflows/hardware-ci.yml

Example:

name: Hardware CI

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  build-firmware:
    runs-on: ubuntu-24.04

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install ARM toolchain
        run: |
          sudo apt-get update
          sudo apt-get install -y make gcc-arm-none-eabi binutils-arm-none-eabi

      - name: Build firmware
        run: |
          make
          mkdir -p build
          cp path/to/firmware.elf build/firmware.elf

      - name: Upload firmware artifact
        uses: actions/upload-artifact@v4
        with:
          name: firmware
          path: build/firmware.elf

  hardware-test:
    runs-on: ubuntu-24.04
    needs: build-firmware

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Download firmware artifact
        uses: actions/download-artifact@v4
        with:
          name: firmware
          path: build

      - name: Install BenchCI
        run: |
          python -m pip install --upgrade pip
          pip install --upgrade benchci

      - name: Login to BenchCI
        run: |
          benchci login \
            --email "$BENCHCI_EMAIL" \
            --password "$BENCHCI_PASSWORD" \
            --api-url "$BENCHCI_API_URL"
        env:
          BENCHCI_EMAIL: ${{ secrets.BENCHCI_EMAIL }}
          BENCHCI_PASSWORD: ${{ secrets.BENCHCI_PASSWORD }}
          BENCHCI_API_URL: ${{ secrets.BENCHCI_API_URL }}

      - name: Run hardware test
        run: |
          benchci run \
            --cloud \
            --bench-id "$BENCHCI_BENCH_ID" \
            --suite suite.yaml \
            --artifact build/firmware.elf \
            --verbose
        env:
          BENCHCI_BENCH_ID: ${{ secrets.BENCHCI_BENCH_ID }}

      - name: Upload BenchCI results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: benchci-results
          path: benchci-results/

Update these paths for your project:

path/to/firmware.elf
suite.yaml

5. Push and inspect the run

After pushing:

  1. GitHub builds firmware

  2. the firmware artifact is passed to the hardware-test job

  3. BenchCI logs in

  4. BenchCI schedules the run on the selected bench

  5. the Agent flashes firmware and executes the suite

  6. BenchCI downloads results into benchci-results/

  7. GitHub uploads results as workflow artifacts


6. Inspect in the dashboard

Open:

https://app.benchci.dev

Use the dashboard to inspect:

  • run status

  • bench assignment

  • agent assignment

  • event timeline

  • failure context

  • artifacts


Example suite.yaml

version: "1"

suite:
  name: firmware_smoke

tests:
  - name: boot_ok
    steps:
      - expect_uart:
          node: dut
          transport: console
          contains: "[BOOT] OK"
          within_ms: 3000

  - name: ping
    steps:
      - send_uart:
          node: dut
          transport: console
          data: "PING\n"

      - expect_uart:
          node: dut
          transport: console
          contains: "PONG"
          within_ms: 1000

Expected artifacts

BenchCI writes results into:

benchci-results/

Typical contents include:

results.json
flash.log
transport-*.log
gpio.log
power.log

The exact logs depend on your bench and suite.


Why this example matters

This is the workflow BenchCI is designed for:

software CI discipline
        +
real hardware validation

A pull request can now build firmware, schedule a real hardware run, flash a device, validate behavior, and return machine-readable results.