IMA4 2017/2018 P10 : Différence entre versions
m (→Semaine 8) |
m (→Semaine 10) |
||
(31 révisions intermédiaires par le même utilisateur non affichées) | |||
Ligne 248 : | Ligne 248 : | ||
==Semaine 8== | ==Semaine 8== | ||
J'ai trouvé la source du problème des transmissions longues impossibles, et il s'agit en fait d'un problème dans mon code de test. En effet, j'utilisais le mode "taille de paquet fixe" du cc430, c'est à dire que la taille du paquet transmit devait être le premier octet transmit, et dans mes tests de paquets de tailles plus longue que 2 octets, j'oubliais de mettre à jour ce champ. En corrigeant ce problème dans mon programme de test, les réceptions se produisent correctement, dans la limite du buffer de 64 octets autorisés par le cc430. | J'ai trouvé la source du problème des transmissions longues impossibles, et il s'agit en fait d'un problème dans mon code de test. En effet, j'utilisais le mode "taille de paquet fixe" du cc430, c'est à dire que la taille du paquet transmit devait être le premier octet transmit, et dans mes tests de paquets de tailles plus longue que 2 octets, j'oubliais de mettre à jour ce champ. En corrigeant ce problème dans mon programme de test, les réceptions se produisent correctement, dans la limite du buffer de 64 octets autorisés par le cc430. | ||
+ | |||
+ | Une fois ce bug résolu, j'ai entamé l'étape de nettoyage du code pour le rendre parfaitement compatible avec RIOT OS, et respectant les conventions imposées par RIOT. J'ai donc utilisé un "code beautifier" nommé uncrustify afin de rectifier la majorité des "erreurs". J'ai de plus du retirer la plupart des caractères "blanc" (ou whitespace) en fin de ligne, afin d'éviter les erreurs de compilation Murdok. Malgré quelques recherches, je n'ai pas trouvé à quoi correspondait cette erreur. | ||
+ | |||
+ | Après consultations de mes encadrants de projets, il a été décidé que le code a ajouter à RIOT est suffisamment simple, et suffisamment différent du code source de Ti pour ne pas se soucier des droits d'auteur. | ||
Afin de montrer que le portage de la radio fonctionne bien, je vais utiliser le protocole RPL entre 3 noeuds, en utilisant la bibliothèque créée pour RIOT. Je vais me baser sur les projets IMA https://projets-ima.plil.fr/mediawiki/index.php/P15_R%C3%A9seau_de_capteurs_temps_r%C3%A9el et https://projets-ima.plil.fr/mediawiki/index.php/P7_R%C3%A9gulation_temps_r%C3%A9el_sur_r%C3%A9seau_sans_fil pour comprendre le fonctionnement de RPL et comment RIOT l'a implémenté. | Afin de montrer que le portage de la radio fonctionne bien, je vais utiliser le protocole RPL entre 3 noeuds, en utilisant la bibliothèque créée pour RIOT. Je vais me baser sur les projets IMA https://projets-ima.plil.fr/mediawiki/index.php/P15_R%C3%A9seau_de_capteurs_temps_r%C3%A9el et https://projets-ima.plil.fr/mediawiki/index.php/P7_R%C3%A9gulation_temps_r%C3%A9el_sur_r%C3%A9seau_sans_fil pour comprendre le fonctionnement de RPL et comment RIOT l'a implémenté. | ||
+ | |||
+ | ==Semaine 9== | ||
+ | Le Pull Request a été effectué, et après plusieurs corrections, le code a passé les tests automatiques de CI. Il ne reste plus qu'à attendre les retours des responsables de RIOT OS. | ||
+ | |||
+ | En parallèle, j'a commencé à me renseigner sur le protocole RPL. | ||
+ | Le protocole RPL est un protocole de routage et a été conçu principalement pour les objets connectés afin de s'adapter au LLN (Low-power and Lossy Networks). La description en détail de ce protocole peut être trouvé à la rfc6550 (https://tools.ietf.org/html/rfc6550). | ||
+ | Le principe du RPL est la construction d'un arbre DODAG (Destination-Oriented Directed Acyclic Graph) avec au moins un nœud racine. | ||
+ | |||
+ | [[Fichier:Rpl dodag.png | center |500px | thumb | DODAG RPL]] | ||
+ | |||
+ | (Image empruntée au projet https://projets-ima.plil.fr/mediawiki/index.php/P15_R%C3%A9seau_de_capteurs_temps_r%C3%A9el) | ||
+ | |||
+ | L'objectif du protocole RPL est d'optimiser la transmission d'information dans des réseaux sans fils. Chaque noeud essaye de transmettre ses données au noeud racine (sur l'image rank 0) en utilisant le chemin le plus court et le plus sur possible, d'où la création de l'arbre DODAG. La création de l'arbre est effectuée grâce a des messages ICMPv6 (message de type DIO, DIS, DAO). | ||
+ | |||
+ | ==Semaine 10== | ||
+ | RPL est implémenté nativement dans RIOT, ce qui rend son utilisation assez simple. De plus, des programmes d'exemples avec des tutoriaux pour utiliser RPL sur une machine. | ||
+ | En suivant ce tutoriel (https://github.com/RIOT-OS/RIOT/wiki/Tutorial:-RIOT-and-Multi-Hop-Routing-with-RPL), basé sur l'exemple situé dans | ||
+ | |||
+ | RIOT/examples/gnrc_networking | ||
+ | |||
+ | il est très simple de mettre en place un réseau RPL en mode natif, c'est à dire sur un Linux et non sur un microcontrôleur. | ||
+ | |||
+ | En revanche, en commençant à regarder comment effectuer un réseau RPL avec des CC430, je me suis rendu compte que je n'avais pas bien compris comment intégrer les capacités radio. | ||
+ | |||
+ | Au début du projet j'avais voulu suivre le tutoriel deport de module radio pour RIOT, https://github.com/RIOT-OS/RIOT/wiki/Tutorial:-How-to-port-a-radio-module-driver-to-RIOT-OS. Malheureusement, ce tutoriel est en construction, et surtout ne concerne que les modules radio externes aux microcontrôleurs, c'est à dire les modules radios qui communiquent avec le microcontrôleur par des fils, généralement un liaison SPI. Or ce n'est pas le cas pour le cc430, ce dernier accédant à son module par registre. J'ai donc laissé de coté ce tutoriel. | ||
+ | |||
+ | De plus, en comparant plusieurs ports déjà existant pour des boards contenant des msp430, je n'ai rien vu de particulier a faire pour le port du module radio, et je l'ai donc traité comme un TIMEr ou un ADC. | ||
+ | |||
+ | Or RIOT étant un OS en partie dédié à l'IoT, il implémente une pile réseau dans laquelle doit s'imbriquer les capacités radio des différents processeurs. | ||
+ | |||
+ | Je dois donc reprendre une partie du port pour le rendre compatible avec la pile réseau de RIOT. | ||
+ | |||
+ | ==Semaine 11== | ||
+ | |||
+ | RIOT a implémenté une pile réseau en plusieurs couches, avec chaque couche étant indépendante, fournissant son API propre aux autres couches, et utilisant l'API des autres couches quand cela est nécessaire. La couche qui nous interesse pour le port est la couche gnrc_netdev (pour gnrc NETwork DEVice). Cette couche fait la liaison entre le module radio et le reste de la pile réseau. | ||
+ | |||
+ | [[Fichier:RIOT_gnrc.png | center | 500px | thumb | Pile Réseau GNRC de RIOT]] | ||
+ | |||
+ | Afin de mieux comprendre comment ce port doit être effectué j'ai cherché un autre microcontrôleur supporté par RIOT avec un module radio intégré, et le cc2538 convenait. De plus, le module radio cc110x, sur lequel est basé le module radio du cc430 est aussi supporté en partie par RIOT. | ||
+ | |||
+ | Leurs sources se trouvent dans le dossier suivant : | ||
+ | |||
+ | RIOT/cpu/cc2538/ | ||
+ | radio/ | ||
+ | include/ | ||
+ | |||
+ | RIOT/drivers/cc110x | ||
+ | |||
+ | En comparant les sources et le tutoriel de port pour radio module, il s'avère que le port d'un module radio doit suivre le même modèle, que ce soit pour un module radio interne ou externe. | ||
+ | |||
+ | Les fichiers important à créer sont au nombre de 6, et il s'agit de | ||
+ | |||
+ | <driver_name>_internal.c et <driver_name>_internal.h, contenant toutes les fonctions accédant directement au module radio, | ||
+ | <driver_name>_getset. et <driver_name>_getset.h, contenant un ensemble de getter et de setter, | ||
+ | <driver_name>_netdev. et <driver_name>_netdev.h, contenant l'API du niveau gnrc_netdev, faisant le lien entre la partie matéreil et la partie réseau. | ||
+ | |||
+ | Pour un module radio externe, ces dossiers doivent être placés dans les répertoires suivant : | ||
+ | ~/RIOT/drivers/<driver-name>/<driver-name>_internal.c | ||
+ | ~/RIOT/drivers/<driver-name>/<driver-name>_getset.c | ||
+ | ~/RIOT/drivers/<driver-name>/<driver-name>_netdev.c | ||
+ | ~/RIOT/drivers/<driver-name>/include/<driver-name>_internal.h | ||
+ | ~/RIOT/drivers/<driver-name>/include/<driver-name>_getset.h | ||
+ | ~/RIOT/drivers/<driver-name>/include/<driver-name>_netdev.h | ||
+ | |||
+ | Dans notre cas, il faut suivre l'exemple du cc2538, et suivre l'arborescence suivante : | ||
+ | |||
+ | RIOT/cpu/cc430/radio/ | ||
+ | cc430_rf_internal.c | ||
+ | cc430_rf_getset.c | ||
+ | cc430_rf_netdev.c | ||
+ | RIOT/cpu/cc430/radio/include/ | ||
+ | cc430_rf_internal.h | ||
+ | cc430_rf_getset.h | ||
+ | cc430_rf_netdev.h | ||
+ | |||
+ | Mon travail pour les prochaines semaines sera de comprendre comment intégrer mes sources à cette architecture, et que doit offrir l'API. | ||
+ | |||
+ | ==Semaine 12== | ||
+ | Des 6 fichiers à créer pour RIOT, seulement 2 demandent du travail, il s'agit de _netdev.h et netdev.c. En effet, les 4 autres fichiers correspondent en fait aux sources que j'avais déjà. De plus, ces 4 fichiers ne font pas vraiment partie du paquet GNRC de RIOT, et contiennent simplement des accès physiques au cc430, donc je peux conserver ou non cette architecture si je le veux. J'ai décidé de la conserver afin de garder une cohérence avec la structure générale de RIOT | ||
+ | |||
+ | En revanche, les fichiers _netdev.h et netdev.c sont plus complexe à prendre en main. | ||
+ | |||
+ | La documentation en ligne de RIOT à propos de netdev est assez clair sur le concept général de netdev, mais reste assez haut niveau et surtout n'explique pas comment netdev doit fonctionner au niveau du microcontrôleur. | ||
+ | |||
+ | En recoupant ce que la documentation en ligne proposait, les sources du cc110x et du cc2538, j'ai compris comment doit être fait un véritable port. | ||
+ | |||
+ | La principale difficulté face à cette compréhension a été le manque de documentation, et des codes commentés de manières peu utile, par exemple dans le port du cc110x. | ||
+ | |||
+ | L'interface netdev est axée autour de 7 fonctions constituant le driver du module, et donc l'API fournie. Ces fonctions sont : | ||
+ | |||
+ | void _irq_handler(void) | ||
+ | Fonction servant simplement à signaler à netdev qu'une interruption matériel s'est produite afin que netdev la traite hors d'un contexte d'interruption | ||
+ | |||
+ | int(* send )(netdev_t *dev, const iolist_t *iolist) | ||
+ | Fonction permettant l'envoi d'un paquet contenu dans le parameêtre iolist, et donc l'API est stocker dans dev | ||
+ | |||
+ | int(* recv )(netdev_t *dev, void *buf, size_t len, void *info) | ||
+ | Fonction permettant la reception de paquet. En fonction des différents paramètres, cette fonction doit se comporter de manière différente : | ||
+ | * Si buf == NULL et len == 0, la fonction doit retourner la taille du paquet à recevoir | ||
+ | * Si buf == NULL et len > 0, on doit abandonner le paquet | ||
+ | * Si buf != NULL, la fonction doit écrire le paquet reçu dans buf | ||
+ | |||
+ | int(* init )(netdev_t *dev) | ||
+ | Fonction d'initialisation du module radio, peu d'informations sur ce que cela veut dire | ||
+ | |||
+ | void(* isr )(netdev_t *dev) | ||
+ | Une fonction servant de handler d'interruption, permettant de définir quelle interruption a été générer et de la traiter dans un thread plutot que dans une intérruption. | ||
+ | |||
+ | int(* get )(netdev_t *dev, netopt_t opt, void *value, size_t max_len) | ||
+ | Fonction retournant une valeur d'une option du module, option choisie par le paramètre opt, de type netopt_t défini dans | ||
+ | RIOT/sys/include/net/netopt.h | ||
+ | |||
+ | Il y a un grand nombre de netopt possible, mais un module n'est pas tenu de tous les gérer. | ||
+ | |||
+ | int(* set )(netdev_t *dev, netopt_t opt, const void *value, size_t value_len) | ||
+ | Fonction définissant une valeur d'une option du module, option choisie par le paramètre opt | ||
+ | |||
+ | Les netopt devant être gérer par l'interface netdev n'étant pas défini clairement, j'ai choisi de gérer ceux qui était déjà géré par le driver pour le cc110x. Il s'agit des netopt suivant : | ||
+ | |||
+ | Partie _get | ||
+ | |||
+ | * NETOPT_DEVICE_TYPE : importante, permet de préciser aux autres couches quel type de module est utilisé, ici il s'agira d'un module cc110x | ||
+ | |||
+ | * NETOPT_CHANNEL : sur quel channel la radio emet | ||
+ | |||
+ | * NETOPT_ADDRESS : quelle est l'adresse physique du module | ||
+ | |||
+ | * NETOPT_MAX_PACKET_SIZE : quelle est la taille maximale du paquet | ||
+ | |||
+ | * NETOPT_IPV6_IID : l'identificateur IPv6 de l'interface | ||
+ | |||
+ | * NETOPT_ADDR_LEN : taille de l'adresse cible en octet | ||
+ | |||
+ | * NETOPT_SRC_LEN : taille de l'adresse cible en octet | ||
+ | |||
+ | Partie _set | ||
+ | |||
+ | * NETOPT_ADDRESS : Adresse pysique du module | ||
+ | |||
+ | * NETOPT_CHANNEL : sur quel channel la radio emet | ||
+ | |||
+ | J'ai modifé les fonction _get et _set avec mes propres sources, il me reste a modifier les autres, sachant que je n'ai toujours pas trouvé d'information sur _init. | ||
+ | |||
+ | ==Semaine 13== | ||
+ | L'objectif de cette semaine était de terminer un premier jet du port. Cet objectif a été partiellement atteint, les fonctions les plus simples (_get, _set, _irq_handler) ont été très rapide a faire. En revanche, il m'a fallut un peu plus de temps pour comprendre l'imbrication des structure de données de netdev. En effet, pour reprendre le même fonctionnement que le driver du cc110x, il faut utiliser les structure suivante : | ||
+ | |||
+ | La structure qui sera passée en paramètre de toute les fonctions du driver | ||
+ | typedef struct netdev_cc110x { | ||
+ | netdev_t netdev; //L'interface netdev générique | ||
+ | cc110x_t cc110x; //Structure contenant les information du cc110x | ||
+ | } netdev_cc110x_t; | ||
+ | |||
+ | La structure netdev générique | ||
+ | struct netdev { | ||
+ | const struct netdev_driver *driver; /**< pointer vers le driver contenant les fonction de l'API */ | ||
+ | netdev_event_cb_t event_callback; /**< fonction de callback */ | ||
+ | void* context; /**< pointer vers le contexte de la pile réseau */ | ||
+ | }; | ||
+ | |||
+ | La structure contenant les fonctions évoquées précédement | ||
+ | struct netdev_driver { | ||
+ | int (*send)(netdev_t *dev, const iolist_t *iolist); | ||
+ | |||
+ | int (*recv)(netdev_t *dev, void *buf, size_t len, void *info); | ||
+ | |||
+ | int (*init)(netdev_t *dev); | ||
+ | |||
+ | void (*isr)(netdev_t *dev); | ||
+ | |||
+ | int (*get)(netdev_t *dev, netopt_t opt, | ||
+ | void *value, size_t max_len); | ||
+ | |||
+ | int (*set)(netdev_t *dev, netopt_t opt, | ||
+ | const void *value, size_t value_len); | ||
+ | } netdev_driver_t; | ||
+ | |||
+ | Structure contenant les informations du cc110x | ||
+ | struct cc110x { | ||
+ | |||
+ | cc110x_statistic_t cc110x_statistic; /**< Structure de Statistiques pour debug */ | ||
+ | |||
+ | uint8_t radio_state; /**< Etat de la Radio */ | ||
+ | uint8_t radio_channel; /**< channel de la radio*/ | ||
+ | uint8_t radio_address; /**< adresse de la radio */ | ||
+ | |||
+ | cc110x_pkt_buf_t pkt_buf; /**< RX/TX buffer */ | ||
+ | }; | ||
+ | |||
+ | Structure du buffer d'envoi et de reception | ||
+ | typedef struct { | ||
+ | uint8_t rssi; /**< valeur du RSSI */ | ||
+ | uint8_t lqi; /**< Indicateur de Qualité du Lien */ | ||
+ | uint8_t pos; /**< I have no clue. */ | ||
+ | cc110x_pkt_t packet; /**< Le paquet */ | ||
+ | } cc110x_pkt_buf_t; | ||
+ | |||
+ | Structure du paquet de donnée | ||
+ | typedef struct __attribute__((packed)) | ||
+ | { | ||
+ | uint8_t length; /**< Taille du paquet (sans prendre en compte le champs length) */ | ||
+ | uint8_t address; /**< Addresse de Destination */ | ||
+ | uint8_t phy_src; /**< Addresse Source */ | ||
+ | uint8_t flags; /**< drapeau */ | ||
+ | uint8_t data[CC430_MAX_DATA_LENGTH]; /**< Données (protocol de plus haut niveau) */ | ||
+ | } cc110x_pkt_t; | ||
+ | |||
+ | Une fois cette architecture comprise, completer les fonction _recv et _send est assez simple, en suivant les modeles du cc110x et du cc2538. | ||
+ | |||
+ | En revanche, la fonction _init a été assez complexe a comprendre, car on initialise pas le cc110x/cc430 et le2538 de la même façon. De plus le cc110x étant initialisé par des pin GPIO et les communications (commande, emission/reception, déclenchement d'interruptions ...) etant effectuées au travers d'un bus SPI, il n'est pas possible de copier simplement l'éxistant. | ||
+ | |||
+ | Il s'avère au final que la fonction _init doive faire les choses suivantes : | ||
+ | * créer un lien entre l'interface netdev et les interruption du cc430 (lier _irq_handler à la fonction de callback de netdev) | ||
+ | * Récupérer l'adresse physique et le channel d'émission du module | ||
+ | * Activer la réception | ||
+ | * Indiquer que l'appareil en en mode réception | ||
+ | |||
+ | ==Semaine 14+ == | ||
+ | L'interface Netdev a été terminée et testée comme dans les exemples de la documentation de netdev, donc il semblerait que cette version soit en cohérence avec ce qu'attend RIOT au niveau NetDev. Un Pull request a de nouveau été effectué. | ||
+ | |||
+ | En revanche, je me suis rendu compte que les codes d'exemples utilisés précédemment par d'autre groupes et par moi même (en l’occurrence gnrc_networking) étaient beaucoup trop lourd et ne rentrait pas dans le cc430 . | ||
+ | J'ai donc essayé de reconstruire la pile réseau gnrc à la main, sans utiliser les fonctionnalités d'auto initialisation et l'utilisation du shell de RIOT, mais sans succès. Je ne saurais dire si c'est mon port qui n'est pas correct, ou si ma compréhension de gnrc n'est pas complète. | ||
+ | |||
+ | De plus, en effectuant des tests pour ma couche netdev, je me suis rendu compte que les sources dont je disposais, celle de TI et celle du précédent projet, ne permettait pas de transmettre des paquets de + de 64 octets, c'est à dire la taille du buffer FIFO. Les algorithmes proposés par TI impliquent l'utilisation d'un timer pour scruter périodiquement la quantité d'octets dans le buffer, mais cette méthode n'est pas très bonne dans notre cas, car modifier les timers peut entrer en conflit avec les timers du scheduler de RIOT. J'ai essayé de trouver des solutions à ce problème, mais je n'ai pas réussi a émettre des paquets de tailles > à 64 octets. | ||
=Documents Rendus= | =Documents Rendus= | ||
+ | '''Rapport final''' | ||
+ | [[Fichier:Rapport_Projet_IMA4_P10_Baptiste_CARTIE.pdf]] | ||
+ | |||
+ | '''Pull Request''' https://github.com/baptiste-cartier/RIOT | ||
+ | |||
+ | '''cc430BV''' | ||
+ | [[Fichier:Cc430BV.zip]] : Dossier de la carte a insérer dans le dossier RIOT/boards | ||
+ | |||
+ | '''Programmes de tests''' | ||
+ | [[Fichier:Projects.zip]] : Dossier contenant des programmes de tests, à insérer dans le dossier RIOT/ |
Version actuelle datée du 20 mai 2018 à 14:18
Sommaire
Présentation générale
Description
Le but de ce projet est d’effectuer le portage du système d’exploitation RIOT-OS pour le microcontrôleur CC430 dans l’optique IoT. L’IoT, ou en français « Internet des Objets », est en quelque sorte l’extension d’Internet aux objets du quotidien et aux personne. Avec l’IoT, il ne s’agit plus de connecté via Internet seulement des serveurs et des ordinateurs, mais aussi des montres, des caméra, des feux de signalisation ou des capteurs en tout genre. RIOT-OS est un système d’exploitation destiné aux objets connectés, avec comme préoccupation principale une consommation minimale en mémoire et en puissance. C’est un OS OpenSource communautaire dont le site est le suivant : https://riot-os.org/. RIOT-OS a une organisation en module, avec des modules indépendants du hardware utilisé se reposant sur des modules dépendant du hardware utilisé, c’est-à-dire qu’il n’est pas nécessaire de réécrire tout l’OS pour l’adapter au microcontrôleur voulu, seulement les modules dépendant du hardware. Il est possible d’émuler le fonctionnement d’une application utilisant RIOT-OS sur un ordinateur, permettant de tester l’application développer sans les inconvénients du hardware. Le principal intérêt de cette fonctionnalité est de vérifier la viabilité d’une application avant de commencer le portage sur la cible voulue.
Le CC430 est une famille de microcontrôleur de la marque Texas Instrument, intégrant un microprocesseur MSP430 et un module radio CC1101. La famille MSP430 est une famille de microcontrôleur très utilisé dans le domaine professionnel. Le module radio CC1101 est un module radio conçu pour des applications embarquées à basse consommation, aussi de la marque Texas Instrument.
Objectifs
Les objectifs de ce projets sont :
- Rendre compatible RIOT-OS avec le CC430
- Permettre la communication par radio entre deux CC430 ayant RIOT-OS d'installé
Analyse du projet
Positionnement par rapport à l'existant
Il existe de nombreux OS pour l’embarqué et les systèmes connectés (Contiki, FreeRTOS, Linux, Windows For IOT…). Chaque Système d’Exploitation possède ses intérêts et ses défauts. Notre objectif est l’IoT, donc les objets connectés et les réseaux de capteurs. De plus, RIOT-OS est open source, nous allons donc comparer notre projet face à d’autres OS open source visant les objets connectés qui supportent les MSP430, donc proche du CC430.
Analyse du premier concurrent : FreeRTOS
FreeRTOS est l’un des OS les plus populaire du monde de l’embarqué. Très simple à prendre en main avec très peu de fichiers .C, il est disponible pour de très nombreuses plateforme. Les sources de cet OS sont disponible sur leur site https://www.freertos.org/. La plus grande force de FreeRTOS est sa simplicité d’utilisation, en effet, il n’y a qu’un seul fichier source à modifier pour rendre l’OS compatible avec n’importe quelle plateforme supportée. En revanche, FreeRTOS occupe « beaucoup » d’espace en mémoire, avec en moyenne 4kB d’utilisation de RAM et 9kB d’utilisation de ROM, comparé à respectivement 1.5kB de Ram et 5kB de ROM pour RIOT. De plus, dans une optique IoT, FreeRTOS ne supporte que les protocoles TCP, UDP et Ethernet pour la communication par Internet, tandis que RIOT, bien que ne supportant pas Ethernet, supporte TCP, UDP et surtout CoAP, un protocole de transmission d’information via Web optimisé pour les réseaux contraints sans fil. Enfin, il n’est possible que de développer en C sur FreeRTOS, alors que RIOT laisse le choix entre C et C++, permettant l’utilisation de plus bibliothèques.
Analyse du second concurrent : Contiki
Contiki est un OS ayant pour cible des petits microcontrôleurs à faibles cout et faible consommation pour l’IoT. Le site de Contiki est le suivant : http://www.contiki-os.org/. Contiki a été pensé pour les réseaux de capteurs et implémente plusieurs standards et protocoles de communications sans fils et Internet, tel que IPv4 et IPv6, 6loWPAN, RPL ou encore CoAP. Les principales forces de Contiki sont l’importance de la gestion de l’énergie des microcontrôleurs sur lequel il est porté et son émulateur Cooja permettant de simuler des réseaux Contiki et vérifier le bon fonctionnement de l’application développée. En revanche, tout comme FreeRTOS, Contiki est gourmand en mémoire RAM et ROM, nécessitant environ 2kB de RAM et 30kB de ROM. De plus, contrairement à RIOT-OS, les capacités temps réel de Contiki sont limitées, son scheduler est basé sur un algorithme coopératif et non préemptif comme c’est le cas pour RIOT-OS.
Scénario d'usage du produit ou du concept envisagé
Carrie F. est développeuse de systèmes embarqués et doit mettre en place réseau de capteurs afin de mesurer la pollution d’une zone sensible, une usine, une ville, une zone contaminée tel que Tchernobyl, dont les données doivent être transmises à un serveur de manière sécurisé. Elle n'a pas beaucoup de temps et doit choisir un produit lui permettant de réaliser son projet dans le temps qui lui est attribué. Elle fait le choix du CC430 qui est un microcontrôleur possédant nativement des capacités de communications sans fil. Afin de mettre en place rapidement son réseau de capteur, elle a besoin d'un OS pret à l'emploi. La combinaison RIOT-OS-CC430 permettrait de répondre à toutes les problématiques. En effet, à la fois le MSP430 intégré dans le CC430 et RIOT-OS offrent des solutions afin de sécuriser à la fois les transmissions ET le capteur, permettent une communication sans fils via Internet, et RIOT-OS étant axé sur l’économie d’énergie, permet de faire fonctionner plus longtemps les capteurs sur batterie, permettant de soit palier à un défaut d’alimentation, voir de ne dépendre que du soleil afin de s’alimenter.
Réponse à la question difficile
La question difficile était : "Sur quel microcontrôleur de la famille des MSP430 le port doit-il être fait?". Après discussion, il s'est avéré que le sujet réel était "Portage de RIOT-OS sur CC430 pour IOT" et non "Portage de RIOT-OS sur MSP430 pour IOT" , et donc le sujet du projet répondait à cette question : le port doit être effectué sur le CC430, un microcontrôleur de la famille des MSP430 ayant un CC1101. La réponse à la question difficile est donc triviale : le microcontrôleur doit être le CC430.
Préparation du projet
Cahier des charges
Choix techniques : matériel et logiciel
La référence exacte du CC430 utilisé est : CC430F5137IRGZT (datasheet http://www.ti.com/lit/ds/symlink/cc430f5137.pdf , http://www.ti.com/lit/ug/slau259e/slau259e.pdf)
Les programmes seront écrit en C et compilés avec le compilateur GCC pour msp430 (http://www.ti.com/tool/msp430-gcc-opensource).
Le CC430 sera flashé grâce à un launchpad de la marque TI : le MSP-EXP430G2 (http://www.ti.com/tool/MSP-EXP430G2).
Afin de vérifier que les CC430 émettent correctement les messages radios, un autre microcontrôleur sera utilisé comme agent de contrôle : le MSP430-CCRF (https://www.olimex.com/Products/MSP430/Starter/MSP430-CCRF/)
Liste des tâches à effectuer
Afin de réaliser le projet, il faut :
- Mettre en place la chaîne de compilation (ggc pour msp430, comprendre comment utiliser le flasheur..)
- Analyser le fonctionnement de RIOT-OS
- Charger la version de RIOT-OS pour MSP430 sur le CC430 et déterminer quelles parties doivent être faites/refaites
Calendrier prévisionnel
Réalisation du Projet
Feuille d'heures
Tâche | Prélude | Heures S1 | Heures S2 | Heures S3 | Heures S4 | Heures S5 | Heures S6 | Heures S7 | Heures S8 | Heures S9 | Heures S10 | Total |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Analyse du projet | 0 | |||||||||||
Mise en place de la chaine de compilation | 8 | 1 | ||||||||||
Analyse du fonctionnement de RIOT-OS | 6 | 2 | ||||||||||
Portage du module radio | 5 | 7 | 7 | 6 + 4 | 4 |
Prologue
Semaine 1
La première semaine du projet a été consacrée à la mise en placede la chaine de compilation nécessaire pour complier le code pour le CC430 et et le falsher sur la carte.
Le compilateur msp430-gcc a été installé et l'outil mspdebug a été installé pour pouvoir flasher les cartes.
Les connexions physiques entre la carte habritant le cc430 et le launchpad msp-exp430g2 ont été repérées.
Des câbles avec une extremité femelle ont été soudés à une carte afin de permettre une communication (et donc une programmation) facile entre cette carte et le launchpad.
Semaine 2
Beaucoup de lecture et de recherche sur le fonctionnement de RIOT-OS ont été effectuées
Semaine 3
J'ai récupéré des sources d'un ancien projet IMA5 (https://projets-ima.plil.fr/mediawiki/index.php/P11_Spectateur_augment%C3%A9) dans lequel existait des codes fonctionnels et des bibliothèques fonctionnelles pour utiliser le module radio du CC430. Je compte utiliser ces sources comme bases à intégrer a RIOTOS Afin de porter intégralement RIOTOS sur le CC430, il faut ajouter aux source de RIOTOS les sources permettant d'utiliser les différents modules disponibles sur le CC430. Parmi les modules disponibles sur le cc430, il ne faut porter que les modules suivants : le module CRC16, le module radio (le cc1101) et le module de cryptage AES. Après discussion avec les encadrants de projets, il n'est nécessaire pour l'instant que de porter le module radio
RIOT-OS fonctionne grâce à une série de makefile en cascade et à une arborescence précise, chaque fichier et makefile devant se trouver au bon endroit et modifiés de manière cohérente par rapport a l'architecture de compilation de RIOTOS.
Les différents microcontrôleurs supportés par RIOTOS sont rangés dans le dossier */RIOT/cpu et les différentes board sont rangés dans le dossier */RIOT/boards. Afin de porter RIOT OS pour le cc430, il faut donc ajouter les sources du module radio au dossier des sources du CC430, soit dans le dossier */RIOT/cpu/cc430. Il faut aussi créer un répertoire propre à la carte électronique fourni par les encadrants, j'ai donc crée dans l'architecture de RIOT un répertoire */RIOT/boards/CC430BV. En me basant sur l'architecture de la board msb430, une board utilisant un msp430 donc très proche de celle dont je dispose, j'ai recréé l'architecture de répertoire et de makefile pour cette board.
J'ai ensuite ajouté dans les sources du cc430 les bibliothèques pour le module radio en suivant l'architecture des différents répertoires, c'est à dire :
- j'ai ajouté dans le dossier /RIOT/cpu/cc430/include/ le fichier RF1A.h, correspondant au fichier header pour faire fonctionner le module radio
- j'ai ajouté dans le dossier /RIOT/cpu/cc430/ le fichier RF1A.c, correspondant au fichier source pour faire fonctionner le module radio
J'ai de plus ajouté des fichiers utiles pour un projet que je compte réutilisé dans les sources du CC430BV, principalement des sources pour la PMM et la commande des LED de la board.
J'ai ensuite crée un dossier pour mes projets de test au sein de l'arborescence de RIOT afin que les makefile retrouvent facilement les chemins des sources de l'OS.
J'ai rencontré beaucoup de problème liés à la compilation, la plupart de mes nouvelles sources n'étant pas reconnues. Afin de résoudre le problème, j'ai commencé par tenter de compiler un des mini projet d'exemples, en l’occurrence un simple projet "hello world", trouvable dans le répertoire */RIOT/examples/hello-world, et je me suis rendu compte que ce projet, compilant pour d'autres board, ne compilait pas pour ma nouvelle board cc430BV. EN suivant les messages d'erreurs j'ai pu résoudre ce problème et l'exemple compile sans erreur. En revanche, je n'arrive toujours pas a compiler mon projet de test avec mes nouvelles sources.
Semaine 4
Cette semaine a été consacrée à résoudre les problème de compilation liés au portage du module radio. Le principale problème rencontré est que à la compilation, certains fichiers sources nouvellement ajoutés ne sont pas reconnus, et donc certaines fonctions et certaines variables n’existent pas. Ce problème peut être facilement résolu en faisant un #include "monfichier.h" et en l'ajoutant au répertoire courant du projet, mais cela ne présente pas un grand intérêt dans le cadre de l'utilisation de RIOTOS et va a l'encontre de l’intérêt du portage.
Le problème est que pour que le port soit bien fait, n'importe quel projet puisse utiliser les sources pour la communication radio, et ce pour n'importe board existante utilisant le cc430, le module radio étant inclut dans ce microcontrôleur. Les sources doivent donc être ajouté au dossier même du cc430, et je n'ai pas trouvé de solution afin que ces fichiers soient bien inclus lors de la compilation.
J'ai modifié les makefiles que j'avais crée car il se trouvait qu'il y avait des erreurs, résolvant certains problèmes, mais le plus gros persiste, mes nouvelles sources ne sont pas prises en compte.
Après une remarque de M. Vantroys, j'ai décidé de prendre exemple sur l'architecture des répertoires et des makefiles de la board chronos, board utilisant aussi un cc430. Malgré cela, j'ai toujours les meme problèmes de compilation. En observant le cheminement du makefile grace à l'option -n, je remarque bien pourtant que mes fichiers, principalement mes fichiers sources RF1A.h et RF1A.c sont bien pris en compte et normalement bien ajoutés à la compilation grâce à l'option -I de gcc, mais certains #define sont inaccessible.
Semaine 5
En fouillant dans les exemples fournis par RIOTOS et dans la documentation de l'OS, j'ai trouvé la bonne méthode à utiliser pour porter l'OS sur le cc30.
Contrairement à ce que je pensais, il faut bien utiliser les #include "header.h"
pour inclure les sources nécessaires, avec header.h dans RIOT/cpu/cc430/includes et sources.c dans RIOT/cpu/cc430. J'ai donc déplacer mes fichiers RF1A.c et RF1A.h dans les bons répertoires (RIOT/cpu/cc430 et RIOT/cpu/cc430/includes).
Dans le dossier correspondant à la board doit être inclut tous les fichiers sources relevant des drivers nécessaires au fonctionnements des périphériques n'étant pas nativement sur le cpu. Les sources de la gestion de led doivent donc y être placés.
L'architecture doit donc être la suivante (sans tenir compte des dossiers et des fichiers déjà existant et ne devant pas être déplacés) :
RIOT/ cpu/ cc430/ RF1A.c includes/ RF1A.h board/ cc430BV/ drivers/ Makefile led_commands.c include/ led_command.h
Une fois les fichiers au bon endroit et en utilisant les bon #include
, il suffit de compiler de se placer dans le répertoire du projet et d'utiliser le makefile avec la commande suivante :
make BOARD=cc430BV
et le projet compile sans erreur.
Afin de tester l'intégration de l'OS, j'ai décidé de reprendre les sources d'un code simple provenant du même projet que les sources déjà utilisés précédemment permettant l'envoi d'un message par une board et allumant une LED à chaque envoi, et permettant à une autre board de recevoir un message et allumant une LED à chaque réception. J'ai testé ces sources en les uploadant simplement sans modification sur les board que j'ai et elles fonctionnent comme escomptées.
J'ai ensuite intégrés ces quelques lignes de codes dans un programme créant un thread sur RIOTOS afin de tester les capacités d'émission et de réception.
Lors de ces tests, il s'est avéré que les LED ne clignotent pas comme dans les sources de base. En effet, afin de contrôler les LED, il faut utiliser un timer, et il s'avère que le timer utiliser pour la gestion des LED est le même que celui utilisé par RIOT pour son ordonnanceur. Les LED ne reste allumé que très peu de temps comparer à la version sans RIOT, mais à première vue, cela ne dérange pas le bon fonctionnement de RIOT. Ce problème n'est pas très important pour le moment, car il suffit de changer la gestion des LED, et cela ne rentre de tout façon pas en compte dans le port de RIOT pour le cc430, ce problème ne concernant que notre board.
En revanche, sachant que les codes sources fonctionnaient, j'ai testé la reception et l'émission de la manière suivante :
- : émission sans RIOT et réception avec RIOT
- : émission avec RIOT et réception sans RIOT
- : émission et réception avec RIOT
Les résultats de ces tests sont :
Pour le test 1, il y a bien un allumage de LED à chaque réception d'un message, et si on coupe la source d'émission, il n'y a plus d'allumage à la réception, la réception à l'air de fonctionner
Pour le test 2, des problèmes sont apparus. Le récepteur ne captait qu'un seul message, et l'émetteur allumait sa LED mais ne l'éteignait jamais. Il s’avère que dans le code utilisé pour faire fonctionner le module radio, la fin de chaque transmission déclenchait une interruption. Je n'ai pas encore regardé comment RIOT gérait les interruptions, mais il est possible que le problème vienne de là, car le code que j'ai récupéré ne traitait pas l'interruption et laissait donc le code de base. Les prochaines séances vont être consacrées à la résolution de ce problème. Une fois ce problème surpassé, il faudra refactorer toutes les sources afin qu'elles correspondent aux normes de RIOT et d'écrire une API afin de gérer proprement les interruptions généré par la liaison radio.
Pour le moment, j'ai désactivé l'interruption et, au moins sur des messages très court, les board communiquent par radio avec des threads de RIOT.
Semaine 6
L'objectif de cette semaine était de faire fonctionner RIOT en parallèle des interruptions générés par le module radio.
Les vecteurs d'interruptions sont accessible et modifiable par la directive "pragma". Je n'ai pas encore très bien compris le fonctionnement des pragma, car spécifique à chaque compilateur, mais le code suivant permet de réveiller un thread suite à la réception d'un message par le module radio si ce dernier à été paramétré correctement (c'est à dire en mode réception RX avec la bonne interruption d'autorisée) :
#pragma vector=CC1101_VECTOR __attribute__((interrupt(CC1101_VECTOR))) void CC1101_ISR(void) { if(RF1AIV==RF1AIV_RFIFG4){ thread_wakeup(pid) } }
Pour le moment j'ai décidé de ne pas repérer la fin d'une transmission par un interruption, le code que j'ai actuellement fonctionne et je ferais des améliorations une fois que tout le reste fonctionnera.
J'ai ensuite refactoré tout le code afin de respecter les conventions de RIOT, c'est-à-dire respecter en particulier les consignes suivantes :
while(REGISTRE == VALUE); n'est pas correct while(REGISTRE == VALUE) {} est correct.
void maFonction() {...} n'est pas correct void rf1a_ma_fonction(void) {...} est correct
Indentation de 4 espaces, et non une tabulation ou 2 espaces
Les consignes complètes se trouvent à ce lien : https://github.com/RIOT-OS/RIOT/wiki/Coding-conventions
Durant la semaine de repos, j'ai commencé à tester mon codes afin de vérifier que tout fonctionnait bien, et je me suis rendu compte d'un problème très génant : je peux transmettre autant de paquets de deux octets que je le souhaites, mais je ne peux transmettre que 2 à 3 paquets de plus de 2 octets. Je ne sais pas pourquoi ce comportement est présent, je n'ai pas encore trouvé le problème.
Semaine 7
J'ai continué de cherché la raison du bug empéchant de transmettre des paquets de plus de 2 octets, et en me documentant sur le fonctionnement exact du cc430, j'ai découvert que le code que j'ai récupéré d'un ancien projet IMA provenait en fait de codes d'exemple fourni par TI sous le couvert d'une license TI assez restrictive. Je ne sais donc pas si le code que j'utilise peux être intégré à RIOT OS. Le code de TI peut être trouvé à cette adresse www.ti.com/lit/sw/slac525b/slac525b.zip .
De plus , en comparant ma version du code et la verison de TI, je n'ai pas trouvé de différences sur les parties qui me concernait , et ce code est sencé fonctionner pour transmettre des paquet de n'importe quelle taille.
Semaine 8
J'ai trouvé la source du problème des transmissions longues impossibles, et il s'agit en fait d'un problème dans mon code de test. En effet, j'utilisais le mode "taille de paquet fixe" du cc430, c'est à dire que la taille du paquet transmit devait être le premier octet transmit, et dans mes tests de paquets de tailles plus longue que 2 octets, j'oubliais de mettre à jour ce champ. En corrigeant ce problème dans mon programme de test, les réceptions se produisent correctement, dans la limite du buffer de 64 octets autorisés par le cc430.
Une fois ce bug résolu, j'ai entamé l'étape de nettoyage du code pour le rendre parfaitement compatible avec RIOT OS, et respectant les conventions imposées par RIOT. J'ai donc utilisé un "code beautifier" nommé uncrustify afin de rectifier la majorité des "erreurs". J'ai de plus du retirer la plupart des caractères "blanc" (ou whitespace) en fin de ligne, afin d'éviter les erreurs de compilation Murdok. Malgré quelques recherches, je n'ai pas trouvé à quoi correspondait cette erreur.
Après consultations de mes encadrants de projets, il a été décidé que le code a ajouter à RIOT est suffisamment simple, et suffisamment différent du code source de Ti pour ne pas se soucier des droits d'auteur.
Afin de montrer que le portage de la radio fonctionne bien, je vais utiliser le protocole RPL entre 3 noeuds, en utilisant la bibliothèque créée pour RIOT. Je vais me baser sur les projets IMA https://projets-ima.plil.fr/mediawiki/index.php/P15_R%C3%A9seau_de_capteurs_temps_r%C3%A9el et https://projets-ima.plil.fr/mediawiki/index.php/P7_R%C3%A9gulation_temps_r%C3%A9el_sur_r%C3%A9seau_sans_fil pour comprendre le fonctionnement de RPL et comment RIOT l'a implémenté.
Semaine 9
Le Pull Request a été effectué, et après plusieurs corrections, le code a passé les tests automatiques de CI. Il ne reste plus qu'à attendre les retours des responsables de RIOT OS.
En parallèle, j'a commencé à me renseigner sur le protocole RPL. Le protocole RPL est un protocole de routage et a été conçu principalement pour les objets connectés afin de s'adapter au LLN (Low-power and Lossy Networks). La description en détail de ce protocole peut être trouvé à la rfc6550 (https://tools.ietf.org/html/rfc6550). Le principe du RPL est la construction d'un arbre DODAG (Destination-Oriented Directed Acyclic Graph) avec au moins un nœud racine.
(Image empruntée au projet https://projets-ima.plil.fr/mediawiki/index.php/P15_R%C3%A9seau_de_capteurs_temps_r%C3%A9el)
L'objectif du protocole RPL est d'optimiser la transmission d'information dans des réseaux sans fils. Chaque noeud essaye de transmettre ses données au noeud racine (sur l'image rank 0) en utilisant le chemin le plus court et le plus sur possible, d'où la création de l'arbre DODAG. La création de l'arbre est effectuée grâce a des messages ICMPv6 (message de type DIO, DIS, DAO).
Semaine 10
RPL est implémenté nativement dans RIOT, ce qui rend son utilisation assez simple. De plus, des programmes d'exemples avec des tutoriaux pour utiliser RPL sur une machine. En suivant ce tutoriel (https://github.com/RIOT-OS/RIOT/wiki/Tutorial:-RIOT-and-Multi-Hop-Routing-with-RPL), basé sur l'exemple situé dans
RIOT/examples/gnrc_networking
il est très simple de mettre en place un réseau RPL en mode natif, c'est à dire sur un Linux et non sur un microcontrôleur.
En revanche, en commençant à regarder comment effectuer un réseau RPL avec des CC430, je me suis rendu compte que je n'avais pas bien compris comment intégrer les capacités radio.
Au début du projet j'avais voulu suivre le tutoriel deport de module radio pour RIOT, https://github.com/RIOT-OS/RIOT/wiki/Tutorial:-How-to-port-a-radio-module-driver-to-RIOT-OS. Malheureusement, ce tutoriel est en construction, et surtout ne concerne que les modules radio externes aux microcontrôleurs, c'est à dire les modules radios qui communiquent avec le microcontrôleur par des fils, généralement un liaison SPI. Or ce n'est pas le cas pour le cc430, ce dernier accédant à son module par registre. J'ai donc laissé de coté ce tutoriel.
De plus, en comparant plusieurs ports déjà existant pour des boards contenant des msp430, je n'ai rien vu de particulier a faire pour le port du module radio, et je l'ai donc traité comme un TIMEr ou un ADC.
Or RIOT étant un OS en partie dédié à l'IoT, il implémente une pile réseau dans laquelle doit s'imbriquer les capacités radio des différents processeurs.
Je dois donc reprendre une partie du port pour le rendre compatible avec la pile réseau de RIOT.
Semaine 11
RIOT a implémenté une pile réseau en plusieurs couches, avec chaque couche étant indépendante, fournissant son API propre aux autres couches, et utilisant l'API des autres couches quand cela est nécessaire. La couche qui nous interesse pour le port est la couche gnrc_netdev (pour gnrc NETwork DEVice). Cette couche fait la liaison entre le module radio et le reste de la pile réseau.
Afin de mieux comprendre comment ce port doit être effectué j'ai cherché un autre microcontrôleur supporté par RIOT avec un module radio intégré, et le cc2538 convenait. De plus, le module radio cc110x, sur lequel est basé le module radio du cc430 est aussi supporté en partie par RIOT.
Leurs sources se trouvent dans le dossier suivant :
RIOT/cpu/cc2538/ radio/ include/
RIOT/drivers/cc110x
En comparant les sources et le tutoriel de port pour radio module, il s'avère que le port d'un module radio doit suivre le même modèle, que ce soit pour un module radio interne ou externe.
Les fichiers important à créer sont au nombre de 6, et il s'agit de
<driver_name>_internal.c et <driver_name>_internal.h, contenant toutes les fonctions accédant directement au module radio, <driver_name>_getset. et <driver_name>_getset.h, contenant un ensemble de getter et de setter, <driver_name>_netdev. et <driver_name>_netdev.h, contenant l'API du niveau gnrc_netdev, faisant le lien entre la partie matéreil et la partie réseau.
Pour un module radio externe, ces dossiers doivent être placés dans les répertoires suivant :
~/RIOT/drivers/<driver-name>/<driver-name>_internal.c ~/RIOT/drivers/<driver-name>/<driver-name>_getset.c ~/RIOT/drivers/<driver-name>/<driver-name>_netdev.c ~/RIOT/drivers/<driver-name>/include/<driver-name>_internal.h ~/RIOT/drivers/<driver-name>/include/<driver-name>_getset.h ~/RIOT/drivers/<driver-name>/include/<driver-name>_netdev.h
Dans notre cas, il faut suivre l'exemple du cc2538, et suivre l'arborescence suivante :
RIOT/cpu/cc430/radio/ cc430_rf_internal.c cc430_rf_getset.c cc430_rf_netdev.c RIOT/cpu/cc430/radio/include/ cc430_rf_internal.h cc430_rf_getset.h cc430_rf_netdev.h
Mon travail pour les prochaines semaines sera de comprendre comment intégrer mes sources à cette architecture, et que doit offrir l'API.
Semaine 12
Des 6 fichiers à créer pour RIOT, seulement 2 demandent du travail, il s'agit de _netdev.h et netdev.c. En effet, les 4 autres fichiers correspondent en fait aux sources que j'avais déjà. De plus, ces 4 fichiers ne font pas vraiment partie du paquet GNRC de RIOT, et contiennent simplement des accès physiques au cc430, donc je peux conserver ou non cette architecture si je le veux. J'ai décidé de la conserver afin de garder une cohérence avec la structure générale de RIOT
En revanche, les fichiers _netdev.h et netdev.c sont plus complexe à prendre en main.
La documentation en ligne de RIOT à propos de netdev est assez clair sur le concept général de netdev, mais reste assez haut niveau et surtout n'explique pas comment netdev doit fonctionner au niveau du microcontrôleur.
En recoupant ce que la documentation en ligne proposait, les sources du cc110x et du cc2538, j'ai compris comment doit être fait un véritable port.
La principale difficulté face à cette compréhension a été le manque de documentation, et des codes commentés de manières peu utile, par exemple dans le port du cc110x.
L'interface netdev est axée autour de 7 fonctions constituant le driver du module, et donc l'API fournie. Ces fonctions sont :
void _irq_handler(void)
Fonction servant simplement à signaler à netdev qu'une interruption matériel s'est produite afin que netdev la traite hors d'un contexte d'interruption
int(* send )(netdev_t *dev, const iolist_t *iolist)
Fonction permettant l'envoi d'un paquet contenu dans le parameêtre iolist, et donc l'API est stocker dans dev
int(* recv )(netdev_t *dev, void *buf, size_t len, void *info)
Fonction permettant la reception de paquet. En fonction des différents paramètres, cette fonction doit se comporter de manière différente :
- Si buf == NULL et len == 0, la fonction doit retourner la taille du paquet à recevoir
- Si buf == NULL et len > 0, on doit abandonner le paquet
- Si buf != NULL, la fonction doit écrire le paquet reçu dans buf
int(* init )(netdev_t *dev)
Fonction d'initialisation du module radio, peu d'informations sur ce que cela veut dire
void(* isr )(netdev_t *dev)
Une fonction servant de handler d'interruption, permettant de définir quelle interruption a été générer et de la traiter dans un thread plutot que dans une intérruption.
int(* get )(netdev_t *dev, netopt_t opt, void *value, size_t max_len)
Fonction retournant une valeur d'une option du module, option choisie par le paramètre opt, de type netopt_t défini dans
RIOT/sys/include/net/netopt.h
Il y a un grand nombre de netopt possible, mais un module n'est pas tenu de tous les gérer.
int(* set )(netdev_t *dev, netopt_t opt, const void *value, size_t value_len)
Fonction définissant une valeur d'une option du module, option choisie par le paramètre opt
Les netopt devant être gérer par l'interface netdev n'étant pas défini clairement, j'ai choisi de gérer ceux qui était déjà géré par le driver pour le cc110x. Il s'agit des netopt suivant :
Partie _get
- NETOPT_DEVICE_TYPE : importante, permet de préciser aux autres couches quel type de module est utilisé, ici il s'agira d'un module cc110x
- NETOPT_CHANNEL : sur quel channel la radio emet
- NETOPT_ADDRESS : quelle est l'adresse physique du module
- NETOPT_MAX_PACKET_SIZE : quelle est la taille maximale du paquet
- NETOPT_IPV6_IID : l'identificateur IPv6 de l'interface
- NETOPT_ADDR_LEN : taille de l'adresse cible en octet
- NETOPT_SRC_LEN : taille de l'adresse cible en octet
Partie _set
- NETOPT_ADDRESS : Adresse pysique du module
- NETOPT_CHANNEL : sur quel channel la radio emet
J'ai modifé les fonction _get et _set avec mes propres sources, il me reste a modifier les autres, sachant que je n'ai toujours pas trouvé d'information sur _init.
Semaine 13
L'objectif de cette semaine était de terminer un premier jet du port. Cet objectif a été partiellement atteint, les fonctions les plus simples (_get, _set, _irq_handler) ont été très rapide a faire. En revanche, il m'a fallut un peu plus de temps pour comprendre l'imbrication des structure de données de netdev. En effet, pour reprendre le même fonctionnement que le driver du cc110x, il faut utiliser les structure suivante :
La structure qui sera passée en paramètre de toute les fonctions du driver
typedef struct netdev_cc110x { netdev_t netdev; //L'interface netdev générique cc110x_t cc110x; //Structure contenant les information du cc110x } netdev_cc110x_t;
La structure netdev générique
struct netdev { const struct netdev_driver *driver; /**< pointer vers le driver contenant les fonction de l'API */ netdev_event_cb_t event_callback; /**< fonction de callback */ void* context; /**< pointer vers le contexte de la pile réseau */
};
La structure contenant les fonctions évoquées précédement
struct netdev_driver { int (*send)(netdev_t *dev, const iolist_t *iolist); int (*recv)(netdev_t *dev, void *buf, size_t len, void *info); int (*init)(netdev_t *dev); void (*isr)(netdev_t *dev); int (*get)(netdev_t *dev, netopt_t opt, void *value, size_t max_len); int (*set)(netdev_t *dev, netopt_t opt, const void *value, size_t value_len); } netdev_driver_t;
Structure contenant les informations du cc110x
struct cc110x {
cc110x_statistic_t cc110x_statistic; /**< Structure de Statistiques pour debug */
uint8_t radio_state; /**< Etat de la Radio */ uint8_t radio_channel; /**< channel de la radio*/ uint8_t radio_address; /**< adresse de la radio */
cc110x_pkt_buf_t pkt_buf; /**< RX/TX buffer */
};
Structure du buffer d'envoi et de reception
typedef struct { uint8_t rssi; /**< valeur du RSSI */ uint8_t lqi; /**< Indicateur de Qualité du Lien */ uint8_t pos; /**< I have no clue. */ cc110x_pkt_t packet; /**< Le paquet */ } cc110x_pkt_buf_t;
Structure du paquet de donnée
typedef struct __attribute__((packed)) { uint8_t length; /**< Taille du paquet (sans prendre en compte le champs length) */ uint8_t address; /**< Addresse de Destination */ uint8_t phy_src; /**< Addresse Source */ uint8_t flags; /**< drapeau */ uint8_t data[CC430_MAX_DATA_LENGTH]; /**< Données (protocol de plus haut niveau) */ } cc110x_pkt_t;
Une fois cette architecture comprise, completer les fonction _recv et _send est assez simple, en suivant les modeles du cc110x et du cc2538.
En revanche, la fonction _init a été assez complexe a comprendre, car on initialise pas le cc110x/cc430 et le2538 de la même façon. De plus le cc110x étant initialisé par des pin GPIO et les communications (commande, emission/reception, déclenchement d'interruptions ...) etant effectuées au travers d'un bus SPI, il n'est pas possible de copier simplement l'éxistant.
Il s'avère au final que la fonction _init doive faire les choses suivantes :
- créer un lien entre l'interface netdev et les interruption du cc430 (lier _irq_handler à la fonction de callback de netdev)
- Récupérer l'adresse physique et le channel d'émission du module
- Activer la réception
- Indiquer que l'appareil en en mode réception
Semaine 14+
L'interface Netdev a été terminée et testée comme dans les exemples de la documentation de netdev, donc il semblerait que cette version soit en cohérence avec ce qu'attend RIOT au niveau NetDev. Un Pull request a de nouveau été effectué.
En revanche, je me suis rendu compte que les codes d'exemples utilisés précédemment par d'autre groupes et par moi même (en l’occurrence gnrc_networking) étaient beaucoup trop lourd et ne rentrait pas dans le cc430 . J'ai donc essayé de reconstruire la pile réseau gnrc à la main, sans utiliser les fonctionnalités d'auto initialisation et l'utilisation du shell de RIOT, mais sans succès. Je ne saurais dire si c'est mon port qui n'est pas correct, ou si ma compréhension de gnrc n'est pas complète.
De plus, en effectuant des tests pour ma couche netdev, je me suis rendu compte que les sources dont je disposais, celle de TI et celle du précédent projet, ne permettait pas de transmettre des paquets de + de 64 octets, c'est à dire la taille du buffer FIFO. Les algorithmes proposés par TI impliquent l'utilisation d'un timer pour scruter périodiquement la quantité d'octets dans le buffer, mais cette méthode n'est pas très bonne dans notre cas, car modifier les timers peut entrer en conflit avec les timers du scheduler de RIOT. J'ai essayé de trouver des solutions à ce problème, mais je n'ai pas réussi a émettre des paquets de tailles > à 64 octets.
Documents Rendus
Rapport final Fichier:Rapport Projet IMA4 P10 Baptiste CARTIE.pdf
Pull Request https://github.com/baptiste-cartier/RIOT
cc430BV Fichier:Cc430BV.zip : Dossier de la carte a insérer dans le dossier RIOT/boards
Programmes de tests Fichier:Projects.zip : Dossier contenant des programmes de tests, à insérer dans le dossier RIOT/