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.
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
Added an rpi-wifi-firmware package to include the non-free blobs the RPi3 radio requires. (Uses the github.com/RPi-Distro/firmware-nonfree repo for the files.)
Added some custom applications primarily as an experiment in how to add custom packages to Buildroot. The source for all of them are github repos.
- serialecho - a C, Makefile based app
- tspress - a Qt5 Widgets GUI app using qmake
- pytouch - a PyQt5 app, the build/install for this is just a copy
Added some custom Buildroot
configsto support all the RPi boards. The configs add Qt5 (no QML), PyQt5 and Python3 including Numpy. This generates an image approaching 180MB, which is big, but this is only for evaluation.
Created some sample overlays for the rootfs to customize some conf files.
Added some kernel build patches so that DTS overlays (DTBOs) are built from the kernel source and not just downloaded from the RPi firmware github repo.
Added some custom DTS files for hardware PWM.
The two custom
- jumpnow_rpi3_defconfig - For the RPi2, RPi3 and CM3 boards
- jumpnow_rpi0_defconfig - For the original RPi, RPi Zero and CM1 boards
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.
.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
/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
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
Or it can be set as an environment variable in the shell
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
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 #
/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.