Creating an IoT thermostat (part IV)

Dealing with driver issue’s

Setup Wifi

In order to connect to the pi wireless I’ve added a RTL8192CU based WiFi adapter. To use the wireless connection we must do 3 things:

Setup wpa_supplicant:

root@thermopi2:~# vi /etc/wpa_supplicant.conf

And enter your wireless credentials:

</pre>
<h3>Next enable wlan0:</h3>
<pre>

root@thermopi2:~# vi /etc/wpa_supplicant.conf

Make sure auto wlan0 is not commented.

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

auto wlan0
iface wlan0 inet dhcp
        wireless_mode managed
        wpa-conf /etc/wpa_supplicant.conf

Restart the pi, or restart the wlan0 inteface:

root@thermopi2:~# ifdown wlan0
root@thermopi2:~# ifup wlan0
Successfully initialized wpa_supplicant
udhcpc (v1.24.1) started
Sending discover...
Sending discover...
Sending select for 192.168.0.203...
Lease of 192.168.0.203 obtained, lease time 3600
RTNETLINK answers: File exists
/etc/udhcpc.d/50default: Adding DNS 195.130.130.2
/etc/udhcpc.d/50default: Adding DNS 195.130.131.2
root@thermopi2:~# ifconfig
eth0      Link encap:Ethernet  HWaddr B8:27:EB:FA:03:83
          inet addr:192.168.0.186  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: 2a02:1812:2521:3400:ba27:ebff:fefa:383%1/64 Scope:Global
          inet6 addr: fe80::ba27:ebff:fefa:383%lo/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1222 errors:0 dropped:1 overruns:0 frame:0
          TX packets:945 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:111800 (109.1 KiB)  TX bytes:183704 (179.3 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1%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 74:DA:38:8A:EA:3B
          inet addr:192.168.0.203  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: 2a02:1812:2521:3400:76da:38ff:fe8a:ea3b%1/64 Scope:Global
          inet6 addr: fe80::76da:38ff:fe8a:ea3b%lo/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:11 errors:0 dropped:0 overruns:0 frame:0
          TX packets:11 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:2790 (2.7 KiB)  TX bytes:1738 (1.6 KiB)

Dealing with driver issue's

I noticed on the first yocto image that I've made there were some issue's regarding the wireless connection. I was not sure exactly what went wrong, but it seemed to be a kernel/driver issue. The rtl8192cu wifi interface had connection problems whenever I disconnected the eth0 interface. I din't find a lot of clue's except from some folks complaining about the 8192cu kernel module not really working out to well. Through lsmod I found out that I was using this module. When using the adapter on my Ubuntu machine I noticed other drivers and modules got loaded - not the pesky 8192cu module - and the wireless interface was indeed working out well.

First idea would be to swap kernel modules by reconfiguring the kernel config in Yocto. However, since I'm recompiling I might as well pull in the latest sources for my working branch and just recompile the entire image. As a result I'd upgrade from Linux 4.4 to 4.9.
I also wrapped some of my work into script so that updating another time later on would make life much easier. Here is what I did:

Updating yocto to latest sources

The script below will pull in all latest meta-layers from the morty branch. Although newer branches are available like pyro and rocko, I'm not yet thinking about pulling these in since this will have a bigger impact on the package versions. Here is the script:

#!/bin/bash
BRANCH=morty
echo "##############################################"
echo "# upgrading sources to latest [$BRANCH]"
echo "##############################################"
echo ""
echo "####################################################################"
echo "### upgrading yocto-poky                                           #"
echo "####################################################################"
cd poky-morty/
git pull origin $BRANCH
echo ""
echo "####################################################################"
echo "### upgrading meta-openembedded                                    #"
echo "####################################################################"
cd meta-openembedded/
git pull origin $BRANCH
echo ""
echo "####################################################################"
echo "### upgrading meta-qt5                                             #"
echo "####################################################################"
cd ../meta-qt5/
git pull origin $BRANCH
echo ""
echo "####################################################################"
echo "### upgrading meta-raspberrypi                                     #"
echo "####################################################################"
cd ../meta-raspberrypi/
git pull origin $BRANCH
echo ""
echo "####################################################################"
echo "### upgrading meta-rpi (jumpnowtek)                                #"
echo "####################################################################"
cd ../../rpi/meta-rpi/
git pull origin $BRANCH

Save it in the directory directly above poky-morty, make it executable, and run it. Feel free to alter the script to pull in newer branches.

Rebuilding the image

Following script rebuilds the entire image:

#!/bin/bash
source poky-morty/oe-init-build-env /media/geoffrey/Data/yocto-pi/rpi/build
bitbake -c cleanall qt5-image
bitbake qt5-image

Again, save it to the same directoy, make it executable and run it (overnight).

Deploy the image to SD card

Jumpnowtek already provided some tools to automate some of the task that need to be done to get all your binaries on a SD card. The following script just wraps some of these tools for convenience:

#!/bin/bash
echo "##############################################"
echo "# Choose your SD card                        #"
echo "##############################################"
lsblk -dn -o NAME,SIZE
while true; do
	echo "# Device to format: "
	read SDCARD
	echo "selected: $SDCARD"
	if lsblk -dn -o NAME | grep "$SDCARD"; then

		break;
	else
		echo "Device not supported... retry"
	fi
done
echo "##############################################"
echo "# using card [$SDCARD]"
echo "##############################################"
echo ""
echo "##############################################"
echo "# making preparations                        #"
echo "##############################################"
MOUNTDIR="/media/card"
if [ ! -d "$MOUNTDIR" ]; then
	echo "Creating directory $MOUNTDIR"
	sudo mkdir "$MOUNTDIR"
else
	echo "Using $MOUNTDIR"
	umount "/media/card"
fi	

echo "exporting variables"
export OETMP=/media/geoffrey/Data/yocto-pi/rpi/build/tmp
export MACHINE=raspberrypi2

echo "unmounting stuff"
PART1="/dev/$SDCARD""p1"
PART2="/dev/$SDCARD""p2"
umount $PART1
umount $PART2

echo "##############################################"
echo "# copy boot partition                        #"
echo "##############################################"
./rpi/meta-rpi/scripts/copy_boot.sh $SDCARD

echo "##############################################"
echo "# copy boot partition                        #"
echo "##############################################"
IMAGENAME="qt5"
HOSTNME="thermopi2"
./rpi/meta-rpi/scripts/copy_rootfs.sh $SDCARD $IMAGENAME $HOSTNME

Once again, save the script and run it.

Boot the new system

With the system back up you first may want to check if you get the wlan0 interface working this time. But let's check the kernel version and driver modules first:

root@thermopi2:~# uname -a
Linux thermopi2 4.9.30 #1 SMP Sun Nov 5 20:10:01 CET 2017 armv7l armv7l armv7l GNU/Linux
root@thermopi2:~# lsmod
Module                  Size  Used by
ctr                     4263  2
ccm                     9163  1
ipv6                  412068  32
i2c_dev                 7169  0
arc4                    2211  2
rtl8192cu              81389  0
rtl_usb                12772  1 rtl8192cu
rtl8192c_common        58586  1 rtl8192cu
rtlwifi                88883  3 rtl_usb,rtl8192c_common,rtl8192cu
mac80211              664199  3 rtl_usb,rtlwifi,rtl8192cu
cfg80211              554896  2 mac80211,rtlwifi
rfkill                 21968  2 cfg80211
joydev                  9988  0
evdev                  12423  0
bcm2835_gpiomem         3900  0
i2c_bcm2835             7231  0
rpi_ft5406              5447  0
uio_pdrv_genirq         3923  0
rpi_backlight           2632  0
fixed                   3285  0
uio                    10396  1 uio_pdrv_genirq

We notice the newer 4.9 kernel is in use, and furthermore we also see the rtl8192cu and some other related modules loaded just as I saw on my Ubuntu machine. I went ahead and configured wlan again as mentioned earlier, é voila problem solved!

Deploying custom software

I'm not yet about to open source the entire codebase for my thermostat, I think there's already lots of stuff here to get you going. I also haven't yet made a decicated recipe in Yocto included my own made binaries. For now I will deploy my thermostat software by hand and create a little init script so that it gets loaded automatically at system boot.
For starters create the init script with following content:

#!/bin/bash

case "$1" in
        start)
                source /etc/profile.d/qt5-env.sh
		source /etc/profile.d/tslib.sh
                cd /usr/bin && ./QuickTemp &
                ;;
        stop)
                killall QuickTemp
                ;;
        reload|force-reload)
                ;;
        restart)
                ;;
        status)
                ;;
        *)
                echo"Usage: $SCRIPTNAME{start|stop|restart|force-reload|reload|status}" >&2
                exit 3
                ;;
