Contrôle d'accéléromètre, 2014/2015, TD2

De Wiki d'activités IMA
Révision datée du 16 avril 2015 à 13:17 par Nrichez (discussion | contributions) (Troisième séance : Déploiement sur FOX Board)

Présentation du Projet

Ce projet sur les systèmes communicants intitulé "Contrôle d'accéléromètre" est réalisé par les élèves suivants :

  • Manuel BUENO
  • Vianney PAYELLE
  • Nathan RICHEZ
  • Maxime SZWECHOWIEZ


L'objectif de ce projet et d'utiliser un accéléromètre en tant que télécommande.
En effet, grâce à cet accéléromètre, nous devons être capable d'interagir "en direct" avec une interface web.

Partie Informatique

Responsables : Vianney PAYELLE & Nathan RICHEZ

Préambule

Nous avons, pour commencer, un accéléromètre à notre disposition.
Cet accéléromètre est relié à un arduino nano et un module de communication sans fil (Wifi / Protocole ZigBee).
Ce module de communication est relié à un ordinateur via un port USB, cet ordinateur étant un premier support pour recevoir les données envoyées par l'accéléromètre.
Pour terminer, nous pouvons développer une page internet sur l'ordinateur afin d'afficher les valeurs envoyées de l'accéléromètre (cf. schéma ci dessous).

Schema1 P INFO.png


Dans l'avenir, lorsque l'accéléromètre utilisé dans la partie électronique sera finalisé, cet accéléromètre devra être relié à une NanoBoard qui elle même sera reliée en série au serveur websocket.
Ce serveur sera hébergé sur une FoxBoard, qui hébergera également la page internet.
Nous pourrons accéder à la FoxBoard par protocole TCP-IP (cf. schéma ci dessous).

Schema2 P INFO.png

Première séance : Prise en main et définition des tâches

Lors de cette séance, nous avons d’abord pris connaissance du sujet puis nous nous sommes intéressés sur le fonctionnement de l’accéléromètre.
Grâce à un programme C fourni comme aide, légèrement modifié pour pouvoir configurer le bon mode de transmission (à savoir USB) nous avons compris que l'accéléromètre envoie 4 octets représentant respectivement l'accélération sur chacune des trois axes de l'espace ainsi que l'état des 2 boutons.


Nous avons voulu par la suite nous intéresser directement à la page html servant d'interface web pour le projet.
Nous avons, grâce à des codes en exemple, vu le fonctionnement des Canvas sous Java. Nous avons essayé de produire un tableau de carrés de taille 3x2 pour commencer, malheureusement sans succès.
Nous voulons, pour le projet, faire un tableau de carrés de taille assez grande (4x4 pour commencer) pour être capable de dessiner avec l'accéléromètre.
En effet, en fonction de la position de l’accéléromètre, un carré sera colorié (en vert clair par exemple) pour pouvoir nous situer dans le tableau.
Puis avec l'appui du premier bouton, le carré colorié en vert clair (donc le carré sélectionné) deviendra noir et le restera quelque soit les futurs mouvements de l'accéléromètre.


Carres.gif
Exemple sur un tableau 8x8


Nous pouvons aussi ajouter une fonction de gomme sur le deuxième bouton (le carré sélectionné redeviendra blanc) ou une fonction changement de couleur (ce qui revient à la gomme si la couleur choisie est le blanc).



N'arrivant pas à faire un simple petit tableau, nous allons essayer, lors de la prochaine séance, de nous concentrer sur la réception et la diffusion des données envoyées par l'accéléromètre.
Par exemple, afficher les valeurs pour les axes X,Y et Z et l'état des boutons sur une page html (qui exécutera du java script), et ce, via la NanoBoard et la FoxBoard et non plus un ordinateur.

Deuxième séance : Le WebSocket

Nous avons modifié et fusionné des codes fournis en exemple afin de pouvoir recevoir et envoyer les données de l'accéléromètre via websocket.
D'abord, nous avions une page html exécutant du javascript et relié au websocket qui affichait continuellement les données envoyés par l'accéléromètre.
Les données sont reçu sous forme d'un mot de plusieurs octets comme par exemple '125 126 102 0' représentant respectivement les accélérations suivant les axes X, Y et Z, ainsi que l'état des boutons.


