Contiki NG, Linux WPAN und 6LoWPAN

mk16.de

Neulich bin ich in Wikipedia auf eine Liste von Betriebssystemen gestoßen und dort habe ich Contiki NG wiederentdeckt. Ich hatte schon früher gesehen, dass Contiki NG ein Betriebssystem für kleine Embedded Systeme ist, jedoch hatte ich nie Hardware, mit welcher ich es ausprobieren konnte. Contiki NG hat eine sehr schöne Dokumentation und in dieser habe ich gesehen, dass es 6LoWPAN unterstützt - ein IPv6-only low-rate low-power 2.4 GHz Netzwerk-Standard - als IPv6-Fan hat sich dies natürlich super angehört. 6LoWPAN wurde eigentlich für IoT-Geräte entwickelt, aber wer sagt, dass ich es nicht zum Spaß ausprobieren kann?

Hardware

Danach habe ich geschaut, auf welcher Hardware Contiki NG funktioniert und bin dabei auf NRF-Chips gestoßen, welche ich schon von Meshtastic kannte. Des Weiteren waren sie die einzigen günstigen, einfach Verfügbaren. Ich habe mich für den recht kompakten nRF52840 MDK USB Dongle entschieden, welchen es Online sogar mit Hülle gab.

Man sieht eine Art USB-Stick auf einer Holzoberfläche liegen. Es ist ein "M" als Logo draufgedruckt. Des Weiteren ist eine kleine Ausstanzung für einen Schlüsselanhänger zu sehen. Darüberhinaus sieht man einen kleinen Button sowie eine LED (bzw. das es einen grauen Punkt gibt, welcher leuchten kann). Der USB-Dongle ist weiß.

Des Weiteren habe ich natürlich auch geschaut, ob Linux 6LoWPAN unterstützt und siehe da - es ist sogar im Mainline Kernel. Die Linux Implementierung wird vom Linux WPAN Projekt angetrieben. Alles, was Linux braucht, ist eine unterstützte Hardware. Auf der Website des Projektes gibt es eine Liste von diesen mit Links zu Online Shops (viele davon führen zu 404-Seiten). Ich habe als Nächstes geschaut, welches es davon in USB-Format und möglichst günstig gibt. Ich habe mich daraufhin für ATUSB-Dongle entschieden - leider gab es dazu keine Hülle.

Man sieht eine Platine. An der rechts Seite ist ein USB-Anschluss. An der linken Seite kann man eine eingebaute Antenne in der Platine sehen. Auf der Platine sieht man zwei markante schwarze Chips. Die Platine selber ist grün.

Linux WPAN

Als Erstes ist er ATUSB-Dongle bei mir angekommen. Es erscheint automatisch ein wpan0-Interface mit einem MTU von 123 (und damit nicht IPv6-fähig). Auf der Website ist eine Anleitung, wie man davon ein lowpan0-Interface herleiten kann, welches mit einer MTU von 1280 auch IPv6 unterstützt. Um die Pakete trotzdem möglichst kleinzuhalten, wird eine Header-Komprimierung verwendet. Darüber hinaus werden, wenn nötig, die Pakete auf MAC-Ebene fragmentiert. Um also eine möglichst gute Performance zu erhalten, ist es dennoch empfohlen, die Pakete möglichst kleinzuhalten. Folgendes sind die Befehle, welche ich zum Konfigurieren verwendet habe:

sudo ip link set dev wpan0 down
sudo iwpan dev wpan0 set pan_id 0xbada
sudo iwpan phy phy0 set channel 0 26
sudo ip link set dev wpan0 up
sudo ip link add link wpan0 name lowpan0 type lowpan
sudo ip link set dev lowpan0 up

Dabei ist 0xbada die PAN ID (vergleichbar mit einer SSID beim WLAN) und 0 26 der Channel (Standard Channel bei Contiki NG).

Firmware

Ein Tag nach dem ATUSB-Dongle kam auch der nRF52840 MDK USB Dongle an. Dieser wird mit dem UF2-Bootloader ausgeliefert, welchen ich auch schon von Meshtastic kannte. Dieser ist recht einfach zu bedienen und schützt (zumindest teilweise) vor einem Briek des Gerätes. Um eine Firmware zu flashen, schaltet man mithilfe des Button das Gerät in den Bootloader Modus und mountet es als Dateisystem. Danach kann man einfach die Firmware (ausgeliefert als .uf2-Datei) auf das Gerät kopieren und die Firmware wird automatisch installiert.

Ich habe also wie in der Dokumentation beschrieben versucht Contiki NG zu kompilieren - jedoc ohne Erfolg: Meine GCC Version war zu neu. Contiki NG benötigt aktuell eine GCC Version von 10 (die aktuelle ist 13). Dazu gibt es auch bereits ein Issue. Glücklicherweise konnte man sich die GCC Version 10 installieren. Nachdem ich das Programm mit GCC 10 kompiliert hatte, erhielt ich eine .nrf-Datei. Die Makefile von Contiki NG unterstützten eigentlich auch das Hochladen der Firmware über USB, jedoch wird kein UF2-Bootloader, sondern das Gerät als serielle Schnittstelle erwartet. Da dies bei mir nicht der Fall war, musste ich die .nrf-Datei in eine .uf2-Datei umwandeln. Beim Versuch des Hochladens ist mir aufgefallen, dass Contiki NG im Build-Ordner eine .hex-Datei erzeugt. Diese lässt sich wiederum problemlos mit einem Python-Tool in eine .uf2-Datei umwandeln. Ich habe zum Erzeugen der .uf2-Datei also folgende Befehle ausgeführt:

make TARGET=nrf BOARD=nrf52840/dongle node.upload
uf2conv build/nrf/nrf52840/dongle/node.hex --family 0xADA52840 --convert --output node.uf2

Mein Programm habe ich dabei node genannt.

Quelltext

Contiki-NG liefert einige Beispiele mit. Ich wollte jedoch einfach nur ein Programm, mit welchem ich andere Knoten anpingen kann - ich habe aus den Beispielen also Folgendes verwendet: node.c:

#include "contiki.h"
#include "sys/log.h"

#define LOG_MODULE "Contiki-NG node"
#define LOG_LEVEL LOG_LEVEL_INFO

#include <stdbool.h>

/*---------------------------------------------------------------------------*/
PROCESS(node_process, "Contiki-NG node");
AUTOSTART_PROCESSES(&node_process);
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(node_process, ev, data)
{
    PROCESS_BEGIN();

    LOG_INFO("Contiki-NG node Started\n");

    while (true) {
        PROCESS_WAIT_EVENT();
    }

    PROCESS_END();
}
/*---------------------------------------------------------------------------*/

Dieses Programm startet lediglich das Betriebssystem und gibt einmallig beim Start Contiki-NG node Started aus.

project-conf.h:

#ifndef PROJECT_CONF_H
#define PROJECT_CONF_H

#define IEEE802154_CONF_PANID 0xbada

#define UIP_CONF_ND6_SEND_RA 0

#endif

In dieser Datei werden die Projekteinstellungen vorgenommen. In diesem Fall wollte ich, dass Contiki NG weder als radv-Client noch als radv-Server agiert und setze ich PANID entsprechend.

Makefile:

CONTIKI_PROJECT = node
all: $(CONTIKI_PROJECT)
CONTIKI=../..

include $(CONTIKI)/Makefile.dir-variables

MAKE_MAC = MAKE_MAC_CSMA
MAKE_ROUTING = MAKE_ROUTING_NULLROUTING

MODULES += $(CONTIKI_NG_SERVICES_DIR)/shell

include $(CONTIKI)/Makefile.include

In Contiki NG spielen Makefiles tatsächlich eine größere Rolle, da in ihnen auch verschiedene Projekteinstellungen vorgenommen werden. In diesem Fall habe ich eingestellt, dass nur die grundlegende MAC-Ebene verwendet werden soll und keine Erweiterung wie TSCH, 6TiSCH oder 6top, da Linux WPAN dies (noch) nicht unterstützt. Darüber hinaus erscheinen diese Erweiterungen sehr zentral, was einen Single Point of Failure erzeugt (siehe hier). Danach habe ich eingestellt, dass kein Routing-Protokoll verwendet wird. Für 6LoWPAN wird normalerweise RPL als Routing-Protokoll verwendet (auch von Linux unterstützt), jedoch scheint das Routing-Protokoll sehr zentral zu sein. Des Weiteren bietet Contiki NG ein Shell-Modul. Dies stellt später eine serielle Konsole bereit, wo ich verschiedene Aktionen ausführen kann.

Ping

Nachdem ich sowohl das Linux 6LoWPAN-Interface konfiguriert als auch Contiki NG geflasht hatte, habe ich eine serielle Konsole zu Contiki NG geöffnet:

#f4ce.369a.e71b.e9cf> hrelp
Available commands:
'> help': Shows this help
'> reboot': Reboot the board by watchdog_reboot()
ng.
'> mac-addr': Shows the node's MAC address
'> ip-addr': Shows all IPv6 addresses
'> ip-nbr': Shows all IPv6 neighbors
'> ping addr': Pings the IPv6 address 'addr'
'> routes': Shows the route entries
#f4ce.369a.e71b.e9cf> mac-addr
Node MAC address: f4ce.369a.e71b.e9cf
#f4ce.369a.e71b.e9cf> ip-addr
Node IPv6 addresses:
-- fe80::f6ce:369a:e71b:e9cf
#f4ce.369a.e71b.e9cf> ip-nbr
Node IPv6 neighbors: none

Danach habe ich versucht, über Contiki NG mein Linux Gerät anzupingen:

#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Timeout
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 39 ms
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 46 ms
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 15 ms
#f4ce.369a.e71b.e9cf> ping fe80::12e2:d5ff:ff00:463
Pinging fe80::12e2:d5ff:ff00:463
Received ping reply from fe80::12e2:d5ff:ff00:463, len 4, ttl 64, delay 23 ms
#f4ce.369a.e71b.e9cf> ip-nbr
Node IPv6 neighbors:
-- fe80::12e2:d5ff:ff00:463 <-> 10e2.d5ff.ff00.0463, router 128, state Reachable 

Bei meinen Tests erschien beim ersten Mal immer ein Timeout (vielleicht aufgrund der Neighbor Discovery?!). Danach funktioniert das Ping jedoch problemlos.

Andersherum - von Linux zu Contiki NG funktioniert der Ping auch problemlos:

$ ping -c 4 fe80::f6ce:369a:e71b:e9cf%lowpan0
PING fe80::f6ce:369a:e71b:e9cf%lowpan0 (fe80::f6ce:369a:e71b:e9cf%lowpan0) 56 data bytes
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=1 ttl=64 time=14.1 ms
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=2 ttl=64 time=11.2 ms
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=3 ttl=64 time=58.2 ms
64 bytes from fe80::f6ce:369a:e71b:e9cf%lowpan0: icmp_seq=4 ttl=64 time=17.0 ms

--- fe80::f6ce:369a:e71b:e9cf%lowpan0 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 11.226/25.143/58.241/19.218 ms