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