Nous avons alors ajouté un petit bout de code en C nous permettant non plus d'envoyer la valeur des accélérations et l'état des boutons, mais plutôt d'envoyer 0 ou 1 en fonction de la valeur des accélérations et l'état des boutons.
Ces 0 ou 1 sont toujours envoyés en tant que mot et se présente de la manière suivante : 'X+ X- Z+ Z- B1 B2' donc par exemple '0 0 1 0 0 1' si on penche l'accéléromètre vers la droite tout en appuyant sur le bouton 2.
L'accélération suivant l'axe Y ici n'est pas utilisée car nous interagissons avec un plan 2D. De plus, nous gérons l'accélération grâce à la gravité, et non pas grâce au mouvement du poignet.
Nous avons donc défini des seuils pour chaque inclinaison de l'accéléromètre.


Transcode.png
Exemple de conversion pour un déplacement dans le sens X+ avec les 2 boutons enfoncés


Acc seuil.png
Schéma illustrant les seuils en fonction de l'inclinaison de l'accéléromètre


Extrait du code convertissant les valeurs reçues :

for(i=0;i<4;i++)
{
  if(read(sd,&val[i],sizeof(char))!=1)
  {
    perror("callback_my.read");
    exit(-1);
  }
}

if(val[0]>=140)
{
  Xp=1;
}

if(val[0]<=110)
{
  Xm=1;
}

if(val[2]>=140)
{
  Zp=1;
}
      
if(val[2]<=110)
{
  Zm=1;
}

if(val[3]==1 || val[3]==3)
{
  B1=1;
}

if(val[3]==2 || val[3]==3)
{
  B2=1;
}

char *out=message+LWS_SEND_BUFFER_PRE_PADDING;
sprintf(out,"%d %d %d %d %d %d",Xp,Xm,Zp,Zm,B1,B2);
libwebsocket_write(wsi,(unsigned char *)out,strlen(out),LWS_WRITE_TEXT);
libwebsocket_callback_on_writable(this,wsi);



