6LoWPAN on a Raspberry Pi

The aim is to get 6LoWPAN running on a Raspberry Pi using a Raspberry Pi 802.15.4 radio from openlabs (based on the Atmel at86rf233) so we can later use it as a e.g. border router.

Raspberry Pi with 802.15.4 radio

The default Raspberry Pi kernel used to lack support for recent wpan-tools. So we cross build a kernel and boot it via tftp. Newer kernels have basic support so you only need to do this if you want to hack on the kernel's 6LoWPAN parts.

In the following we assume the Raspberry Pi is at IP address 192.168.1.2 and does DHCP during boot. It has been tested on a Raspberry Pi B+, the model 2 and model 3 Pis have issues. If you get it working on these, let me know.

This is mostly a combination of information from different sites like linux-wpan, openlabs and eLinux.org. For details head over to these sites. They have more details and less errors.

The source code of this page is available here:

https://honk.sigxcpu.org/gitweb/?p=piki.git;a=summary

Send fixes.

Setup the build environment and the RPi

Since the kernel build would take a long time on the Raspberry Pi we're using a Debian System to crossbuild the kernel.

Prepare cross toolchain for Debian Jessie + Sid

If you're running a RPi 2 you should be able to use the armhf toolchain provided by Debian already.

$ dpkg --add-architecture armhf
$ apt-get update
$ apt-get install make ncurses-dev gcc-arm-linux-gnueabihf build-essential

$ arm-linux-gnueabihf-gcc -v
Using built-in specs.
COLLECT_GCC=arm-linux-gnueabihf-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabihf/4.9/lto-wrapper
Target: arm-linux-gnueabihf
Configured with: ../src/configure -v --with-pkgversion=' 4.9.2-10' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,fortran --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libitm --disable-libquadmath --enable-plugin --with-system-zlib --enable-multiarch --disable-sjlj-exceptions --with-arch=armv7-a --with-fpu=vfpv3-d16 --with-float=hard --with-mode=thumb --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=arm-linux-gnueabihf --program-prefix=arm-linux-gnueabihf-
Thread model: posix
gcc version 4.9.2 ( 4.9.2-10)

I don't have a Model 2 yet so I we have to grab the toolchain externally.

Linaro

For the Raspberry Pi 1B and 1B+ use the Linaro toolchain:

$ cd ~
$ git clone https://github.com/raspberrypi/tools
$ export PATH=~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin:$PATH

If you clone to another location be sure to adjusts the PATHs below too.

The Linaro toolchain is 32bit and 64bit

Lots of the examples are using the 32bit one so lets use this too:

$ apt-get install lib32stdc++6 lib32z1
$ arm-linux-gnueabihf-gcc -v
Using built-in specs.
COLLECT_GCC=arm-linux-gnueabihf-gcc
COLLECT_LTO_WRAPPER=/home/agx/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/../libexec/gcc/arm-linux-gnueabihf/4.8.3/lto-wrapper
Target: arm-linux-gnueabihf
Configured with: /cbuild/slaves/oorts/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/src/gcc-linaro-4.8-2014.01/configure --build=i686-build_pc-linux-gnu --host=i686-build_pc-linux-gnu --target=arm-linux-gnueabihf --prefix=/cbuild/slaves/oorts/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/install --with-sysroot=/cbuild/slaves/oorts/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/install/arm-linux-gnueabihf/libc --enable-languages=c,c++,fortran --disable-multilib --enable-multiarch --with-arch=armv6 --with-tune=arm1176jz-s --with-fpu=vfp --with-float=hard --with-pkgversion='crosstool-NG linaro-1.13.1-4.8-2014.01 - Linaro GCC 2013.11' --with-bugurl=https://bugs.launchpad.net/gcc-linaro --enable-__cxa_atexit --enable-libmudflap --enable-libgomp --enable-libssp --with-gmp=/cbuild/slaves/oorts/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --with-mpfr=/cbuild/slaves/oorts/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --with-mpc=/cbuild/slaves/oorts/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --with-isl=/cbuild/slaves/oorts/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --with-cloog=/cbuild/slaves/oorts/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --with-libelf=/cbuild/slaves/oorts/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --enable-threads=posix --disable-libstdcxx-pch --enable-linker-build-id --enable-plugin --enable-gold --with-local-prefix=/cbuild/slaves/oorts/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/install/arm-linux-gnueabihf/libc --enable-c99 --enable-long-long --with-float=hard
Thread model: posix
gcc version 4.8.3 20140106 (prerelease) (crosstool-NG linaro-1.13.1-4.8-2014.01 - Linaro GCC 2013.11)

Crossbuild an upstream kernel for the RPi

For 6LoWPAN the bluetooth-next tree is a good starting point since it contains the latest WPAN code (as of 4.0 you can also use Linus Tree for basic support):

$ git clone --depth=1 git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
$ export PATH=~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin:$PATH
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2835_defconfig
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- chrt -i 0 make -j 4

On the tftp server

Move the necessary files to the tftp server (${TFT_IP} being the address of your tftp server):

