External Applications
oveRTOS applications can live outside the oveRTOS source tree. An external app is a standalone directory with its own app.yaml, Makefile, defconfigs, and optionally RTOS patches and config overlays. The build system discovers it via environment variable and builds it exactly like an in-tree app.
Directory Layout
my_app/
├── Makefile # Build entry point (3 lines)
├── app.yaml # Application manifest
├── include/ # Header files
│ └── app_conf.h
├── src/ # Source files
│ ├── app.c
│ └── ...
├── defconfigs/ # Saved configurations
│ └── <board>/
│ └── <board>_<rtos>_<appname>_defconfig
├── nuttx/ # NuttX RTOS overlay (optional)
│ └── <board-name>_defconfig
├── patches/ # RTOS source patches (optional)
│ ├── freertos/
│ │ └── 0001-fix-something.patch
│ ├── nuttx/
│ │ └── 0001-fix-something.patch
│ └── zephyr/
│ └── 0001-fix-something.patch
└── output/ # Build artifacts (generated)
The App Makefile
The app Makefile is the build entry point. It is just three lines:
APP_DIR := $(CURDIR)
OVE_DIR ?= /path/to/oveRTOS
include $(OVE_DIR)/config/make/ove_app.mk
APP_DIRmust point to the app directory (always$(CURDIR)).OVE_DIRmust resolve to the oveRTOS source tree. Set it to an absolute path or a$(realpath ...)expression matching your directory layout. Use?=so it can be overridden from the environment (e.g.make OVE_DIR=/opt/oveRTOS).- The
includeline pulls inove_app.mk, which provides all build targets.
ove_app.mk does the following:
- Sets
OVE_EXTERNAL_APPS=$(APP_DIR)so the build system discovers the app. - Bootstraps the Python venv and
oveCLI if not already set up. - Exposes these Make targets:
| Target | Description |
|---|---|
make (default) |
Full pipeline: download + configure + build |
make <name>_defconfig |
Load a saved defconfig |
make menuconfig |
Interactive Kconfig TUI |
make savedefconfig |
Save current config as minimal defconfig |
make nuttx-menuconfig |
NuttX native menuconfig (layered) |
make zephyr-menuconfig |
Zephyr native menuconfig (layered) |
make download |
Fetch RTOS sources |
make configure |
Generate build files from .config |
make all |
Download + configure + build |
make run |
Run firmware (QEMU or host) |
make flash |
Flash firmware to hardware |
make alldefconfigs |
Build every defconfig in defconfigs/ |
make clean |
Remove output/ |
make help |
List targets and available defconfigs |
All output goes into the app's own output/ directory, not into the oveRTOS tree.
app.yaml
The app.yaml manifest declares the application language, sources, and optional Kconfig parameters.
Required Fields
| Field | Type | Description |
|---|---|---|
lang |
string | c, cpp, rust, or zig |
description |
string | Shown in the Kconfig application menu |
Language-Specific Fields
C / C++ (require sources):
lang: c
description: "My App"
sources:
- src/app.c
- src/util.c
includes:
- include
Rust (requires rust.lib_name):
lang: rust
description: "Rust App"
rust:
lib_name: my_app
crate_dir: "." # optional, default: "."
Zig (requires zig.lib_name):
lang: zig
description: "Zig App"
zig:
lib_name: my_app
src_dir: src # optional, default: "src"
Platform and Board Source Overrides
Use platform_sources to compile different files depending on the RTOS, and board_sources to override per board. Board-specific sources take priority over platform-specific.
lang: c
description: "My DSP App"
sources:
- src/app.c
- src/ui.c
platform_sources:
default:
- src/dsp_stub.c # fallback for all RTOSes
freertos:
- src/dsp_freertos.c # FreeRTOS-specific DSP
board_sources:
stm32f746g-discovery:
- src/dsp_cmsis.c # hardware-accelerated DSP for this board
includes:
- include
Resolution order:
- If the active board is listed in
board_sources, use those files. - Otherwise, if the active RTOS is listed in
platform_sources, use those files. - Otherwise, if
platform_sources.defaultexists, use that. - Otherwise, no extra sources are added.
The resolved extra sources are appended to the base sources list.
App-Specific Kconfig
The kconfig field injects raw Kconfig content into the app's configuration menu. These options appear under the Application menu in make menuconfig and are available as CONFIG_* macros in generated headers.
kconfig: |
config DSP_BUFFER_SIZE
int "DSP buffer size (samples)"
default 512
config DSP_RATE
int "Sample rate (Hz)"
default 44100
Access these in C via the generated ove_config.h:
#include "generated/ove_config.h"
static int16_t buf[CONFIG_DSP_BUFFER_SIZE];
Defconfigs
Defconfigs are minimal Kconfig snapshots that select the app, RTOS, board, and enabled modules. They live under defconfigs/<board>/ and must follow the naming convention:
<board>_<rtos>_<appname>_defconfig
Example defconfig (defconfigs/stm32f746/stm32f746_freertos_myapp_defconfig):
CONFIG_OVE_APP_MYAPP=y
CONFIG_OVE_APP_LANG_C=y
CONFIG_OVE_RTOS_FREERTOS=y
CONFIG_OVE_BOARD_STM32F746G_DISCO=y
CONFIG_OVE_AUDIO=y
CONFIG_OVE_FS=y
CONFIG_OVE_CONSOLE=y
CONFIG_OVE_LOG=y
CONFIG_OVE_LOG_LEVEL_INF=y
CONFIG_OVE_TOOLCHAIN_DOWNLOAD=y
The board subdirectory name (e.g. stm32f746) is parsed from the path to determine the workspace layout. The RTOS and app name are parsed from the filename.
Load a defconfig:
make stm32f746_freertos_myapp_defconfig
Save the current config as a minimal defconfig:
make savedefconfig
# writes defconfig to the oveRTOS root — move it to your defconfigs/ dir
mv /path/to/oveRTOS/defconfig defconfigs/stm32f746/stm32f746_freertos_myapp_defconfig
Configuration Layering
For NuttX and Zephyr, the RTOS has its own Kconfig system separate from oveRTOS. The build system merges multiple configuration layers at build time, with higher layers overriding lower ones:
NuttX Config Layers
Layer 1: oveRTOS template base generated/nuttx_defconfig
(auto-generated from oveRTOS .config)
↓ overridden by
Layer 2: Board overlay boards/<board>/nuttx/ove_board_defconfig
(board-specific NuttX settings)
↓ overridden by
Layer 3: App overlay <app>/nuttx/<board-name>_defconfig
(app-specific NuttX settings)
↓ overridden by
Layer 4: User customizations rtos.config
(saved by `make nuttx-menuconfig`)
Layer 1 is generated from oveRTOS Kconfig options using a Jinja2 template. It sets base NuttX requirements like the entry point, watchdog, stack coloration, TLS, and LVGL support.
Layer 2 is a file in the oveRTOS board directory (boards/<board>/nuttx/ove_board_defconfig). It configures board-specific NuttX peripherals like SDMMC, LTDC, DTCM, and UART baud rate. This file is maintained in the oveRTOS tree and shared by all apps targeting that board.
Layer 3 is provided by the app. Place a file named <board-name>_defconfig (e.g. stm32f746g-discovery_defconfig) in a nuttx/ directory at the app root. This is where the app enables NuttX drivers and subsystems it needs. For example, an audio app would enable SAI, I2S, codec drivers, and FAT filesystem here:
# nuttx/stm32f746g-discovery_defconfig
CONFIG_INIT_STACKSIZE=8192
CONFIG_STM32F7_SDMMC1=y
CONFIG_FS_FAT=y
CONFIG_DRIVERS_AUDIO=y
CONFIG_AUDIO_WM8994=y
CONFIG_STM32F7_SAI2=y
If no board-specific overlay exists, the build system also checks for a generic <app>/nuttx_defconfig.
Layer 4 is created automatically when you run make nuttx-menuconfig. The NuttX native menuconfig presents the merged result of layers 1-3 as a baseline, then saves only the delta (your changes) to rtos.config in the workspace. This file should not be edited by hand.
Zephyr Config Layers
Layer 1: oveRTOS template base generated/prj.conf
↓ appended by
Layer 3: App overlay <app>/prj.conf
↓ appended by
Layer 4: User customizations rtos.config
(saved by `make zephyr-menuconfig`)
Layer 2 (board overlay) is skipped for Zephyr because Zephyr's CMake system auto-detects board-specific boards/*.conf files.
FreeRTOS
FreeRTOS configuration is handled entirely through the oveRTOS Kconfig system. The generated FreeRTOSConfig.h is derived from oveRTOS .config options. There are no additional layers.
Patches
External apps can carry patches for RTOS source trees. Place .patch files in:
patches/
├── freertos/
│ └── 0001-fix-something.patch
├── nuttx/
│ └── 0001-add-codec-driver.patch
└── zephyr/
└── 0001-fix-board-dts.patch
Patches are applied during the build, after board patches and before compilation. The application order is:
- Board patches from
boards/<board>/<rtos>/patches/(maintained in oveRTOS) - App patches from
<app>/patches/<rtos>/(maintained in the external app)
App patches are applied on top of board patches so they can depend on board-level fixes. Patches use git apply format and are applied idempotently (a stamp file tracks which patches have already been applied). The downloaded RTOS sources in dl/ are never modified; patches are applied to build-tree copies.
To create a patch:
cd output/<board>/<rtos>/<app>/build/nuttx # or the relevant RTOS build dir
# Make your changes
git diff > /path/to/my_app/patches/nuttx/0001-description.patch
Build Workflow
Quick Start
cd my_app/
# 1. Load a defconfig
make stm32f746_freertos_myapp_defconfig
# 2. Build (downloads RTOS sources, generates configs, compiles)
make
# 3. Run on QEMU or flash to hardware
make run
# or
make flash
Step by Step
# Load config
make stm32f746_nuttx_myapp_defconfig
# Download RTOS sources
make download
# Generate build files
make configure
# Build
make all
# (Optional) Tune NuttX options
make nuttx-menuconfig
# Rebuild
make
Interactive Configuration
# oveRTOS-level config (app, board, RTOS, modules)
make menuconfig
# NuttX-native config (drivers, stack sizes, peripherals)
make nuttx-menuconfig
# Zephyr-native config
make zephyr-menuconfig
Building All Defconfigs
make alldefconfigs
This iterates over every *_defconfig file in defconfigs/, loads it, and runs the full build pipeline. A summary is printed at the end. This is useful for CI.
Workspace Layout
Each defconfig creates an isolated workspace:
output/
└── <board>/
└── <rtos>/
└── <app>/
├── .config # Expanded Kconfig
├── build/ # RTOS build tree
├── generated/ # Generated headers and build fragments
│ ├── ove_config.h
│ ├── ove_config.cmake
│ ├── app_sources.mk
│ ├── app_sources.cmake
│ ├── board_desc.h
│ └── nuttx_defconfig (or prj.conf, FreeRTOSConfig.h)
├── images/ # Firmware binaries
│ ├── firmware.elf
│ └── firmware.bin
└── dl/ # Downloaded RTOS sources
Multiple workspaces can coexist. Switching between them is done by loading a different defconfig.
Walkthrough: Creating a New External App
1. Create the Directory Structure
mkdir -p my_app/{src,include,defconfigs/qemu}
cd my_app
2. Write the Makefile
cat > Makefile << 'EOF'
APP_DIR := $(CURDIR)
OVE_DIR ?= /path/to/oveRTOS
include $(OVE_DIR)/config/make/ove_app.mk
EOF
Set OVE_DIR to the actual path of your oveRTOS checkout.
3. Write app.yaml
cat > app.yaml << 'EOF'
lang: c
description: "My First oveRTOS App"
sources:
- src/app.c
includes:
- include
EOF
4. Write the Application
src/app.c:
#include "ove/app.h"
#include "ove/log.h"
#include "ove/thread.h"
OVE_LOG_MODULE_REGISTER(myapp);
void ove_main(void)
{
OVE_LOG_INF("Hello from my external app!");
}
5. Create a Defconfig
cat > defconfigs/qemu/qemu_freertos_myapp_defconfig << 'EOF'
CONFIG_OVE_APP_MYAPP=y
CONFIG_OVE_APP_LANG_C=y
CONFIG_OVE_RTOS_FREERTOS=y
CONFIG_OVE_BOARD_QEMU_MPS2_AN500=y
CONFIG_OVE_CONSOLE=y
CONFIG_OVE_LOG=y
CONFIG_OVE_LOG_LEVEL_INF=y
CONFIG_FREERTOS_SOURCE_GIT=y
CONFIG_OVE_TOOLCHAIN_DOWNLOAD=y
EOF
6. Build and Run
make qemu_freertos_myapp_defconfig
make
make run
7. (Optional) Add a NuttX Config Overlay
If your app needs specific NuttX peripherals:
mkdir -p nuttx
cat > nuttx/stm32f746g-discovery_defconfig << 'EOF'
# App-specific NuttX peripheral config
CONFIG_INIT_STACKSIZE=8192
CONFIG_STM32F7_SDMMC1=y
CONFIG_FS_FAT=y
EOF
8. (Optional) Add RTOS Patches
mkdir -p patches/nuttx
# Create your patch file
cp /path/to/0001-my-fix.patch patches/nuttx/