P7 Régulation temps réel sur réseau sans fil
Sommaire
- 1 Régulation temps réel sur réseau sans fil
- 1.1 Présentation du projet
- 1.2 Avancement du projet
- 1.3 Liens bibliographiques et documents
- 1.4 Archive GIT
- 1.5 Livrables
Régulation temps réel sur réseau sans fil
Présentation du projet
Les réseaux temps-réels réclament l'acheminement en temps contrôlé des données avec un grand niveau de confiance. L'utilisation de réseaux sans fil pose donc un grand problème dû à la difficulté d'être sûr de la délivrance des données dans un temps borné.
Liste des tâches
Le projet consistera en :
- Recherche bibliographique sur les réseaux sans fils temps-réel,
- Implémentation d’un protocole de communication avec des notions temps-réel, soit à partir de la littérature (si possible), soit développé par nos soins.
Les différents algorithmes seront développés sur cartes de développement STM32F4 avec un lien radio de type Zigbee, La délivrance de l’information par lien radio étant sujette à risque, éventuellement modification de l’algorithme de commande pour résister à une perte plus ou moins sévère d’information en provenance des capteurs.
Avancement du projet
Recherches bibliographiques et définition du sujet
Recherches
La première phase de notre projet consiste à réaliser des recherches bibliographiques sur les réseaux sans fils temps-réel. Pour partager les résultats de ces recherches, nous avons choisi d'utiliser Google Drive : ainsi, nous pouvons à tout moment consulter les documents déjà trouvés sur internet, en déposer de nouveaux, et partager tout cela avec nos tuteurs école. Le but de ces recherches est, notamment, de répondre aux questions suivantes : - Existent-ils des réseaux sans fils temps-réel ? - Si oui, lesquels ? Quelles sont leurs limites ? Comment fonctionnent-ils ? - Si non, quelles sont les difficultés connues et que l'on peut rencontrer lorsqu'on souhaite mettre en place un tel réseau ?
Malheureusement, nous n'avons pas eu beaucoup de résultats, que ce soit sur Internet ou encore à la bibliothèque universitaire. Ceci est simplement dû au fait que peu de travaux n'ont été effectué sur ce sujet. Est-ce parce que les technologies actuellement existantes ne le permettent pas ou parce que l'apport d'une solution à ce problème n'apporte pas grand-chose technologiquement parlant ? Le travail que nous réaliserons lors de ce projet permettra peut-être de répondre à cette question. Néanmoins, nous avons trouvé un article très intéressant qui propose un protocole temps réel pour un réseau linéaire (cf. rubrique « Liens Bibliographiques » : Proposition et validation formelle d'un protocole MAC temps reel pour reseaux de capteurs lineraires sans fils.pdf). Même s’il s’agit ici d’un protocole MAC et d’un réseau linéaire, cet article nous a permis de comprendre plusieurs concepts et de mieux appréhender la suite du projet.
Problématiques
Grâce aux résultats de nos recherches, nous avons défini 3 problématiques qu’il serait intéressant de traiter pendant ce projet : - le routage : mettre en place un routage "complexe" : non-linéaire et dynamique - le type de temps-réel : doit-on mettre en place un temps-réel dur ? - Que faire en mode dégradé ? (Cette question est liée à la problématique précédente)
D'autres questions pourraient être intéressantes à traiter comme la sécurité et la gestion de l'énergie. Toutefois, nous décidons de ne pas prendre en compte ces problématiques pour notre projet.
Méthode de travail
Tout au long de notre travail, nous mettrons l’accent sur l’aspect « gestion de projet ». En effet, il est fort possible que notre travail soit repris par une autre personne afin de le compléter. Le but est donc que cette personne puisse reprendre aisément le projet sans perdre trop de temps à tenter de comprendre ce que nous avons fait. Ainsi, nous avons décidé de faire des réunions régulières avec nos tuteurs. A la fin de chaque réunion, un compte-rendu sera édité et sera disponible sur ce wiki dans la rubrique « Documents ». Nous essaierons autant que possible d’être clair dans notre démarche, et nous proposerons des guides si cela s’avère nécessaire. Nous avons également mis en place un calendrier prévisionnel afin de nous imposer des échéances :
Tâche | Date | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2016 | 2017 | ||||||||||||||||||||||||
19/09 | 26/09 | 03/10 | 10/10 | 17/10 | 24/10 | 31/10 | 07/11 | 14/11 | 21/11 | 28/11 | 05/12 | 12/12 | 19/12 | 26/12 | 02/01 | 09/01 | 16/01 | 23/01 | 30/01 | 06/02 | 13/02 | 20/02 | |||
Recherche bibliographique sur les réseaux sans fils temps-réel | |||||||||||||||||||||||||
Définition des problématiques et d'une application | |||||||||||||||||||||||||
Recherches sur Riot, Contiki, RPL | |||||||||||||||||||||||||
Choix de l'OS et implémentation sur les cartes STM32F4 | |||||||||||||||||||||||||
1er test : discussion entre 3 nœuds sans notions de temps-réel | |||||||||||||||||||||||||
2eme test : ajout d'un nœud pour ajouter une contrainte de routage (statique) | |||||||||||||||||||||||||
Ajout des contraintes temps-réel (deadlines, priorités...) sans prendre en compte les fautes de transmission | |||||||||||||||||||||||||
Prise en compte des fautes de transmission |
Choix de l’application
L’application est importante car elle sera la vitrine du travail que nous réaliserons. Toutefois, nous ne souhaitons pas que celle-ci soit chronophage en terme de réalisation. Notre première idée était donc de faire de la détection de présence : lorsqu’une personne entre dans une pièce, nous allumons tout simplement une lampe. Toutefois, après réflexion, nous nous sommes rendus compte que cette application était trop simpliste. En effet, seuls deux états sont possibles (lampe éteinte ou allumée). Finalement, nous avons choisi l’application suivante : commander un robot en ligne droite. Il y aura donc une notion de régulation et un vrai problème en cas de dégradation de la transmission des données (il devra continuer à avancer en ligne droite même s'il ne reçoit plus les ordres). Détaillons un peu cette application : Nous définirons 3 modes : mode normal, mode dégradé et mode d’arrêt d’urgence. En mode normal, la transmission des données se fait normalement et le robot avance selon les ordres du donneur d’ordres. Si un ordre (c’est-à-dire une trame) parvient au robot après la deadline (celle-ci reste à définir), alors celui-ci entre dans le mode dégradé : il va alors continuer d’avancer à vitesse réduite. Le robot repasse en mode normal dès que 5 trames consécutives respectant la deadline parviennent au robot. Si ce n’est pas le cas, au bout de 30 secondes, le robot passe en mode arrêt d’urgence. La figure suivante résume tout cela :
Choix du matériel
Le robot sera constitué de deux servomoteurs à rotation continue. Nous souhaitons réaliser un protocole temps réel. Pour cela, il nous faut d’abord un système d’exploitation qui nous fournisse un tel support temps réel. Nous avons donc fait quelques recherches, notamment sur les OS Contiki, FreeRTOS et Riot. Le tableau suivant résume les différentes caractéristiques de ces OS :
Comme nous pouvons le voir, Riot est l’OS le plus intéressant pour nous : il est assez léger, propose un support temps réel et intègre différent support hardware, notamment la carte stm32f4discovery qui est la carte de développement que nous utilisons. De plus, Riot implémente le protocole RPL, un protocole de routage particulièrement adapté aux réseaux de capteurs et donc à notre projet. Le lien entre les différents nœuds du réseau est un lien radio de type Zigbee. Pour cela, Riot intègre plusieurs drivers de puces RF. Il va donc falloir développer une carte électronique à partir de l’une de ces puces pour créer notre module radio (émetteur/récepteur). Nous choisissons la puce AT86RF231. Il a donc fallu commander très rapidement les composants nécessaires à la conception de notre carte électronique, la date limite de dépôt des bons de commandes étant normalement dépassée. Voici la liste du matériel nécessaire :
Prise en main de RIOT
Pour prendre en main RIOT, nous avons décidé de réaliser le programme qui permet de faire le classique "Hello World" : le but étant ici d'allumer une LED. Ce programme a été très simple à réaliser grâce aux fonctions que propose RIOT. Ensuite, nous avons décidé d'ajouter une étape supplémentaire : faire clignoter une LED à l'aide d'un timer pour que nous apprenions à nous servir de ce dernier (cela pourrait être utile pour la suite du projet).
Clignotement de LED grâce aux timers
Comme nous avons pu le dire, nous nous sommes lors de cette semaine sur l'utilisation des timers : le but étant de faire clignoter deux des quatre LED de la carte STM32F4 avec un temps de clignotement différent pour chacune (1 seconde pour la première et 0.5 seconde pour la seconde). Le clignotement se fait donc en mode interruption. Pour plus de clarté, nous avons décidé d'utiliser un timer pour chaque LED (bien qu'il soit possible de n'utiliser qu'un timer). Finalement, le programme est assez simpliste, mais il a fallu un certain temps avant de trouver et de maîtriser les différentes fonctions qui permettent de faire le travail.
Mise en place d'un premier réseau utilisant RPL
Nous avons décidé de faire un premier test en mode native, c'est-à-dire que nous allons compiler RIOT et le faire tourner sur notre système en tant que processus. Le but de ce test est de mettre en place un réseau virtuel composé de 3 instances RIOT et qui implémente le protocole de routage RPL. Pour ce faire, nous avons utilisé l'exemple "gnrc_networking" fourni par RIOT qui permet, notamment, d'expérimenter la communication entre plusieurs instances RIOT.
Pour utiliser cet exemple, il faut d'abord créer 3 interfaces virtuelles tap (tap0, tap1 et tap2) ainsi qu'un bridge les connectant ensemble (tapbr0) : cela constitue un réseau virtuel que les instances RIOT pourront utiliser pour communiquer. RIOT propose un script qui permet de mettre en place cette configuration. Il suffit donc de taper la commande suivante :
$RIOT_PATH/dist/tools/tapsetup/tapsetup --create 3 (où RIOT_PATH correspond à la racine du dossier RIOT et 3 correspond au nombre d'interfaces souhaité)
Ensuite, nous compilons le code exemple "gnrc_networking" et nous ouvrons 2 autres terminaux (chaque terminal représentera un nœud du réseau). Dans chaque terminal, nous entrons la commande :
make term PORT=tap{0,1,2} (selon le terminal choisi)
Un shell RIOT se lance alors dans les trois terminaux. Il nous faut maintenant initialiser RPL. Nous choisissons le nœud tap0 comme nœud RPL root. Sur ce noeud, la commande ifconfig nous retourne :
ifconfig Iface 6 HWaddr: 8e:c0:50:34:31:6c MTU:1500 HL:64 RTR RTR_ADV Source address length: 6 Link type: wired inet6 addr: ff02::1/128 scope: local [multicast] inet6 addr: fe80::8cc0:50ff:fe34:316c/64 scope: local inet6 addr: ff02::1:ff34:316c/128 scope: local [multicast] inet6 addr: ff02::2/128 scope: local [multicast] inet6 addr: ff02::1a/128 scope: local [multicast] Statistics for Layer 2 RX packets 0 bytes 0 TX packets 2 (Multicast: 2) bytes 142 TX succeeded 2 errors 0 Statistics for IPv6 RX packets 0 bytes 0 TX packets 2 (Multicast: 2) bytes 114 TX succeeded 2 errors 0
Pour ce noeud, nous configurons une adresse IPv6 globale :
> ifconfig 6 add 2001:db8::1 success: added 2001:db8::1/64 to interface 6
Nous pouvons maintenant utiliser la commander suivante dans les trois terminaux afin d'initialiser RPL :
> rpl init 6 successfully initialized RPL on interface 6
Comme nous avons pu le dire, c'est le nœud utilisant tap0 qui est le nœud root. Nous le définissons ainsi grâce à la commande :
> rpl root 1 2001:db8::1
Nous avons désormais la possibilité de voir la configuration du nœud grâce à la commande rpl. Par exemple, pour le nœud root, nous avons le retour suivant :
rpl instance table: [X] parent table: [ ] [ ] [ ] instance [1 | Iface: 6 | mop: 2 | ocp: 0 | mhri: 256 | mri 0] dodag [2001:db8::1 | R: 256 | OP: Router | PIO: on | CL: 0s | TR(I=[8,20], k=10, c=1, TC=80s, TI=196s)]
Pour un des deux autres nœuds, nous avons :
rpl instance table: [X] parent table: [X] [ ] [ ] instance [1 | Iface: 6 | mop: 2 | ocp: 0 | mhri: 256 | mri 0] dodag [2001:db8::1 | R: 512 | OP: Router | PIO: on | CL: 0s | TR(I=[8,20], k=10, c=2, TC=0s, TI=1s)] parent [addr: fe80::8cc0:50ff:fe34:316c | rank: 256 | lifetime: 298s]
Nous avons donc construit un réseau virtuel implémentant le protocole de routage RPL, ce dernier comportant 1 nœud root et deux nœuds fils. Si on tente d'envoyer un message udp entre les deux fils par exemple, cela fonctionne. Cependant, si on supprime le nœud root, alors il n'est plus possible de communiquer entre les deux fils (ce qui est normal car le message devait d'abord passer par le root avant d'être redistribué au bon nœud).
Nous avons donc eu, grâce à cet exemple, une première approche de RPL. Il pourrait être intéressant d'implémenter une architecture différente (qui proposerait plusieurs routes possibles) afin de voir si on peut forcer un paquet à prendre telle ou telle route (ce qui nous aidera par la suite lorsqu'on souhaitera faire face à des problèmes de communications).
Pilotage d’un servomoteur et communication entre deux cartes STM32F4
Comme nous avons pu le dire, notre robot sera constitué de deux servomoteurs à rotation continue. Autrement dit, nous pouvons contrôler leurs vitesses mais pas leurs positions. Ils sont contrôlables grâce à un signal PWM. En attendant d’avoir un module radio opérationnel, nous avons tenté de faire communiquer deux cartes STM32F4 grâce à un UART. Le but de l’exemple est simple : lorsque l’on appuie sur le bouton de la première carte, un ordre est transmis à la seconde par l’UART et cette dernière active la rotation du moteur. Même si la communication ne se fera pas via UART, cet exemple nous a permis d’apprendre à contrôler les servomoteurs en passant par RIOT.
Conception du module RF
Afin de permettre la liaison sans fils de nos nœuds, nous sommes amenés à utiliser des modules de communication sans fils. Maintenant que nous avons déterminé le micro kernel (Riot-OS) que nous souhaitions intégrer sur notre microcontrôleur, il nous a été conseillé de choisir un module compatible (C’est-à-dire que que le driver de la puce gérant la radiofréquence doit être intégré).Lors de la réunion, après une rapide études des différentes puces gérées, nous avons choisi l’AT86RF231 car il présentait, dans sa datasheet, un schematic simple et une BOM pour la réalisation du module radio.
Réalisation du schematic sous Eagle
Pour la réalisation de cette carte, nous avons commencé par dessiner, sur Eagle, les différents packages nécessaires. Une fois cela réalisé, nous avons constaté une erreur dans notre commande. Il s’agissait de l’oscillateur à 16 Mhz qui ne correspondait pas et dont l’utilisation était différente. C’est une erreur connue mais que l’on devra corriger. Malgré cela, le schematic a pu être réalisé en intégrant un package d’oscillateur commun. Ensuite, concernant les entrées/sorties numériques de la carte, nous avons respecté le schéma ci-dessus et avons ajouté une entrée d’alimentation 3,3V et la masse.
Réalisation du routage
La réalisation du routage est la partie la plus technique pour cette carte. Le tramsmetteur radio AT86RF231 envoie les données modulées via les pins RFP et RFN. La bande utilisée pour les liaisons Zigbee est à 2,45 GHz ce qui correspond aux hyperfréquences. C’est pourquoi le bloc ci-dessous, constitué d'une antenne, d'un balun et de deux condensateurs, doit être étudié et dimensionné.
Pour cela rappelons quelques éléments de bases nécessaires à notre étude : *L’impédance d’entrée Ze d’un quadripôle est l’impédance équivalente qui, mise au borne d’un générateur, donne la même intensité et la même tension. *L’impédance de sortie Zs d’un quadripôle est l’impédance en série d’un générateur vu par la une résistance Ru en sortie. *Le principe fondamental de l’adaptation d’impédance est le suivant : en connectant sur une charge de résistance R, une ligne de transmission d'impédance caractéristique R, on retrouvera à l'autre extrémité de la ligne la même résistance R. Autrement dit, la source et la charge de résistance R seront « adaptées » si la ligne qui les relie possède une impédance caractéristique de même valeur. L'adaptation sera conservée quelle que soit la longueur de la ligne. *L’impédance caractéristique est la résistance vue par le générateur aux premiers instants de la transmission. Elle dépend uniquement des caractéristiques de la ligne. *Le coefficient de réflexion est un nombre sans dimensions qui indique la quantité d'énergie réfléchie en bout ou en début de ligne. Il est défini par une équation qui met en jeu l'impédance caractéristique de la ligne et l'impédance du bout de ligne ou du générateur. Il est compris entre -1 et +1 et égal à 0 si la ligne est adaptée. *Les feeders sont les lignes qui alimentent les antennes. Ils doivent véhiculer l'énergie de l'ampli final vers l'antenne dans le cas d'émission, avec un minimum d'onde réfléchie. Ils doivent donc avoir comme valeur d'impédance caractéristique la valeur de l'impédance de l'antenne.
L’antenne choisie est une antenne CMS et sera utilisée pour l’émission et la réception. Selon la datasheet, son impédance est de 50 Ohms et il de même pour l’impédance de sortie du balun. Pour adapter ces deux éléments, il nous faut une ligne d’impédance caractéristique 50 Ohms. Pour une ligne microstrip, l'impédance caractéristique dépend de ses dimensions et du matériau isolant. De nombreuse formules sont établies dans la littérature pour calculer cette impédance, nous avons choisi le modèle présent dans logiciel AppCAD.
Pour réaliser cette opération, il nous faut donc l’épaisseur t de la piste, l’épaisseur h du diélectrique ainsi que sa permittivité. A Polytech, il est possible de réaliser des cartes avec des pistes d'épaisseur 35 um avec un diélectrique verre-epoxy FR4 d'épaisseur 0,8 mm. Avec ces informations, nous trouvons une largeur de piste égale à 1,44 mm. Selon la datasheet, il était conseillé d’ajouter un guide d’onde coplanaire constitué de trous afin d'améliorer le transport de l'énergie jusqu'à l'antenne. De plus, l'ajout de vias permet d'atténuer l'effet capacitif des plans de masses situés de part et d'autres du diélectrique. Le rôle du balun est de transformer une impédance symétrique en impédance asymétrique et vice versa. Le balun réalise également une fonction d'adaptation d'impédance entre les ports RFP et RFN et l'antenne. Pour ne pas trop modifier l’impédance différentielle, nous avons décidé de réaliser des pistes de longueur faible et assez large mais surtout des lignes symétriques. Enfin les condensateurs à l'entrées des ports RFP/RFN sont utilisés pour supprimer la composante continue de l'entrée RF provenant de l'antenne.
La première demande de réalisation de la carte a été réalisé le 17/11/2016.
Problèmes rencontrés et résolution
La machine permettant de graver les cartes est tombée en panne. Nous avons donc subi un retard en attendant qu’elle soit remplacée. Nous avons ainsi pu amener plusieurs améliorations sur notre carte : par exemple, nous avons isolé la partie du circuit qui contient l’oscillateur afin d’éviter toutes formes de perturbations. Nous avons pu souder nos deux premières cartes le 15 et 16 décembre.
Pour pouvoir tester nos cartes, Riot propose un programme qui permet d’obtenir certaines informations à propos de la puce RF (comme l’identifiant qui est unique et définit par le vendeur). Pour pouvoir récupérer ces informations, il a fallu configurer Riot pour pouvoir avoir un retour via le port série, comme nous le verrons dans la partie suivante.
Affichage du terminal disponible sous RIOT
L'avantage de RIOT est la mise en place d'un shell permettant d’interagir avec un microcontrôleur de manière assez intuitive. Pour cela les développeurs ont choisi d'utiliser un script python pour l'utilisation dans un terminal. Afin de pouvoir l'utiliser correctement, il est impératif d'avoir installé le paquet python2.7. Ensuite dans ./RIOT/dist/tools/pyterm lancer les commandes suivantes afin d'installer les autres paquets nécessaires au bon fonctionnement de ce script:
./setup.py build ./setup.py install
Ce sera ensuite le script pyterm présent dans ce même dossier qui sera lancé dans la commande make suivante:
sudo BOARD=stm32f4discovery make all flash term
ou directement:
./RIOT/dist/tools/pyterm/pyterm -p /dev/ttyUSB0 -b 115200
Par défaut, le DPI est défini sur /dev/ttyUSB0 (ce paramètre est modifiable). De plus, on s'assurera de brancher les pins TX/RX aux pins PA2-3 de la stm32 configurées pour l'UART.
Tests du module radio
Nous avons ensuite réalisé les différents tests suivants :
- Vérification de toutes les soudures et de la présence de court-circuit. Ces tests ont été effectués à l'aide d'un ohmmètre afin de mesurer l'impédance entre les différentes pins. Le résultat de ce test a démontré un court-circuit au niveau des broches IRQ et XTAL2 placées perpendiculairement l'une de l'autre dans un coin de l'AT86RF231. Ce court-circuit est dû à une mauvaise gravure de la machine car le package de la puce était aux limites des capacités de celle-ci. Pour résoudre ce problème, il a fallu rompre ces court-circuits sur toutes les cartes.
- Vérification du fonctionnement de la carte via un programme de test. Le programme de test est intégré à RIOT et afin de tester l'interaction entre la carte STM32 et le module RF il suffit de modifier le fichier RIOT/drivers/at86rf2xx/include/at86rf2xx_params.h afin d'y indiquer les pins utilisés pour la communication.
#ifndef AT86RF2XX_PARAM_SPI #define AT86RF2XX_PARAM_SPI (SPI_0) // correspond au SP1 de la STM32F4 avec : SCK<->PA5 / MISO<->PA6 / MOSI<->PA7 #endif #ifndef AT86RF2XX_PARAM_SPI_SPEED #define AT86RF2XX_PARAM_SPI_SPEED (SPI_SPEED_5MHZ) // comme indiqué dans la datasheet ne pas dépasser 8MHz #endif // Choix arbitraire des pins correspondant à des GPIOs #ifndef AT86RF2XX_PARAM_CS #define AT86RF2XX_PARAM_CS (GPIO_PIN(PORT_D, 6)) #endif #ifndef AT86RF2XX_PARAM_INT #define AT86RF2XX_PARAM_INT (GPIO_PIN(PORT_E, 2)) #endif #ifndef AT86RF2XX_PARAM_SLEEP #define AT86RF2XX_PARAM_SLEEP (GPIO_PIN(PORT_D, 7)) #endif #ifndef AT86RF2XX_PARAM_RESET #define AT86RF2XX_PARAM_RESET (GPIO_PIN(PORT_D, 10)) #endif
Cependant à ce niveau les résultats du test étaient incohérents (registres contenant l'ID constructeurs différents de la datasheet, valeur de la puissance de sortie à 65125 dBm...)
- Vérification des tensions au bornes des pins /RST, MISO et /SEL Les tensions correspondent aux niveaux indiqués de tension dans la datasheet (environ 1,8V).
- Vérification des oscillations au niveau du Quartz aux pins XTAL1-2 de l'AT86RF231. A ce niveau aucune oscillation n'est détectée.
Sur la datasheet, il est indiqué que l'on peut injecter un signal externe afin de faire fonctionner l'at86rf231. Pour effectuer ce test nous avons dessoudé au préalable le quartz présent sur la carte. Nous avons ensuite introduit un signal de 16MHz avec une amplitude entre 400 et 500 mV comme indiqué. Nous relançons ensuite notre programme de test et obtenons enfin des résultats cohérents.
(photo)
Le problème est donc enfin détecté et se situe au niveau du quartz qui n'arrive pas à osciller. Après plusieurs recherches sur les quartz et grâce à l'expertise de Mr Flamen, nous sommes arrivés à la conclusion que les pins indiquant les bornes du quartz sur la datasheet étaient erronées. En effet, les pins que nous utilisions au préalable, formaient un court-circuit entres-elles. Comme les quartz sont, en général, des matériaux isolants de type céramique, la résistance mesurée par l'ohmmètre aurait dû être très grande. La réalisation d'une nouvelle carte a pu confirmer notre conclusion car nous sommes parvenus à observer les oscillations sur un oscilloscope, et obtenir des informations cohérentes grâce au programme de test. Nous avons donc pu au 17 janvier obtenir 3 modules RF fonctionnels.
Caractérisation du module radio
Par défaut, notre puce AT86RF231 a une fréquence de transmission de données de 250kb/s. A cette fréquence, sa sensibilité de réception est de -101dBm. Nous connaissons également le gain de notre antenne, qui est de -2dBi. Nous avons réalisé un test permettant de connaître, en pratique, la distance maximale avec laquelle 2 nœuds peuvent discuter. Pour cela, nous nous plaçons dans un couloir (en ligne droite) et nous tentons de placer les deux antennes l'une face à l'autre. Nous éloignons petit à petit l'un des nœuds jusqu'à perdre la communication puis nous mesurons la distance (avec un mètre). Nous programmons la puce avec une puissance d'émission maximale, soit 3dBm. Nous obtenons une distance maximale de 30 mètres environ.
Nous pouvons donc connaître, théoriquement, la perte en espace libre grâce à la formule suivante :
avec
- A : pertes en espace libre, exprimées en dB
- d : distance entre l'émetteur et le récepteur, exprimée en mètres
- : longueur d'onde du rayonnement, exprimée en mètres (ici égale à 0.122m car f=2.45GHz)
Pour 30 mètres, nous obtenons donc A = 69.8dBm.
Nous pouvons également calculer la marge restante à cette distance, pour savoir si, théoriquement, nous pourrions encore éloigner les deux nœuds :
Marge = Puissance d'émission + gain antenne émission - pertes en espace libre + gain antenne réception - sensibilité en réception = 3 - 2 - 69.8 - 2 + 101 = 30.2dB
Théoriquement, nous pourrions donc encore augmenter la distance entre les deux nœuds. Cependant, notre résultat peut s'expliquer par le fait que des interférences peuvent provenir d'autres sources (notamment le Wifi qui utilise la même fréquence que notre module), mais également du lieu de test (le couloir étant un espace assez guidé, des réfractions peuvent perturber le signal).
Mise en place du réseau
Intégration des différentes couches réseaux
Maintenant que les modules radio fonctionnent, la suite de notre projet consiste à mettre en place deux types de routage (statique et dynamique) et d'y intégrer des notions temps réel. Le système RIOT intègre un module de gestion des différents protocoles réseaux dédiés à l'internet des objets appelé GNRC network stack.
Nous utiliserons donc ce module pour réaliser notre application et découperons le développement de notre projet en commençant par la réalisation d'un routage statique entre trois noeuds puis l'intégration des notions temps réel pour la délivrance des différents paquets. Enfin nous nous pencherons sur le routage dynamique avec 4 à 5 noeuds en utilisant le protocole RPL et en gardant les notions temps réels développées auparavant.
Routage statique entre 3 noeuds
Afin de mieux prendre en main les outils dont on dispose, il serait intéressant de pouvoir utiliser l'exemple gnrc_networking sur la stm32f4 couplé avec un de nos modules radio. Il faut se rappeler que le système RIOT est modulaire et c'est là que l'on observe tout son potentiel. En effet, le simple fait de rajouter le nom de notre module dans le Makefile, présent dans le dossier de l'exemple, nous a permis d'avoir accès au shell depuis notre stm32 et de pouvoir utiliser notre module radio.
dans $RIOT/examples/gnrc_networking/Makefile # Include packages that pull up and auto-init the link layer. # NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present USEMODULE += gnrc_netdev_default USEMODULE += auto_init_gnrc_netif # Specify the mandatory networking modules for IPv6 and UDP USEMODULE += gnrc_ipv6_router_default USEMODULE += gnrc_udp # Add a routing protocol USEMODULE += gnrc_rpl USEMODULE += auto_init_gnrc_rpl # This application dumps received packets to STDIO using the pktdump module USEMODULE += gnrc_pktdump # Additional networking modules that can be dropped if not needed USEMODULE += gnrc_icmpv6_echo # Add also the shell, some shell commands USEMODULE += shell USEMODULE += shell_commands USEMODULE += ps USEMODULE += netstats_l2 USEMODULE += netstats_ipv6 USEMODULE += netstats_rpl USEMODULE += at86rf231
Plusieurs modules présents dans ce makefile sont inutile pour cette étape du projet mais pour rendre compte de la créations des différents couches réseaux nous pouvons observer avec l'outils ps les différents threads créés au lancement de l'OS sur la carte.
ps # > ps # pid | name | state Q | pri | stack ( used) | base | current # - | isr_stack | - - | - | 512 ( 140) | 0x20000000 | 0x200001c8 # 1 | idle | pending Q | 15 | 256 ( 132) | 0x20000588 | 0x20000604 # 2 | main | running Q | 7 | 1536 ( 720) | 0x2000068c | 0x200009bc # 3 | pktdump | bl rx _ | 6 | 1536 ( 236) | 0x200032f0 | 0x20003804 # 4 | 6lo | bl rx _ | 3 | 1024 ( 392) | 0x20003f2c | 0x2000421c # 5 | ipv6 | bl rx _ | 4 | 1024 ( 372) | 0x20001460 | 0x2000175c # 6 | udp | bl rx _ | 5 | 1024 ( 252) | 0x20004740 | 0x20004a44 # 7 | at86rf2xx | bl rx _ | 2 | 1024 ( 364) | 0x20000d7c | 0x20001084 # 8 | RPL | bl rx _ | 5 | 1024 ( 204) | 0x20003958 | 0x20003c8c # | SUM | | | 8960 ( 2812)
Cette exemple va nous donner l'accès aux différents outils qui vont nous permettre de réaliser notre routage statique.
- ifconfig, de la même manière que sous linux, nous permet de modifier les informations liées à l'interface réseau.
- fibroute, est une table qui permet de lier des adresses MAC à des adresses IP et ainsi, décrire un chemin vers lequel notre paquet doit passer
- pingv6, est un outil permettant de faire transiter des paquets ICMPv6 afin de faire un echo et voir si une adresse IPv6 est belle et bien atteignable
- udp, cette outil nous permettra de créer un serveur udp et d'envoyer des paquets via le protocole udp sur un port prédéfini.
Afin de réaliser notre premier réseau à route statique composé de trois noeuds, il a fallu décrire cette route au travers des fibroutes de chaque noeud. De plus, pour avoir une adresse routable, nous avons défini pour chaque noeud une adresse globale du type dead:beef::HWaddr.
#ifconfig # Iface 6 HWaddr: 34:02 Channel: 26 Page: 0 NID: 0x23 # Long HWaddr: 35:34:51:0e:33:17:34:02 # TX-Power: 0dBm State: IDLE max. Retrans.: 3 CSMA Retries: 4 # ACK_REQ CSMA MTU:1280 HL:64 6LO RTR IPHC # Source address length: 8 # Link type: wireless # inet6 addr: ff02::1/128 scope: local [multicast] # inet6 addr: fe80::3734:510e:3317:3402/64 scope: local # inet6 addr: ff02::1:ff17:3402/128 scope: local [multicast] # inet6 addr: ff02::1a/128 scope: local [multicast] # inet6 addr: dead:beef::3402/64 scope: global # inet6 addr: ff02::1:ff00:3402/128 scope: local [multicast]
Notre réseau statique à 3 noeuds doit se comporter comme un réseau linéaire avec un noeud central se chargeant uniquement de router les paquets vers la destination
Noeud_1[dead:beef::3402] -----> Noeud_2[dead:beef::3762] -----> Noeud_3[dead:beef::3766]
Noeud_1
# > fibroute # Destination Flags Next Hop Flags Expires Interface # dead:beef::3766 0x00000000 H fe80::3634:5110:3473:3762 0x00000000 NEVER 7 # dead:beef::3762 0x00000000 H fe80::3634:5110:3473:3762 0x00000000 NEVER 7
Noeud_2
# > fibroute # Destination Flags Next Hop Flags Expires Interface # dead:beef::3766 0x00000000 H fe80::3634:5110:3471:3766 0x00000000 NEVER 7
Cette configuration est un réseau linéaire unidirectionnel, permettant au donneur d'ordre (Noeud 1) d'envoyer des paquets à la cible (Noeud 3) via le Noeud 2. Afin de vérifier que nos paquets passaient bien par le noeud 2, nous avons effectué un ping depuis le noeud 1 à destination du noeud 3. Ensuite nous avons débranché le noeud 2 et observons que l'echo du ping ne fonctionne plus. Cette première expérience, nous a permis de prendre en main les différentes fonctions sous RIOT pour modifier la fibroute des noeuds et l'ajout d'adresses ip.
Maintenant, afin de nous rapprocher de notre application, nous allons mettre en place une communication UDP entre les noeuds 1 et 3. Pour cela, nous avons créer une socket (serveur) UDP sur le noeud 3, qui écoutera sur le port 1234 et une socket (client) sur le noeud 1 qui enverra des message toutes les secondes.( De plus, nous avons enrichi les fibroutes afin que la communication puisse être bidirectionnelle.) Sous RIOT, la création d'une socket UDP est assez facile, il suffit de créer un thread dédié pour le serveur (ou client), de créer la socket avec sock_udp_create puis, dans une boucle, d'attendre de recevoir des données avec sock_udp_recv et/ou d'envoyer des données avec sock_udp_send.
Une fois toutes ces briques misent en place, nous pouvons intégrer partiellement notre application, c'est à dire sans notions temps réel. Pour ce prototype, nous utiliserons des timers qui nous permettront, en mode dégradé et puis en mode arrêt d'urgence, de commander des pwms afin de contrôler la rotation des moteurs.
- Le client (Noeud 1) envoie des paquets UDP contenant les caractères "go" toutes les secondes sur le port 1234.
- Le serveur (Noeud 3) écoute sur le port 1234 et dans le cas où il reçoit "go" assigne l'ordre à 1, réinitialise le timer du mode dégradé et désactive le timer d'arrêt d'urgence
- Dans le cas où le serveur ne reçoit pas "go" après 5 secondes, le noeud entre dans le mode dégradé et l'ordre passe à 2, la pwm est ralenti donc le moteur tourne au ralenti
- Si 10 secondes plus tard le noeud 3 n'a pas reçu d'instruction "go" la fonction arrêt d'urgence est enclenché coupant la pwm et arrêtant les moteurs.
- Le retour au mode normal est possible car le thread de la socket serveur n'est pas arrêté
Aspect temps-réel
L'aspect temps-réel est essentiel dans notre sujet. Notre idée est la suivante : le nœud qui envoie les données intègre dans ces données l'heure à laquelle elles ont été envoyées. Le nœud qui les reçoit compare cette heure avec l'heure de réception pour en déduire le temps de transmission du paquet. Pour que ce système fonctionne, il faut synchroniser les horloges des nœuds de notre réseau. Pour cela, nous utilisons le protocole SNTP (Simple Network Time Protocol) : ce protocole permet de synchroniser, via un réseau informatique, l'horloge locale de nœuds sur une référence d'heure. Dans Riot OS, cette référence est 1900-01-01 00:00 UTC. Dans notre cas, nous choisissons le modèle client serveur : le premier nœud (celui qui donne les ordres) sera le serveur alors que le dernier nœud (celui qui commande les servomoteurs du robot) sera le client. Pour comprendre comment mettre en place ce protocole, nous avons utilisé ce schéma :
A l'instant T1 : lorsque le client émet son message pour interroger le serveur sur l'heure courante, il envoie un message dans lequel il renseigne le champ TT avec l'heure courante T1 indiquée par son horloge locale ;
A l'instant T'1 : lorsque le serveur reçoit le message, il complète aussitôt le champ RT du message avec l'heure courante T'1 indiquée par son horloge locale, et recopie le champ TT dans le champ OT ;
A l'instant T'2 : lorsque le serveur émet son message de réponse, il complète le champ TT du message avec l'heure courante T'2 indiquée par son horloge locale
A l'instant T2 :lorsque le client reçoit le message de réponse, il note aussitôt l'heure T2 de réception indiquée par son horloge locale.
Le client peut alors calculer le délai aller/retour de ces 2 messages ainsi que l'écart entre son horloge locale et celle du serveur grâce aux formules suivantes :
Riot OS intègre ce protocole mais ne propose pas de fonctions permettant de créer un serveur SNTP : nous avons donc développé celui-ci. Le décalage entre les deux horloges est maintenant connu. Lorsque le premier nœud envoie un ordre, il renseigne donc dans ce message d'ordre l'heure de son horloge locale. Lorsque le dernier nœud reçoit cette ordre, il note l'heure de réception selon son horloge locale et peut en déduire le temps de transmission de la façon suivante :
Nous avons alors décidé de récolter quelques données afin de voir l'influence de certains paramètres sur le temps de transmission. Ainsi, nous avons fait varier la longueur du paquet envoyé à distance fixe, puis nous avons fait varier la distance émetteur-récepteur pour une longueur de paquet fixe. Voici les résultats obtenus :
Comme nous pouvions le penser, la longueur du paquet a une influence sur le temps de transmission. On observe un saut à partir de 60 octets de données utiles (sans les en-têtes), mais globalement, cette évolution est linéaire. En revanche, la distance entre émetteur et récepteur, elle, n'a pas d'influence sur le temps de transmission du paquet : globalement, ce dernier est constant comme nous pouvons le voir. A noter que ces tests ont été réalisés entre deux nœuds. Si le réseau est constitué de N nœuds, alors pour avoir une idée du temps de transmission d'un paquet, il faut appliquer la formule suivante :
Maintenant que nous connaissons le temps de transmission d'un paquet, nous pouvons fixer une deadline cohérente avec notre réseau et programmer les 3 modes de fonctionnement.
Routage dynamique
Introduction au protocole RPL
Le protocole RPL a été introduit pour l'internet des objets afin de constituer des réseaux de nœuds à faible débits et pertes importantes. La base de ce protocole est la construction de graphiques acyclique DAG (permettant d'éviter les boucle). Tous les nœuds feuilles sont contenus dans des chemins terminant vers un ou plusieurs nœuds racines. Un Destination-Oriented DAG (DODAG), est un DAG avec une racine unique, le DODAG root. Une instance RPL peut contenir un ou plusieurs DODAG. Le rang défini pour chaque noeud traduit la distance avec le DODAG root, c'est à dire que plus un noeud est loin du DODAG root plus son rang sera élevé. L'intérêt de ce protocole est qu'il permet de réaliser un routage dynamique entre nos noeuds. Il peut ainsi définir pour un noeud quel parent lui serait le plus approprié ( selon des données traduisant la qualité de transmission/réception) pour réaliser une communication afin d'atteindre une destination. Un noeud peut donc avoir plusieurs parents permettant alors de choisir différents chemins possibles pour réaliser une communication entre deux noeuds distants.
Pour mieux comprendre ce protocole, il est intéressant de comprendre comment se forme un DODAG. L’interaction entre les différents noeuds se fait au travers de RPL Control Messages qui sont des messages ICMPv6. Il en existe 3 types dans ce protocole:
- DAG Information Object (DIO) - transmet des informations qui permettent à un nœud de découvrir une instance RPL, d'apprendre ses paramètres de configuration et de choisir des parents dans le DODAG
- DAG Information Solicitation (DIS) - sollicite un DIO à partir d'un noeud RPL
- Destination Advertisement Object (DAO) - utilisé pour propager les informations de destination vers le haut le long du DODAG.
Pour construire un DODAG, chaque noeud envoie periodiquement des messages DIO en multicast. De plus, chaque noeud peut utiliser des messages DIS pour soliciter un DIO. La fréquence d'envoi des messages DIO dépend de la stabilité ou de la détection de problème de routage. Les noeuds écoutent les messages DIO et utilisent ces informations pour joindre un DODAG. En se basant sur les informations contenu dans les DIOs, un noeud peut ainsi choisir ses parents et réduire au maximum le nombre d'intermédiaire avec le DODAG root.
Afin de reconstruire le DODAG lors de problèmes de connexions, RPL effectue des DODAG Repair.
Un DODAG root établit une opération de réparation globale en incrémentant le numéro de version du DODAG. Ceci déclenche une nouvelle version de DODAG. Les noeuds de la nouvelle version du DODAG peuvent choisir une nouvelle position dont le rang n'est pas limité par leur rang dans l'ancienne version du DODAG. Le message DIO spécifie les paramètres nécessaires tels que configurés et contrôlés par la stratégie du DODAG root.
Application avec routage dynamique
Dans RIOT, le protocole RPL est introduit de la même manière que les autres protocoles. Comme il correspond à une couche réseau, il est défini en temps que module et obtient un thread pour son fonctionnement. De cette façon, il est assez aisé d'utiliser RPL en ajoutant les modules suivant dans le makefile de l'application:
USEMODULE += gnrc_rpl USEMODULE += auto_init_gnrc_rpl
L'auto_init va permettre d'initialiser l'envoi périodique des messages DIO et également d'envoyer un message DIS pour chaque noeud. Mais pour que notre DODAG se construise, il faut que l'on définisse un Noeud comme DODAG root afin qu'il puisse alimenter les DIOs et ainsi permettrent aux autres noeud d'intégrer le DODAG.
Maintenant que l'on a vu comment intégrer des notions temps réel à notre application via le protocole SNTP, nous allons le mettre en place en l'associant au protocole RPL.
Noeud_Root_1[dead:beef::3402] -----> ...[RPL_NODES]... -----> Noeud_N[dead:beef::341e]
intégration pour notre appli
modification du protocole RPL afin qu'il tienne compte de la qualité de transmission
A chaque réception de DIO à la fin on recherche le meilleur parent (tous les parents du nœud actuel sont tester avec which parent)
=> intégrer les metric dans le dio
=> metric dans parent
=> dio recu par un nœud → metric correspondront au parent ayant envoyé le dio
rpl # rpl # instance table: [X] # parent table: [X] [X] [ ] # # instance [1 | Iface: 6 | mop: 2 | ocp: 0 | mhri: 230 | mri 0] # dodag [dead:beef::3402 | R: 715 | OP: Router | PIO: on | CL: 0s | TR(I=[8,20], k=10, c=0, TC=14s, TI=18s)] # parent [addr: fe80::3634:5110:3473:3762 | rank: 485 | lifetime: 38s] # parent [addr: fe80::3734:510b:330b:340a | rank: 486 | lifetime: 53s] fibroute # > fibroute # Destination Flags Next Hop Flags Expires Interface # :: 0x00000000 H fe80::3634:5110:3473:3762 0x00000001 28.7475986
IHM -> utilisation de LED
Liens bibliographiques et documents
Recherches sur les travaux déjà existants liés aux réseaux sans fil temps-réel
Fichier:Proposition et validation formelle d'un protocole MAC temps reel pour reseaux de capteurs lineraires sans fils.pdf
Fichier:A Real-Time and Reliable Transport RT Protocol for Wireless Sensor and Actor Networks.pdf
Fichier:A Survey on Real-Time MAC Protocols in Wireless Sensor Networks.pdf
Fichier:SPEED A Stateless Protocol for Real-Time Communication.pdf
Recherches sur Contiki
http://www.contiki-os.org/
https://fr.wikipedia.org/wiki/Syst%C3%A8me_d%27exploitation_pour_capteur_en_r%C3%A9seau
https://github.com/contiki-os/contiki
Recherches sur Riot
https://riot-os.org/
https://github.com/RIOT-OS
https://riot-os.org/api/
Gnrc stack
http://doc.riot-os.org/mlenders_msc.pdf
http://doc.riot-os.org/mlenders_msc_def.pdf
Recherches sur FreeRTOS
https://www.freertos.org/
https://fr.wikipedia.org/wiki/FreeRTOS
Recherches sur RPL
https://tools.ietf.org/html/rfc6550
http://www.ipso-alliance.org/wp-content/media/rpl.pdf
https://fr.wikipedia.org/wiki/6LoWPAN
Compte-rendu des réunions
Archive GIT
https://archives.plil.fr/mobeissa/PFE7.git
Livrables
Rapport de mi-parcours Fichier:Rapport mi parcours pfe7.pdf