$ TFTP_IP=192.168.1.1
$ scp bluetooth-next/arch/arm/boot/zImage ${TFTP_IP}:/tftpboot/rpi.img
$ scp bluetooth-next/arch/arm/boot/dts/bcm2835-rpi-b-plus.dtb ${TFTP_IP}:/tftpboot/

If you're using another PI model copy the corresponding device tree (dtb) file.

U-Boot

We want to boot the kernel using U-Boot so we can conveniently swap out kernels using TFTP. This is mostly copied from http://elinux.org/RPi_U-Boot .

On the build host

Crossbuild U-Boot

$ git clone --depth=1 git://git.denx.de/u-boot.git
$ export PATH=~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin:$PATH
$ export CROSS_COMPILE=~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-
$ export USE_PRIVATE_LIBGCC=yes
$ cd u-boot
$ make rpi_defconfig
$ make -j4 -s

In case you're on a RPI2 use rpi_2_defconfig instead of rpi_defconfig.

On the RPi

Now configure the RPi to use the just built U-Boot (${PI_IP} being the address of your Raspberry Pi). First on copy U-Boot to the Pi:

$ PI_IP=192.168.1.2
$ scp u-boot.bin root@${PI_IP}:/boot/u-boot.bin

and on the Raspberry Pi boot it by default:

$ echo "kernel=u-boot.bin" >> /boot/config.txt
$ poweroff

First Boot

(Again mostly from http://elinux.org/RPi_U-Boot). Attach to the serial console of your rpi:

$ screen /dev/ttyUSB0 115200

This assumes you're using a USB-serial adapter that shows up as /dev/ttyUSB0. Then power on the RPi and once U-Boot starts do a

$ setenv fdtfile bcm2835-rpi-b-plus.dtb
$ setenv bootargs earlyprintk console=ttyAMA0 console=tty1 root=/dev/mmcblk0p2 rootwait
$ usb start
$ dhcp ${kernel_addr_r} rpi.img
$ tftp ${fdt_addr_r} ${fdtfile}
$ bootz ${kernel_addr_r} - ${fdt_addr_r}

If you're using a rpi 2 use bcm2836-rpi-2-b.dtb instead of bcm2835-rpi-b-plus.dtb.

Automate the boot

Copy the above commands into a file called boot.src and do a

sudo apt-get install uboot-mkimage
mkimage -A arm -O linux -T script -C none -n boot.scr -d boot.scr boot.scr.uimg

move that to /boot on your Raspberry Pi. Booting will from now on happen automatically.

6LoWPAN

Now that we can quickly change and build kernels lets move to the 6LoWPAN part.

Configuring the kernel and patching the DTS

In order to enable support for the at86rf233 and 802.15.14 radio we need to change some kernel configuration. Since the board is connected vie SPI we change the DTS so it gets properly detected. This is readily done in my wpan-next (based on Alexanders repo) git repo which you can add to the bluetooth-next repo you alread cloned above:

$ git remote add agx git://github.com/agx/linux-wpan-rpi-next.git
$ git fetch rpi-6lowpan
$ git checkout -b rpi-6lowpan -b agx/rpi-6lowpan

Now lets rebuild DTS and kernel

$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2835_defconfig
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- chrt -i 0 make -j 4
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make dtbs

Transfer the files to the TFTP server again as described above.

Build the linux-wpan tools

Head over to your RPi. We build the wpan-tools there since they're small:

$ git clone https://github.com/linux-wpan/wpan-tools
$ sudo apt-get install automake autotools-dev libltdl-dev libnl-genl-3-dev
$ cd wpan-tools && ./autogen.sh && ./configure && make

Now we have all the tools:

$ src/iwpan list
wpan_phy phy0
supported channels:
        page 0: 11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26
        current_page: 0
        current_channel: 13
        cca_mode: 1
        tx_power: 0

Make sure there's no ifplugd that brings the interface up. Kill it, if it's there. Then make sure the interface is down, set a pan_id and and add create the 6LoWPAN interface:

$ kill $(ps awux | grep "[i]fplugd.*wpan0" | awk '{print $2}')
$ ifconfig wpan0 | grep -qs UP && ifconfig wpan0 down
$ src/iwpan dev wpan0 set pan_id 0xbeef
$ ip link add link wpan0 name lowpan0 type lowpan
$ ifconfig wpan0 up
$ ifconfig lowpan0 up

You can now (hopefully) ping the other device:

$ ping6 fe80::f836:9287:905c:a684%lowpan0
PING fe80::f836:9287:905c:a684(fe80::f836:9287:905c:a684) from fe80::cb23:b779:742d:4fd2 lowpan0: 56 data bytes
64 bytes from fe80::f836:9287:905c:a684: icmp_seq=4 ttl=64 time=16.6 ms
64 bytes from fe80::f836:9287:905c:a684: icmp_seq=11 ttl=64 time=30.6 ms
64 bytes from fe80::f836:9287:905c:a684: icmp_seq=12 ttl=64 time=16.3 ms
--- fe80::f836:9287:905c:a684 ping statistics ---
67 packets transmitted, 8 received, 88% packet loss, time 66025ms

The packet loss reduces when moving the devices closer together. In order to permanently disable ifplugd you can edit /etc/default/ifplugd.

Upstream info on wpan-tools is at http://wpan.cakelab.org/