Creating an IoT thermostat (part II)

Creating a Yocto based Linux distribution for my embedded thermostat

Intro

This write-up will focus on how to create your own Linux distribution, using Yocto. Yocto is a popular tools these days for creating an embedded operating system from scratch. I’m not going to talk you into how Yocto works, where it came from and so on. If you have no idea at all then go visit the Yocto Project website.

Because this is going to be a single unit creation you might think that it is a bit silly to go through the entire phase of creating you own Linux distribution. Well, off course that’s true, using a full blown distribution like Debian, Fedora or Ubuntu might have the benefits of having the distribution already created for you, with every tool available that you’ll ever need. Creating a Yocto based distribution also has some benefits of its own. For example you can add or remove system components and use only those you really need. This may for example reduce boot time. Furthermore, you have more control over your system, you can toss in/out the components that you require and so the OS isn’t bloated either with programs and tools that you’d never use. This may gratefully reduce the OS’s footprint. And actually it’s a good training to get to know the Yocto Project because it is often used nowadays in embedded systems. At least if that’s where you’re interested in.

The embedded device I’m about to use is the Raspberry Pi 2. The reason I’m choosing this device is simple: it has all the components that I need, it has support for many add-ons, it has a large community which may help you out whenever you get into trouble, it’s relatively cheap to buy and easy to find in stores, and I have already sitting one on my desk waiting for an application to be used in.

Pi2ModB1GB_-comp

(Bit)baking your own  Linux distribution

I could go through all the steps of how to create a Yocto based distribution for your Raspberry Pi. Fact is that someone already did write a very good tutorial which I’ve couldn’t done better, so first head over to the Jumpnowtek website and follow the tutorial. Furthermore this guy already did a lot of work for you so his using his layer for raspberry pi will add support for many devices. You can find his Git repo here. For those who’re not fund on compiling their own Yocto distribution but instead just want to go ahead and use it, you can also find downloadable content on the Jumpnowtek download sector.

For my Yocto project I’ve used my Dell XPS 15 laptop with a second HDD of 512Gb installed, 8Gb of RAM and the Ubuntu 16.04 LTS operating system. Because I had this drive already available I’m using this one, but if you’re about to buy a new build setup look for something which is fast, which has a CPU with many cores, which contains a lot of DRAM, and something that uses a SDD. Using faster build systems might dramatically reduce build times because building your own distribution on a system of few years old might easily take 6 to 8 hours!

I creating a dedicated directory (/media/geoffrey/Data/yocto-pi) on my second hard drive and I’ll use this directory to make all of my yocto builds. First pull-in all required repos as instructed on the Jumpnowtek website and initialize your build directory. Because I’m going to be using the Qt5 framework (a C++ UI framework) I’ll be building a QT5 Yocto image and so I need to include several QT5 layers. Here is my bblayers.conf file:

# POKY_BBLAYERS_CONF_VERSION is increased each time build/conf/bblayers.conf
# changes incompatibly
POKY_BBLAYERS_CONF_VERSION = "2"

BBPATH = "${TOPDIR}"
BBFILES ?= ""

BBLAYERS ?= " \
    /media/geoffrey/Data/yocto-pi/poky-morty/meta \
    /media/geoffrey/Data/yocto-pi/poky-morty/meta-poky \
    /media/geoffrey/Data/yocto-pi/poky-morty/meta-openembedded/meta-oe \
    /media/geoffrey/Data/yocto-pi/poky-morty/meta-openembedded/meta-multimedia \
    /media/geoffrey/Data/yocto-pi/poky-morty/meta-openembedded/meta-networking \
    /media/geoffrey/Data/yocto-pi/poky-morty/meta-openembedded/meta-python \
    /media/geoffrey/Data/yocto-pi/poky-morty/meta-qt5 \
    /media/geoffrey/Data/yocto-pi/poky-morty/meta-raspberrypi \
    /media/geoffrey/Data/yocto-pi/rpi/meta-rpi \
  "

And here is my local.conf:

# Local configuration for meta-rpi images
# Yocto Project 2.2 Poky distribution [morty] branch
# This is a sysvinit system

LICENSE_FLAGS_WHITELIST = "commercial"

