This is a text-only version of the following page on https://raymii.org:
---
Title       :   Yocto boot2qt for the Seeed reTerminal (Qt 6)
Author      :   Remy van Elst
Date        :   18-03-2022
Last update :   04-04-2022
URL         :   https://raymii.org/s/tutorials/Yocto_boot2qt_for_the_Seeed_reTerminal_qt6.html
Format      :   Markdown/HTML
---



In this guide we'll build a linux distribution for Seeed reTerminal, using the Yocto project and the `boot2qt` stack provided by Qt. This `boot2qt` image can be written to the internal eMMC and when booted up, the Seeed reTerminal runs a software stack that integrates nicely with Qt Creator (the Qt IDE), for example, one click deployment to the device. You can run your own Qt application on the reTerminal, full screen, it will boot right into it.
This guide covers Qt 6.2. The guide also covers changing the default startup app to your own app, Qt Creator integration and rotating your Qt app, both Widgets and QML, the latter via Wayland and Weston.


The main difference between the guide that [Seeed provides][1] and this guide
is that this guide uses Qt's own boot2qt Yocto stack and runs Qt 6. The Seeed
guide covers their own image and Qt 5. This guide also shows you how to make
your own Qt app the default and helps you with rotation.

Running your own Yocto distribution over the default Debian image provided by Seeed
has the advantage that it's reproducible and you are fully in control. Using the
`boot2qt` Yocto stack makes the Yocto setup process way easier and faster. By using
your own image, no other application run on the device, so all resources are available
for your app. (No desktop environment or other software, unless you built it in the
image.) This also assures you that in the future, lets say as an example, 5 years later,
you can still build your image and software, since your Yocto build server has all
the sources locally.


Full disclosure: I was contacted by Seeed, they sent me this reTerminal in
exchange for a few articles, this one being the first. No monetary payment is
involved and Seeed has not reviewed this article before publishing. For official
support, please visit the [Seeed wiki][2].

Seeed actually sent me 2 reTerminal units. The first one had a stuck boot-mode
switch. It wouldn't move, and later broke off. The boot mode switch is required
to flash an image, which is quite essential for this Yocto guide. I contacted
them and a replacement unit was sent quickly. So, be careful with your
boot mode switch. I now use a pair of tweezers to switch it, just to be extra cautious.


Article changelog:

- 04-04-2022: Fixed paths in shell commands
- 04-04-2022: Fixed syntax in some recipe files including missing slashes
- 04-04-2022: Added note regarding the `seeed-linux-dtoverlays` on commit `7c846e23de9346d318fbdc8ac92dcc72b90fb6ce`
- Thanks to Ryan Bryngelson for the above issues and fixes!

### What is the reTerminal

![reTerminal][3]

> The reTerminal

The reTerminal is marketed as a future-ready Human-Machine Interface
(HMI). The reTerminal is powered by a Raspberry Pi Compute Module 4 (cm4) which
is a Quad-Core ARM Cortex-A72 CPU running at 1.5GHz and a 5-inch IPS
capacitive multi-touch screen with a resolution of 1280x720. 4GB of RAM and
32 GB of eMMC storage are built in (non-expandable). It has wireless
connectivity with dual-band 2.4GHz/5GHz Wi-Fi and Bluetooth 5.0 BLE.

The reTerminal has a high-speed expansion interface and exposes many I/O ports
and connectors. The device has security features such as a cryptographic
co-processor with secure hardware-based key storage. It also has built-in
modules such as an accelerometer, light sensor and a Real-Time Clock.
reTerminal has a Gigabit Ethernet Port for faster network connections and
also has dual USB 2.0 Type-A ports. The 40-pin Raspberry Pi compatible GPIO
header allows most or all of your hat's and existing projects to work with
the reTerminal.

**You can [buy the reTerminal here, current price is USD 195][9].** That includes
a Compute Module 4.

![reterminal hardware][4]

> External hardware overview of the reTerminal. Notice the many mounting holes and nuts

![reterminal carrier board][5]

> Carrier board including the Compute Module 4

You could bluntly say that the reTerminal is a carrier board for the Compute Module 4
with a multi-touch screen and a convenient case. In my opinion, it's much more than that.

It does lack a built-in battery at the time of writing this article. You power it via a USB-C
cable or by supplying 5V and ground to the correct GPIO pins.

Behind a little cover pad is a high-speed interface, on the picture above it's labeled
`Industrial High Speed Interface`. In the [FAQ][6] they write the following:

> The High-Speed Interface for Expansion Modules consists of 1 PCIe 1-lane
 Host Gen 2 (supporting speeds up to 5Gbps), 1 USB 2.0, 1 PoE and 26 GPIOs.
 The 26 GPIO pins can be further used as 2 I2C, 2 SPI and 1 UART. We plan to
 build expansion modules in the future for reTerminal and we have reserved
 this interface to connect these modules to the reTerminal. We will release
 a wide range of modules such as a Mic Array & Speaker Module, Camera
 Module, Industrial I/O, LoraWAN Module, 5G/4G Module, PoE Module and an
 Ethernet Switch. There are 2xM4 mechanical screw holes on the side of the
 reTerminal to help keep the expansion modules in place.

So, hardware wise, the reTerminal is solid and future proof. If you buy one,
it comes pre-loaded with a Compute Module and Raspbian including drivers plus
a demo application, so you can get started right away. No messy supply chain
issues here, only a bit of waiting on shipping a package from China.

### What is Yocto


There is a lot of ground to cover, if you are not familiar with the Yocto
project, here is a quick, _simplified_, overview of all the terms.

**Seeed have their own [FAQ on Yocto for the reTerminal here][1]**. That FAQ
covers the basics and has tips and tricks for using Yocto. I
recommend you read that guide as well, since it explains different parts.
My guide is tailored to (boot 2) Qt on the reTerminal.