Nous devons maintenant séparer ces valeurs, certainement à l'aide d'une fonction 'split' (découpe une chaine de caractère), puis les ranger dans des variables.
En effet, nous pourrons utiliser ces variables pour la gestion des carrés. Par exemple lors que 'X+' est à 1, le carré à droite du précédent sera sélectionné et pourra être colorié si on appuie sur le bouton 1.
Nous devons encore refaire tout notre interface html et notre programme en java script pour la gestion des "canvas" (c'est à dire des carrés), ainsi qu'exporter tout notre travail sur une foxboard en espérant de pas avoir de problème.

Troisième séance : Déploiement sur FOX Board

Lors de cette séance, notre travail a légèrement été retardé par la présence des 6èmes et des CM2 (absence pour trouver un mindstorm).


Nous avons réussi à stocker nos 0 et nos 1 dans un tableau à l'aide de la fonction split, puis nous les attribuons aux différentes variables.

tmp = message.data.split(' ');
Xp = tmp[0];
Xm = tmp[1];
Zp = tmp[2];
Zm = tmp[3];
B1 = tmp[4];
B2 = tmp[5];

Nous nous sommes familiarisé avec la FOX Board et nous l'avons configuré de tel manière à pouvoir y accéder depuis l'adresse IP suivante : 172.26.79.2
Après avoir fait quelques modifications pour installer la librairie websocket, nous avons importé nos fichiers pour tester le serveur websocket et le html.


Il y a deux dossiers websocket différents car nous devons différencier le serveur websocket fonctionnant avec l'arduino de celui fonctionnant avec la NanoBoard.
Par défaut, nous utiliserons celui avec l'arduino car il est entièrement opérationnel, tandis que nous ne pouvons pas tester celui pour la NanoBoard.
Le code pour la NanoBoard est légèrement différent de celui pour arduino car les données envoyées par la NanoBoard ne sont pas les mêmes que celles envoyées par l'arduino).
De plus, le websocket pour arduino se lance au démarrage de la FOX Board grâce à une ligne d’exécution ajoutée dans le fichier /etc/rc.local.


Fox arbo.png
Schéma représentant l'arborescence pour nos fichiers

NOus avons établi un nouveau proto Pour adapter notre programme initialement prévu pour l'arduino et le rendre compatible avec la NanoBoard, nous avons ajouter une fonction capable de décoder les nouveaux messages.
De plus, les seuils sont modifiés car les valeurs sont codées sur 5 bits.

Cette fonction transforme l'octet reçu en tableau binaire :

unsigned char bin_to_char(char bin[])
{
  int    i,j;
  int    res;
  int temp=1;
  res = bin[0]-48;
  for (i = 8; i >0 ; i--)
  {
    if (bin[i] == '1')
    {
      for(j=0;j<i;j++)
      {
        temp=2*temp;
      }
      res = res + temp;
      temp=1;
    }
  }
  return (res);
}

Celle-ci convertit le tableau binaire en valeur

void binaire(char a, char bin[])
{
    int i = 0;
 
    for(i = 0 ; i < 8 ; i++)
    {
        bin[i] = (a & 1) + '0';
        a >>= 1;
    }
    bin[8] = '\0';
}

L'utilisation se fait de la sorte suivante :

//On transforme l'octet en tableau de caractère '0' ou '1'
binaire(val,bin);

// Si on a le code X : 000xxxxx
if (bin[7]=='0' && bin[6]=='0' && bin[5]=='0')
{
  X=val;
}
// Si on a le code Y : 001xxxxx
if (bin[7]=='0' && bin[6]=='0' && bin[5]=='1')
{
  bin[5]=0;
  Y=bin_to_char(bin);
}
// Si on a le code Z : 010xxxxx
if (bin[7]=='0' && bin[6]=='1' && bin[5]=='0')
{
  bin[6]=0;
  Z=bin_to_char(bin);
}
// Si on a le code B : 011xxxxx
if (bin[7]=='0' && bin[6]=='1' && bin[5]=='1')
{
  bin[5]=0;bin[6]=0;
  B=bin_to_char(bin);
}


Lien de l'application : http://172.26.79.2/controle_accelerometre/index.html
Un fichier readme.txt est disponible à l'emplacement suivant de la FOX Board : /root/server/



Tout nos tests étant concluants, nous avons pu nous concentrer sur le développement de l'interface de dessin (html + js).

Séances supplémentaires : Interface de dessin (html + js)

Le fichier html fait appel aux fichiers square2.js et jquery.js pour charger toutes les fonctions liées au dessin et au websocket.
Le fichier style.css est présent dans le dossier mais n'est pas utile car on ne s'en sert pas. Il pourrait l'être si nous voulions faire une jolie mise en forme pour l'application.

Nous présentons les différentes valeurs de l'accéléromètre en valeur booléenne ainsi que la position du curseur sur le tableau via des balises "div" dans le fichier html.

$('#varXp').html($('<p>',{ text:  "X+ = " + Xp }));
$('#varXm').html($('<p>',{ text:  "X- = " + Xm }));
$('#varZp').html($('<p>',{ text:  "Z+ = " + Zp }));
$('#varZm').html($('<p>',{ text:  "Z- = " + Zm }));
$('#varB1').html($('<p>',{ text:  "B1 = " + B1 }));
$('#varB2').html($('<p>',{ text:  "B2 = " + B2 }));
$('#varposX').html($('<p>',{ text:  "posX = " + posX }));
$('#varposZ').html($('<p>',{ text:  "posZ = " + posZ }));
<div id="varXp"></div>
<div id="varXm"></div>
<div id="varZp"></div>
<div id="varZm"></div>
<div id="varB1"></div>
<div id="varB2"></div>
<div id="varposX"></div>
<div id="varposZ"></div>


Un tableau de 8x8 y est implémenté avec pour chaque cellule un canvas qui à son propre id.
Nous avons ajouté un bouton pour remettre le tableau blanc.

<tr id="buttonValider">
    <td>
        <input type="button" value="Remettre le tableau blanc" onclick="reset()" />
    </td>
</tr>
<table id='canvas'>
<tr>
  <td><canvas id="square0" width="40" height="40" ></canvas></td>
  <td><canvas id="square1" width="40" height="40" ></canvas></td>
  <td><canvas id="square2" width="40" height="40" ></canvas></td>
  <td><canvas id="square3" width="40" height="40" ></canvas></td>
  <td><canvas id="square4" width="40" height="40" ></canvas></td>
  <td><canvas id="square5" width="40" height="40" ></canvas></td>
  <td><canvas id="square6" width="40" height="40" ></canvas></td>
  <td><canvas id="square7" width="40" height="40" ></canvas></td>
</tr>
<tr>
  <td><canvas id="square8" width="40" height="40" ></canvas></td>
  <td><canvas id="square9" width="40" height="40" ></canvas></td>
  <td><canvas id="square10" width="40" height="40" ></canvas></td>
  <td><canvas id="square11" width="40" height="40" ></canvas></td>
  <td><canvas id="square12" width="40" height="40" ></canvas></td>
  <td><canvas id="square13" width="40" height="40" ></canvas></td>
  <td><canvas id="square14" width="40" height="40" ></canvas></td>
  <td><canvas id="square15" width="40" height="40" ></canvas></td>
</tr>
<tr>
  <td><canvas id="square16" width="40" height="40" ></canvas></td>
  <td><canvas id="square17" width="40" height="40" ></canvas></td>
  <td><canvas id="square18" width="40" height="40"></canvas></td>
(...)
  <td><canvas id="square58" width="40" height="40" ></canvas></td>
  <td><canvas id="square59" width="40" height="40" ></canvas></td>
  <td><canvas id="square60" width="40" height="40" ></canvas></td>
  <td><canvas id="square61" width="40" height="40" ></canvas></td>
  <td><canvas id="square62" width="40" height="40" ></canvas></td>
  <td><canvas id="square63" width="40" height="40" ></canvas></td>
</tr>


Le fichier square2.js comporte 4 éléments principaux.

L'initialisation des variables :

var couleurs=['white'];
var id=0;
var posX=3;
var posZ=3;


Le dessin des carrés au sein des canvas selon leurs ids :

function dessinCarre(id)
{
    /* récupération du canvas */
    var canvas=document.getElementById('square'+id);
    var context=canvas.getContext('2d');

    context.fillStyle = couleurs[id];
    context.fillRect( '0', '0', '40', '40');

    context.strokeStyle='black';
    context.lineWidth = 2;
    context.strokeRect('0','0','40','40');
}

function dessinCarres()
{
    var i;
    for(i=0;i<64;i++) dessinCarre(i);
}


Une fonction qui remet tous les carrées en blanc lorsqu'on clique sur le bouton :

function reset()
{
    var i;
    for(i=0;i<64;i++) 
    {
        couleurs[i]='white'
        dessinCarre(i);
    }
}


Une fonction conditionnel qui modifie la couleur de certains carrés selon les valeurs de l'accéléromètre :
- Gris : Coloration temporaire pour signaler la position actuelle
- Noir : Colore la position actuelle de manière permanente en noir
- Blanc : Colore la position actuelle de manière permanente en blanc (effacer)

function Dessin(Xp,Xm,Zp,Zm,B1,B2,time)
{
    var XpD=Xp,XmD=Xm,ZpD=Zp,ZmD=Zm,B1D=B1,B2D=B2;
	
    if(couleurs[id]=='gray')
    {
	couleurs[id]='white';
        dessinCarre(id);
    }  

    if(XmD==1){posX=posX+1;}
    if(XpD==1){posX=posX-1;}
    if(ZpD==1){posZ=posZ+1;}
    if(ZmD==1){posZ=posZ-1;}

    if(posX>=7){posX=7;}
    if(posX<=0){posX=0;}
    if(posZ>=7){posZ=7;}
    if(posZ<=0){posZ=0;}
    id=posZ+(posX*8);

    if(couleurs[id]=='white'&& B1D==0 && B2D==0 )
    {
        couleurs[id]='gray';
        dessinCarre(id);
    }
    if(B1D==1 && B2D==0 )
    {
        couleurs[id]='black';
        dessinCarre(id);
    }
    if(B1D==0 && B2D==1 )
    {
	couleurs[id]='white';
	dessinCarre(id);
    }
}

Partie Electronique

Responsables : Manuel BUENO & Maxime SZWECHOWIEZ

Préambule

Dans cette partie, on cherche à récupérer les données analogiques générées par un module gyroscopique, de les numériser à l'aide d'une nanoboard, puis de les transmettre. Notre travail consiste donc à programmer le module FPGA de la nanoboard grâce à altium.

Première séance

Lors de cette première séance, nous avons commencé par prendre en main le logiciel altium, et de se familiariser avec la nanoboard. De plus, nous avons analysé le sujet et les objectifs et en avons déduit les moyens de les mettre en oeuvre.

Voici donc le schéma de principe :

//Jpeg en attente

La partie FPGA est programmée sous altium:

Premier Montage sous Altium

La PWM (5bits) est filtrée par un passe-bas passif dont la fréquence de coupure se situe entre la fréquence de la PWM et celle de remise à 0 du compteur 5 bits afin d'obtenir un signal en dent de scie.

PWM brute en sortie de Nanoboard
Schéma électronique du filtre passe_bas
PWM filtrée



Ce signal est comparé avec les 3 signaux analogiques x, y et z du module gyroscopique grâce à 3 ampli-op. Les résultats des comparateurs sont récupérés par la nanoboard afin de figer la valeur des compteurs. Les valeurs des 3 signaux sont stockées dans des bascules D, en attente d'être envoyés en liaison série. (Pour l'instant, une seule valeur est stockée dans notre programme FPGA actuel).