esac

exit 0

Save it as /etc/init.d/quicktemp.sh and make it executable.

Next make sure the quicktemp executable is installed in the /usr/bin directoy. When this is done we can test our script:

root@thermopi2:~# mv QuickTemp /usr/bin/QuickTemp
root@thermopi2:~# /etc/init.d/quicktemp.sh start

To stop the service again:

root@thermopi2:~# /etc/init.d/quicktemp.sh stop

We turned our software into a service that can be started through the init daemon by executing the start and stop commands. One last step is need to be made for the service to automatically start at boot: we must link it to runlevel 5 so that the init service picks it up when going through all services connected to the runlevel.

root@thermopi2:/etc/rc5.d# ls -al
total 8
drwxr-xr-x  2 root root 4096 Nov  5 23:41 .
drwxr-xr-x 35 root root 4096 Nov  7 21:12 ..
lrwxrwxrwx  1 root root   20 Nov  5 23:41 S01networking -> ../init.d/networking
lrwxrwxrwx  1 root root   16 Nov  5 23:40 S02dbus-1 -> ../init.d/dbus-1
lrwxrwxrwx  1 root root   14 Nov  5 23:41 S09sshd -> ../init.d/sshd
lrwxrwxrwx  1 root root   21 Nov  5 19:46 S15mountnfs.sh -> ../init.d/mountnfs.sh
lrwxrwxrwx  1 root root   28 Nov  5 23:41 S15pi-blaster.boot.sh -> ../init.d/pi-blaster.boot.sh
lrwxrwxrwx  1 root root   14 Nov  5 23:41 S20ntpd -> ../init.d/ntpd
lrwxrwxrwx  1 root root   18 Nov  5 23:41 S20samba.sh -> ../init.d/samba.sh
lrwxrwxrwx  1 root root   16 Nov  5 23:40 S20syslog -> ../init.d/syslog
lrwxrwxrwx  1 root root   22 Nov  5 19:46 S99rmnologin.sh -> ../init.d/rmnologin.sh
lrwxrwxrwx  1 root root   23 Nov  5 20:56 S99stop-bootlogd -> ../init.d/stop-bootlogd
root@thermopi2:/etc/rc5.d# ln -s ../init.d/quicktemp.sh S95quicktemp
root@thermopi2:/etc/rc5.d# ls -al
total 8
drwxr-xr-x  2 root root 4096 Nov  7 23:29 .
drwxr-xr-x 35 root root 4096 Nov  7 21:12 ..
lrwxrwxrwx  1 root root   20 Nov  5 23:41 S01networking -> ../init.d/networking
lrwxrwxrwx  1 root root   16 Nov  5 23:40 S02dbus-1 -> ../init.d/dbus-1
lrwxrwxrwx  1 root root   14 Nov  5 23:41 S09sshd -> ../init.d/sshd
lrwxrwxrwx  1 root root   21 Nov  5 19:46 S15mountnfs.sh -> ../init.d/mountnfs.sh
lrwxrwxrwx  1 root root   28 Nov  5 23:41 S15pi-blaster.boot.sh -> ../init.d/pi-blaster.boot.sh
lrwxrwxrwx  1 root root   14 Nov  5 23:41 S20ntpd -> ../init.d/ntpd
lrwxrwxrwx  1 root root   18 Nov  5 23:41 S20samba.sh -> ../init.d/samba.sh
lrwxrwxrwx  1 root root   16 Nov  5 23:40 S20syslog -> ../init.d/syslog
lrwxrwxrwx  1 root root   22 Nov  7 23:29 S95quicktemp -> ../init.d/quicktemp.sh
lrwxrwxrwx  1 root root   22 Nov  5 19:46 S99rmnologin.sh -> ../init.d/rmnologin.sh
lrwxrwxrwx  1 root root   23 Nov  5 20:56 S99stop-bootlogd -> ../init.d/stop-bootlogd