Yocto is an open-source project which provides a build framework and metadata
to help to create a custom image for your target board. Yocto uses so-called
(meta) layers and recipes. Recipes are `.bb` (for bitbake) files that contain
build instructions and layers are a collection of specific recipes, classes
and configuration files. You could have a layer named `meta-raspberry`
(all layers, by convention, start with `meta-`) that has recipes applicable
only for the Raspberry Pi.

Yocto helps you to build a linux distro, their reference distro is named
`poky`. The primary build tool is a python command line tool named `bitbake`.
It parses all the recipes and then, one by one, gathers the source code
and compiles all the software, then packages that up into an image for your
board.

Yocto is aimed at embedded linux devices, at work we use it to create our own
Linux distribution (not `boot2qt`) to run on the coffee machines. Many
different boards are supported, most vendors provide a so called board
support package (bsp). Often this contains a kernel and drivers for your
board. There is a board support package for the Raspberry Pi, which we will
use.

`boot2qt` is an set of layers for Yocto which builds a simple linux system
that starts up without a desktop, right into a Qt application. It also builds
a cross-compile SDK for your desktop, which you can use in Qt Creator to
cross-compile your application for the target device. Often the device you
develop for does not have the same CPU architecture as your desktop
workstation. To top this all up, it also integrates into Qt Creator, allowing
you to one-click deploy and run your application, via the network, on your
device.

`boot2qt` also provides a set of scripts to make the `yocto` setup very easy, so
the process you see in this guide is not the same as a default `yocto` setup. It's
specifically for `boot2qt` and for the reTerminal.

So to summarize, `boot2qt` provides a layer on top of yocto that adds
integration between your embedded linux device and the Qt suite of tools,
saving a boatload of time doing manual busywork.

### Development host Yocto setup

The machine that is going to build your `boot2qt` image (the host that runs
`bitbake`) needs a few packages before we can get started building. You need
at least 50 GB's of free space, but my host has 300 GB. Cross-compiling all
the packages takes a huge amount of time, so the more cores and RAM you have,
the better. My test build machine has 4 cores and 8 GB of RAM, the entire
image build took more than 2 days. At work we have a few beefy servers, there
the build takes about 2 hours. Yocto does support incremental builds, so if
you change one recipe, only that part has to be rebuilt, not all the other
parts.

My build machine runs Debian 11, but Ubuntu 20.04 is also known to
work. Install the required packages first:

   apt-get install gawk wget git diffstat unzip texinfo gcc build-essential
   chrpath socat cpio python3 python3-pip python3-pexpect xz-utils
   debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa
   libsdl1.2-dev pylint3 xterm python3-subunit mesa-common-dev python
   git-lfs g++-multilib gcc-multilib libxkbcommon-dev
   libxkbcommon-x11-dev libwayland-cursor++0 libwayland-cursor0

These are a bit more packages than the Qt site recommends, I had build issues
when building the `qbsp` layer, which is where most of the extra packages
come from.

#### Google Repo

Next you need `repo`, a git management tool [by Google][7]. It hides most of
the complexity of submodules and different repo's by putting all of that
extra information in a manifest file. You can install it using apt:

   apt install repo

If it's not in the repository, check the [instructions][7], installation
process is just a simple copy of one executable.


#### Initial boot2qt setup

Create a folder where all of yocto will reside:

   mkdir b2qt
   cd b2qt

Use the `repo` tool to clone all the git repositories of the Yocto project
and `boot2qt` at once. First initialize for the correct Qt version:

   # for Qt 6.2:
   repo init -u git://code.qt.io/yocto/boot2qt-manifest -m v6.2.3.xml

Then sync the repositories:

   repo sync

You must tell which board you want to build for, then source a
script:

   export MACHINE=raspberrypi4-64 && source ./setup-environment.sh

**This step must be repeated on every login**.

Notice that we specify `raspberrypi-64`. This is because the reTerminal uses a
Raspberry Pi Compute Module 4. The `meta-raspberry` layer uses that name for
both the regular Pi 4 and the Compute module. The Yocto layer from Seeed has
a specific board for the reTerminal since 3 days (at the time of writing),
but boot2qt has no support for that, so in this guide we'll port the specific
requirements over to our build image.

Output:

   ### Shell environment set up for builds. ###

   You can now run 'bitbake <target>'

   Common targets are:
   b2qt-embedded-qt6-image
   meta-toolchain-b2qt-embedded-qt6-sdk

   QBSP target is:
   meta-b2qt-embedded-qbsp

   For creating toolchain or QBSP for Windows, set environment variable before running bitbake:
   SDKMACHINE=x86_64-mingw32

   For more information about Boot to Qt, see https://doc.qt.io/QtForDeviceCreation/


You should now be in a folder named `build-raspberrypi4-64`. Using the `repo` tool and
sourcing this script has made a nifty folder structure for yocto:

* `build-raspberrypi4-64`: build directory for the board, you are in this folder
 after sourcing the `setup-environment.sh` file.
* `sources`: has all the `bitbake` layers and recipes
* `download`: folder where all the source code is downloaded (git repo's,
 tar.gz archives)

In the `build-*` directory there is a `conf` folder, that has two important
files. `bblayers.conf` and `local.conf`. The first defines the layers that
your build uses and the second has specific configuration options for
this specific build. We'll start by adding the reTerminal layer, the
config file is covered later.

#### Adding the Seeed reTerminal layer

We need the reTerminal layer for the device tree and a few drivers.

   cd ../sources
   git clone -b main https://github.com/Seeed-Studio/meta-seeed-reterminal.git

I've tested this guide with commit `7c846e23de9346d318fbdc8ac92dcc72b90fb6ce`
and with commit `57d1b68d73e625fe6a4cb14372a0cb7c42bae9c5`. If the
`seeed-linux-dtoverlays` package gives a linker error like below:

   | aarch64-poky-linux-ld: internal error in set_address, at ../../gold/output.h:322