Deux boutons de la nanoboard sont utilisés pour accéder aux modes stylo et effaceur mis en place dans la partie informatique. Leur valeur est stockée dans les deux premiers bits d'un un 4e octet.

L'envoi des données se fait sur un port redirigé vers un module de communication série. Un multiplexeur associé à un compteur nous permet d'envoyer sur ce port les 4 octets de données les uns après les autres. Lorsque les 4 octets sont envoyés, un bit de communication est mis à 1. (ce bit n'est pas encore configuré correctement).

Deuxième séance

Nous avons finalisé la partie électronique analogique lors de cette séance.

Nous avons monté les comparateurs selon le schéma suivant:

Schéma électronique comparateurs


La sortie de l'ampli OP est redressée par un montage à diode redresseur simple afin d'avoir un signal à la masse correspondant à un bit numérique à 0 lorsque le comparateur est à -Vsat.

Les amplis-OP sont alimentés sous +/-4.5V afin d'avoir un signal à ~3.3V correspondant à un bit numérique à 1 lorsque le comparateur est à +Vsat.

Le schéma précédent a été répliqué 3 fois afin de gérer la comparaison de la PWM avec les signaux x, y et z du module gyroscopique.

Voici finalement le schéma principal de la partie analogique ainsi que le montage une fois réalisé:

Montage analogique
Montage analogique


Nous avons aussi finalisé le programme FPGA:

Montage modifié


Cette nouvelle version permet de gérer les trois signaux x,y,z. Une modification a aussi été apportée au niveau du blocage de la valeur PWM lorsque les bits de comparaison passent à 1. L'ancien système bloquait la valeur PWM lorsque le signal était à 1, le nouveau le bloque sur un front montant.

Un protocole de communication a été défini. Les 4 octets x,y,z,boutons sont envoyés à la suite, puis le bus de communication est mis à 0 pendant 4 coups d'horloge, avant de renvoyer 4 nouveaux octets, et ainsi de suite. Le bit de transmission a été configuré par défaut, il passe à 1 lors de l'envoi du dernier octet (boutons), sinon il est à 0. La fréquence d'envoi des octets a été fixée par défaut à la même horloge que le compteur 5 bits. Ces deux derniers paramètres sont susceptibles de changer en fonction des caractéristiques du module de communication série.

FullSizeRender.jpg

Troisième séance

Nous avons modifié le protocole de communication série. Dans sa version initiale, la Foxboard envoyait un signal pour demander les 4 octets de données. Les octets étaient donc reçus dans l'ordre.

Dans notre cas, nous envoyons les octets en permanence, à une cadence relativement lente (~20Hz). Le websocket reçoit donc des octets en permanence, mais ne peut pas faire la différence entre les données X,Y,Z ou l'état des boutons.

Les mesures effectuées, ne sont codées que sur 5 bits de donnée sur 8. Nous avons donc décidé de nous servir des 3 derniers bits pour différencier les octets de donnée:

octet X : 000x xxxx octet Y : 001x xxxx octet Z : 010x xxxx octet boutons : 011x xxxx

Nous avons donc modifié le programme FPGA en conséquence :

Introduction des identifiants des octets

Séance supplémentaire

Nous avons entrepris de réunir la partie informatique et électronique. Le module de liaison série fourni ne fonctionnant pas sur notre Nanoboard (pour x raison), nous avons récupéré un fichier schematic d'un des groupes responsables du projet "Liaison série". Ce fichier étant une ébauche et ne fonctionnant pas, nous nous en sommes inspirés pour développer notre propre programme FPGA d'émission série:


Serie.png

Et nous l'avons intégrés sur notre programme FPGA:

Insertion du module série

Nous avons observé le bon fonctionnement de notre liaison série à l'oscilloscope: //Image oscillo

On observe bel et bien un bit de start, nos données sur 5 bits, et les 3 derniers bits d'identification à une fréquence de 9600 Hz.

Néanmoins, lors de la lecture des octets par le programme websocket 2 sur la nanoboard, les caractères directement reçus ne correspondent pas à ceux visualisés à l'oscilloscope. Nous avons essayé en vain de régler le problème, en ... à suivre

!!! Solution peut etre trouvée, refaire un essai (problème résolu: liaison série était montée à l'envers; image ci-dessus modifiée en conséquence)