Precompiled Linux images and distributions for embedded systems are very common among the maker movement. These distributions already have the components (sometimes more than necessary), so that the students, hobbyists, and enthusiasts can easily start to develop. Ubuntu, Debian, and Arch are a few among such distributions.

Unfortunately, a precompiled distribution for embedded Linux systems may not be the best option when we need a customized and application-specific Linux image for a Computer on Module or industrial product. There is a whole process of building images which can be done to discard any undesirable item that could increase boot time, compromise processing speed, and waste memory from our image. Many times we waste the system resources because we have a lot of unnecessary packages installed and services running. For example, in headless applications, where we don't need a desktop environment, we could build a console based distribution and thus, have a faster and lighter Linux distribution. Another downside of using a precompiled distribution is licensing. Canonical (the company behind Ubuntu), for example, does not allow someone to customize and sell Ubuntu without the proper certifications and partnership. On the other hand, with a customized Linux distribution we can have the total control of installed packages or used licenses. Therefore we can have a Linux image optimized according to the project's software or hardware requirements.

But what if we need to include in our distribution an application developed by us in Qt, or even a binary of an application developed in C? Is it necessary to first compile the application and then copy it to the board? Is it necessary to create packages like .ipk or .deb and copy it to the system? How can one include an application in "IMAGE_INSTALL_append" which is found inside the "local.conf" file? How to make an application to automatically startup after the system boot, just as seen on embedded devices?

In this blog post, we will show how to make all those things in an automated manner using the tools of OpenEmbedded/Yocto build system. We will have a quick look behind the steps used by bitbake like compiling, package installation, and directory creation as well as the addition of a service that automatically starts an application after the operational system boots. At the end we will have a custom embedded Linux distribution ready for our product or Computer on Module!

Although the details shown may vary from one development platform or SBC to another, the principles described here can be applied in general.

To follow the steps found below, it is necessary to have a build environment configured for building embedded Linux images. A tutorial for this configuration can be found at Toradex’s Developer Portal on this link. Toradex uses the OpenEmbedded-core build system for building images. Basically, the tutorial covers:

With all that, we can build our own custom embedded Linux distribution!

Creating applications with Qt Creator

For the purpose of our post, we developed an application following the dual-display (or dual-screen) style, which is actually two applications running on different screens. This kind of application is widely found, for example, in airport check-in kiosks or inside modern cars, where we have a screen with an instrumentation cluster behind the steering wheel and another screen on the panel with media functionalities, GPS maps, etc.

This post does not cover details about configuring Qt for cross-compiling applications. Information about that is well documented at Toradex’s Developer Portal on this link.

The source code of both the applications developed can be found at GitHub. Remember that when we build our image, both applications will be downloaded and compiled automatically following the instructions of the recipe we will write later on.

IMPORTANT: To determine where the applications will be installed when the image is built, the following code in Red needs to be added to the ".pro" project file in Qt, just like the following:

We choose to use GitHub for the fact that it is has version control tools and also because it is a cloud platform, so anyone can have access to the projects and applications stored on it. There is a "private repository" option as well. Later on, we will see the recipe to download our applications from GitHub, so they can be automatically installed on our custom Linux image. For that to be done, it is necessary to sync our local directory, where the applications are found, with a GitHub repository. We need to create one repository for each application.

From the point where we already have an account created, we need to add a repository. Click "+" on the upper right corner and then click on "New Repository". A new page will load. Choose a name, add a description, and click "Create repository".

Image 1: Page where a new repo is created

On the next page, GitHub gives us a few options. For our convenience, we will choose the following:

Image 2: Commands for synchronization

The above commands should be executed on the host computer inside each of our Qt application's directory: screen1 and screen2. Remember to edit the URL with your GitHub username and repository name.

After the push command, type in your GitHub username and password. Then the projects will be uploaded. The same process should be done for the other application. Go ahead and enter your GitHub profile and you should see your new repositories.

Image 3: GitHub repositories tab

Creating our layer and recipes

What is a recipe? According to Yocto Reference Manual, recipes are the files terminated with .bb suffix. Basically the recipe contains information about given software. This information includes from where to fetch sources, patches to be applied, how to compile the source code, and how to package everything at the end of the process.

