Skip to content

BSP and Application Development

This flow covers setting up an edge build environment, choosing a starting point for your firmware, and integrating your own application into the Yocto image.

Involves: Edge, PyToloMEO, Embedded Manager


Development environment

ToloMEO uses kas to manage Yocto builds. The recommended setup mirrors our own: a devcontainer or a kas container so that build dependencies are fully reproducible without polluting the host system.

The meta-tolomeo repository ships a devcontainer configuration that includes kas, all required Yocto host tools, and helpers for signing and OTA package generation. Clone it and open it in VS Code or any devcontainer-compatible editor:

git clone git@gitlab.com:DAVEEmbeddedSystems/public/tolomeo-public-projects/edge/tolomeo-edge/meta-tolomeo.git
cd meta-tolomeo

Alternatively, any machine with kas installed (kas getting-started) can run builds directly through kas without a container container:

kas build kas/tolomeo-qemux86-64_tolomeo-devel_image-devel.yml

Refer to the meta-tolomeo development environment guide for the full setup instructions, including sstate cache configuration to speed up builds.


Choosing a starting point

There are two approaches depending on how much of the meta-tolomeo layer you need to modify.

Option A: Clone and extend meta-tolomeo

Clone the meta-tolomeo repository and add your recipes directly into meta-tolomeo-app. This is the fastest path to get something running but might get harder to maintain in the long run given that ToloMEO releases security updates every odd month.

git clone git@gitlab.com:DAVEEmbeddedSystems/public/tolomeo-public-projects/edge/tolomeo-edge/meta-tolomeo.git

Add your recipes under meta-tolomeo-app/recipes-<language>/<app-name>/ following the patterns described below. We recommend keeping your application source code in its own separate repository and referencing it from the recipe via SRC_URI, rather than embedding sources inside the layer.

Option B: Own meta-layer

Create a separate Yocto layer for your application and bring meta-tolomeo in as a dependency through kas. This is the cleanest long-term approach: your code lives in its own repository, meta-tolomeo updates arrive without merge conflicts, and your CI pipeline stays independent.

If you are new to Yocto, start with Bootlin's simplest-yocto-setup. It is a small, working, real-world example of a Yocto product layer built around the same kas workflow. Its meta-kiss layer and .config.yaml are the clearest available illustration of what you need to create -- read through both before going further.

A Yocto layer is just a directory with a specific layout. Create it manually in a new repository:

meta-myapp/
├── conf/
│   └── layer.conf
└── recipes-myapp/
    └── my-app/
        ├── my-app.bb
        └── my-app.service

The conf/layer.conf declares the layer to BitBake. Model it directly on meta-kiss/conf/layer.conf, adjusting the collection name and dependencies:

BBPATH .= ":${LAYERDIR}"

BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
            ${LAYERDIR}/recipes-*/*/*.bbappend"

BBFILE_COLLECTIONS += "meta-myapp"
BBFILE_PATTERN_meta-myapp = "^${LAYERDIR}/"
BBFILE_PRIORITY_meta-myapp = "9"

LAYERDEPENDS_meta-myapp = "core meta-tolomeo-distro"
LAYERSERIES_COMPAT_meta-myapp = "scarthgap"

Create a kas configuration file at the root:

workspace/
├── meta-myapp/
└── my-build.yml
---
header:
  version: 14
  includes:
    - sources/meta-tolomeo/kas/common.yml

repos:
  meta-myapp:
    layers:
      .:

  meta-tolomeo:
    url: git@gitlab.com:DAVEEmbeddedSystems/public/tolomeo-public-projects/edge/tolomeo-edge/meta-tolomeo.git
    branch: scarthgap
    tag: v1.2.1+26.03
    path: sources/meta-tolomeo
    layers:
      meta-tolomeo-app:
      meta-tolomeo-bsp:
      meta-tolomeo-distro:

kas pulls in all layers declared in the included meta-tolomeo configuration inside sources/ and adds your layer on top. No manual sourcing of oe-init-build-env is needed. For the full kas configuration reference see the kas documentation.


Writing the recipe

Regardless of which starting point you chose, recipes follow the same structure and conventions. Place your recipe in a recipes-<language>/<app-name>/ directory inside your layer -- meta-tolomeo-app for Option A, meta-myapp for Option B:

<your-layer>/
└── recipes-python/
    └── my-app/
        ├── my-app.bb
        └── my-app.service

For a full reference on recipe syntax see the Yocto Project recipe writing guide.

Python application

The tlm-services recipe in meta-tolomeo-app/recipes-python/tlm-services/ is the canonical example of a Python application packaged with systemd integration. It uses the python_hatchling build backend and ships multiple subpackages, each with its own systemd service unit.

A recipe for a single Python application with a systemd service follows this pattern:

SUMMARY = "My application"
DESCRIPTION = "Short description of what my-app does."

inherit python_hatchling systemd

SRC_URI = "git://git@gitlab.com/myorg/my-app.git;protocol=ssh;branch=main \
           file://my-app.service \
           "
SRCREV = "abc123..."

S = "${WORKDIR}/git"

SYSTEMD_SERVICE:${PN} = "my-app.service"

FILES:${PN} += "${systemd_system_unitdir}/my-app.service"

RDEPENDS:${PN} = "python3-py-tolomeo"

The my-app.service file lives next to the .bb file and is a standard systemd unit. BitBake's systemd class installs and enables it automatically.

If your application needs writable storage on an otherwise read-only rootfs, declare an overlayfs mount for it:

inherit overlayfs

OVERLAYFS_MOUNT_POINT[data] = "/data/overlay-my-app"
OVERLAYFS_WRITABLE_PATHS[data] = "/var/lib/my-app"

Pre-compiled binary

When distributing a pre-built binary for multiple architectures, place one binary per target architecture next to the recipe and select the correct one at install time:

SUMMARY = "My pre-compiled agent"

inherit systemd

SRC_URI = "file://my-agent-${PV}_x86_64 \
           file://my-agent-${PV}_arm64 \
           file://my-agent.service \
           "

do_install() {
    install -d ${D}${bindir}

    case "${TARGET_ARCH}" in
        x86_64)
            install -m 0755 ${WORKDIR}/my-agent-${PV}_x86_64 ${D}${bindir}/my-agent
            ;;
        aarch64)
            install -m 0755 ${WORKDIR}/my-agent-${PV}_arm64 ${D}${bindir}/my-agent
            ;;
    esac

    install -d ${D}${systemd_system_unitdir}
    install -m 0644 ${WORKDIR}/my-agent.service ${D}${systemd_system_unitdir}/my-agent.service
}

SYSTEMD_SERVICE:${PN} = "my-agent.service"

Adding the application to the image

Applications are included in the image through package groups. Create a package group recipe in a recipes-core/packagegroups/ directory inside your layer:

SUMMARY = "My application package group"

inherit packagegroup

RDEPENDS:${PN} = "my-app"

Option A -- extend TOLOMEO_IMAGE_INSTALL in meta-tolomeo-app/recipes-core/images/include/tolomeo-image-common.inc:

TOLOMEO_IMAGE_INSTALL += "packagegroup-my-app"

Option B -- add a local_conf_header entry to your kas configuration so the package is appended without touching meta-tolomeo files:

local_conf_header:
  meta-myapp: |
    IMAGE_INSTALL += "packagegroup-my-app"

For quick iteration during development you can also append the package directly without a package group:

local_conf_header:
  meta-myapp: |
    IMAGE_INSTALL += "my-app"

Building

Build the full image with kas, passing your kas configuration file:

# Option A
kas build kas/tolomeo-qemux86-64_tolomeo-devel_image-devel.yml
# Option B
kas build my-build.yml

To iterate on a single recipe without rebuilding the entire image, open a kas shell and invoke bitbake directly:

kas shell <your-kas-config>.yml
bitbake my-app

To rebuild after modifying the recipe or its source:

bitbake -c cleanall my-app && bitbake my-app

Once the recipe builds cleanly, rebuild the full image to verify it integrates correctly before moving to the OTA Release flow.

For production images, follow the Hardening and Secure Boot flow to generate signing keys and build with the hardened tolomeo-prod distribution. To add telemetry services to the image, see IoT and Telemetry.