Reboot the pi, you'll now see that your software is auto loaded at boot!

Extending the SD card lifetime

As I've mentioned in other blog post having your logs written to a tempfs volatile file system (in RAM) extends the SD card life time a lot. I wont go into detail again here, just have a look here. Note that in our Yocto image there is already the tmpfs created for you so actually we don't have to do anything extra here.

Edit

Although the RTL8192CU driver brought some improvements, it didn't offer a permanent solution. To fix it I've blacklisted the driver again:

root@thermopi2:/etc/rc5.d# echo "blacklist rtl8192cu" >> /etc/modprobe.d/blacklist.conf
Advertisements

Creating an IoT thermostat (part III)

Improving our Yocto based distribution

Intro

Previously we made ourselves a Linux distribution ourselves for our target embedded system. We included basic Qt5 support which allows us to create a fast and responsive C++ frontend. This time we will further develop our embedded system and prepare it for usage.

Adding QtQuick – QML support

In the previous article we succeeded in created a QtWidget based application. However, with QtQuick there is a new UI framework available which has its own set of benefits. You may already have noticed that QtCreator comes with lots of examples and you may have even tried some of them. However, if you (like me) created your Yocto based OS using the bitbake qt5-basic-image command you will find that some programs may not work when yo run them on your embedded device:

