Projet IMA3 P3, 2015/2016, TD1 : Différence entre versions

De Wiki de Projets IMA
(Partie informatique)
(Partie informatique)
Ligne 221 : Ligne 221 :
  
  
De même, un exemple de fichier configurant le serveur Weebsocket nous a été fourni. Nous l'adaptons donc suivant les données de notre projet qui doivent transiter par le serveur (température, nombre de personne dans la pièce):
+
De même, un exemple de fichier configurant le serveur Websocket nous a été fourni. Nous l'adaptons donc suivant les données de notre projet qui doivent transiter par le serveur (température, nombre de personne dans la pièce):
  
 
   #include <stdio.h>
 
   #include <stdio.h>

Version du 15 juin 2016 à 10:20

Projet IMA3-SC 2015/2016 : Gestion de l'éclairage et du chauffage d'une pièce en fonction du nombre de personne.

Contexte

La gestion de l'énergie étant une problématique d'actualité, nous nous sommes dirigés vers le domaine de la domotique. Notre objectif est de concevoir un système capable, d'une part, de gérer automatiquement l'éclairage d'une pièce et d'autre part, de conseiller l'utilisateur sur le chauffage de cette dernière via une application WEB.

Cahier des charges

Concernant l'éclairage :

  • 2 capteurs de présence (LED IR ou SONAR à définir)
  • Des LEDs représentant l'éclairage

Concernant la gestion du chauffage :

  • 1 capteur de température

Pour acquérir, traiter et afficher les données :

  • Une Nanoboard
  • Une Raspberry PI

Séance 1

Partie électronique

Prise en main de la RedBoard :

  • Clignotement d'une LED
  • Mesures de distance avec SONAR qui servira de capteur de présence.
  • Caractéristiques du capteur de température TMP36GZ :
 Étendue de mesure : -40°C à +125°C
 Précision : +- 1°C à 25°C
 Résolution : 10mV/°C
 Offset : 0.5V
 25°C->750mV

Utilisation de la RedBoard pour acquérir les signaux des capteurs et transmission par la liaison série intégrée jusqu'à l’ordinateur.

Partie informatique

  • (Re)découverte du langage HTML.
  • Découverte du langage CSS.

Recherche d'outils permettant la réalisation de graphiques 3D dynamique sur une page WEB :

  • JavaScript
  • CANVAS

Séance 2

Partie électronique

Nous choisissons de changer le capteur de présence par deux LED IR. Une personne est comptée entrante/sortante dans la pièce lorsque le faisceau IR est interrompu. Nous devons donc numériser deux signaux, celui à la sortie du capteur de température et celui des LEDs. Pour cela il nous faut utiliser un convertisseur analogique-numérique. Nous sommes contraint de réaliser ce composant avec le FPGA et nous nécessitons donc une conversion par MLI. Cette conversion nécessite la génération d'un signal triangulaire (utilisation d'un filtre passe bas) par le biais du FPGA et l’utilisation d'un comparateur entre ce signal et le signal du capteur pour obtenir un signal carré interprétable en binaire. Le schéma suivant illustre la conversion.

Pwmproj.png

Pour retrouver la valeur de la tension délivrée par le capteur de température à partir du signal carré, il faut mesurer la valeur moyenne de ce signal.

Pour le comptage de personne, nous utilisons un comparateur qui permet l’émission d'un état haut lorsque la luminosité reçue par la photodiode descend en dessous d'un certains niveau. Cela correspond à l'interruption de notre faisceau et au passage d'une personne. Lors de chaque interruption, l'information est directement envoyée au serveur websocket.

Partie informatique

Programmation du capteur de température et de la photodiode.

Séance 3

Partie électronique