DISTRO_FEATURES = "ext2 pam opengl usbhost ${DISTRO_FEATURES_LIBC}"

DISTRO_FEATURES_BACKFILL_CONSIDERED += "pulseaudio"

PREFERRED_PROVIDER_jpeg = "libjpeg-turbo"
PREFERRED_PROVIDER_jpeg-native = "libjpeg-turbo-native"

PREFERRED_PROVIDER_udev = "eudev"
VIRTUAL_RUNTIME_init_manager = "sysvinit"

MACHINE_FEATURES_remove = "apm"

IMAGE_FSTYPES = "tar.xz ext3"

PREFERRED_VERSION_linux-raspberrypi = "4.4.%"

MACHINE = "raspberrypi"

#DL_DIR = "/media/geoffrey/Data/yocto-pi/rpi/build/sources"

#SSTATE_DIR = "/media/geoffrey/Data/yocto-pi/rpi/build/sstate-cache"

#TMPDIR = "/media/geoffrey/Data/yocto-pi/rpi/build/tmp"

DISTRO = "poky"

PACKAGE_CLASSES = "package_ipk"

DISABLE_OVERSCAN = "1"
DISPMANX_OFFLINE = "1"
ENABLE_UART = "1"
ENABLE_RPI3_SERIAL_CONSOLE = "1"

# i686 or x86_64
SDKMACHINE = "x86_64"

EXTRA_IMAGE_FEATURES = "debug-tweaks"

USER_CLASSES = "image-mklibs image-prelink"

PATCHRESOLVE = "noop"

RM_OLD_IMAGE = "1"

CONF_VERSION = "1"

I’ve build the Yocto image using following command:

geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi$ source poky-morty/oe-init-build-env /media/geoffrey/Data/yocto-pi/rpi/build

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

You can now run 'bitbake '

Common targets are:
 core-image-minimal
 core-image-sato
 meta-toolchain
 meta-toolchain-sdk
 adt-installer
 meta-ide-support

You can also run generated qemu images with a command like 'runqemu qemux86'

geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/build$
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/build$ bitbake qt5-basic-image

Wait for the build to complete and next copy your files to the SD card using the Jumpnowtek site’s instructions. Make sure you copy it to the correct disk!

geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 465,8G 0 disk
├─sda1 8:1 0 244,8G 0 part /
├─sda2 8:2 0 220,6G 0 part
└─sda3 8:3 0 450M 0 part
sdc 8:32 1 3,7G 0 disk
├─sdc1 8:33 1 64M 0 part
└─sdc2 8:34 1 3,6G 0 part 

geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$ sudo mkdir /media/card
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$ sudo ./mk2parts.sh sdc
[sudo] wachtwoord voor geoffrey: 

Working on /dev/sdc

umount: /dev/sdc: not mounted
DISK SIZE – 3963617280 bytes 

Okay, here we go ... 

=== Zeroing the MBR === 

1024+0 records gelezen
1024+0 records geschreven
1048576 bytes (1,0 MB, 1,0 MiB) copied, 0,53551 s, 2,0 MB/s 

=== Creating 2 partitions === 

Checking that no-one is using this disk right now ... OK 

Disk /dev/sdc: 3,7 GiB, 3963617280 bytes, 7741440 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes 

>>> Created a new DOS disklabel with disk identifier 0x6efbfa0c.
Created a new partition 1 of type 'W95 FAT32 (LBA)' and of size 64 MiB.
/dev/sdc2: Created a new partition 2 of type 'Linux' and of size 3,6 GiB.
/dev/sdc3:
New situation:

Apparaat Op. Start Einde Sectoren Size Id Type
/dev/sdc1 * 8192 139263 131072 64M c W95 FAT32 (LBA)
/dev/sdc2 139264 7741439 7602176 3,6G 83 Linux

The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

=== Done! ===

geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 465,8G 0 disk
├─sda1 8:1 0 244,8G 0 part /
├─sda2 8:2 0 220,6G 0 part
└─sda3 8:3 0 450M 0 part
sdc 8:32 1 3,7G 0 disk
├─sdc1 8:33 1 64M 0 part
└─sdc2 8:34 1 3,6G 0 part
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$ export OETMP=/media/geoffrey/Data/yocto-pi/rpi/build/tmp
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$ export MACHINE=raspberrypi
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$ ./copy_boot.sh sdc
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$ ./copy_rootfs.sh sdc qt5 raspberryyocto