Then, remove the `ld-is-gold` option that the `boot2qt` `distro.conf` file enabled,
as we do in our local config later on:

   DISTRO_FEATURES_remove = "ld-is-gold"

I've made an upstream [bug report][12] for it. If you use commit `7c846e23de9346d318fbdc8ac92dcc72b90fb6ce`, the error will not appear.

You must add the Yocto version that `boot2qt` uses to the layer configuration. Edit
the following file:

   vi meta-seeed-reterminal/conf/layer.conf

Add `hardknott` to the `LAYERSERIES_COMPAT` line:

   LAYERSERIES_COMPAT_meta-reterminal = "honister hardknott"

Save and close. Change to our build folder:

   cd ../build-raspberrypi4-64

Add the layer to our config:

   bitbake-layers add-layer ../sources/meta-seeed-reterminal


#### Making our own layer for b2qt image overrides

To override a few parts of the default `b2qt-embedded-qt6-image`, we must
make our own layer. This layer will include more customization later on,
but for the initial build, we'll only override parts of the image. Why do
we not just overwrite the contents of the original file? By using a `.bbappend`
file, we can keep our changes separated, so we know later on what our specific
changes are. It also makes it easier to apply upstream changes.

Start by making a few folders for your layer, in the `b2qt` folder:

   mkdir -p sources/meta-raymii/conf/
   mkdir -p sources/meta-raymii/recipes-qt/images/

Edit the following file:

   sources/meta-raymii/conf/layer.conf

Place the below contents:

   BBPATH .= ":${LAYERDIR}"

   BBFILES += "${LAYERDIR}/recipes*/*/*.bb \
               ${LAYERDIR}/recipes*/*/*.bbappend \
               "
   BBFILE_COLLECTIONS += "raymii"
   BBFILE_PATTERN_raymii := "^${LAYERDIR}/"
   BBFILE_PRIORITY_raymii = "1"

   LAYERSERIES_COMPAT_raymii = "thud zeus dunfell gatesgarth hardknott"

Add our layer to the Yocto build for the reTerminal:

   cd build-raspberrypi4-64
   bitbake-layers add-layer ../sources/meta-raymii

Now on to the changes to the default `boot2qt` image. The following
file is a `.bbappend` file, which, if it has the same name, path and version,
will, as you might expect, append stuff to the original. In this case, we
will extend the default `b2qt-embedded-qt6-image` image to include our
`myapp` recipe in the installation. Edit this file:

   vi sources/meta-raymii/recipes-qt/images/b2qt-embedded-qt6-image.bbappend

Add the following:

   SUMMARY = "reTerminal changes for Qt image"

   SPLASH = "psplash-raspberrypi"

   IMAGE_FEATURES_append = " \
           splash \
           "

   IMAGE_INSTALL_append = " \
           kernel-modules \
           evtest \
           iperf3 \
           i2c-tools \
           util-linux \
           "

The changes, as you can see, are related to the reTerminal image. Later on
we'll add our application here, but for now this is just the essentials to
make a bootable image.

#### Tweaking our `local.conf` for the reTerminal

We need to add a few variables to the local config file (`conf/local.conf`). All of these
have to do with the reTerminal hardware, except for one, the removal of `webengine`. If your
Qt app uses webengine, leave it in, otherwise, remove it. Not including it saves you a bunch
of time compiling and the image is smaller.


The reTerminal related features are pulled from the official layer, but
tweaked for `boot2qt`. You can see the [backport here][8], quite a recent
addition to the reTerminal Yocto layer, 3 days ago when writing this
article.

The difference between `local.conf` and the `b2qt-embedded-qt6-image.bbappend` is that
`local.conf` is for this device only. In my case I also have a `raspberrypi4` machine
build folder for [the compute module 4][13]. You could also create your own `distro.conf`
or define a new device with machine specific overrides, but for this article, `local.conf`
is simple enough.

Edit the following file:

   # in the folder: build-raspberrypi4-64
   vi conf/local.conf

Add the following:

   RPI_KERNEL_DEVICETREE_OVERLAYS_append = " overlays/reTerminal.dtbo overlays/i2c3.dtbo overlays/vc4-kms-v3d-pi4.dtbo"
   ENABLE_UART = "1"
   ENABLE_I2C = "1"

   KERNEL_MODULE_AUTOLOAD_rpi += "i2c-dev"

   MACHINE_EXTRA_RRECOMMENDS += "\
       seeed-linux-dtoverlays \
   "

   VC4DTBO ?= "vc4-kms-v3d"

   PACKAGECONFIG_append_pn-qtbase = " eglfs "

   DISTRO_FEATURES_remove = "webengine ld-is-gold"

   PREFERRED_VERSION_linux-raspberrypi ?= "5.10.%"

You're all set up for the initial build of `boot2qt`. I recommend you start a
`screen` or `tmux` session as the build will take a long time. My initial build
on the hardware described earlier took more than 2 days.

If you use commit `7c846e23de9346d318fbdc8ac92dcc72b90fb6ce`, you do not need
to add the `MACHINE_EXTRA_RRECOMMENDS += "seeed-linux-dtoverlays"`. The
device tree overlays [are a patch][31] in that commit, later on they
became a git submodule.

### Bitbaking the image

With `boot2qt` and the reTerminal hardware layer setup, we can do our initial build.

Make sure you have `source`-ed the script and are in the correct folder:

   cd ~/b2qt
   export MACHINE=raspberrypi4-64 && source ./setup-environment.sh

Start the image build:

   bitbake b2qt-embedded-qt6-image

