Leverage Yocto/OpenEmbedded for your embedded software deployment

December 16, 2014 OpenSystems Media

Developing an application and storing it on the device’s ROM is no longer a simple deployment process on current embedded devices.

Once you have your application ready for the next testing/release cycle, you still have to reintegrate it with your OS, tweak configuration files, enable the automatic start of your stuff, and so on, and package it into a form that allows updating the devices.

This article outlines how this integration can be automated for Embedded Linux-based devices with the help of Yocto/OpenEmbedded and thus make the process less error prone, more repeatable, and using well defined versions of each of the components you use.

Chances are high that your hardware supplier already uses Yocto/OpenEmbedded for its Linux BSP. Instead of using that generic BSP as is, simply integrate your application into the Yocto/OpenEmbedded build process and have a fully automated build of your finished deliverables.


Yocto and OpenEmbedded together form a build framework that creates kernel images, root file system images, and installable packages from source code.

The framework uses meta information (called recipes) for downloading/compiling/deploying of software packages on a x86/x86_64 Linux build host for a target device.

The recipes are structured into layers. Layers aggregate recipes for distribution, BSP functionalities for different CPU/SBC/Module vendors, the base system, domain specific software and so on. Many of the available layers and recipe can be found through this web interface.

A basic understanding of Yocto/OpenEmbedded is required to make full use of this article. The project comes with excellent documentation, see the above links or dive directly into the manual.

On top of that I can recommend Otavio Salvador and Daiana Angolini’s book to get you jump started.

Setting up Yocto/Openembedded

For this article we will use the Toradex Colibri T30 computer module. Follow the set up instructions.

Find and execute the instructions provided by your SoC or module supplier to get your initial set up.

Then test your installation by building one of the already provided targets to make sure that the installation worked before we start fiddling with it.

To save some time, build a simple image. Don’t forget that you will have to source a script to set the environment before any bitbake operation. In our case:

oe-core> . export
build> bitbake -k core-image-base

The build will take some time and then you’re hopefully greeted with a final message: “NOTE: Tasks Summary: Attempted 1806 tasks of which 23 didn’t need to be rerun and all succeeded.”

Now we are ready to customize the build for our product’s needs.

Creating the layer that holds your application and image recipe

This is explained in great detail here. Note that the layer described below can also be downloaded here.

We add a new layer in stuff/ that will hold our application’s recipe and the modifications to the image recipe. Lets call the new layer meta-product:

stuff> git init meta-product
stuff> mkdir -p meta-product/conf

meta-product> cat conf/layer.conf
BBFILES += "${LAYERDIR}/recipes*/*/*.bb ${LAYERDIR}/recipes*/*/*.bbappend"
BBFILE_COLLECTIONS += "meta-product"
BBFILE_PATTERN_meta-product := "^${LAYERDIR}/"
BBFILE_PRIORITY_meta-product = "90"

And then we have to add our layer to the bitbake configuration:

build> cat conf/bblayers.conf
   ${TOPDIR}/../stuff/meta-fsl-demos \
   ${TOPDIR}/../stuff/meta-product \

Adding your application

As we concentrate on deploying your application rather than developing it we assume that you have your sources available in a source control system and use bitbake to build a well defined version thereof.

Our product will draw a pattern on /dev/fb0 and /dev/fb1. Probably nothing that will create hype in the market, but it’s a good, simple example.

I pushed the sources onto github/toradex/fb-draw.git. The program is Makefile based and allows for a very simple recipe as explained here.

So let’s create the file holding the application’s recipe:

meta-product> cat recipes-graphics/fb-draw/fb-draw_git.bb
SUMMARY = "Writes patterns to the fb device"

LIC_FILES_CHKSUM = "file://COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"

SRCREV = "23ac1d9e478d278646a8829def74745ea652eb53"
SRC_URI = "git://github.com/toradex/fb-draw.git;protocol=https;branch=master"

S = "${WORKDIR}/git"