Après plusieurs tentatives infructueuses d'accès à la salle d'électronique (car il n'y avait pas suffisamment de poste avec nanoboard pour le nombre de groupe du TD), nous avons pu réaliser les premiers schémas sur Altium. Le premier correspond à l’émission d'un signal carré par le FPGA pour sa conversion future en signal triangulaire.

PWM1 OAG.png

Il est composés des éléments suivants : Un compteur 8 bits relié à deux blocs différents : L'horloge et un module délivrant un mot de 8 bit correspondant à la largeur d’impulsion désirée. Nous utilisons le bloc PWM prédéfini dans Altium pour délivrer sur la broche 2 le signal carré.

Le signal de sortie de ce bloc est un signal carré que nous rendons triangulaire grâce au filtre passe bas. Il suffit ensuite d'utiliser un comparateur (composant électronique) pour obtenir notre signal convertit.

Cependant, nous rencontrons des difficultés lors de la compilation du programme sous Altium. Nous remarquons que cela vient d'un problème de bibliothèque du logiciel et décidons de changer de poste pour le résoudre. Ce problème nous a fortement ralenti et nous décidons de réaliser une séance supplémentaire pour avancer le projet.

Partie informatique

Définition des données à échanger entre la partie électronique et le serveur Websocket. Finition du code gérant les capteurs.


Voici le programme implémenté dans la Redboard:

int pd=2;
int senRead=0;   // diode #1
int senRead2=2;  // diode #2             
int seuil=20;    //seuil pour un obstacle  
int cpt_IN=0;    //compteur d'entrées
int cpt_OUT=0;   //compteur de sorties
int signal_coupe=0;    //indique le numéro de la diode dont le signal a été coupé   
//pour le capteur de température
int val_t;
int tempPin = 1;

void setup()    
{  
 pinMode(pd,OUTPUT); 
 digitalWrite(pd,HIGH);       //alimentation photodiode 
 Serial.begin(9600);           
}  
void loop()
{  
 // Acquisition, calcul et affichage de la température
 val_t = analogRead(tempPin);
 float cel = ( val_t*125)/767.25; //Conversion en degrés Celsius
 Serial.print(cel);
 Serial.print("*C");
   
 // Lecture photodiode 1
 int val=analogRead(senRead);  //Acquisition de la valeur numérique (comprise entre 0 et 1023) de la première diode   
 delay(10);
 // Lecture photodiode 2
 int val2=analogRead(senRead2); // Acquisition de la valeur numérique (comprise entre 0 et 1023) de la seconde diode 
 delay(10);
 
 // test signal coupé
 if ((val>seuil) && (val2<seuil)){
   Serial.print("Signal 1 COUPE");
   signal_coupe = 1;
   cpt_OUT++;
   delay(100);
 }
 if ((val2>seuil) && (val<seuil)){
   Serial.print("Signal 2 COUPE");
   signal_coupe = 1;
   cpt_IN++;
   delay(100);
 }
 Serial.print(signal_coupe);
 
}

Séance supplémentaire

Partie électronique

Le problème de compilation résolu, c'est le signal en sortie de la nanoboard qui nous pose problème, il n'est pas de forme carrée mais plutôt aléatoire. Après des recherches infructueuses et chronophages, nous nous intéressons à la réalisation de la liaison série. Nous souhaitons transférer la donnée température sous forme d'un mot de 8 bits. Le transfert du nombre de personnes comptées est direct.

En prenant le cas du transfert de la données température, il convient de la mettre sous forme d'une trame utilisée par la liaisons RS232. Soit un bit de start, 8 bits de données et un bit de stop. Pas de bit de parité. La meilleur façon d'assembler ces bits consiste à utiliser un multiplexeur 8 vers 1 pour une émissions bit à bit temporisée par un compteur. Ce compteur correspond à la vitesse de transmission en bauds, choisit arbitrairement à 9600 bits/s, il faut connecter une horloge de 9600Hz pour cela au compteur. Nous trouvons ces informations en nous renseignant sur internet et en utilisant les travaux réalisés lors des projets précédents. Un programme Altium réalisable correspond au programme suivant, nous ne l'avons pas testé par manque de temps.

Transmission OAG.PNG

Un bloc supplémentaire de cadencement du compteur est utilisé afin d'obtenir la vitesse de 9600 bauds. L'envoie des données se fait par détection de l'appui sur bouton poussoir (SW_USER0. Ci-dessous le détails du bloc de cadencement.

Counter9600.PNG

   L'entrée Bp correspondant au bouton poussoir
   L'entrée clk correspondant à l'horloge à 50Mhz
   L'entrée Counter[3..0] correspondant au bus du compteur
   La sortie CE_out correspondant à la sortie du système gérant l'entrée CE du compteur 
   Le calcul de la nouvelle séquence d'horloge se fait de la manière suivante :
   Front = Bp . /Bp-1
   C = ( /Counter . C-1 ) + Front
   CE_out = C + Counter

Ces résultats sont à implémenter sur la nanoboard et il reste de plus à corriger les erreurs liées à la conversion.

Partie informatique

Nous nous sommes occupé ici de la partie configuration du serveur et de la liaison série.

Le code paramétrant la liaison série est fourni dans le sujet du projet, il n'y a pas de modification à faire:

 /*
 * Serial library
 */
 ////
 // Include files
 ////
 #include <stdio.h>
 #include <stdlib.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 "serial.h"
 ////
 // Functions
 ////
 //
 // 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); 
 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);
 }