The output will vary. First it lists all your layers and config, at
the bottom it shows the current task. Example output:

   WARNING: Host distribution "debian-11" has not been validated with this version of the build system; you may possibly experience unexpected failures. It is recommended that you use a tested distribution.
   Loading cache: 100% |################################################################################################| Time: 0:00:00
   Loaded 4374 entries from dependency cache.
   Parsing recipes: 100% |##############################################################################################| Time: 0:00:00
   Parsing of 2813 .bb files complete (2809 cached, 4 parsed). 4377 targets, 611 skipped, 0 masked, 0 errors.
   NOTE: Resolving any missing task queue dependencies

   Build Configuration:
   BB_VERSION           = "1.50.0"
   BUILD_SYS            = "x86_64-linux"
   NATIVELSBSTRING      = "universal"
   TARGET_SYS           = "aarch64-poky-linux"
   MACHINE              = "raspberrypi4-64"
   DISTRO               = "b2qt"
   DISTRO_VERSION       = "3.3.4"
   TUNE_FEATURES        = "aarch64 armv8a crc crypto cortexa72"
   TARGET_FPU           = ""
   SDKMACHINE           = "x86_64"
   meta
   meta-poky            = "HEAD:c40ac16d79026169639f47be76a3f7b9d8b5178e"
   meta-raspberrypi     = "HEAD:b4ec97e4eb8e36efd1f7e2f8ae020a9e55cfc239"
   meta-oe
   meta-python
   meta-networking
   meta-initramfs
   meta-multimedia      = "HEAD:f72a73b42fa740130b388ba8555cdbefdee8d37d"
   meta-python2         = "HEAD:810d6d842f103eb59f18b06426106462b15de7e2"
   meta-boot2qt
   meta-boot2qt-distro  = "HEAD:e59a2e20697e0afc2a0b068835cd90e102dec589"
   meta-mingw           = "HEAD:422b96cb2b6116442be1f40dfb5bd77447d1219e"
   meta-qt6             = "HEAD:eb3719266fc03b96d5056980b135b371f31811f4"
   meta-seeed-reterminal = "main:57d1b68d73e625fe6a4cb14372a0cb7c42bae9c5"
   meta-raymii          = "master:84123f093be34a9a4d73de545132cffc3e210c19"

   Initialising tasks: 100% |###########################################################################################| Time: 0:00:10
   Sstate summary: Wanted 1706 Local 49 Network 0 Missed 1657 Current 1441 (2% match, 47% complete)
   Removing 375 stale sstate objects for arch raspberrypi4_64: 100% |#################################################| Time: 0:00:02
   Removing 6 stale sstate objects for arch allarch: 100% |###########################################################| Time: 0:00:00
   Removing 956 stale sstate objects for arch cortexa72: 100% |#######################################################| Time: 0:00:02
   NOTE: Executing Tasks
   Currently  8 running tasks (2488 of 9043)  27% |######################                                                           |
   0: libunistring-0.9.10-r0 do_configure - 27s (pid 1946515)
   1: libpciaccess-0.16-r0 do_configure - 22s (pid 1949317)
   2: icu-68.2-r0 do_compile - 15s (pid 1959678)
   3: libpam-1.5.1-r0 do_compile - 15s (pid 1959794)
   4: tslib-1.22-r0 do_configure - 13s (pid 1961800)
   5: nettle-3.7.3-r0 do_configure - 10s (pid 1963210)
   6: libpcre2-10.36-r0 do_configure - 8s (pid 1963889)
   7: libogg-1.3.4-r0 do_configure - 5s (pid 1964770)


Now is a great time to go do something else and return in a few days. If you
have a beefy machine, the build will be faster, but it will still take a while.

Once the build is done, the image is located in the folder:

   build-raspberrypi4-64/tmp/deploy/images/raspberrypi4-64/

The image is a `bmap` file. `bmap` is a special format which should be faster to flash
and verifies data during flash, initially created by Intel for their Tizen
project. If you run Ubuntu, you must install the `bmap-tools` package.

#### Flashing the image

Because the reTerminal has a Compute Module 4 with eMMC, the SD card slot
won't work. You must flash the image to the eMMC, using a tool provided by
the Raspberry Pi. This tool is named `rpiboot`, which you must build
yourself. Start by installing a dependency:

   sudo apt install git libusb-1.0-0-dev

Next, clone the repo

   git clone --depth=1 https://github.com/raspberrypi/usbboot
   cd usbboot

Start the build process:

   make

The above steps are one time only. You now have the `rpiboot` tool in this
folder. Every time you flash an image, you must repeat the below process.

[Flip the boot mode switch][10]. Be very careful, my first unit had a
stuck boot mode switch. I use a pair of tweezers to carefully flip the
tiny switch.

![boot mode switch][11]


While you're playing around with the reTerminal and constantly flashing
images, I recommend to leave the device open, not screw back the cooling
or the plastic cover. This way the boot switch is easily accessible.
My device did get a bit hot, but not extremely.

Plug in the USB C cable and run the `rpiboot` utility as root:

   $ sudo ./rpiboot
   RPIBOOT: build-date Feb 22 2022 version 20220208~181027 042cd145
   Waiting for BCM2835/6/7/2711...
   Loading embedded: bootcode4.bin
   Sending bootcode.bin
   Successful read 4 bytes
   Waiting for BCM2835/6/7/2711...
   Loading embedded: bootcode4.bin
   Second stage boot server
   Loading embedded: start4.elf
   File read: start4.elf
   Second stage boot server done


Unmount the folders, otherwise the image flashing will fail:

   sudo umount /dev/sda1; sudo umount /dev/sda2


Flash the image, using `bmaptool`:

   cd TO_THE_YOCTO_IMAGE_FOLDER
   # cd b2qt/build-raspberrypi4-64/tmp/deploy/images/raspberrypi4-64/


   sudo bmaptool copy b2qt-embedded-qt6-image-raspberrypi4-64-20220316191856.rootfs.wic.bz2 --bmap b2qt-embedded-qt6-image-raspberrypi4-64-20220316191856.rootfs.wic.bmap /dev/sda

