Projet IMA3 P3, 2015/2016, TD1
Sommaire
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.
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.
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.
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.
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.