Projet IMA3 P4, 2015/2016, TD2 : Différence entre versions

De Wiki de Projets IMA
(Réalisation du serveur WebSocket)
(Réalisation du serveur WebSocket)
Ligne 136 : Ligne 136 :
 
  return fd;
 
  return fd;
 
  }
 
  }
+
 
 
  //
 
  //
 
  // Serial port configuration
 
  // Serial port configuration
Ligne 151 : Ligne 151 :
 
  if(tcsetattr(fd,TCSANOW,&new)<0){ perror("serialInit.tcsetattr"); exit(-1); }
 
  if(tcsetattr(fd,TCSANOW,&new)<0){ perror("serialInit.tcsetattr"); exit(-1); }
 
  }
 
  }
+
 
 
  //
 
  //
 
  // Serial port termination
 
  // Serial port termination
Ligne 158 : Ligne 158 :
 
  close(fd);
 
  close(fd);
 
  }
 
  }
+
 
 
  static int callback_http(
 
  static int callback_http(
 
   struct libwebsocket_context *this,
 
   struct libwebsocket_context *this,
Ligne 166 : Ligne 166 :
 
  return 0;
 
  return 0;
 
  }
 
  }
+
 
 
  static int callback_my(
 
  static int callback_my(
 
   struct libwebsocket_context * this,
 
   struct libwebsocket_context * this,
Ligne 188 : Ligne 188 :
 
   case LWS_CALLBACK_SERVER_WRITEABLE:
 
   case LWS_CALLBACK_SERVER_WRITEABLE:
 
                 // Ici sont envoyes les messages au navigateur
 
                 // Ici sont envoyes les messages au navigateur
   
+
     
 
     if(read(sd,&x,1)==1)
 
     if(read(sd,&x,1)==1)
 
       {
 
       {
Ligne 204 : Ligne 204 :
 
  return 0;
 
  return 0;
 
  }
 
  }
 
+
 
  static struct libwebsocket_protocols protocols[] = {
 
  static struct libwebsocket_protocols protocols[] = {
 
   {
 
   {
Ligne 215 : Ligne 215 :
 
   {NULL,NULL,0,0}
 
   {NULL,NULL,0,0}
 
   };
 
   };
 
+
 
  int main(void) {
 
  int main(void) {
 
  int port=9000;
 
  int port=9000;
Ligne 230 : Ligne 230 :
 
   }
 
   }
 
  printf("starting server...\n");
 
  printf("starting server...\n");
+
 
 
  sd=serialOpen(SERIAL_DEVICE,SERIAL_BOTH);
 
  sd=serialOpen(SERIAL_DEVICE,SERIAL_BOTH);
 
  serialConfig(sd,B9600);
 
  serialConfig(sd,B9600);
+
 
 
  while(1){
 
  while(1){
 
   libwebsocket_service(context,WAIT_DELAY);
 
   libwebsocket_service(context,WAIT_DELAY);
 
   }
 
   }
+
 
 
  serialClose(sd);
 
  serialClose(sd);
 
   
 
   

Version du 2 juin 2016 à 16:02

Projet IMA3-SC 2015/2016 : Accéléromètre

Cahier des charges

Pour notre projet systèmes communicants nous avons choisi de contrôler un pointeur via un accéléromètre. Pour cela, nous utiliserons l'accéléromètre comme un détecteur d'inclinaison. Selon l'angle d'inclinaison nous ferons se déplacer un "pointeur" à l'écran. L'application web sera utilisée pour l'animation du pointeur, avec une page en HTML5 et des canvas pour réaliser notre page web dynamique JavaScript où sera faite l'animation. La communication entre l'application web et le port série (accès matériel) se fera en utilisant un serveur websocket. Via ce serveur nous pourrons avoir une communication en temps réel ce qui permettra d'avoir une animation qui réagira rapidement au mouvement de l'accéléromètre. Pour accéder aux données fournies par notre accéléromètre nous devrons utiliser une conversion analogique numérique. Pour cela, on utilise des signaux PWM. Le filtrage de ces signaux (filtre passe-bas) permet d'obtenir une tension en "dents de scie" (variations de la valeurs moyenne) et ensuite cette tension est comparée aux valeurs de l'accéléromètre.


Matériel nécessaire:
• Une rasberry pi
• Une nanoboard
• Un accéléromètre
• Une résistance et un condensateur (filtre passe-bas)


Optionnel:
• Bouton poussoir

Séance 1

Partie électronique

Analyse des objectifs

Nous avons d'abord commencé par établir les objectifs et rechercher la façon de réaliser notre partie électronique. La partie électronique aura pour rôle de récupérer les données fournies par l'accéléromètre et de les envoyer vers le serveur. L'accéléromètre fournit une tension qui correspond à une valeur d'accélération, pour que cette donnée sois exploitable par notre application web, il nous faut récupérer une valeur numérique c'est pourquoi nous devons réaliser une conversion analogique-numérique (CAN). La méthode proposée est l'utilisation d'un signal PWM pour fournir un signal en dents de scie après filtrage. Cette méthode est appelée conversion simple rampe. Le signal analogique à convertir est comparé à la valeur de la rampe, une fois que la rampe atteint la valeur du signal, la sortie du comparateur passe à 1.


Figure 1: Principe de la comparaison


Voici en principe comment devrait fonctionner la conversion. Nous utilisons un compteur qui s'incrémente à une fréquence donnée et est remis à zéro quand le comparateur passe à 1. Nous sauvegardons la valeur du compteur à ce moment là.


Figure 2: Schéma de principe

Ce système nous permet de récupérer une valeur numérique proportionnelle à la valeur analogique. Posons a le coefficient directeur de la rampe tel que V=at, ainsi si le signal analogique à la valeur Ve est égal à la rampe au temps Te nous avons Te=\frac{Ve}{a}. Le compteur s'incrémente à une fréquence fh, N est la valeur atteinte avant la raz, nous avons donc N=fhTe, ce qui nous donne N=\frac{fh}{a}Ve.

Prise en main d'Altium

Nous avons ensuite pris en main le logiciel Altium en suivant le tutoriel proposé. Ce tutoriel consiste en la réalisation d'un compteur 4 bits et de son affichage sur les leds disponibles sur la nanoboard:

Figure 3: Compteur 4 bits

Réalisation d'une PWM

Après avoir compris plus clairement le fonctionnement d'Altium et de la nanoboard (compilation du programme et envoi sur la nanoboard), nous avons étudié la façon de réaliser une PWM. Nous avons étudié le fonctionnement du bloc PWM disponible sur Altium afin de comprendre comment l'utiliser. Nous avons donc réalisé le schematic suivant:

Figure 4: Utilisation du bloc PWM

L'entrée D[7..0] est la valeur du rapport cyclique voulu, l'entrée C est la fréquence du signal. Notre programme envoie un signal de type MLI sur la sortie HA2.

Partie informatique

Réalisation du prototype Arduino de l'accéléromètre

Lors de cette première séance, nous avons commencé par mettre au point le prototype de notre accéléromètre avec une carte Arduino. Nous avons connecté l'accéléromètre à la carte Arduino (voir figure 1) puis nous avons réalisé un programme permettant de récupérer et convertir les valeurs analogiques que renvoie la carte à l'ordinateur via le port USB en valeurs numériques de type entier. Les valeurs analogiques envoyées par l’accéléromètre à l'ordinateur (des tensions allant de 0 à 3,3 Volts) correspondent aux accélérations selon les axes x, y et z. Le code de notre programme Arduino est donné ci-dessous :

int x, y, z;
void setup() 
 {
  Serial.begin(9600);
 }
void loop() 
 {
  x=analogRead(0);
  y=analogRead(1);
  z=analogRead(2);
  Serial.print("accelerations are x, y, z: ");
  Serial.print(x, DEC);
  Serial.print(" ");
  Serial.print(y, DEC);
  Serial.print(" ");
  Serial.println(z, DEC);
  delay(100);
 }

Nous avons ensuite testé le programme en en affichant les valeurs numériques de x, y et z envoyées sur le port série de la carte Arduino pour différentes accélérations de l'accéléromètre. Les valeurs numériques de x, y et z sont comprises entre 0 et 500, elles varient bien quand on bouge l'accéléromètre.

Arduino
Figure 1 - Prototype Arduino de l'accéléromètre

Configuration du Raspberry Pi

Lors de cette première séance nous avons aussi configuré et testé notre Raspberry Pi.
Nous avons tout d'abord connecté le Raspberry Pi à l'ordinateur fixe via un câble série afin de configurer sa carte Ethernet. On utilise le logiciel minicom pour accéder à la Raspberry et l'éditeur de texte nano pour copier les lignes de code suivantes dans le fichier /etc/network/interfaces :

auto eth0
iface eth0 inet static
  address 172.26.79.9
  netmask 255.255.240.0
  gateway 172.26.79.254

On indique aussi le serveur DNS dans le fichier /etc/resolv.conf :

nameserver 193.48.57.34

Nous pouvons maintenant accéder à notre Raspberry Pi via un câble ethernet et la commande ssh pi@172.26.79.9 sur la console linux de l'ordinateur fixe.
On installe ensuite le paquetage libwebsockets-dev qui contient une bibliothèque C de WebSocket qui sera utile par la suite à l'aide de la commande apt-get ainsi que le paquetage apache2 qui permet de créer un serveur HTTP sur la Raspberry. Le fichier HTML de notre interface web sera copié dans le répertoire /var/www. On télécharge aussi la bibliothèque Web 2.0 jquery.js sur le site http://jquery.com/. Cette bibliothèque permet de lancer des requêtes HTTP asynchrones mais aussi de faciliter la programmation JavaScript.
Pour vérifier le bon fonctionnement du serveur web HTTP, on le teste avec le serveur WebSocket donné en exemple sur le sujet du projet ainsi que la page HTLM qui se connecte dessus. Les chaînes tapés dans le champ texte de la page HTML sont envoyés au serveur WebSocket puis retournées par le serveur au navigateur Web et affichées dans une balise div. On copie le fichiers jquery.js et le fichier HTML dans /var/www et le fichier C du WebSocket dans /pi/home. On compile et on exécute le fichier C et on tape l’adresse 172.26.79.9 dans un navigateur web pour accéder à l'interface web. Tout fonctionne correctement, nous pourrons utiliser la Raspberry avec notre WebSocket et notre interface web. La commande d'exécution du serveur WebSocket devra être écrite dans le fichier /etc/rc.local pour qu'il s'exécute automatiquement au démarrage du Raspberry.

Séance 2

Partie électronique

Partie informatique

Réalisation du serveur WebSocket

Lors de cette séance, nous avons réalisé le serveur WebSocket permettant la communication entre le navigateur et le port série. Le code en C de notre WebSocket est donné ci-dessous :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <linux/serial.h>
#include <libwebsockets.h>

#define MAX_FRAME_SIZE  1024
#define WAIT_DELAY      50
#define MAX_MESSAGE     12

#define SERIAL_READ 0
#define SERIAL_WRITE 1
#define SERIAL_BOTH 2

#define SERIAL_DEVICE "/dev/ttyUSB0"

int sd;

//
// Open serial port device
//
int serialOpen(char *device,int mode){
int flags=(mode==SERIAL_READ?O_RDONLY:(mode==SERIAL_WRITE?O_WRONLY:O_RDWR));
int fd=open(device,flags|O_NOCTTY|O_NONBLOCK); 
if(fd<0){ perror(device); exit(-1); }
return fd;
}
 
//
// Serial port configuration
//
void serialConfig(int fd,int speed){
struct termios new;
bzero(&new,sizeof(new));
new.c_cflag=CLOCAL|CREAD|speed|CS8;
new.c_iflag=0;
new.c_oflag=0;
new.c_lflag=0;     /* set input mode (non-canonical, no echo,...) */
new.c_cc[VTIME]=0; /* inter-character timer unused */
new.c_cc[VMIN]=1;  /* blocking read until 1 char received */
if(tcsetattr(fd,TCSANOW,&new)<0){ perror("serialInit.tcsetattr"); exit(-1); }
}
 
//
// Serial port termination
//
void serialClose(int fd){
close(fd);
}
 
static int callback_http(
  struct libwebsocket_context *this,
  struct libwebsocket *wsi,enum libwebsocket_callback_reasons reason,
  void *user,void *in,size_t len)
{
return 0;
}
 
static int callback_my(
  struct libwebsocket_context * this,
  struct libwebsocket *wsi,enum libwebsocket_callback_reasons reason,
  void *user,void *in,size_t len)
{
static char message[MAX_MESSAGE+LWS_SEND_BUFFER_PRE_PADDING+LWS_SEND_BUFFER_POST_PADDING];
unsigned char x,y,z;
switch(reason){
  case LWS_CALLBACK_ESTABLISHED:
    printf("connection established\n");
                // Declenchement d'un prochain envoi au navigateur
    unsigned char dummy;
    while(read(sd,&dummy,1)==1);
    libwebsocket_callback_on_writable(this,wsi);
    break;
  case LWS_CALLBACK_RECEIVE:
                // Ici sont traites les messages envoyes par le navigateur
                           //RIEN À ENVOYER !
    break;
  case LWS_CALLBACK_SERVER_WRITEABLE:
                // Ici sont envoyes les messages au navigateur
     
    if(read(sd,&x,1)==1)
      {
      while(read(sd,&y,1)!=1);
      while(read(sd,&z,1)!=1);
      char *out=message+LWS_SEND_BUFFER_PRE_PADDING;
      sprintf(out,"%03d %03d %03d",x,y,z);
      libwebsocket_write(wsi,(unsigned char *)out,MAX_MESSAGE,LWS_WRITE_TEXT);
      }
    libwebsocket_callback_on_writable(this,wsi);
    break;
  default:
    break;
  }
return 0;
}

static struct libwebsocket_protocols protocols[] = {
  {
    "http-only",   // name
    callback_http, // callback
    0,             // data size
    0              // maximum frame size
  },
  {"myprotocol",callback_my,0,MAX_FRAME_SIZE},
  {NULL,NULL,0,0}
  };

int main(void) {
int port=9000;
struct lws_context_creation_info info;
memset(&info,0,sizeof info);
info.port=port;
info.protocols=protocols;
info.gid=-1;
info.uid=-1;
struct libwebsocket_context *context=libwebsocket_create_context(&info);
if(context==NULL){
  fprintf(stderr, "libwebsocket init failed\n");
  return -1;
  }
printf("starting server...\n");
 
sd=serialOpen(SERIAL_DEVICE,SERIAL_BOTH);
serialConfig(sd,B9600);
 
while(1){
  libwebsocket_service(context,WAIT_DELAY);
  }
 
serialClose(sd);

libwebsocket_context_destroy(context);
return 0;
}

Séance 3

Partie électronique

Partie informatique

Démonstration

Conclusion