Output:

   bmaptool: info: block map format version 2.0
   bmaptool: info: 698368 blocks of size 4096 (2.7 GiB), mapped 367758 blocks (1.4 GiB or 52.7%)
   bmaptool: info: copying image 'rootfs.wic.bz2' to block device '/dev/sda' using bmap file 'rootfs.wic.bmap'
   bmaptool: info: 100% copied
   bmaptool: info: synchronizing '/dev/sda'
   bmaptool: info: copying time: 5m 6.5s, copying speed 4.7 MiB/sec


Flashing takes a while, for me with the default image it takes about 5 minutes.
The image filename varies, the date and time is in there. Switch the boot mode
switch back (be careful), remove the USB C power cable and plug it back in.


### First boot and a quick recap


![first boot][14]

> First bootup of your own `boot2qt` image!

Sit back, relax and enjoy your own home-grown image booting. Well done!

Play around with the example app, you can click "Learn More" and scroll around
a bit.

You might notice the application is rotated. Actually the screen's default
orientation is portrait, but the terminal is landscape. The [FAQ][15] addresses
this for [Ubuntu][16] and Debian, we will fix this later on when we deploy our
own Qt app. The fix depends on what kind of Qt app you want to run and if you
have access to the source code of said app.

You can also plugin a network cable and `ssh` in to the machine, the IP address
will be displayed on screen.

To recap what you already have achieved:

- Installed Yocto
- Installed the `boot2qt` stack
- Customized Yocto by creating your own layer
- Added the reTerminal hardware layer
- Built your own image
- Flashed it onto the reTerminal.

If you make changes to Yocto, subsequent builds will be faster since it only
has to re-do stuff that changed. The steps you always have to do on the Yocto
side are:

- `cd b2qt/`
- `export MACHINE=raspberrypi4-64 && source ./setup-environment.sh`
- (do your changes)
- `bitbake b2qt-embedded-qt6-image`

On the reTerminal side for flashing:

- Flip the `boot mode switch` and plugin a USB C cable
- `./rpiboot` to be able to flash the image
- `bmaptool copy b2qt-embedded-qt6-image-raspberrypi4-64-2022*.rootfs.wic.bz2 --bmap b2qt-embedded-qt6-image-raspberrypi4-64-2022*.rootfs.wic.bmap /dev/sda`
- Flip back the boot mode switch
- Remove the power cable and plug it back in

Now that you have your device set up, it's time to make use of all the handy features
`boot2qt` offers us, most important, the integration with the Qt IDE, Qt Creator. It
allows for rapid development and testing, right from inside the IDE you can deploy to
the device and test your changes. That workflow is really nice and quick, including
debugging, breakpoints, etc.

### Qt SDK and Qt Creator integration

In [my other article][13] on Yocto on the regular Pi Compute Module 4, I've written
out all the steps required to build the Qt SDK and integrate it with Qt Creator. Because
the process is quite long, I've decided not to duplicate it here but rather refer
you to my other guide, which is full of screenshots and explains the whole process.

Please check out [that article][13] for the full guide. The only thing that changes is
the first command to `source` the setup file, we're exporting the `MACHINE` variable
for the 64 bit version, like so:

   export MACHINE=raspberrypi4-64 && source ./setup-environment.sh

Next, you build the SDK, which takes a while:

   bitbake meta-toolchain-b2qt-embedded-qt6-sdk

When you've completed all the [steps in my other guide][13], you should
have a new Qt kit configured in Qt Creator and a new device to deploy to.


### Making your own app the default

Now that you've got the image working and you have your Qt integration set up,
you should be able to run and deploy your app to the reTerminal via Qt Creator.

If, and only if that's the case, continue on with the guide. We're going to
replace the default `b2qt` `startupscreen` app with our own compiled program.

In my case it's a [drag and drop demo app][17]. It's modified a little bit to
also show the current IP address, which is useful when debugging on the device.

This demo app is cool because it allows me to check the touch screen as well. Later
on in the guide, in case of a Qt Widgets app, the rotation of the screen and the
touch-screen rotation must match, otherwise the app will be rotated, but not the
touch. Very weird stuff happens then.

Start by making a new recipe for your app. We'll name it `myapp` for this example:

       mkdir -p sources/meta-raymii/recipes-myapp/myapp/files/

In the folder `files/` we've just created, place the compiled binary for your app,
name it `myapp` for this guides example. Make sure the architecture matches the
Pi 64 bit build:

   $ file myapp
   myapp: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.14.0, BuildID[sha1]=f2d876e1fe62e2eec1d5c0ead27a99c74a1f77ca, with debug_info, not stripped

Next, create the actual recipe file with filename `myapp_1.0.bb` (not in the
`files` folder, but one folder above). The underscore and version number are
important, if you raise that number, a new package will be built by Yocto.
Paste in the following contents:

   DESCRIPTION = "My Custom Qt App"
   AUTHOR = "[email protected]"
   LICENSE = "CLOSED"
   PR = "r0"

   SRC_URI =+ "file://myapp \
              "

   DEPENDS += "qtbase qtdeclarative qtdeclarative-native"

   do_install() {
       install -d ${D}/${bindir}
       install -m 0755 ${WORKDIR}/myapp ${D}/${bindir}/myapp

       lnr ${D}/${bindir}/myapp ${D}/${bindir}/b2qt

   }

   FILES_${PN} = "${bindir}/myapp \
                  ${bindir}/b2qt \
                 "

The line starting with `lnr` replaces the build in `b2qt` startupscreen app
(which is a symlink) to your own application. `boot2qt` ships with a simple
launcher that, on boot, launches whatever is the current symlink at
`/usr/bin/b2qt` and it has a few more utilities (like start/stop or replacing
the symlink)

Add the new `myapp` recipe to our custom image `.bbappend` file:

   vi sources/meta-raymii/recipes-qt/images/b2qt-embedded-qt6-image.bbappend

Edit the `IMAGE_INSTALL_append` section and add `myapp` right above the
`kernel-modules` line:

           myapp \

