Using Yocto to create a Linux-based gaming console

In this blog post, I will explain how I built the world’s worst gaming console (at least by today’s standards but it makes it one of the best anti-gaming consoles today in my opinion):

worst_console.jpeg

As you can see from the picture above, the user experience is terrible and this console is extremely poorly portable because of cabling and jumper wires used to connect the Wii Nunchuck and the STM32MP157D-DK1 board. Other than that there is only one game you can play and it’s nInvaders - a ncurses Space Invaders clone.

This is exactly why it’s one of the best anti-gaming consoles.

Table of Contents

Introduction

in this blog post, I will be using the setup I describred in my Setting up workspace for developing and building custom Linux-based distributions with Yocto blog post.

Similary to my previous posts, this writeup is based on Bootlin’s Yocto labs. And as always, thanks to Bootlin for making their materials free and open source!

Before we continue, I would like to give a quick refresher of some basic terms used when working with Yocto as described in my previous post with regards the Setting up Yocto for efficient embedded Linux system development:

If you want more details, either check out the previous blog post or better yet, check out the official Yocto Project terms.

Writing a recipe to support nInvaders and integrating it to an existing layer

Before downloading nInvaders, make sure you run the follwing command in poky’s root directory:

source oe-init-build-env

This now lets you use bitbake for your builds.

Next, locate the recipes-extended directory and create a directory for nInvaders where you will download the source code of the game. In my case, this was:

/yocto-stm32-labs/meta-openembedded/meta-oe/recipes-extended/ninvaders

Next, inside the same directory, create a recipe which will tell bitbake how to handle building nInvders. The recipe name should follow the Yocto nomenclature - {recipe_name}_{recipe_version}.bb. More info on Yocto naming conventions can be found here.

The link to my recipe can be found on my corresponding GitHub page. Here is a preview of how I’ve written the recipe:

nInvaders_src

You can now build the recipe by running:

bitbake ninvaders

Output of the build process should look something like:

bitbake_ninvaders

Build files of the recipe we have now built is in tmp/work/... folder:

ninvaders_dir_in_build_work_dir

A good practice before generating a new image which will end up on the target board is to verify in which layer is the recipe which contains our application present as well as is the application built for proper architecture which is arm in our case.

Running bibake-layers show-layer ninvaders from the build folder will show where is our recipe present:

check_ninvaders_recipe

Great, our recipe is included in the meta-oe layer. Even though this is not the best practice (and we will fix that in quite soon), we can be sure that it is included in the build since meta-oe is definately included in the build:

bitbake_layers

Finally, let’s verify that the ninvaders recipe has been built for proper architecture by running bitbake -e ninvaders | ^TARGET_ARCH=:

bitbake_Xcompiling_check

Once nInvaders recipe has been successfully built, generate a new rootfs image which will contain nInvaders by running:

bitbake core-image-minimal

The image with our ninvaders application is now in /tmp/deploy/images... and we can now extract the image to our NFS root directory on the host machine (/nfs):

extract_ninvaders_to_nfs

where we see our ninvaders app in the third column in the lower mid section. Great!

We can now just connect over ssh to board, and run nInvaders from /usr/bin by running /usr/bin/ninvaders:

play_ninvaders play_ninvaders_pt2

We can now use the keyboard to play nInvaders on STM32MP157D-DK1!

Creating and integrating a custom Yocto layer into the build

It is a good practice to leave your custom software fully decoupled from basic Yocto project files. This goes for both adding new recipes or customizing the existing ones. This is why I will go through explaining how to create a new layer with our application which we will integrate into the build.

Let’s add a new layer into the build. This can be done by running:

bitbake-layers reate-layer meta-bootlinlabs --priority 7

You can now verify that the layer has been added either by running bitbake-layers show-layers: ninvaders_part_of_different_layer_now

To integrate this newly created layer into the build, add it to the conf/bblayers.conf file:

add_meta_bootlinlabs

We can now move the ninvaders recipe into our custom meta-bootlinlabs layer by simply copying all of the contents from /yocto-stm32-labs/meta-openembedded/meta-oe/recipes-extended/ninvaders to yocto-stm32-labs/meta-bootlinlabs. To can now either delete old files from the recipes-extended directory or leave them as they are. bitbake will build the ninvaders recipe with the higher priority first anyway. You can now check that ninvaders recipe is part of our new meta-bootlinlabs layer by running:

bitbake-layers show-recipes ninvaders

ninvaders_part_of_different_layer_now

Applying patches to an existing recipe by adding joystick support

bitbake allows users to extend recipes by the means of .bbappend files. In this chapter we will go thorugh extending the linux-stm32mp recipe by applying a patch and including it into the build. This patch which will enable us to play nInvaders with Wii’s Nunchuck joystick.

First, we will create a linux-stm32mp_6.1.bbappend file in the /yocto-stm32-labs/meta-bootlinlabs/recipes-kernel/linux directory.

You can now run bitbake-layers show-appends to see all the available bbappend files and the recipe they apply to. There you can see the .bbappend file we have created for our kernel recipe:

append_present

The content of the .bbappend file can be seen here, and here is the preview for illustrative purposes:

append_content

Notice how the .bbappend file matches the exact recipe name of a file we want to extend. The needed patch for using the Nunchuck joystick as input to our console was provided by Bootlin and is present on this link.

As you can see, defconfig and patches are present in the source file list. Last two lines are used as an inidication to the linux-stm32mp recipe to use kernel configuration as defined by defconfig.

Treeview of the directory responsible for handling the nunchuck at the kernel level is now:

patch_file_organization

You can now rebuild the kernel to verify that the patches have been included into the build by running cat ... after rebuilding the kernel with bitbake virtual/kernel:

patch_is_present_virtual

.bbappend file responsible for handling the nunchuck at the application level is here and looks like:

joystick_ninvaders_support_patch

Treeview of the directory responsible for using the nunchuck in the game is now:

patch_file_organization_ninvaders

Transferring kernel image and device tree blob to TFTP

We can now rebuild the whole core-image-minimal and copy the newly generated kernel and device tree images to the TFTP server home directory (/srv/tftp in my case). Files you are looking for and that you want to transfer to the directory are:

zImage_and_dtb_local

Whith these files now present, the board’s bootloade can now load the proper image with proper hardware support.

We can now reset the board and observe the boot logs until we have access to the command line. Near the end of the boot, we should now observe the following.

serial_boot_nunchuck_partTwo

Testing the Nunchuck

Connection diagram for the Nunchuck can be seen on following images provided by Bootlin:

connection_diagram connection_diagram_board

You can now check that the Nunchuck is present and working by checking the presence of the js0 device file by running:

cat /dev/input/js0

nunchuck_present

Notice the random characters that appear while both playing with the Nunchuck’s joystick or by even moving the controller since the driver we integrated also handles acclerometer events.

And now finnaly, simply run /usr/bin/ninvaders and use the C button on the joystik to confirm and fire and Z to pause the game:

playing_real_deal_zoomed_in

Amd that’s it.