Jumpnow Technologies

home code consulting contact

Building Raspberry Pi Systems with Buildroot

16 Feb 2017

I have started using Buildroot as an alternative to Yocto for building Linux systems for the Raspberry Pi boards.

In general I am not interested in building desktop like systems that support multiple GUI applications.

In the projects I work on there is typically a single UI application running, maybe using a touchscreen. Or the projects have only a remote interface like a web service or no interface at all.

The preference for these systems is to be a small as possible, no software that isn’t needed.

I do usually add Qt5 support since so many of the projects I work on use it. But since I require only one UI application at a time, the Qt EGLFS platform plugin is what I want.

There are two versions of Qt5 available, 5.8 and the LTS version 5.6.2. I’m using 5.8.

Buildroot is considerably simpler and light-weight in comparison to Yocto, which should be nice when it comes to assisting clients setting up their internal build systems.

So here are some notes on my what I’m using so far.

I created a Buildroot clone in Github.

The [master] branch of the repository is a mirror of the official Buildroot repository.

The default [rpi] branch has a few additions on top of [master] for my own customizations.

The changes to [master] are

The two custom configs are

To build a system, run the following (see the ccache notes below)

scott@t410:~$ git clone -b rpi https://github.com/jumpnow/buildroot
scott@t410:~$ cd buildroot
scott@t410:~/buildroot$ make jumpnow_rpi3_defconfig
scott@t410:~/buildroot$ make

Note: Don’t run make with a -jN argument. The main Makefile is not designed to be run as a parallel build. The sub-projects will be run in parallel automatically.

If you are missing tools on your workstation, you will get error messages telling you what you are missing. The dependencies are nothing out of the ordinary for a developer workstation and you can search the web for the particular packages you need to install for your Linux distribution.

The command

make jumpnow_rpi3_defconfig 

created a .config file that completely describes to Buildroot how to generate the system.

When the build is done, insert an SD card and copy the image like this

scott@t410:~/buildroot$ sudo dd if=output/images/rpi3-sdcard.img of=/dev/sdb bs=1M

Replace /dev/sdb for where the SD card shows up on your workstation.

Customizing the Build

The Buildroot Documentation is good and you should probably be reading that first.

One easy optimization is use ccache to reduce redundant work by the C/C++ preprocessor.

Make sure your workstation has ccache installed, then run the Buildroot configuration tool after you have your initial .config generated.

scott@t410:~/buildroot$ make menuconfig 

Under Build options select Enable compiler cache and then save the configuration.
This will update your .config.

You will need the ncurses development package for your distribution before you can run menuconfig.

After that run make as usual to build your system.

Another option I’ve been using is to save the downloaded source files to a location outside the buildroot repository.

The download location is determined by the BR2_DL_DIR variable in the .config

BR2_DL_DIR="$(HOME)/dl"

Or it can be set as an environment variable in the shell

export BR2_DL_DIR=${HOME}/dl

This allows you to share common downloads among different builds and if you choose to delete the repo you don’t have to lose the downloads.

Another option is to build externally outside of the Buildroot repository.

You can specify it like this when you do the first make <some_defconfig>.

scott@fractal:~/buildroot$ make O=/br5/rpi3 jumpnow_rpi3_defconfig

After that, go to the directory you chose to run Buildroot make commands

scott@fractal:~/buildroot$ cd /br5/rpi3
scott@fractal:/br5/rpi3$ make menuconfig (optional)
scott@fractal:/br5/rpi3$ make

In this particular case I have /br5/rpi3 on a drive partition separate from my workstation rootfs and my home directory.

So what does the resulting system look like?

I uploaded some sdcard.imgs here if you want a quick look.

Here’s a short run through.

The RPi serial console console is configured and I’m running the following commands using that.

Welcome to Buildroot
rpi3 login: root

# uname -a
Linux rpi3 4.4.48-v7 #1 SMP Tue Feb 14 12:02:12 EST 2017 armv7l GNU/Linux

# free
             total       used       free     shared    buffers     cached
Mem:        947732      33736     913996        120       3216       9220
-/+ buffers/cache:      21300     926432
Swap:            0          0          0

The SD card is not fully utilized because we used the sdcard.img and didn’t resize. That’s easily fixed with some setup scripts I’ll get to later.

# df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root               223.0M    194.5M     13.3M  94% /
devtmpfs                458.5M         0    458.5M   0% /dev
tmpfs                   462.8M         0    462.8M   0% /dev/shm
tmpfs                   462.8M     32.0K    462.7M   0% /tmp
tmpfs                   462.8M     88.0K    462.7M   0% /run

The system is pretty big at 195M but that’s because of all the Qt5 and Python stuff I threw in.

# ls -l /var/log
lrwxrwxrwx    1 root     root             6 Jan 13 17:23 /var/log -> ../tmp

Logs are going to a tmpfs which is what you normally want on an embedded system.

The expected interfaces are present. The default /etc/network/interfaces brings up eth0 using dhcp.

I have verified the wifi interface works.

# ifconfig -a
eth0      Link encap:Ethernet  HWaddr B8:27:EB:56:9B:DC
          inet addr:192.168.10.116  Bcast:192.168.10.255  Mask:255.255.255.0
          inet6 addr: fe80::ba27:ebff:fe56:9bdc/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:50 errors:0 dropped:0 overruns:0 frame:0
          TX packets:45 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
         RX bytes:4493 (4.3 KiB)  TX bytes:4946 (4.8 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

wlan0     Link encap:Ethernet  HWaddr B8:27:EB:03:CE:89
          BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

The ssh server is listening and I can use it.

# netstat -an | grep tcp
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 :::22                   :::*                    LISTEN

I also added an ntp package and set the timezone to EST5EDT in the defconfig and that is working.

# date
Tue Jan 10 15:50:24 EST 2017

My little Qt Widgets touchscreen test application tspress works fine.

# tspress
Unable to query physical screen size, defaulting to 100 dpi.
To override, set QT_QPA_EGLFS_PHYSICAL_WIDTH and QT_QPA_EGLFS_PHYSICAL_HEIGHT (in millimeters).
Down: 667 554
Up  : 671 554
Down: 893 671
Up  : 893 671
Down: 976 482
Up  : 976 486
#

See the /etc/profile.d/qt5-env.sh script for setting Qt5 environment variables like WIDTH and HEIGHT.

I have a USB Bluetooth mouse and a USB keyboard/mouse trackpad attached as well as an HDMI display.

They all work.

You can see from the Qt messages that the eglfs plugin is being used.

I did include the linuxfb plugin in the build just for testing.

# ls -l /usr/lib/qt/plugins/platforms/
total 656
-rwxr-xr-x    1 root     root          7544 Jan 10 09:26 libqeglfs.so
-rwxr-xr-x    1 root     root        283680 Jan 10 09:26 libqlinuxfb.so
-rwxr-xr-x    1 root     root        119840 Jan 10 09:26 libqminimal.so
-rwxr-xr-x    1 root     root        147044 Jan 10 09:26 libqminimalegl.so
-rwxr-xr-x    1 root     root        106472 Jan 10 09:26 libqoffscreen.so

There is currently a linker issue with running PyQt5 applications. The work-around I’ve been using is to invoke the applications with an LD_PRELOAD statement like this

# LD_PRELOAD=libGLESv2.so pytouch.py

This is still on the TODO to look into.

So far I’m pretty happy with the systems that Buildroot is generating.

The one feature that might be missed is having a toolchain on the target device to do native compiles. This is really only a development convenience, production builds usually strip any tools like this.

Next up is some testing of the SDK toolchain that Buildroot generates.