OETMP: /media/geoffrey/Data/yocto-pi/rpi/build/tmp
IMAGE: qt5
HOSTNAME: raspberryyocto

Formatting /dev/sdc2 as ext4
[sudo] wachtwoord voor geoffrey:
/dev/sdc2 bevat een ext4-bestandssysteem met label 'ROOT'
 laatst aangekoppeld op /media/card op Wed Mar 29 11:33:06 2017
Toch doorgaan? (j,n) j
Mounting /dev/sdc2
Extracting qt5-image-raspberrypi.tar.xz to /media/card
Writing raspberryyocto /etc/hostname
Unmounting /dev/sdc2
Done

Creating a cross-platform toolchain for application development

Even though our embedded device might contain all the tools needed to compile native applications, most of the time you’ll be wanting to use the processing power of your development machine to speed up development. For this reason we need to install the cross-compilation toolchain. But first we need to create the toolchain using bitbake. Navigate to your working directory and use following command to build the toolchain:

geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi$ source poky-morty/oe-init-build-env /media/geoffrey/Data/yocto-pi/rpi/build

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

You can now run 'bitbake '

Common targets are:
 core-image-minimal
 core-image-sato
 meta-toolchain
 meta-ide-support

You can also run generated qemu images with a command like 'runqemu qemux86'
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/build$ clear; bitbake meta-toolchain-qt5
Loading cache: 100% |##################################################################| Time: 0:00:00
Loaded 2660 entries from dependency cache.
Parsing recipes: 100% |################################################################| Time: 0:00:01
Parsing of 1972 .bb files complete (1964 cached, 8 parsed). 2668 targets, 353 skipped, 0 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies

Build Configuration:
BB_VERSION = "1.32.0"
BUILD_SYS = "x86_64-linux"
NATIVELSBSTRING = "universal"
TARGET_SYS = "arm-poky-linux-gnueabi"
MACHINE = "raspberrypi"
DISTRO = "poky"
DISTRO_VERSION = "2.2.1"
TUNE_FEATURES = "arm armv6 vfp arm1176jzfs callconvention-hard"
TARGET_FPU = "hard"
meta
meta-poky = "morty:a3fa5ce87619e81d7acfa43340dd18d8f2b2d7dc"
meta-oe
meta-multimedia
meta-networking
meta-python = "morty:1efa5d623bc64659b57389e50be2568b1355d5f7"
meta-qt5 = "morty:9aa870eecf6dc7a87678393bd55b97e21033ab48"
meta-raspberrypi = "master:e1f69daa805cb02ddd123ae2d4d48035cb5b41d0"
meta-rpi = "morty:03841471ccaed549a2a14a896c13f71af76cf482"

Initialising tasks: 100% |#############################################################| Time: 0:00:05
NOTE: Executing SetScene Tasks
NOTE: Executing RunQueue Tasks
NOTE: Tasks Summary: Attempted 3170 tasks of which 1605 didn't need to be rerun and all succeeded.
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/build$

Again, this will take a lot of time, depending on how fast your dev-pc is. When the build completes you’ll get as a result the script that install the SDK  on your development pc. On my pc it was found in the /media/geoffrey/Data/yocto-pi/rpi/build/tmp/deploy/sdk folder. The script is named poky-glibc-x86_64-meta-toolchain-qt5-arm1176jzfshf-vfp-toolchain-2.2.1.sh.

Run the script, and when asked for the install folder I’ve chosen the default option. If you plan to create more than one SDK I’d highly recommend to install each in their own directory. There is no need to run this script as root user. Note that running this script takes roughly 2 minutes to complete, enough time to grab a dieet coke!

geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/build/tmp/deploy/sdk$ ./poky-glibc-x86_64-meta-toolchain-qt5-arm1176jzfshf-vfp-toolchain-2.2.
1.sh
Poky (Yocto Project Reference Distro) SDK installer version 2.2.1
=================================================================
Enter target directory for SDK (default: /opt/poky/2.2.1):
You are about to install the SDK to "/opt/poky/2.2.1". Proceed[Y/n]? Y
[sudo] password for geoffrey:
Extracting SDK............................................................................................................................done
Setting it up...done
SDK has been successfully set up and is ready to be used.
Each time you wish to use the SDK in a new shell session, you need to source the environment setup script e.g.
 $ . /opt/poky/2.2.1/environment-setup-arm1176jzfshf-vfp-poky-linux-gnueabi
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/build/tmp/deploy/sdk$

Compiling your first program

We came as far as creating a self-made Yocto based distribution with Qt5.7 pre-installed, plus having all the tools installed on our development pc to create a nice embedded application. If you don’t use an IDE then go ahead and use the instructions on the Jumpnowtek website to go ahead and compile your application using qmake.

geoffrey@geoffrey-Dell-XPS-L502X:~/Qt5.5.1/Projects/HelloQtWidgets$ source /opt/poky/2.2.1/environment-setup-arm1176jzfshf-vfp-poky-linux-gnueabi
geoffrey@geoffrey-Dell-XPS-L502X:~/Qt5.5.1/Projects/HelloQtWidgets$ qmake && make -j4
sh: OE_QMAKE_CXX: opdracht niet gevonden
sh: OE_QMAKE_CXXFLAGS: opdracht niet gevonden
Info: creating stash file /home/geoffrey/Qt5.5.1/Projects/HelloQtWidgets/.qmake.stash
/opt/poky/2.2.1/sysroots/x86_64-pokysdk-linux/usr/bin/qt5/uic mainwindow.ui -o ui_mainwindow.h
arm-poky-linux-gnueabi-g++ -march=armv6 -mfpu=vfp -mfloat-abi=hard -mtune=arm1176jzf-s -mfpu=vfp --sysroot=/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi -c -pipe -O2 -pipe -g -feliminate-unused-debug-types -std=c++11 -O2 -Wall -W -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5 -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtWidgets -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtGui -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtCore -I. -I. -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/lib/qt5/mkspecs/linux-oe-g++ -o main.o main.cpp
arm-poky-linux-gnueabi-g++ -march=armv6 -mfpu=vfp -mfloat-abi=hard -mtune=arm1176jzf-s -mfpu=vfp --sysroot=/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi -c -pipe -O2 -pipe -g -feliminate-unused-debug-types -std=c++11 -O2 -Wall -W -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5 -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtWidgets -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtGui -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtCore -I. -I. -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/lib/qt5/mkspecs/linux-oe-g++ -o qsimpledigitalclock.o qsimpledigitalclock.cpp
/opt/poky/2.2.1/sysroots/x86_64-pokysdk-linux/usr/bin/qt5/moc -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/lib/qt5/mkspecs/linux-oe-g++ -I/home/geoffrey/Qt5.5.1/Projects/HelloQtWidgets -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5 -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtWidgets -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtGui -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtCore -I/usr/include -I/usr/local/include mainwindow.h -o moc_mainwindow.cpp
/opt/poky/2.2.1/sysroots/x86_64-pokysdk-linux/usr/bin/qt5/moc -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/lib/qt5/mkspecs/linux-oe-g++ -I/home/geoffrey/Qt5.5.1/Projects/HelloQtWidgets -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5 -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtWidgets -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtGui -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtCore -I/usr/include -I/usr/local/include qsimpledigitalclock.h -o moc_qsimpledigitalclock.cpp
arm-poky-linux-gnueabi-g++ -march=armv6 -mfpu=vfp -mfloat-abi=hard -mtune=arm1176jzf-s -mfpu=vfp --sysroot=/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi -c -pipe -O2 -pipe -g -feliminate-unused-debug-types -std=c++11 -O2 -Wall -W -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5 -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtWidgets -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtGui -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtCore -I. -I. -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/lib/qt5/mkspecs/linux-oe-g++ -o mainwindow.o mainwindow.cpp
arm-poky-linux-gnueabi-g++ -march=armv6 -mfpu=vfp -mfloat-abi=hard -mtune=arm1176jzf-s -mfpu=vfp --sysroot=/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi -c -pipe -O2 -pipe -g -feliminate-unused-debug-types -std=c++11 -O2 -Wall -W -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5 -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtWidgets -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtGui -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtCore -I. -I. -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/lib/qt5/mkspecs/linux-oe-g++ -o moc_mainwindow.o moc_mainwindow.cpp
arm-poky-linux-gnueabi-g++ -march=armv6 -mfpu=vfp -mfloat-abi=hard -mtune=arm1176jzf-s -mfpu=vfp --sysroot=/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi -c -pipe -O2 -pipe -g -feliminate-unused-debug-types -std=c++11 -O2 -Wall -W -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5 -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtWidgets -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtGui -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/include/qt5/QtCore -I. -I. -I/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/lib/qt5/mkspecs/linux-oe-g++ -o moc_qsimpledigitalclock.o moc_qsimpledigitalclock.cpp
arm-poky-linux-gnueabi-g++ -march=armv6 -mfpu=vfp -mfloat-abi=hard -mtune=arm1176jzf-s -mfpu=vfp --sysroot=/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -Wl,-O1 -o HelloQtWidgets main.o mainwindow.o qsimpledigitalclock.o moc_mainwindow.o moc_qsimpledigitalclock.o -L/opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi/usr/lib -lQt5Widgets -lQt5Gui -lQt5Core -lGLESv2 -lpthread
geoffrey@geoffrey-Dell-XPS-L502X:~/Qt5.5.1/Projects/HelloQtWidgets$

