Zephyr RTOS skill for Codex

Devicetree and Kconfig instead of hard-coded pins. Codex keeps Zephyr apps portable by putting pins and peripherals in devicetree overlays and features in Kconfig, then checks the merged zephyr.dts and final .config instead of guessing why a driver is not ready. Logic is tested with ztest and Twister on native_sim, and boards use the current hardware model v2 names.

  • Embedded & IoT
  • $zephyr-rtos
  • 175 lines
  • 2 files
  • ~1,750 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- zephyr-rtos

Installs to ~/.agents/skills/zephyr-rtos/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $zephyr-rtos, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Builds Zephyr RTOS applications with west - board targets in the HWMv2 naming, prj.conf and Kconfig fragments, devicetree overlays and the DT_/GPIO_DT_SPEC_GET macros, threads, message queues and work queues, logging, Twister tests on native_sim, and sysbuild with MCUboot. Use when a Zephyr build fails on devicetree or Kconfig errors, a driver reports not ready, porting an app to a new board, adding a sensor or peripheral, or setting up tests and CI for a Zephyr or nRF Connect SDK project.

Use it when

  • A Zephyr or nRF Connect SDK build fails on devicetree or Kconfig
  • Adding a sensor, bus or GPIO to a Zephyr board through an overlay
  • Porting a Zephyr application to another board
  • Setting up ztest and Twister for host-side tests in CI

Use something else when

  • The project uses bare FreeRTOS with a vendor HAL: use freertos
  • You need MCUboot signing keys, slot layout and rollout policy: use firmware-ota

How to check the result

Do a pristine build for every supported board, confirm the merged zephyr.dts and final .config show the intended nodes and symbols, and run Twister on native_sim.

The mistake it prevents

Setting a Kconfig symbol whose dependencies are unmet: Zephyr drops it with only a warning, so the feature silently stays off.

~/.agents/skills/zephyr-rtos/SKILL.md

--- name: zephyr-rtos description: Builds Zephyr RTOS applications with west - board targets in the HWMv2 naming, prj.conf and Kconfig fragments, devicetree overlays and the… license: MIT ---

Done well means: hardware is described in devicetree and features in Kconfig, never hard-coded in C; the app builds for every board it claims to support from a pinned west manifest; and logic is tested on native_sim before it touches hardware.

Inspect first

  1. Read AGENTS.md, then west.yml (or the workspace manifest) for the Zephyr or nRF Connect SDK revision. APIs and board names change between releases; match the pinned one.
  2. App layout: CMakeLists.txt, prj.conf, boards/*.overlay, boards/*.conf, src/, optional sysbuild.conf, Kconfig, sample.yaml or testcase.yaml.
  3. Build once unchanged and keep the log; generated files are the ground truth.
west build -b nrf52840dk/nrf52840 app -p auto
west build -t menuconfig                 # interactive Kconfig for the current build dir
west build -t rom_report                 # also ram_report
west flash                               # user runs this with the board attached

Setting up a workspace (west init, west update, Python requirements, the Zephyr SDK) needs network and a large download; ask before doing it in the sandbox. If the user’s workspace exists, point Codex at it instead of creating another.

Board names

Zephyr 3.7+ uses hardware model v2: board/soc[/cpucluster][/variant], for example nrf5340dk/nrf5340/cpuapp, nrf52840dk/nrf52840, nucleo_f401re, rpi_pico/rp2040, esp32s3_devkitc/esp32s3/procpu, native_sim. Board overlay and conf files replace / with _: boards/nrf5340dk_nrf5340_cpuapp.overlay. west boards | grep nucleo lists them.

Where a change belongs

You want toPut it in
Enable a subsystem or driver (I2C, logging, BLE, shell)prj.conf (CONFIG_I2C=y)
Board-specific Kconfigboards/<board>.conf
Pin, bus, address, interrupt line, sensor instanceDevicetree overlay
An app option users can tuneApp Kconfig with source "Kconfig.zephyr" at the end
Build-variant config (debug vs release)-DEXTRA_CONF_FILE=debug.conf
A second overlay for a shield or variant-DEXTRA_DTC_OVERLAY_FILE=sensor.overlay or --shield

Devicetree

Overlay that enables I2C and adds a sensor, plus an alias the code can use:

/* boards/nrf52840dk_nrf52840.overlay */
&i2c0 {
    status = "okay";
    bme280: bme280@76 {
        compatible = "bosch,bme280";
        reg = <0x76>;
    };
};

/ {
    aliases {
        status-led = &led1;
    };
};
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(app, LOG_LEVEL_INF);