root@raspberryyocto:~# ./clocks
./clocks: error while loading shared libraries: libQt5Quick.so.5: cannot open shared object file: No such file or directory
root@raspberryyocto:~#

Basically we didn’t include support for QtQuick when we compiled our OS. So if you’re into using QtQuick go back through your Yocto working folder and bitbake the qt5-image:

geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/build$ bitbake qt5-image
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:08
NOTE: Executing SetScene Tasks
NOTE: Executing RunQueue Tasks
NOTE: Tasks Summary: Attempted 3619 tasks of which 3538 didn't need to be rerun and all succeeded.
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/build$

Notice how fast compiling the image goes this time compared to when we build the qt5-basic-image. The reason for this faster build time is because the qt5-image inherits the qt5-basic-image and Yocto just needed to recompile some of the components that were not yet compiled. Also note that you don’t need a new SDK and you shouldn’t need to re-configure QtCreator.

With your SD card already formatted you only need to copy the compiled files to your SD card:

geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/build$ cd ../meta-rpi/scripts/
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$ sudo umount /dev/mmcblk0p1
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$ sudo umount /dev/mmcblk0p2
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$ export OETMP=/media/geoffrey/Data/yocto-pi/rpi/build/tmp
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$ ./copy_rootfs.sh mmcblk0 qt5 raspberryyocto

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

File found: /media/geoffrey/Data/yocto-pi/rpi/build/tmp/deploy/images/raspberrypi/qt5-image-raspberrypi.tar.xz

Block device not found: /dev/mmcblk02, trying p2

Formatting /dev/mmcblk0p2 as ext4
[sudo] wachtwoord voor geoffrey:
/dev/mmcblk0p2 bevat een ext4-bestandssysteem met label 'ROOT'
laatst aangekoppeld op / op Sun Feb 19 22:07:51 2017
Toch doorgaan? (j,n) j
Mounting /dev/mmcblk0p2
Extracting qt5-image-raspberrypi.tar.xz to /media/card
Writing raspberryyocto to /etc/hostname
Unmounting /dev/mmcblk0p2
Done
geoffrey@geoffrey-Dell-XPS-L502X:/media/geoffrey/Data/yocto-pi/rpi/meta-rpi/scripts$

