Jumpnow Technologies

home code consulting contact

Building Raspberry Pi Systems with Buildroot

21 Mar 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.

Buildroot offers two versions of Qt5, 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.

I switched the kernel from 4.4 to 4.9 since it looks like that will be the official RPi kernel very soon.

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 [jumpnow] branch has a few additions on top of [master] for my own customizations.

NOTE: I switched to the [jumpnow] branch from [rpi] since I started adding some more boards to my Buildroot repo.

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 jumpnow 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.9.11-v7 #1 SMP Thu Feb 23 15:30:11 EST 2017 armv7l GNU/Linux

# free
             total       used       free     shared    buffers     cached
Mem:        945520      36016     909504        168       3664      11048
-/+ buffers/cache:      21304     924216
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               227.2M    198.4M     13.3M  94% /
devtmpfs                457.2M         0    457.2M   0% /dev
tmpfs                   461.7M         0    461.7M   0% /dev/shm
tmpfs                   461.7M     32.0K    461.6M   0% /tmp
tmpfs                   461.7M    136.0K    461.5M   0% /run
/dev/mmcblk0p1           31.9M      8.5M     23.4M  27% /mnt

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
Thu Feb 23 15:43:01 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 887
-rwxr-xr-x    1 root     root          7332 Feb 23 15:31 libqeglfs.so
-rwxr-xr-x    1 root     root        294196 Feb 23 15:31 libqlinuxfb.so
-rwxr-xr-x    1 root     root        119980 Feb 23 15:31 libqminimal.so
-rwxr-xr-x    1 root     root        150524 Feb 23 15:31 libqminimalegl.so
-rwxr-xr-x    1 root     root        135836 Feb 23 15:31 libqoffscreen.so
-rwxr-xr-x    1 root     root        191876 Feb 23 15:31 libqvnc.so

PyQt5 applications work fine. There is small example installed called pytouch.py.

You can run it like this

# pytouch.py

Using the Buildroot cross-toolchain

Some quick notes on using the cross-toolchain.

The toolchain gets installed under the build output/host directory.

In my example where I used an external build directory

~/buildroot$ make O=/br5/rpi3 jumpnow_rpi3_defconfig

My build output ended up here

/br5/rpi3/host

The cross-compiler and associated tools can be found under

/br5/rpi3/host/usr/bin

The toolchain is not relocatable, use it in place.

To use it in place, add the path to <output>/host/usr/bin to your path and invoke the compiler by name, in this case arm-linux-gcc, arm-linux-g++, etc…

Some quick examples, first add the PATH to the cross-compiler

$ export PATH=/br5/rpi3/host/usr/bin:${PATH}
$ echo $PATH
/br5/rpi3/host/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

A simple C, Makefile example

scott@fractal:~/projects$ git clone https://github.com/scottellis/serialecho
Cloning into 'serialecho'...

scott@fractal:~/projects$ cd serialecho/

scott@fractal:~/projects/serialecho$ cat Makefile
TARGET = serialecho

$(TARGET) : serialecho.c
        $(CC) serialecho.c -o $(TARGET)

clean:
        rm -f $(TARGET)

scott@fractal:~/projects/serialecho$ export CC=arm-linux-gcc

scott@fractal:~/projects/serialecho$ make
arm-linux-gcc serialecho.c -o serialecho

scott@fractal:~/projects/serialecho$ file serialecho
serialecho: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 4.9.0, not stripped

A Qt5 project, first check our Qt version

scott@fractal:~/projects$ which qmake
/br5/rpi3/host/usr/bin/qmake

scott@fractal:~/projects$ qmake --version
QMake version 3.1
Using Qt version 5.8.0 in /br5/rpi3/host/usr/arm-buildroot-linux-gnueabihf/sysroot/usr/lib

Fetch and build a project

scott@fractal:~/projects$ git clone https://github.com/scottellis/tspress
Cloning into 'tspress'...

scott@fractal:~/projects$ cd tspress

scott@fractal:~/projects/tspress$ qmake
Info: creating stash file /home/scott/projects/tspress/.qmake.stash

scott@fractal:~/projects/tspress$ make
... (build stuff) ...

scott@fractal:~/projects/tspress$ file tspress
tspress: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 4.9.0, not stripped