The entire section looks like this now:

   IMAGE_INSTALL_append = " \
           myapp \
           kernel-modules \
           evtest \
           iperf3 \
           i2c-tools \
           util-linux \
           "


After the changes, build a new image:

   bitbake b2qt-embedded-qt6-image

Flash that using `bmaptool` as described above.

Because we're appending to the default `b2qt` image, and because we're
symlinking our own binary where the `b2qt` launcher expects it, the next
time you boot up after building and flashing an image, your own app should start.

Here's a picture of my custom app after startup, still incorrectly rotated:

![reterminal myapp][18]

If you want to upload a new version of your app, replace the `myapp` binary
in the `files` folder and increment the `PR = "r0"` line to `r1`. Then bitbake
a new image and flash it. Rinse and repeat for every release.

If you just want to test out a new version, you can also `bitbake myapp`, then
you'll just build the app without a new image. There will be a new `IPK` package
in the `tmp/deploy/ipk` folder:

   $ file tmp/deploy/ipk/cortexa72/myapp_1.0-r2_cortexa72.ipk
   tmp/deploy/ipk/cortexa72/myapp_1.0-r2_cortexa72.ipk: Debian binary package (format 2.0), with control.tar.gz, data compression xz

`scp` this file over to the device and install it using the package manager `opkg`:

   opkg install myapp_1.0-r2_cortexa72.ipk

Then either reboot or restart the `startupscreen` service to load the new app.

However, if you just want to test new builds of your app, use the built in Qt creator
integration you set up earlier. Works way faster and right from Qt Creator, including
remote debugging.

### Rotating your app in b2qt

The next sections will cover the rotation aspect. It differs for Qt Widgets and Qt QML apps,
and, in my opinion, is a big mess. The [FAQ][15] covers rotation and I've found a Japanese
Seeed blog [covering rotation][19], but that is all for the `X11` display server.

`boot2qt` uses `eglfs` to directly run your app. EGLFS is a platform plugin
for running Qt applications on top of EGL and OpenGL ES 2.0, without an
actual windowing system like `X11` or `Wayland`.

Using `eglfs`, you can set an environment variable to rotate your application,
but that will only work if you have a Qt Widgets app. For a QML app, you must
either add a `transform: Rotation {}` to your program or run a display server
like `weston` on `wayland` and let that handle rotation. We'll cover all the options
in the next sections, starting with Qt Widgets.

#### Qt Widgets rotation

The simplest option is for a Qt Widget application, you can define 2 environment
variables, one for rotation and one for touchscreen rotation, that's all:

   QT_QPA_EGLFS_ROTATION=-90
   QT_QPA_GENERIC_PLUGINS=evdevtouch:/dev/input/event0:rotate=270

Why does one use `-90` and one use `270`? Because when I tried to use `270`
instead of `-90`, on startup the app gave an error: `Invalid rotation 270
specified in QT_QPA_EGLFS_ROTATION`. I did try
`QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS`, but that failed to work for touchscreen rotation.

Now, how do we apply this to Yocto? There is a default qt environment file in `/etc/default/qt`,
which we need to override in our Yocto build. Start by making a folder where our override `.bbappend`
will reside:

   mkdir -p sources/meta-raymii/recipes-qt/boot2qt-addons/default-qt-envs/

Copy over the default environment file from the `boot2qt` layer to that folder:

   cp sources/meta-boot2qt/meta-boot2qt/recipes-qt/boot2qt-addons/default-qt-envs/defaults sources/meta-raymii/recipes-qt/boot2qt-addons/default-qt-envs/


Edit the `defaults` file:

   vim sources/meta-raymii/recipes-qt/boot2qt-addons/default-qt-envs/defaults

Append the two lines below the existing ones:

   QT_QPA_EGLFS_ROTATION=-90
   QT_QPA_GENERIC_PLUGINS=evdevtouch:/dev/input/event0:rotate=270

In my case, the user programmable keys don't show up, so the touchscreen
is `/dev/input/event0`. It might be `event1`, but you can check using the
`evtest` command:


   root@b2qt-raspberrypi4-64:~# evtest
   No device specified, trying to scan all of /dev/input/event*
   Available devices:
   /dev/input/event0:      seeed-tp
   /dev/input/event1:      vc4
   /dev/input/event2:      vc4
   Select the device event number [0-2]:

`seeed-tp` is the touchscreen (tp stands for touch-panel).

Create a `.bbappend` file, which will override the default recipe:

   vim sources/meta-raymii/recipes-qt/boot2qt-addons/default-qt-envs.bbappend

Add the following:

   FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"

This line adds the folder in our own layer to the package, so it will look for
the files in that folder as well as the original folder. Your new folder is checked
first. [The Yocto manual explains this variable][20] and why using `:=` is important.

You can check that your `.bbappend` is used using the following command:

   bitbake-layers show-appends default-qt-envs

Output:

   === Matched appended recipes ===
   default-qt-envs.bb:
     /home/remy/b2qt/sources/meta-raymii/recipes-qt/boot2qt-addons/default-qt-envs.bbappend
     /home/remy/b2qt/sources/meta-boot2qt/meta-boot2qt-distro/dynamic-layers/raspberrypi/recipes-qt/boot2qt-addons/default-qt-envs.bbappend

`bitbake` the image and flash it, now your Qt Widgets app should be rotated correctly. Here
is a picture of the incorrectly rotated example Qt widgets controls demo app:

![widgets][21]

And here is the app with the environment variables set, rotated as you'd expect:

![widgets rotated][22]

#### Qt QML rotation

For a QML application, rotation is a bit harder. You either need to make changes
to the source code, or use `wayland` and `weston`, a display manager and server
which handle the rotation. The latter solution is only if you cannot change
the source code of your application.

If you can change your `QML` source code, add [a `Rotation` transformation][23]:

   transform: Rotation {
       angle: 270
       origin.x: parent.width / 2
       origin.y: parent.width / 2
   }

