Documentation

Everything you need to get firmware CI running on your repo.

Quick start

  1. 1

    Install the GitHub App

    Visit github.com/apps/embedci/installations/new and grant access to the repositories you want to test.

  2. 2

    Add .embedci.yml to your repo root

    Create a .embedci.yml file at the root of your repository. See the .embedci.yml reference below for all available options.

  3. 3

    Open a PR — EmbedCI runs automatically

    On every pull request, EmbedCI compiles your firmware, boots it in the Renode emulator, and posts a check run with pass/fail status directly on the PR.

.embedci.yml reference

FieldRequiredDefaultDescription
boardyesZephyr board name (e.g. stm32f4_disco)
appno.Path to Zephyr app directory
testnotests/smoke.robotPath to Robot Framework test file
modenoadvisoryadvisory | blocking. Advisory (Free) posts the verdict but never blocks the merge. Blocking (Team) makes EmbedCI a required check that stops the merge on failure.
gatesnoOptional budget/quality thresholds. A violated gate fails the check. See Budget gates below.

Minimal

.embedci.yml
board: stm32f4_disco

Full

.embedci.yml
board: stm32f4_disco
app: ./firmware
test: tests/smoke.robot

Budget gates

Gates turn metrics into enforcement. Add a gates: block to your .embedci.yml and any threshold you exceed will fail the check — even if the build and tests otherwise pass. Gates are fully opt-in: with no gates: block, nothing is enforced. A gate whose metric isn't available for a given build is skipped.

GateDescription
flash_max_pctFail if flash usage exceeds this % of the region
ram_max_pctFail if RAM usage exceeds this % of the region
flash_max_bytesFail if flash usage exceeds N bytes
ram_max_bytesFail if RAM usage exceeds N bytes
stack_max_bytesFail if the worst-case stack frame exceeds N bytes
flash_growth_bytesFail if flash grew more than N bytes vs the last build
ram_growth_bytesFail if RAM grew more than N bytes vs the last build
misra_maxFail if MISRA findings exceed N
warnings_maxFail if static-analysis warnings exceed N
coverage_min_pctFail if line coverage is below this % (when coverage is measured)
.embedci.yml
board: stm32f4_disco
gates:
  flash_max_pct: 95
  ram_max_pct: 90
  stack_max_bytes: 2048
  flash_growth_bytes: 512
  misra_max: 0

Supported boards

Powered by Renode — supports the most common embedded MCU families out of the box.

STM32 F-series

stm32f4_disconucleo_f401renucleo_f746zg

STM32 H/G/L-series

nucleo_h743zinucleo_g071rbnucleo_l073rz

Nordic

nrf52840dkarduino_nano_33_ble

NXP

mimxrt1064_evks32k388evbfrdm_k64f

Renesas

ek_ra2e1ek_ra4m2ek_ra6m4ek_ra8m1

Microchip

sam4s_xplainedatsamd21_xproatsamd51_xpro

Writing tests

Tests use Robot Framework with the renode-test library. The library provides keywords for controlling the Renode emulator — loading platform descriptions, loading your compiled ELF, and asserting on UART output.

Minimal smoke test

tests/smoke.robot
*** Settings ***
Library    renode-test

*** Test Cases ***
Boot check
    Execute Command    mach create
    Execute Command    machine LoadPlatformDescription @platforms/boards/stm32f4_discovery.repl
    Execute Command    sysbus LoadELF @${ELF}
    Create Terminal Tester    sysbus.usart2
    Execute Command    start
    Wait For Line On Uart    Hello World

The ${ELF} variable is automatically injected by EmbedCI and points to the compiled firmware binary.

Asserting GPIO & registers

Beyond UART, EmbedCI ships a keyword resource at ${EMBEDCI_KEYWORDS} for asserting peripheral registers, memory, and GPIO pin state against the emulated hardware. Import it alongside the Renode keywords.

tests/gpio.robot
*** Settings ***
Resource    ${RENODEKEYWORDS}
Resource    ${EMBEDCI_KEYWORDS}

*** Test Cases ***
LED drives high
    ...  # boot + load ELF as above
    Wait For Uart Line    LED on
    Pin Should Be High    0x40020C14    12   # GPIOD ODR, pin 12
    Register Should Be    0x40023830    0x8   # RCC clock enable

Available keywords: Wait For Uart Line, Read Memory Word, Memory Word Should Be, Read Register, Register Should Be, Register Bits Should Be Set, Pin Should Be High, Pin Should Be Low.

Check run output

After each build, EmbedCI posts a GitHub check run on your pull request. The result means one of the following:

Pass

Firmware compiled successfully, booted in the emulator, and all Robot Framework assertions passed — including the UART output check.

Fail

Either the firmware failed to compile (build error), or it compiled but a test assertion failed — for example, expected UART output was never received.

Advisory vs. blocking

The same verdict can either inform or enforce, set by mode in .embedci.yml:

  • Advisory (default — Free) — EmbedCI posts the verdict so you can see whether the PR would merge, but the check is non-blocking: the merge button stays enabled even when the verdict fails.
  • Blocking (Team) — set mode: blocking and make EmbedCI a required status check; a failing verdict then actually blocks the merge.

On Free, requesting mode: blocking (or running on a private repo) runs in advisory with an upgrade note — it never hard-fails your build.

Full build logs, compiler output, and Robot Framework test results are available in the Details link on the check run, directly on the pull request page. You can inspect failed assertions, UART traces, and the exact compiler error message without leaving GitHub.