Dans cet article nous allons explorer quelques solutions open source afin de mettre en place un réseau pour objets connectés. En voici un exemple simple : la mise en place d’une boîte à meuh connectée. Notre objet connecté aura pour rôle d’envoyer une requête au cloud à chaque fois qu’il détecte un changement d’orientation grâce à son accéléromètre. Cette requête déclenchera ensuite un événement de lecture d’un son sur notre téléphone portable.
Matériels utilisés
Sensortag
Notre objet connecté sera un SensorTag CC2650 de Texas Instrument. Le SensorTag est composé d’un microcontrôleur sans fil ARM Cortex M3 SimpleLink™ CC2650. Il dispose d’une connectivité Bluetooth Low Energy et 802.15.4. Il comprend aussi plusieurs capteurs micro-électromécanique (mouvement, température, humidité, lumière ambiante …).
http://www.ti.com/tool/cc2650stk
http://www.ti.com/product/CC2650
Raspberry Pi
Nous utiliserons aussi une Raspberry Pi B pour servir de passerelle entre notre réseau d’objets connectés et Internet.
802.15.4 et 6LoWPAN
Le 802.15.4 est un protocole de communication de niveau liaison défini par l’IEEE. Il est destiné aux réseaux sans fil de la famille des LR WPAN (Low Rate Wireless Personal Area Network) du fait de leur faible consommation, de leur faible portée et du faible débit des dispositifs utilisant ce protocole. 802.15.4 propose comme fonctionnement une topologie en étoile, maillée et en arborescence.
6LoWPAN peut être vu comme une couche d’adaptation entre les couches liaison et réseau du modèle OSI. Ce protocole va permettre aux réseaux à faible consommation énergétique tel que le 802.15.4 de communiquer avec des réseaux traditionnels en IPv6.
6LoWPAN cherche à résoudre deux principaux problèmes :
- Sur un réseau 802.15.4 la taille d’un paquet est limitée à 127 octets alors qu’IPv6 impose une taille d’au moins 1280 octets.
- La taille de l’en-tête IPv6 est élevée (au moins 40 octets), et si on ajoute à celle-ci la taille et l’en-tête MAC 802.15.4 (20 octets), de la sécurisation (23 octets), et de TCP ou UDP (respectivement 20 et 8 octets), il reste très peu de place aux données utiles.
6LoWPAN ajoute un système de fragmentation/ré-assemblage des paquets IPv6 et un mécanisme de compression de l’en-tête pour répondre aux problèmes posés par les réseaux LowPAN.
https://fr.wikipedia.org/wiki/IEEE_802.15.4
https://fr.wikipedia.org/wiki/6LoWPAN
Lorsqu'on souhaite connecter un réseau 6LoWPAN à un réseau IPv6, il est nécessaire d’avoir un équipement intermédiaire appelé routeur de bordure qui va se charger de transformer et router les trames d’un monde à l’autre. Pour notre exemple, nous utiliserons une Raspberry Pi pour jouer ce rôle.
Contiki
Contiki est un système d’exploitation libre sous licence BSD, spécialement conçu pour les capteurs miniatures ayant peu de ressources disponibles. Un des points forts de Contiki est son composant µip qui est une implémentation de la pile TCP/IP. Elle supporte IPv4, IPv6, UDP, TCP, ICMP et bien évidemment 6LoWPAN. Contiki peut s’exécuter sur un large panel de plateformes, et notamment sur le CC2650 de notre SensorTag.
6lbr
6lbr est un fork de Contiki spécialisé dans la mise en place d’un routeur de bordure entre un réseau IP et 6LoWPAN. La particularité de 6lbr est qu’il est possible de le faire fonctionner comme un routeur autonome sur un matériel embarqué grâce à sa parenté avec Contiki, ou dans un processus sur n’importe quelle distribution Linux.
6lbr peut être utilisé en mode pont ou routeur et le choix du mode de fonctionnement dépendra des situations et de la topologie réseau voulue.
Mode pont
Dans ce mode, 6lbr va agir comme un pont intelligent permettant d'interconnecter un réseau standard basé sur IPv6 avec un réseau de capteurs sans fil en maillage basé sur 6LoWPAN. Les nœuds du réseau de capteurs sont vus comme si ils appartenaient au réseau IPv6 préexistant.
C’est le mode à privilégier si on souhaite intégrer nos capteurs dans un réseau IPv6.
Mode Routeur
Dans ce mode 6lbr va agir comme un routeur IPv6 à part entière, reliant deux sous-réseaux IPv6. Il va gérer d’un côté un réseau IPv6 et de l’autre un réseau 6LoWPAN. Ces deux réseaux auront des préfixes IPv6 différents.
Ce mode est à privilégier si vous souhaitez isoler les capteurs dans des réseaux à part, pour les identifier plus facilement.
Pour plus de renseignements sur les modes de fonctionnement de 6lbr, consulter le wiki du projet 6lbr.
Exemple "Boîte à meuh"
Nous allons mettre en œuvre les technologies présentées dans une démo.
Nous aurons un capteur qui exécutera Contiki et une application simple sur un Sensortag. Le but de cette application est d’envoyer une requête au service Web IFTTT à chaque fois que le capteur est retourné. On nommera cette application meuh.
Sur IFTTT nous connecterons ensuite l’événement meuh avec une notification sonore.
Pour terminer, nous créerons une distribution Yocto embarquant 6lbr pour transformer une Raspberry Pi en routeur de bordure. Notre routeur permettra à l’application meuh d’avoir accès à internet et donc au service IFTTT. Le routeur utilisera un Sensortag configuré en antenne SLIP pour communiquer avec le réseau 802.15.4.
A la fin des manipulations, nous devrions obtenir la topologie réseau suivante:
Application Meuh
Sur le capteur, nous allons mettre en place une petite application pour Contiki, qui, lorsque le capteur est retourné sur l’axe Z, va envoyer une requête HTTP à IFTTT.
Pour commencer, nous avons besoin de récupérer les sources de Contiki :
git clone https://github.com/contiki-os/contiki.git cd contiki git submodule init git submodule update
Dans un répertoire à coté, nous allons créer notre application.
git clone https://github.com/Openwide-Ingenierie/contiki-boite-a-meuh.git cd contiki-boite-a-meuh
#include "contiki.h" #include "contiki-net.h" #include "board-peripherals.h" #include "http-socket/http-socket.h" #include "ip64-addr.h" /* Utilisé pour afficher les adresses IP sur la liaison série PRINT6ADDR */ #define DEBUG DEBUG_PRINT #include "net/ip/uip-debug.h" #define SENSOR_READ_PERIOD (CLOCK_SECOND) /* URL du service Maker de IFTT */ #define IFTTT_MAKER_URL "http://maker.ifttt.com/trigger/meuh/with/key/" #define IFTTT_URL IFTTT_MAKER_URL MAKER_KEY /* https://github.com/contiki-os/contiki/wiki/Processes */ /* On déclare le processus bam (boite à meuh) */ PROCESS_NAME(bam_process); PROCESS(bam_process, "\"Boite a meuh\" Process"); /* on veut que le processus bam soit lancé automatiquement au démarrage du capteur */ AUTOSTART_PROCESSES(&bam_process); /* Stucture Contiki permettant de manipuler les socket HTTP*/ static struct http_socket http; void meuh(void) { /* envoi de la requête au serveur IFTTT signalant que le capteur à été retourné */ http_socket_get(&http, IFTTT_URL, 0, 0, NULL, NULL); printf("MEUH\n"); http_socket_close(&http); } static void _accelerometer_handler(void) { static int z = 0; int val = 0; /* On récupère la valeur de l'axe Z de l'accéléromètre */ val = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_ACC_Z); /* si elle a changé de signe par rapport à la précedente lecture on meugle */ if (val < 0 && z >= 0) { meuh(); z = val; } else if (val >= 0 && z < 0) { meuh(); z = val; } /* On réactive les capteurs */ mpu_9250_sensor.configure(SENSORS_ACTIVE, MPU_9250_SENSOR_TYPE_ALL); } PROCESS_THREAD(bam_process, ev, data) { /* Structure la manipulation des timers dans Contiki*/ static struct etimer et; uip_ip4addr_t ip4addr; uip_ip6addr_t ip6addr; PROCESS_BEGIN(); printf("The \"Boite à Meuh\" ipv6 ready\n"); printf("Looking for IP addr\n"); /* transformation de l'adresse du dns google en ipv6. */ /* On utilise l'adresse ipv4 car le service IFTTT ne dispose pas d'ipv6 */ /* donc on veut que DNS nous donne des adresses ipv4. */ uip_ipaddr(&ip4addr, 8,8,8,8); ip64_addr_4to6(&ip4addr, &ip6addr); /* Mise a jour du dns de uip */ uip_nameserver_update(&ip6addr, UIP_NAMESERVER_INFINITE_LIFETIME); /* on arme un timer de 1s */ etimer_set(&et, CLOCK_SECOND); /* initialisation de la structure permettant de faire des requête http */ http_socket_init(&http); /* activation des capteurs du Sensortag */ mpu_9250_sensor.configure(SENSORS_ACTIVE, MPU_9250_SENSOR_TYPE_ALL); while(1) { /* Le timer a expiré */ if(ev == PROCESS_EVENT_TIMER && etimer_expired(&et)) { /* récuperation de l'adresse ip du capteur*/ uip_ds6_addr_t *addr = uip_ds6_get_global(ADDR_PREFERRED); /* Si on est pas encore configuré on réarme le timer*/ if( addr == NULL) { etimer_set(&et, CLOCK_SECOND); } /* Sinon on affiche l'adresse */ else { printf("New Address : "); PRINT6ADDR(&addr->ipaddr); printf("\n"); } } /* les valeurs des capteurs ont été mises à jour */ if (ev == sensors_event && data == &mpu_9250_sensor) { /* on lit les valeur et on meugle si besoin */ _accelerometer_handler(); } /* on rend la main au autre processus */ PROCESS_YIELD(); } PROCESS_END(); }
Avant de compiler l’application, nous devons récupérer la clé du service Maker associé à notre compte IFTTT.
Pour compiler l'application :
make CONTIKI=<racine du répertoire contiki> TARGET=srf06-cc26xx BOARD=sensortag/cc2650 MAKER_KEY=<clé service Maker >
Ensuite, nous flashons le firmware qui vient d’être produit avec TI Uniflash sur le Sensortag.
Si on veut qu’un son soit joué sur notre téléphone, il faudra en plus installer l’application pour smartphone IFTTT et rajouter le fichier meuh.mp3 à notre bibliothèque musicale. Sur la page de IFTTT, nous ajoutons la recette à notre compte utilisateur.
Radio SLIP
La radio SLIP est utilisée pour que notre routeur de bordure, qui est une Raspberry Pi, puisse communiquer avec le réseau 802.15.4. On va utiliser une application fournie en exemple dans Contiki. Cette application lit sur le port série des paquets à l’aide du protocole SLIP (Serial Line Internet Protocol) et les transmet sur l’interface 802.15.4. Nous brancherons ensuite notre Sensortag embarquant cette application sur un des ports USB de notre Raspberry Pi.
Retourner dans le répertoire de Contiki et faire :
cd examples/ipv6/slip-radio/ make TARGET=srf06-cc26xx BOARD=sensortag/cc2650
Une fois la compilation terminée, on obtient le firmware (slip-radio.hex) permettant d’utiliser notre Sensortag en tant que SLIP radio. On le flash de la même manière que l’application meuh sur un autre SensorTag et ensuite, on le branche en USB sur le Raspberry Pi.
Routeur de bordure
Création de l'image avec Yocto
Pour la création de notre routeur de bordure, nous commençons par récupérer les dépôts git du projet Yocto et OpenEmbedded.
git clone -b krogoth git://git.yoctoproject.org/poky.git border-router cd border-router git clone -b krogoth git://git.openembedded.org/meta-openembedded git clone -b krogoth git://git.yoctoproject.org/meta-raspberrypi git clone https://github.com/Openwide-Ingenierie/meta-6lbr.git
Maintenant que nous avons l’ensemble des sources permettant de générer notre image Yocto, il faut positionner l’environnement à l’aide du script oe-init-build-env.
source oe-init-build-env br_build
Nous allons ensuite éditer le fichier conf/bblayers.conf pour renseigner les layers nécessaires à la création de notre image.
METAPATH = "${@os.path.dirname(d.getVar('TOPDIR', True))}" BBLAYERS ?= " ${METAPATH}/meta ${METAPATH}/meta-poky ${METAPATH}/meta-yocto-bsp ${METAPATH}/meta-raspberrypi ${METAPATH}/meta-openembedded/meta-oe ${METAPATH}/meta-openembedded/meta-networking ${METAPATH}/meta-openembedded/meta-python ${METAPATH}/meta-6lbr "
La prochaine étape est de configurer l’image en ajoutant les logiciels utiles au fonctionnement de notre routeur. On va éditer le fichier conf/local.conf.
# notre image est destinée à être exécuté sur la machine Raspberry Pi MACHINE = "raspberrypi" # Ajout du démon 6lbr à l’image IMAGE_INSTALL_append = "6lbr" #Le module tun permettant de créer des interfaces réseaux virtuelles sera chargé au démarrage KERNEL_MODULE_AUTOLOAD += "tun" # Le choisit systemd comme système d’initialisation VIRTUAL-RUNTIME_init_manager = "systemd" # On rajoute networkd resolved lors de la configuration de systemd pour gérer le réseau et la résolution des noms PACKAGECONFIG_append_pn-systemd = " networkd resolved" DISTRO_FEATURES_append = " systemd" CMDLINE_DEBUG=""
Enfin, la commande bitbake (équivalente à la commande make de Yocto) va nous permettre de générer notre image.
bitbake rpi-basic-image
Configuration
A ce stade, nous disposons d’une image installable sur une Raspberry Pi, mais cette image n’est pas configurée. Par exemple, les interfaces réseaux ne sont pas configurées et donc notre routeur ne pourra communiquer avec aucun réseau, ce qui est un peu dommage pour un routeur.
Nous allons créer une nouvelle couche (Layer) Yocto que nous allons appeler config, et dans laquelle nous allons ajouter une recette pour configurer le réseau Ethernet.
cd .. yocto-layer create config cd meta-config mkdir -p recipes-core/wired-init-systemd/files cd recipes-core/wired-init-systemd
Ajouter le fichier wired-init-systemd.bb contenant :
SUMMARY = "Configuration file to set up Ethernet interface" DESCRIPTION = "Used to configure an ethernet interface with systemd" LICENSE = "MIT" LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" SRC_URI = " file://COPYING file://wired.network " S = "${WORKDIR}" FILES_${PN} = "${sysconfdir}/systemd/network/wired.network " do_install () { install -d ${D}${sysconfdir}/systemd/network install -m 0755 ${WORKDIR}/wired.network ${D}${sysconfdir}/systemd/network }
Le fichier files/wired.network va nous permettre de configurer notre interface eth0 à l’aide de networkd.
[Match] Name=eth* [Network] DHCP=yes [DHCP] UseDomains=true
On retourne dans notre dossier br_build et on ajoute notre nouvelle Layer dans la configuration en ajoutant la ligne :
${METAPATH}/meta-config
On rajoute également la recette wired-init-systemd à notre image. Dans le fichier conf/local.conf :
IMAGE_INSTALL_append = "6lbr wired-init-systemd"
On relance la commande bitbake pour générer la nouvelle itération de l’image.
bitbake rpi-basic-image
Test de la distribution
La prochaine étape est de copier notre image sur un carte sd pour la tester sur notre Raspberry Pi.
sudo dd if=$PWD/tmp/deploy/images/raspberrypi/rpi-basic-image-raspberrypi.rpi-sdimg of=/dev/mmcblk0
On se connecte sur le port série du Raspberry Pi pour récupérer une invite de commande.
http://elinux.org/RPi_Serial_Connection
Une fois connecté au Raspberry Pi, on exécute la commande suivante pour récupérer l’adresse IPv6 de 6lbr.
cat /var/log/6lbr.ip 2a03:7220:8083:4700:212:4b00:7a8:ce85
Si on obtient aucun résultat, c’est que notre notre réseau ne gère peut être pas l’IPv6. On vérifie alors si c’est le cas en regardant si notre interface Ethernet a obtenu une adresse IPv6 lors du démarrage avec la commande ifconfig.
Dans le cas où notre réseau ne prend pas en charge l’IPv6, il faudra changer la configuration de 6lbr. En effet, celui-ci est configuré par défaut pour se comporter en bridge et donc, si notre réseau ne prend pas en charge l’IPv6, le mode bridge ne sera pas adapté.
Le fichier /etc/6lbr/6lbr.conf.example nous en apprendra plus sur la configuration de 6lbr. Le wiki du projet est aussi une bonne source d’informations.
Avec l’adresse de 6lbr, nous pouvons nous connecter au serveur web de configuration, embarqué dans l’application. Ce serveur nous permet d’obtenir des informations sur l’état de 6lbr, la topologie réseau des capteurs et d’avoir accès à une page de configuration.
Nous allons sur la page Configuration pour activer l’IP64 et le DHCP.
Cette configuration est indispensable car IFTTT n’est disponible qu’en IPv4. IP64 est un protocole qui permet d’interconnecter un réseau en IPv6 avec un réseau en IPv4. Enfin, DHCP va permettre à 6lbr de récupérer sa configuration IPv4 nécessaire à IP64.
Voilà maintenant le capteur devrait accéder à IFTTT et envoyer des requêtes HTTP.
https://www.youtube.com/watch?v=fbrHLVOBeig