do_install () {
   oe_runmake install DESTDIR=${D}

That’s it. This allows bitbake to fetch the git repository, check out the version defined by the SRCREV git hash, check if the licensing has changed, and then build it by using make, make install.

Time for a first build of our application:

oe-core> . export
build> bitbake fb-draw

We want fb-draw to be automatically started after each boot. As always with Linux there are several ways to accomplish this. We will use the init manager systemd to start it.

So lets create a systemd service file and add it to the fb-draw.bb recipe:

meta-product> cat recipes-graphics/fb-draw/fb-draw/fb-draw.service
Description=Draws a pattern on fb0, fb1



Additionally, since we want to be the master of the framebuffer we do not want a login console service also competing for the framebuffer resource. One way to convince systemd that we really don’t want its heuristic on spawning getty on tty1 kicking in is to create a symlink to /dev/null with the name of the service we want to keep from being started.

So we modify the recipe to add the service file and use the systemd.bbclass magic to have the service file integrated into the init process.

The patch of the changes to the recipe:

--- a/recipes-graphics/fb-draw/fb-draw_git.bb
+++ b/recipes-graphics/fb-draw/fb-draw_git.bb
@@ -3,11 +3,22 @@ SUMMARY = "Writes patterns to the fb device"
 LIC_FILES_CHKSUM = "file://COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
+inherit systemd
 SRCREV = "23ac1d9e478d278646a8829def74745ea652eb53"
 SRC_URI = "git://github.com/toradex/fb-draw.git;protocol=https;branch=master"
+SRC_URI += "file://fb-draw.service"
 S = "${WORKDIR}/git"
 do_install () {
     oe_runmake install DESTDIR=${D}
+    install -d ${D}${systemd_unitdir}/system/ ${D}${sysconfdir}/systemd/system/
+    install -m 0644 ${WORKDIR}/fb-draw.service ${D}${systemd_unitdir}/system
+    ln -s /dev/null ${D}${sysconfdir}/systemd/system/getty@tty1.service
+SYSTEMD_SERVICE_${PN} = "fb-draw.service"

Modifying the image

Now that we have a recipe that builds our application we have to add it to the list of deployed packages in our image. Also add some other stuff to make the resulting image more usable, a SSH server and the connman network manager adds some networking capabilities.

By default an image builds a root file system and binaries of the bootloader and kernel. Some BSPs provide additional logic to ease the deployment to the target hardware. For instance the meta-fsl-arm BSP for Freescale-based SoCs builds a SD card image file from which the target can directly boot. With the Toradex BSP one can build a tarball that contains all the tools and data to deploy the image onto a module.

The meta-fsl-arm deployment helpers are realized with classes/image_types_fsl.bbclass and are included through the machine configuration files. Nothing needs to be done in the image to have that functionality in place.

In order to use Toradex BSP’s deployment function the image recipe must define the variable IMAGE_NAME and include recipes/images/trdx-image-fstype.inc:

meta-product> cat recipes-core/images/product-image.bb:
SUMMARY = "A console-image for our fb-test product."

IMAGE_FEATURES += "ssh-server-openssh"
IMAGE_INSTALL += "fb-draw"
IMAGE_INSTALL += "connman connman-systemd connman-plugin-loopback connman-plugin-ethernet"


#create the deployment directory-tree
PV = "V1.0"
IMAGE_NAME = "${MACHINE}_product"
require recipes/images/trdx-image-fstype.inc

inherit core-image

Tweaking U-Boot and the Linux kernel

As we use the framebuffer directly with our application we need to remove the framebuffer console from the kernel’s configuration or the kernel will keep displaying a cursor in an area of the screen.

(Alternatively just define the vt.global_cursor_default=0 kernel boot argument, see http://developer.toradex.com/knowledge-base/splash-screen-linux)

Additionally, just for the exercise we will be changing the U-Boot environment to remove the kernel console output to the framebuffer console as well as changing the display resolution.

Recipes can be altered by providing a file with the same basename as the recipe but the extension bbappend. The content of the original file will be appended with what is in the bbappend file. With this mechanism we can inject a patch file used to alter the kernel and U-Boot sources:

meta-product> cat recipes-kernel/linux/linux-toradex_git.bbappend
SRC_URI += "file://defconfig-product.patch" 

meta-product> cat recipes-kernel/linux/linux-toradex/defconfig-product.patch
diff --git a/arch/arm/configs/colibri_t30_defconfig b/arch/arm/configs/colibri_t30_defconfig
index 050351d..e1ef222 100644
--- a/arch/arm/configs/colibri_t30_defconfig
+++ b/arch/arm/configs/colibri_t30_defconfig

meta-product> cat recipes-bsp/u-boot/u-boot-toradex_git.bbappend
SRC_URI_T30 += "file://u-boot-product.patch"

meta-product> cat recipes-bsp/u-boot/u-boot-toradex/u-boot-product.patch
diff --git a/include/configs/colibri_t30.h b/include/configs/colibri_t30.h
index ffcf88f..b5d889e 100644
--- a/include/configs/colibri_t30.h
+++ b/include/configs/colibri_t30.h
@@ -185,12 +185,12 @@
   "setup=setenv setupargs asix_mac=${ethaddr} " \
-      "consoleblank=0  no_console_suspend=1 console=tty1 " \
+      "consoleblank=0  no_console_suspend=1 " \
       "console=${console},${baudrate}n8 debug_uartport=lsport,0 " \
       "${memargs}\0" \
   "setupdate=load mmc 1:1 ${kernel_addr_r} flash_mmc.img " \
       "&& source ${kernel_addr_r}\0" \
-  "vidargs=video=tegrafb0:640x480-16@60\0"
+  "vidargs=video=tegrafb0:800x600-16@60\0"
#endif /* __CONFIG_H */

Now we are ready to build our image:

oe-core> . export
build> bitbake product-image

The image can be found in build/out-eglibc/deploy/images/colibri-t30/colibri-t30_productV1.0_20141128.tar.bz2.

Next steps

With little additional effort we can make use of vendor supplied Yocto/OpenEmbedded meta-data to create a tailored Linux image that contains all necessary components to deploy a product’s software.

The process ensures automated creation of our image with well-defined versions of third party as well as our own components and simplifies the error prone process of going though deployment checklists to put together an image manually out of bits and pieces grabbed from several different sources.

To go even further one could incorporate the meta-product layer into the initial Yocto/OpenEmbedded set up process, such as adding it to the repo manifest in our case.

Max Krummenacher is an Embedded Software Engineer at Toradex.

Max Krummenacher, Toradex
Previous Article
Improving security for network-connected, Linux-based systems
Improving security for network-connected, Linux-based systems

Embedded systems' ability to access devices over the Internet or local networks facilitates a wide range of...

Next Article
What's Hot in 2015: Real-time frameworks, agile modeling, and code generation
What's Hot in 2015: Real-time frameworks, agile modeling, and code generation

In this Special Advertising Section, Quantum Leaps, LLC explains what will be the hot trends of 2015.


Stay updated on Developer Tools & Operating Systems with the Dev Tools and OS edition of our Embedded Daily newsletter

Subscribed! Look for 1st copy soon.
Error - something went wrong!