With that done we need to have a QtQuick test application. If you haven’t yet tried on of the QtCreator’s QtQuick examples do so now. Get back to your embedded device, insert the SD card and boot. Once booted, retrieve the device’s IP address and copy over any QtQuick test application. I’ve used the clocks application. Once you have the application compiled and copied over to the raspberry pi, ssh into your pi and start the application (or launch it from within QtCreator if you’ve had it still openened). You should see more or less something like this:

qtquick-demo-clocks-small

If you happen to run into “out of memory” errors:

root@raspberryyocto:~# ./clocks
QML debugging is enabled. Only use this in a safe environment.
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).
JIT is disabled for QML. Property bindings and animations will be very slow. Visit https://wiki.qt.io/V4 to learn about possible solutions for your platform.
glGetError 0x505
QSGTextureAtlas: texture atlas allocation failed, out of memory

… then you should tweak the CPU/GPU memory allocation settings. This settings is loaded at boot and it is also saved in a config file on your boot partition. To adjust the config file we first need to mount the boot partition, and next we can edit the file using the vi editor:

root@raspberryyocto:~# mkdir /mnt/fat
root@raspberryyocto:~# mount /dev/mmcblk0p1 /mnt/fat
root@raspberryyocto:~# vi /mnt/fat/config.txt

Add gpu_mem=256 to this file and reboot your pi. Try again running the clocks application, things should go now as intended:

root@raspberryyocto:~# ./clocks
QML debugging is enabled. Only use this in a safe environment.
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).
JIT is disabled for QML. Property bindings and animations will be very slow. Visit https://wiki.qt.io/V4 to learn about possible solutions for your platform.

Setting the timezone

To show the current time we will rely on NTP. RaspberryPi does not come with a RTC see I don’t see any other option. NTP is already added via Yocto, we only have to set the correct timezone:

root@yoctopi:~# ls -l /etc/localtime
lrwxrwxrwx 1 root root 27 Apr 10 18:36 /etc/localtime -> /usr/share/zoneinfo/EST5EDT
root@yoctopi:~# rm /etc/localtime
root@yoctopi:~# ln -s /usr/share/zoneinfo/Europe/Paris /etc/localtime
root@yoctopi:~# date
Sun Apr 16 12:30:40 CEST 2017

Adding a touch screen

The RaspberryPi community has a very decent 7″ touch screen. There is not much to say about, I got one, followed the instructions to hook it op and basically started testing some my applications straight away!

2033-01

Side note: I used the same housing as show above. This one has the display upside down, the rotate the display through software we must again edit the config file on the boot partition:

root@raspberryyocto:~# mkdir /mnt/fat
root@raspberryyocto:~# mount /dev/mmcblk0p1 /mnt/fat
root@raspberryyocto:~# vi /mnt/fat/config.txt

and add:

display_rotate=2

Restart to apply your changes.

Adding a HTU21D I²C temperature sensor

The HTU21D is a decent temperature and humidity sensor which perfectly suits our needs. To hook it up to our Raspberry Pi 2:

htu21d-block-s

Before we can use the I2C bus we must again edit the Pi’s config file:

root@raspberryyocto:~# mkdir /mnt/fat
root@raspberryyocto:~# mount /dev/mmcblk0p1 /mnt/fat
root@raspberryyocto:~# vi /mnt/fat/config.txt

and add:

dtparam=i2c_arm=on

Restart to apply your changes.
After the system has booted into Linux again we will first check if the i2c_bcm2708 module has been loaded:

root@yoctopi:~# lsmod
Module                  Size  Used by
ipv6                  350447  28
i2c_dev                 6115  2
evdev                  11396  1
joydev                  8960  0
bcm2835_gpiomem         3036  0
i2c_bcm2708             4834  0
bcm2835_wdt             3225  0
rpi_ft5406              4612  0
uio_pdrv_genirq         3164  0
rpi_backlight           2064  0
uio                     8128  1 uio_pdrv_genirq

Now we must add the i2c device by doing:

root@yoctopi:~# touch /etc/modules
root@yoctopi:~# echo 'i2c-dev' >> /etc/modules

Reboot again. When all went good we can now use the ic2detect tool to see if the Pi is able to communicate with our temperature sensor:

root@yoctopi:~# i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

With everything setup and good to go I can now start developing my application. Stay tuned for more!