static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(status_led), gpios);
static const struct device *const bme = DEVICE_DT_GET(DT_NODELABEL(bme280));

int main(void)
{
    if (!gpio_is_ready_dt(&led) || !device_is_ready(bme)) {
        LOG_ERR("hardware not ready");
        return -ENODEV;
    }
    gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);

    while (1) {
        struct sensor_value t;
        if (sensor_sample_fetch(bme) == 0 &&
            sensor_channel_get(bme, SENSOR_CHAN_AMBIENT_TEMP, &t) == 0) {
            LOG_INF("temp %d.%06d C", t.val1, t.val2);
        }
        gpio_pin_toggle_dt(&led);
        k_sleep(K_SECONDS(1));
    }
}
  • In C, devicetree names use underscores: alias status-led becomes DT_ALIAS(status_led).
  • device_is_ready() false means the driver did not initialise: check the node has status = "okay", the matching CONFIG_ for the driver is set (most sensor drivers default on when CONFIG_SENSOR=y and an enabled node exists), and the bus came up.
  • After a build, read build/zephyr/zephyr.dts (the merged tree) and build/zephyr/.config (final Kconfig) before guessing. With sysbuild the app lives in build/<app-name>/zephyr/.
  • Bindings live in zephyr/dts/bindings; the binding defines which properties are required. Read it when DTC errors mention missing properties.

Kconfig traps

  • A symbol set in prj.conf with unmet dependencies is silently dropped, with only a warning in the build log. Grep the final .config to confirm the value.
  • Disable with CONFIG_FOO=n in prj.conf. Never edit build/zephyr/.config by hand; it is regenerated, and menuconfig changes vanish on a pristine build unless copied back.
  • Symbols without a prompt cannot be set from prj.conf at all; they follow their default and select statements. menuconfig shows why a symbol has its value.
  • Stack sizes are Kconfig (CONFIG_MAIN_STACK_SIZE, CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE). Turn on CONFIG_THREAD_ANALYZER=y during development to see real usage.

Concurrency

NeedPrimitive
Fixed thread at bootK_THREAD_DEFINE(name, stack, fn, p1, p2, p3, prio, 0, 0)
Pass fixed-size messagesK_MSGQ_DEFINE with k_msgq_put/k_msgq_get
Defer work from an ISRk_work_submit on the system work queue; own queue if it blocks
Periodic workk_work_delayable or k_timer handing off to work
Protect shared data between threadsk_mutex (priority inheritance); k_spinlock for ISR-shared

Cooperative threads (negative priority) are never preempted by other threads; use them only for short, deliberate sections. Never call blocking APIs or k_sleep from an ISR.

Tests with Twister

west build -b native_sim tests/unit -p auto && ./build/zephyr/zephyr.exe
west twister -T tests -p native_sim --inline-logs

Write tests with the ztest API (ZTEST_SUITE, ZTEST, zassert_equal) and declare platforms and tags in testcase.yaml. Use emulated devices (zephyr,i2c-emul-controller and sensor emulators) for driver-facing code. native_sim builds need a Linux host; on macOS run them in the project’s container or CI.

Bootloader and updates

west build -b nrf52840dk/nrf52840 app --sysbuild -p auto -- -DSB_CONFIG_BOOTLOADER_MCUBOOT=y

MCUboot signs images with a development key by default; that key is public. Production signing, slot layout and confirming images belong to firmware-ota.

Verification

  • Clean pristine build (-p always) for every board in sample.yaml or the README
  • No Kconfig warnings about unmet dependencies in the log
  • zephyr.dts shows the intended pins and status = "okay" on used nodes
  • Twister passes on native_sim; hardware log (west flash then RTT or UART output) pasted by the user shows the expected boot sequence
  • rom_report/ram_report compared against the previous build

Report

**Workspace:** Zephyr v4.1.0 via west.yml; boards nrf52840dk/nrf52840, native_sim
**Change:** BME280 moved to devicetree overlay; LED via alias; sensor thread -> k_work_delayable
**Checks:** pristine builds pass; twister 12/12 on native_sim; .config shows CONFIG_BME280=y
**Size:** flash 61.2 -> 63.0 KB, RAM 18.4 -> 18.9 KB
**Open:** production MCUboot key not configured (dev key in use)

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Zephyr RTOS"
  short_description: "Devicetree and Kconfig instead of hard-coded pins"
  default_prompt: "$zephyr-rtos device_is_ready returns false for my BME280 on Zephyr, figure out why"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc