Mise en place d'un réseau de capteurs sans fil (2019)

De Ensiwiki
Aller à : navigation, rechercher


Project schedule.png
Titre du projet Mise en place d'un réseau de capteurs sans fil (2019)
Cadre Projets Réseaux Mobiles et Avancés

Équipe Lucas Peirone, Valderane Potong,Michel Yoeung, Jean-Charles Mugnier,
Encadrants Franck Rousseau


Introduction

Le "Réseau de capteur sans fil" est un projet que l'on peut considérer comme "fil rouge" dans le contexte de ce projet Réseaux Mobiles et Avancés. Si le but de la manipulation, établir une communication entre un groupe de capteurs, est commun, une liberté sur le contexte d'exécution, ainsi que sur le protocole mis en place, est laissé aux élèves, permettant une multitude de réponse pour ce qui pourrait être considéré comme un problème unique.

Dans le cadre de ce projet, nous avons essayé de mettre au point une communication permettant l'entrée et la sortie de capteurs du réseau crée. Plutôt que les performances, une des priorités du projet est d'essayer de consommer le moins d'énergie possible, à notre sens une des motivations principales pour ne pas simplement utiliser une connection Internet "normale".

Les grandes étapes du développement sont alors de mettre en place l'environnement, de théoriser le protocole, et enfin d'implanter une PoC tendant vers notre solution théorique.

Mise en place de l'environnement

S'appuyant sur le travail des précédents projets, la mise en place de l'environnement était censée être rapide et facile.

Seulement voilà. N'ayant pas nos stations personnelles de disponibles, nous nous sommes attelés à faire fonctionner cet environnement sur les PCs de l'Ensimag .

Cette étape rapide et facile s'est alors rapidement transformée en une chasse à la fonctionnalité pour trouver un moyen de passer par delà les contraintes imposées sur ces stations, transformant alors cette trivialité en véritable sous-projet.

Dans la suite de cette section, les contraintes et essais seront détaillés, afin de faciliter le travail pour de potentiels prochains groupes. Si vous ne cherchez qu'à faire fonctionner l'environnement, vous pouvez aller direction à la solution.

Les besoins

Pour que l'environnement fonctionne, il y a en réalité besoin que d'assez peu de conditions:

  • Un PC avec les droits super utilisateur et Docker
  • L'accès aux ports USB de la machine (physiquement et logiquement)

Les droits super utilisateur nous permettent d'installer les logiciels requis, ainsi que de "réserver" un port USB pour le flash/debug. Des droits corrects sur les utilisateurs normaux pourraient aussi nous le permettre, mais il ne sont pas accordés aux élèves.

L'accès au port USB, pour des capteurs USB, paraît probablement être une évidence. Puisqu'en effet, ça l'est. Est-ce pourtant une garantie ? Non, nous allons le voir.

Les essais

La première idée que nous avons eu est d'essayer de simplement faire une installation utilisateur des logiciels requis. Après tout, de nombreux logiciels sous Linux ne requièrent des droits privilégiés que dans l'unique but de s'installer dans un répertoire système. Changer le répertoire d'installation pourrait alors être une solution simple. Mais mspdebug et l'installation du compilateur ne vont pas dans ce sens, impossible de tout installer/exécuter sans les droits su, y compris en changeant les paramètres.

Bien entendu, il n'y avait que peu d'espoirs que cette première solution naïve fonctionne. Mais, si ce n'est qu'un problème d'accès, des solutions de virtualisation comme Vagrant ou Docker sont idéales. Docker n'étant pas (encore ?) installé sur les stations de l'école, nous essayons alors Vagrant.

Un Vagrantfile est alors crée avec toutes les dispositions nécessaires. Tous les logiciels s'installent correctement. Seulement, le logiciel de flashing des cartes ne détecte aucune cartes. En effet, le backend du Vagrant installé est VirualBox. Ce dernier ne partage pas par défaut les périphériques USBs avec la VM. Dans une installation normale, il s'agit juste d'un setting à changer, pour autoriser le passthrough d'un périphérique.

Mais, probablement pour des raisons obscures de sécurité et de normes, ce réglage ne peut pas être modifié par un utilisateur normal. Cette seconde version disposait alors d'un environnement parfaitement fonctionnel... Mais qui ne pouvait pas être utilisé sur les capteurs.

Reste Docker, un conteneur n'étant pas une VM, ce blocage induit par VirtualBox ne devrait pas s'appliquer. Seulement, comme dit précédemment, Docker n'est pas installé sur les PCs, et son accès passe par une commande "lance-vm-5MMSSI.sh". C'est à dire par une VM. Lancée par VirtualBox. Cette troisième solution a donc également été un échec.

Abandonnant l'idée de faire fonctionner la solution sur CentOS, nous nous sommes ensuite dirigés vers FreeBSD, un système d'exploitation alternatif préinstallé sur les stations. Les outils de développement n'étant pas prévus pour ce système, nous essayons de contourner ce problème avec Docker ou Vagrant. Mais ces outils ne sont pas non plus nativement compatibles avec cet OS, et après quelques essais, il est devenu évident que cette quatrième solution n'était pas non plus la bonne.

Il restait alors encore un système d'exploitation alternatif : Windows. En effet, sur Windows (7, Windows 10 plante au démarrage), nous disposons des droits d'administrateurs. Nous pouvons alors installer les outils. Nous avons également accès aux ports USB. Il semble, dans la version utilisée de mspdebug, qu'il y ait une présence d'un bug sur certaines méthodes. Si ce n'est pas confirmé cette fois, nous sommes directement passé par la virtualisation.

Or, à ce stade du développement, nous utilisions un conteneur Docker pour travailler sur nos machines (fixes) personnelles. Nous avons donc voulu utiliser Docker sur Windows pour avoir un environnement cohérent. Pour avoir accès aux ports USB de la machine physique, un conteneur a besoin d'être privilégié. C'est une option accessible dans docker run. Malheureusement, sous Windows, à cause de la manière dont Docker est implémenté (via une machine virtuelle), cette option n'est pas disponible. Cette sixième tentative a néanmoins ouvert la voie pour la solution trouvée.

La solution

Après tous ces essais, nous avons finalement décidé d'installer VirtualBox sur Windows et d'installer Ubuntu sur une VM (Vagrant ou autre). Ayant les droits d'administrateur sur Windows, il nous a été possible de simplement configurer le passthrough USB pour que la machine virtuelle Ubuntu ait accès aux capteurs. Installant ensuite Docker sur cette machine virtuelle, nous avons enfin pu faire fonctionner le conteneur en mode privilégié et pu programmer les capteurs.

Voici le Dockerfile utilisé. Le dépot Gitlab contenant les différentes archives utilisées est disponible dans les sources

La méthode suivie par ce DockerFile est celle-ci. Les dépendances sont pré-récupérées sous forme d'archive pour fixer les versions et être certain d'avoir un environnement stable (certains logiciels ne sont pas versionnés au téléchargement). La seule section vraiment remarquable est l'installation du compilateur.

En effet, l'installation est par défaut graphique, ce qui est très embêtant dans notre contexte. Même en mode "textuel", avec l'option --text, des interactions utilisateurs sont demandées. Ce problème est contourné par l'utilisateur de yes, une commande qui répond y à chaque question (beaucoup de questions demandées sont des questions fermées). Cela nous permet d'installer le logiciel, mais avec un effet de bord : une des questions est le chemin d'installation. Le logiciel est donc installé dans le répertoire "y". La ligne suivante corrige donc ce problème.

L'inconvénient de cette solution est qu'elle requiert de toujours utiliser le même PC. En effet, les actions sur Windows sont propres à l'ordinateur et non à l'utilisateur. Heureusement, nous avons toujours été dans la même salle pour les séances encadrées. Il devient néanmoins plus compliqué de travailler à l'ENSIMAG sur son temps libre, puisque notre salle était souvent utilisée pour d'autres cours.

Les ports USB des machines sont scellés. Nous avons utilisé un hub USB pour nous permettre de brancher un capteur sans devoir sacrifier le clavier ou la souris.

Les partitions Windows des machines ont un espace disque très limité. Si vous rencontrez des problèmes liés à cela, nous vous conseillons de désinstaller des logiciels (Visual Studio Code, par exemple) ou de vider le dossier Téléchargement.

Pour information, nous sommes arrivés à cette solution à l'issue de l'avant dernière séance encadrée.

Le protocole

Le protocole que nous implémentons se base grossomodo sur un protocole existant permettant d'établir un réseau de communication pair-à-pair : Gnutella (imaginé en 2000 par Tom Pepper).

Détails du protocole

Voici la structure du paquet Gnutella qui va être encapsulé dans un paquet MRFI :

Paquet.png

Avec :

  • Packet length : La taille du paquet MRFI (= 28 par défaut)
  • Source address : L'adresse MAC source
  • Destination address : L'adresse MAC destination
  • Flag : Type du message du protocole (voir ci-dessous les valeurs correspondant à chaque type de message)
  • TTL (Time To Live) : Nombre de sauts avant la destruction du paquet (mis à 15 par défaut, à adapter selon la taille du réseau donc du nombre de pairs acceptés)
  • Hops : Nombre de sauts effectués (TTL_départ = TTL_courant + Hops)
  • Sequence ID : Le numéro de séquence qui identifie de façon unique du message au sein d'un nœud
  • Payload : La partie correspondante aux données du paquet

Les 5 types de messages du protocole sont :

  • Ping : Signalement du nœud sur l'ensemble du réseau pour être connu de l'ensemble des pairs (par défaut : 0x00)
  • Pong : Réponse au nœud ayant effectué un Ping pour être connu du nouveau pair (par défaut : 0x01)
  • Query : Requête d'une donnée spécifique dans le réseau (par défaut : 0x02)
  • Query-hit : Réponse au noeud ayant effectué un Query pour signaler la possession de la donnée (par défaut : 0x03)
  • Push : Demande de la donnée spécifiée dans la Query précédente auprès du pair choisi parmi ceux ayant répondu avec un Query-hit (par défaut : 0x04) (pas implémenté)

Notes :

  • Sur chaque paquet envoyé, on peut identifier un message de façon unique en concaténant l'adresse MAC source avec le numéro de séquence.
  • Le générateur de nombre aléatoire (pour avoir une durée d'attente aléatoire) est initialisé avec un seed correspondant à l'adresse MAC du capteur (donc unique).

Protocole

  1. Lorsqu'un nœud reçoit un message :
  2. Il décrémente le TTL, il incrémente le HOPS
    • S'il n'a pas encore reçu le message [pas implémenté]
      • Si le TTL est différent de 0, il ne fait rien
        • Si c'est un PING
          • Si l'adresse source est inconnue, alors il l'ajoute dans sa liste d'adresses et il envoie un PONG (après une durée d'attente aléatoire) vers la source et il propage le paquet (après une durée d'attente aléatoire)
        • Si c'est un PONG
          • Si l'adresse source est inconnue, alors il l'ajoute dans sa liste d'adresses
          • Sinon, on propage le paquet reçu (après une durée d'attente aléatoire)
        • Si c'est un QUERY
          • Si l'adresse source est connue
            • S'il peut répondre à la requête, alors il envoie un QUERY-HIT (après une durée d'attente aléatoire) vers la source avec la vitesse de transmission atteignable [incomplètement implémenté]
            • Sinon il propage le paquet reçu (après une durée d'attente aléatoire)
        • Si c'est un QUERY-HIT
          • Si l'adresse source est connue, alors il récupère les vitesses de transmission atteignables et il choisie le nœud proposant la meilleure vitesse [pas implémenté] pour faire un PUSH
          • Sinon on propage le paquet reçu (après une durée d'attente aléatoire)
        • Si c'est un PUSH [pas implémenté]
          • Si l'adresse source est connue, alors il répond à la requête en transmettant la réponse à destination de la source
          • Sinon, il propage le paquet reçu (après une durée d'attente aléatoire)

Optimisations possibles du protocole

-> Activation de la radio à des intervalles données (slots)

Pour l'instant, nous désactivons seulement le CPU et la CLOCK en attendant l'arrivée d'une interruption pour pouvoir diminuer la consommation des capteurs.
Mais nous pouvons optimiser la baisse de consommation en activant la radio qu'à des intervalles données (slots) mais cela nécessite bien évidemment la réactivation de la CLOCK.
Toutefois, la baisse de consommation des capteurs sera plus importante dans ce deuxième cas.

-> Ajout d'un Bootstrap Node (BN)

Le réseau pair-à-pair est constitué de plusieurs nœuds et on désigne parmi ces nœuds, un nœud d'amorçage appelé Bootstrap Node (BN).
Le BN va posséder en mémoire une liste qui est censée contenir les adresses MAC de tous les nœuds du réseau pair-à-pair.
Le BN va donc se comporter comme un serveur.

Arrivé d'un nouveau nœud dans le réseau pair à pair

  1. Ce nouveau nœud (client) va envoyer une requête au BN avec un unicast pour obtenir la liste des adresses MAC de tous les nœuds (cela va permettre d'éviter d'inonder le réseau).
  2. Le BN va lui répondre en mettant dans le payload la liste des adresses MAC de tous les nœuds.
  3. Le nouveau nœud va ensuite envoyer un ping à chacun des nœuds de la liste jusqu'à recevoir le premier pong, ce qui marquera le début de la propagation de pair en pair de son adresse MAC pour tous les autres nœuds du réseau (ainsi, tous les nœuds du réseau vont pouvoir connaître ce nouveau nœud).

Pour identifier les nœuds qui ont crash, à intervalles réguliers T temps, le BN va mettre à jour sa liste des nœuds

  1. Pour chaque adresse des nœuds du réseau, envoyer un ping à chacun pour savoir si les nœuds sont vivants.
  2. Après un timeout t (avec t < T), si le BN n'a pas reçu le pong de la part d'un nœud, il va le considérer comme mort durant la session en cours (de période T).
  3. Si jamais le BN reçoit le pong d'un noeud précédemment considéré comme mort, alors le BN va attendre la prochaine session (de période T) pour mettre à jour sa liste des nœuds et le reconsidérer comme vivant.

Avantages et inconvénients

Avantages

Simplicité : le protocole est relativement simple à comprendre et à mettre en place.

Economie d'énergie : possibilité d'utiliser une version "Slotted" de ce protocole afin de désactiver les antennes de communication la plupart du temps.

Inconvénients

Sécurité : étant donné le fonctionnement par flooding de ce protocole, il devient difficile d'assurer une confidentialité dans ce réseau.

Performance : le fonctionnement par flooding solicite beaucoup les canaux de communication.

Identifier les capteurs de façon unique

Pour pouvoir identifier les capteurs entre eux, nous avions besoin de récupérer un identifiant unique. Dans un réseau classique, c'est une adresse unique à un matériel et attribuée par le fabricant. Nous pouvons émuler ce fonctionnement en allant chercher une valeur pré-flashée dans chaque capteur à l'adresse 0x10F0. Il est possible que certains capteurs n'aient pas d'adresse Mac directement associée, dans ce cas, il est possible de d'utiliser cet utilitaire pour flasher les cartes.

Récupération de l'adresse Mac

Problèmes pas encore résolus

Un phénomène survient lorsqu'on branche les capteurs et qu'on visualise les communications à travers le port série via l'outil Minicom. Lorsqu'on lance donc le programme Minicom sur les ports USB qui hébergent les capteurs, des messages apparaissent dans le terminal. Il faut noter que lors de chaque INPUT clavier effectué dans Minicom, l'interruption est levé dans notre code et un message de type Query est envoyé dans le réseau. Ces messages peuvent provenir :

  • Soit d'une initialisation effectuée par Minicom
  • Soit d'une initialisation dans le protocole de synchronisation des ports USB
  • Soit d'interruptions du type INPUT qui sont générés au début du programme flashés dans les capteurs

Etapes pour tester le code

Si vous voulez tester le travail décrit plus haut, cette section est faite pour vous. Pour cela, il vous faut:

  • au moins deux capteurs ez430-RF2500
  • avoir son environnement de travail prêt (voir prérequis plus haut)

Les étapes:

  • tout d'abord il faut connecter les capteurs aux ports USB de la machine (utilisez un hub USB si vous n'avez pas assez de ports)
  • depuis la racine du projet, faire make run (cette commande crée un container docker avec tous les outils de compilation nécessaire pour générer l'exécutable du protocole. ILvous connecte à ce container dans le dossier où se trouve le code du protocole)
  • faire make txrx_gnutella pour générer l'exécutable. (Le code du protocole Gnutella est dans txrx_gnutella.c)
  • faire make flash_txrx_gnutella pour flasher le code. ATTENTION: le code n'est flashé que sur un capteur à la fois (quand le code est flashé les leds du capteur en question clignotent), il faut donc à chaque fois déconnecter du port USB le capteur qui vient d'être flashé et refaire la commande pour flasher un autre capteur , jusqu'à ce que tous les capteurs aient été flashés. Aussi, entre chaque flash il faut changer la variable myData dans txrx_gnutella.c pour simuler le fait que les capteurs possèdent des données différentes
  • une fois tous les capteurs flashés, les reconnecter. Puis pour chaque capteur lancer minicom dans un terminal avec minicom -D /dev/ttyACM{indice du capeur} avec indice du capteur allant de 0 à nombre de capteurs - 1 . A ce stade vous êtes prêts à observer les messages des capteurs. NB: comme dit plus haut, il se peut que des choses s'affichent au début sans que vous ayez rien touché sur les capteurs. Cela vient du fait que minicom fait des interruptions clavier au lancement de minicom, et comme on traite les interruptions clavier dans le code, des envois de paquets se déclenchent et les capteurs affichent des messages. Il faut donc patienter que tout se soit stable et effacer les terminaux minicom (avec ctrl A puis C)
  • ici, les terminaux minicom sont vides (rien ne se passe).
  • pour faire une QUERY à partir d'un capteur, il faut saisir un texte (c'est la donnée du query) dans le terminal minicom correspondant au capteur, puis soit faire Entrée soit saisir plus de 16 caractères.
  • pour faire un PING il faut appuyer sur le bouton poussoir du capteur correspondant
  • quand un capteur reçoit un PING d'un nouveau capteur il affiche "PING" puis sa table des adresses. Quand il reçoit un PING d'un capteur qu'il connait déjà il affiche "ALREADY EXISTS" puis "FORWARD" pour dire qu'il forward le paquet PING
  • quand un capteur reçoit un PONG, il affiche "PONG" puis sa table d'adresse si le PONG lui est destiné, "FORWARD" sinon (il forward juste le paquet
  • si le paquet reçu a un ttl <= 0 le capteur affiche "TTL <= 0" puis "DROP" pour dire qu'il jette le paquet
  • si un capteur reçoit un QUERY, il affiche "UNKNOWN" puis "DROP" si il ne connait pas l'adresse de l'émetteur du paquet, "NO DATA" puis "FORWARD" si il connait l'émetteur du paquet mais qu'il n'a pas la donnée demandée par l'émetteur, "QUERY_HIT" si il connait le capteur émetteur et qu'il a la donnée demandée
  • quand un paquet reçoit un QUERY_HIT, il affiche "RECEIVED : < la donnée reçue >"

Sources