De même, un exemple de fichier configurant le serveur Websocket nous a été fourni. Nous l'adaptons donc suivant les données de notre projet qui doivent transiter par le serveur (température, nombre de personne dans la pièce):

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <libwebsockets.h>
 #include <unistd.h>
 #include <termios.h>
 #include "serial.h"
 #define         SERIAL_DEVICE           "/dev/ttyUSB0"
 #define MAX_FRAME_SIZE  1024
 #define WAIT_DELAY      50
 int sd;
 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)
 {
 unsigned char signal,temp; 	 // "signal" contient le numero de la diode dont le signal a été coupé, "temp" la valeur de la temperature
 int nb_personne;  			 // compteur de personne
 nb_personne = 0;
 switch(reason){
 case LWS_CALLBACK_ESTABLISHED:
   printf("connection established\n");
   // Declenchement d'un prochain envoi au navigateur
   libwebsocket_callback_on_writable(this,wsi);
   break;
 /*case LWS_CALLBACK_RECEIVE:
               // Ici sont traites les messages envoyes par le navigateur
   printf("received data: %s\n",(char *)in);
   sscanf(in,"%d %d",&temp_min,&temp_max);
   temp_min = (temp_min+50)*(2.55/5);    
   //envoyer la température min
   if(temp_min <= 450 && temp_min>= -50 && temp_max <= 450 && temp_max>= -50)
      {
   if(write(sd,&temp_min,sizeof(char))!=1){ perror("main.write"); exit(-1); 
      //envoyer la température max
      temp_max = (temp_max+50)*(2.55/5);
      if(write(sd,&temp_max,sizeof(char))!=1){ perror("main.write"); exit(-1); 
      else{
      printf("Data incorrect");
      }
   break; */
   case LWS_CALLBACK_SERVER_WRITEABLE:
               // Ici sont envoyes les messages au navigateur
   if(read(sd,&temp,sizeof(char))==1){
     char tampon[LWS_SEND_BUFFER_PRE_PADDING+64+LWS_SEND_BUFFER_POST_PADDING];
     char *out=tampon+LWS_SEND_BUFFER_PRE_PADDING;
     sprintf(out,"%d",temp); 
     temp = ((temp*125)/767.25); // conversion de la temprature en degrés Celsius
     printf("%d\n",temp);
     libwebsocket_write(wsi,(unsigned char *)out,strlen(out)+1,LWS_WRITE_TEXT);
     // traitement sur signal pour avoir le nombre de personne
     sprintf(out,"%d",signal);
     if(signal==0){
     nb_personne++;
     }
     if(signal == 1){
     nb_personne--;
     }	
     printf("%d\n",nb_personne);	
     libwebsocket_write(wsi,(unsigned char *)out,strlen(out)+1,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;
 }

Comme le fonctionnement exact du serveur Websocket et la manière dont sont transmises les données à l'application WEB restent encore flou pour nous, l'application WEB reste très limitée et ne répond en aucun cas au cahier des charges souhaité. Ainsi elle affiche seulement un repère gradué. En ayant connu la façon dont les données sont récupérées les données par l'application WEB nous aurions créé une fonction dessinant à intervalle de temps régulier les données récupérées.

<!DOCTYPE html>
<html>
<body>
<canvas id="graph" width="1000" height="600" style="border:1px solid #000000;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var i;
var c = document.getElementById("graph");
var ctx = c.getContext("2d");
drawAxes(ctx);
   
function drawAxes(ctx){
ctx.moveTo(50,550); //origine
ctx.lineTo(50,50); //axe y
ctx.stroke();
     
ctx.moveTo(50,550); //origine
ctx.lineTo(950,550); //axe x
ctx.stroke();
   
ctx.font = "12px Arial";
for(i=0;i<=10;i++) //graduation axe y
{
ctx.fillText(i*5,33,550-50*i);
}
ctx.fillText("Temperature en Celsius",20,25);
for(i=0;i<=9;i++) //graduation axe x
{
ctx.fillText(i*10,50+i*100,567);
}
ctx.fillText("Temps en minutes",850,585);	   
}			   
</script>   
</body>
</html>

Démonstration

Conclusion

Ce premier projet SC en IMA3 nous a permis d’acquérir de multiples compétences. En premier lieu des compétences techniques en Arduino, FPGA, Altium, WebSocket, HTML et CANVAS. Nous n'avions aucunes connaissances dans ces domaines ce qui a été un frein très important sur notre progression. Cependant leurs découverte n'a pu apporter que des choses bénéfiques. Notre programme Arduino fonctionne et nous pourrions l’implémenter directement sur la Raspberry Pie car il n'est en effet pas nécessaire d'utiliser un FPGA dans notre situation. Nous avons pu découvrir deux environnements totalement différents, l'électronique et l'informatique, qu'il a fallu relier entre eux ce qui consistait en un bon exercice. Nous avons développé nos compétences sur le travail en autonomie car nous étions la plupart du temps seuls face aux différents problèmes à résoudre. Nous aurions du revoir notre projet à la baisse au vue des contraintes imposées et de la difficultés de documentation et de prise en main des differents aspects du projet. De cette manière nous aurions pu finaliser un projet moins ambitieux mais fonctionnel.