In this case I assume the rotation is on your root element. Not the `Window`, but the
`Loader` or `StackLayout`, or `Rectangle` in the window, whatever is your root element.

No changes to the environment are required, just upload a new version of your
application as described above.

##### Weston and Wayland

If you cannot change your source code, you must install a display manager. We're using
Wayland and Weston, not `X11`. The Qt documentation has an [article explaining why not X11][24].
Here is a quote summarizing the important bits:

> X11 is quite large and complex, and it lacks customizability. In fact, it is
 difficult to run a client fluidly with X11, and reach 60 fps without
 tearing. Wayland, in contrast, is easier to implement, has better
 performance, and contains all the necessary parts to run efficiently on
 modern graphics hardware. For embedded, multi-process systems on Linux,
 Wayland is the standard.


Qt also has a Wayland Compositor, but we're not going to use that. We're going
to run our `myapp` program as a client application in `Weston`. Weston is the
reference compositor, sort of like the window manager in X11. Why Weston? It ships
with the `boot2qt` Yocto stack and can run one program fullscreen without decorations
and panels (using the `kiosk` plugin).

We're currently at Weston version 9. In [version 10 there will be support for
autostarting programs][25], but for now we must do that ourselves using a
`systemd` service.

As we've done a few times in this guide, we'll make a `.bbappend` file
to override the default recipes with our changes. Start by creating a
folder structure:

   mkdir -p sources/meta-raymii/recipes-graphics/wayland/weston-init/

In that folder, create a file named `weston.ini` and put the following in:

   # configuration file for Weston

   [core]
   shell=kiosk-shell.so
   require-input=false

   [output]
   name=DSI-1
   mode=720x1280@60
   transform=rotate-270

   [screen-share]
   command=/usr/bin/weston --backend=rdp-backend.so --shell=fullscreen-shell.so --no-clients-resize

   [shell]
   panel-position=none

The default shipped config starts a desktop, but we're changing that. The
`kiosk-shell.so` plugin runs one app fullscreen without any window
decorations. The `output` section, including the `transform=rotate-270` is
the magic section you need to rotate your QML application.

Create a `.bbappend` recipe:

   FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"

   SRC_URI += " file://weston.ini "

   do_install_append() {
       # Remove upstream weston.ini to avoid conflict with weston-ini-conf package
       rm -f ${D}${sysconfdir}/xdg/weston/weston.ini

       install -D -p -m0644 ${WORKDIR}/weston.ini ${D}${sysconfdir}/xdg/weston/weston.ini
   }

   SYSTEMD_AUTO_ENABLE_${PN} = "enable"

This looks like what we did earlier to override the default Qt environment.
`FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"` adds the current folder in our
own layer, where we just placed the config file. I had some vague issues where
the default `weston.ini` file was not replaced, which is why there is such an
explicit `do_install_append` section. Normally that is not required, but in
my case, the file would not be replaced unless I did it this way.

`SYSTEMD_AUTO_ENABLE_${PN} = "enable"` enables the `weston` systemd service.

Now, to make sure `weston` starts and not `b2qt` or the `startupscreen`, we must
make a few more `.bbappend` recipes to disable those systemd services. There can
only be one display service running, if `b2qt` runs, then `weston` will fail to
start.

Same as earlier, create the following file:

   sources/meta-raymii/recipes-qt/boot2qt-addons/boot2qt-startupscreen_%.bbappend

Put in the below line:

   SYSTEMD_AUTO_ENABLE_${PN} = "disable"

This disables the `startupscreen` service.

Repeat the above for the `b2qt` service, which, confusingly, is in
the `default-qt-envs` recipe file:

   sources/meta-raymii/recipes-qt/boot2qt-addons/default-qt-envs.bbappend

The same line goes in to disable the systemd service:

   SYSTEMD_AUTO_ENABLE_${PN} = "disable"


The next part involves our custom image `.bbappend` file, where we need to add
`weston` to the distro features, to make sure `systemd` boots up to the `graphical`
target and not the `multiuser` target:

   sources/meta-raymii/recipes-qt/images/b2qt-embedded-qt6-image.bbappend