A good habit while adding a new recipe to a build environment is to put it inside a new layer. Layers are often organized by sets of meta-data, according to machine types, functionalities, or similar items. We could take meta-toradex layer as an example. Toradex uses layers to give its customers access to Board Support Packages (BSP's), customized kernel, U-boot, graphical features, and a lot more. Other known layers are, meta-beagleboard, meta-fsl-arm, and meta-intel-galileo. We have also found some interesting layers like meta-games, meta-maker, and meta-uav for drones. A vast list of layers can be found here. As an example we will create a layer called "meta-projects".

Inside the oe-core directory, we find the stuff folder. Inside the stuff folder, we find the layers including the layer meta-toradex. Create a new folder called meta-projects inside stuff.

~/oe-core/stuff/
$ mkdir meta-projects

Create a new folder called "conf" inside meta-projects

~/oe-core/stuff/meta-projects/
$ mkdir conf

Create a new file inside conf, called layer.conf, with the following content inside the file:

We can see that all recipes and applications related to Qt are inside "recipes-qt", which is related to kernel. It is inside "recipes-kernel" and so forth. Since our applications are related to Qt, we create a recipes-qt folder inside meta-projects.

~/oe-core/stuff/meta-projects/
$ mkdir recipes-qt

Inside recipes-qt, we create a folder with the name of our application. One folder is created for each application.

Next, we have the do_install function, which is responsible for installing our application’s initialization script, as well as a unit configuration file(.service) which is responsible for automatically starting our application. Below we see the files in Red.

Initialization scripts and unit files for both applications should be inside GitHub repository.

A unit configuration file, the name of which ends in .service, encodes information about a process controlled and supervised by systemd. Service files can be found in /etc/systemd/system/ and for distribution provided ones in /lib/systemd/system/. Services can be started or permanently enabled using the systemctl command.

Note the command export QT_QPA_EGLFS_FB=/dev/fb0. This command indicates the framebuffer on which the application will run. This new variable was introduced in new versions of Qt5.

Both scripts should be uploaded to GitHub's repository of each application and will be downloaded and properly installed when the image is built.

Building the image

At this point we should have:

A configured build environment

Qt applications pushed to GitHub

Created our layer meta-projects and recipes

Inside oe-core directory Source the file 'export' to setup the environment. On first invocation this also copies a sample configuration to build/conf/*.conf.

~/oe-core/
$ . export

The command takes us to the build directory where we "bitbake" our images.

Inside build/conf we find the files bblayers.conf and local.conf. The layers that have all the resources for our images are listed in the bblayers.conf file. Previously we cloned meta-qt5 layer inside stuff folder. Now we should list both meta-qt5 and meta-projects in bblayers.conf.

In local.conf we find some build options and settings like, which machine to build for, how many cores to use for building the image, download directories, etc. We create a new variable called IMAGE_INSTALL_append, and indicated some items to be installed as well as our Qt applications.

The variable ACCEPT_FSL_EULA needs to be set, confirming that we agree with the license from former Freescale. That should be done for all iMX6 modules. We also removed some Desktop environment items because our image will be a console image.

After editing both configuration files we go inside the build directory to run the bitbake command and finally start the build process.

Check out this video demonstrating the booting process and application auto-startup:

Conclusion

This article was meant to be a foundation for building custom Linux for embedded systems. We saw that an image can be customized and with a few improvements, the image can be used in a product. We saw concepts of git, layers, and recipes. Many of the concepts in this article are used by companies like Toradex, a SBC developer. Toradex provides many development resources to its customers through the layers meta-toradex and meta-toradex-extra. These development resources include Board Support Package, examples, demos, etc. Who knows if you dear reader, will be the next creator of images, layers, or applications that will revolutionize the world of embedded systems!

I'm sorry but I couldn't fully understand your question. The image provided by Toradex uses OPKG, which does not handle DEB packages.

If you would like to describe the issue in more detail, including log from OpenEmbedded, please direct your question to our support team through the Toradex Community (https://www.toradex.com/community) or via support@toradex.com.

Dear Leonardo,
thank you very much for your quick reply. I would like to build an image using dpkg and NOT opkg. Therefore I would like to overwrite: "# We default to ipk:" within local.conf. Is a change of the package manager possible at all?
Best regards