If you’d get the error:

qmake: could not exec '/usr/lib/x86_64-linux-gnu/qt4/bin/qmake': No such file or directory

… you probable forget to use the source command! Note that the source command is bound to the shell where you used it. If you used it inside a shell script than it will only take effect to the commands inside the shell script!

After compiling has completed you get several files as output. One of the files is the binary executable that represent our program. We can also verify if is has been compiled for ARM:

geoffrey@geoffrey-Dell-XPS-L502X:~/Qt5.5.1/Projects/HelloQtWidgets$ ls -l HelloQtWidgets
-rwxrwxr-x 1 geoffrey geoffrey 1181132 feb 18 17:33 HelloQtWidgets
geoffrey@geoffrey-Dell-XPS-L502X:~/Qt5.5.1/Projects/HelloQtWidgets$ ^C
geoffrey@geoffrey-Dell-XPS-L502X:~/Qt5.5.1/Projects/HelloQtWidgets$ file HelloQtWidgets
HelloQtWidgets: ELF 32-bit LSB executable, ARM, EABI5 version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=56d390438db87a396f5f28e7adbbdd69cdd0ee68, not stripped

We can now copy the file over to our embedded system:

geoffrey@geoffrey-Dell-XPS-L502X:~/Qt5.5.1/Projects/HelloQtWidgets$ scp HelloQtWidgets root@192.168.0.205:/home/root
HelloQtWidgets 100% 1153KB 1.1MB/s 00:00

Next, login to the embedded system using ssh and execute the binary file. You’ll need to have a display attached to your pi in order to view the UI:

geoffrey@geoffrey-Dell-XPS-L502X:~$ ssh root@192.168.0.205
root@raspberryyocto:~# ls
HelloQtWidgets
root@raspberryyocto:~# ./HelloQtWidgets
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).

Setting up QtCreator

Whenever you’re about to create a more advanced program with an UI you’ll find yourselve in the need of a IDE. QtCreator is quit good in providing all the tools you need to create graphical applications so I highly recommend using QtCreator to develop your applications. We’ll now configure QtCreator to use the SDK we’ve used above.

First you need to install QtCreator. Go to the Qt website, fill in the licence form and download Qt5.7. You may also look for the open source version of Qt here. Next make the downloaded file executable and run it. You may install Qt in the default folder. Next we’ll configure QtCreator to use the compiler, debugger, etc. Note that for compiling native applications for our embedded system we need to source the SDK folder each time we run QtCreator. For convenience I created a script that does this for me:

geoffrey@geoffrey-Dell-XPS-L502X:~$ vim qtcreator4pi.sh

Enter following content:

#!/bin/bash
source /opt/poky/2.2.1/environment-setup-arm1176jzfshf-vfp-poky-linux-gnueabi
/home/geoffrey/Qt5.7.0/Tools/QtCreator/bin/qtcreator&

Save the script, make it executable and run it:

geoffrey@geoffrey-Dell-XPS-L502X:~$ chmod +x qtcreator4pi.sh
geoffrey@geoffrey-Dell-XPS-L502X:~$ ./qtcreator4pi.sh

schermafdruk-van-2017-02-18-15-14-49

You should now see the default welcome page for QtCreator. Next from the main menu bar at the top of the application click the Tools menu item and open the Options window. Now do following steps (note that some steps might look a bit different as the screenshots I’ve made are made on a Qt5.5 setup):

Create your device

Navigate to Devices, click Add, select Generic Linux Device and start the Wizard. Enter following content:
Name: [pick your own name, for example Pi One]
Hostname: [the IP address of your raspberry pi]
Username: root
Authentication type: Password
Password: [leave empty]

Click next, QtCreator will now test your connection and if all goes well you’d get following output:

Connecting to host...

Checking kernel version...

Linux 4.4.43 armv6l

Checking if specified ports are available...

All specified ports are available.

Device test finished successfully.

schermafdruk-van-2017-02-18-12-12-56

Make sure to Apply your configuration at this point.

Configure the Qt Version

Navigate to Build &Run > Qt Versions, click Add. If not already selected, navigate and select the /opt/poky/2.2.1/sysroots/x86_64-pokysdk-linux/usr/bin/qt5/qmake executable. Adjust the path to your needs.

schermafdruk-van-2017-02-18-12-24-46

Configure the compiler

Navigate to Build & Run > Compilers, click Add, select GCC and use following settings:
Name: [choose your own name, for example Pi One GCC]
Compiler Path: /opt/poky/2.2.1/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-g++

schermafdruk-van-2017-02-18-12-46-26

Configure the debugger

Navigate to Build & Run > Debuggers, click Add and use following settings:
Name: [choose your own name, for example Pi One GDB]
Path: /opt/poky/2.2.1/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gdb

schermafdruk-van-2017-02-18-12-50-00

Configure the kit (aka hook everything up together)

Navigate to Build & Run > Kits, click Add, and enter following content.
Name: [choose your own name, for example Pi One]
Device Type: Generic Linux Device
Device: [choose the device we created earlier]
Sysroot: /opt/poky/2.2.1/sysroots/arm1176jzfshf-vfp-poky-linux-gnueabi
Compiler: [choose the compiler we configured earlier]
Debugger: [choose the debugger we configured earlier]
Qt Version: [choose the Qt version we configured earlier]
Qt mkspec: linux-oe-g++

schermafdruk-van-2017-02-18-17-59-50

We’ve now correctly configured QtCreator for cross-platform development. We need to perform this configuration only once per SDK. Note that whenever you’re going to compile, deploy or debug your application on your embedded device that you launch QtCreator using the script we created earlier (the one that contains the source command). If however your target machine is your own pc you don’t need to source in any SDK and so you can launch QtCreator directly from command line or or using any desktop shortcut.

Building & running applications

With QtCreator configured to use our embedded device’s SDK we can now open a demo application. Note, because I’ve chosen the qt5-basic-image we must take note that not all Qt5 libraries will be available on our target machine. So while QtCreator does provide quite some Qt demo apps, some may work for you.

For this purpose I’ll use a self made demo app. Make sure you’ve launched QtCreator with using our launch script, and open the demo application project. The project might not yet contain build settings that allows us to choose the Pi One kit as build target. We can add this by selecting the Projects button on the left menu bar, click Add kit and select the one we’ve configured earlier. In my case I’d select Pi One:

schermafdruk-van-2017-02-18-14-04-49

Now, from the left menu bar select your target kit:

schermafdruk-van-2017-02-18-14-04-49

Use the hammer icon the build your application. As a result you’ll find a new directory inside your QtCreator working directory, and inside you’ll find the executable binary file we just created.

By now we have our own Linux based OS with all the tools installed to develop our thermostat UI an backend software. Stay tuned for more.

Advertisements

2 thoughts on “Creating an IoT thermostat (part II)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s