Append `weston \` to the `IMAGE_FEATURES_append` section:

   IMAGE_FEATURES_append = " \
           splash \
           weston \
           "

Include the `weston-init` package in the `IMAGE_INSTALL_append` section:

   IMAGE_INSTALL_append = " \
           myapp \
           kernel-modules \
           evtest \
           iperf3 \
           i2c-tools \
           util-linux \
           weston-init \
           "


The last part involves updating our `myapp` recipe to include a `systemd` service,
which will start up our application after `weston` is started. Create a new file:

   vim sources/meta-raymii/recipes-myapp/myapp/files/myapp.service

Place the following contents:

   [Unit]
   Description=MyApp on Weston
   After=weston.service

   [Service]
   User=weston
   Restart=always
   Type=simple
   Environment=QT_QPA_PLATFORM=wayland
   ExecStartPre=/bin/sh -c 'echo XDG_RUNTIME_DIR="$(loginctl show-user --property=RuntimePath --value \"$USER\")" > /tmp/qtenv'
   EnvironmentFile=-/tmp/qtenv
   ExecStopPost=/bin/rm /tmp/qtenv
   ExecStart=/usr/bin/myapp
   WorkingDirectory=/home/weston

   [Install]
   WantedBy=multi-user.target

If you run a Qt app on Wayland, it required the `XDG_RUNTIME_DIR` environment variable. We
could hardcode this to `/run/user/1000`, which would work for this specific setup, but we
can also use the `loginctl` command to query the actual path, which works for all future
devices and setups.

`systemd` has no option to evaluate a shell command as an `Environment` option, so we use
a hack to do that. First, it executes the `ExecStartPre` command, which start a subshell  to
execute the command and writes the output to a file in `/tmp`. Then we specify the line
`EnvironmentFile`, but, **important**, start it with a dash (`-`). This ensures the correct
order, first the command, then the file. Not documented well, I found a stackoverflow post
explaining it, but I cannot find that anymore to quote as source. Once the app stops,
the environment file is removed.

Change our `myapp_1.0.bb` recipe to include this systemd recipe:

   DESCRIPTION = "My Custom Qt App"
   AUTHOR = "[email protected]"
   LICENSE = "CLOSED"
   PR = "r1"

   SRC_URI =+ "file://myapp \
               file://myapp.service \
              "

   inherit systemd

   DEPENDS += "qtbase qtdeclarative qtdeclarative-native"

   do_install() {
       install -d ${D}/${bindir}
       install -m 0755 ${WORKDIR}/myapp ${D}/${bindir}/myapp

       lnr ${D}/${bindir}/myapp ${D}/${bindir}/b2qt

       install -m 0755 -d ${D}${systemd_unitdir}/system
       install -m 0644 ${WORKDIR}/myapp.service ${D}${systemd_unitdir}/system/
   }

   FILES_${PN} = "${bindir}/myapp \
                  ${bindir}/b2qt \
                 "
   SYSTEMD_SERVICE:${PN} = "myapp.service"

By specifying `SYSTEMD_SERVICE`, it will be enabled by default on boot.

Build a new image and after all that effort, a correctly rotated QML application
should be the result:

![qml rotated][26]

In my personal opinion this rotation for QML is an area the Qt Company could improve,
rather, make just as easy as Qt Widgets.

You can run any Qt app this way manually, compile and copy it via Qt Creator, or if you
can't compile it, just copy it. Set the correct environment variables and run it. For
example, the Qt built in Bear Whack example (fun game, under `quick/touchinteraction`):

   XDG_RUNTIME_DIR=/run/user/1000 QT_QPA_PLATFORM=wayland /usr/share/examples/quick/touchinteraction/touchinteraction

Here's a picture of [Bear Whack][29]:

![bear whack][28]

And here's a picture of the [SameGame demo][30] running. Particles and effects are
very smooth on the reTerminal:

![samegame][27]


[1]: http://web.archive.org/web/20220315202600/https://wiki.seeedstudio.com/reTerminal-Yocto/
[2]: https://wiki.seeedstudio.com/reTerminal/
[3]: /s/inc/img/reterminal_1.png
[4]: /s/inc/img/reterminal_2.png
[5]: /s/inc/img/reterminal_3.jpg
[6]: https://wiki.seeedstudio.com/reTerminal-hardware-interfaces-usage/#high-speed-interface-for-expansion-modules
[7]: https://web.archive.org/web/20220219191821/https://gerrit.googlesource.com/git-repo/
[8]: https://github.com/Seeed-Studio/meta-seeed-cm4/commit/885d6a332a25832dd954beb8cb5337c24bf9d77f
[9]: https://www.seeedstudio.com/ReTerminal-with-CM4-p-4904.html
[10]: https://wiki.seeedstudio.com/reTerminal/#flash-raspberry-pi-os-64-bit-ubuntu-os-or-other-os-to-emmc
[11]: /s/inc/img/reterminal_6.png
[12]: https://github.com/Seeed-Studio/seeed-linux-dtoverlays/issues/37
[13]: /s/tutorials/Yocto_boot2qt_for_the_Raspberry_Pi_4_both_Qt_6_and_Qt_5.html
[14]: /s/inc/img/reterminal_7.jpg
[15]: https://wiki.seeedstudio.com/reTerminal-FAQ/#q11-the-screen-orientation-is-incorrect-after-installing-raspberry-pi-os-bullseye
[16]: /s/inc/img/reterminal_8.png
[17]: /s/tutorials/Qml_Drag_and_Drop_example_including_reordering_the_Cpp_Model.html
[18]: /s/inc/img/reterminal_9.jpg
[19]: https://lab.seeed.co.jp/entry/2022/01/20/120000
[20]: https://docs.yoctoproject.org/3.3/singleindex.html#term-FILESEXTRAPATHS
[21]: /s/inc/img/reterminal_10.jpg
[22]: /s/inc/img/reterminal_11.jpg
[23]: https://web.archive.org/web/20220318200959/https://doc.qt.io/qt-6/qml-qtquick-rotation.html
[24]: https://web.archive.org/web/20220318194533/https://doc.qt.io/qt-6/wayland-and-qt.html
[25]: https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/652
[26]: /s/inc/img/reterminal_12.jpg
[27]: /s/inc/img/reterminal_13.jpg
[28]: /s/inc/img/reterminal_14.jpg
[29]: https://web.archive.org/web/20220318213600/https://doc.qt.io/qt-6/qtquick-touchinteraction-example.html
[30]: https://web.archive.org/web/20220318213644/https://doc.qt.io/qt-6/qtquick-tutorials-samegame-samegame4-example.html
[31]: https://github.com/Seeed-Studio/meta-seeed-cm4/blob/7c846e23de9346d318fbdc8ac92dcc72b90fb6ce/recipes-kernel/linux/files/0003-add-reTerminal-dtoverlay-for-yocto.patch

---

License:
All the text on this website is free as in freedom unless stated otherwise.
This means you can use it in any way you want, you can copy it, change it
the way you like and republish it, as long as you release the (modified)
content under the same license to give others the same freedoms you've got
and place my name and a link to this site with the article as source.

This site uses Google Analytics for statistics and Google Adwords for
advertisements. You are tracked and Google knows everything about you.
Use an adblocker like ublock-origin if you don't want it.

All the code on this website is licensed under the GNU GPL v3 license
unless already licensed under a license which does not allows this form
of licensing or if another license is stated on that page / in that software:

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

Just to be clear, the information on this website is for meant for educational
purposes and you use it at your own risk. I do not take responsibility if you
screw something up. Use common sense, do not 'rm -rf /' as root for example.
If you have any questions then do not hesitate to contact me.

See https://raymii.org/s/static/About.html for details.