Projet IMA3 P1, 2016/2017, TD1 : Différence entre versions
(→Projet IMA3-SC 2016/2017 : Pot de fleur connecté) |
(→Projet IMA3-SC 2016/2017 : Pot de fleur connecté) |
||
(88 révisions intermédiaires par 3 utilisateurs non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
+ | __TOC__ | ||
+ | <br style="clear: both;"/> | ||
+ | |||
= Projet IMA3-SC 2016/2017 : Pot de fleur connecté = | = Projet IMA3-SC 2016/2017 : Pot de fleur connecté = | ||
− | [https://archives.plil.fr/aduqueno/2017_TD1_1.git Lien du projet GIT] | + | [https://archives.plil.fr/aduqueno/2017_TD1_1.git Lien du projet GIT (vidéo incluse)] |
− | + | <br/> | |
− | <include iframe src=" | + | <include iframe src="https://www.youtube.com/embed/AaqR9-eAByk" width="300px" height="200px" frameborder="0" scrolling="yes" /> |
== Cahier des charges == | == Cahier des charges == | ||
Ligne 11 : | Ligne 14 : | ||
* Affichage des données (humidité, température et lumière) sur une interface Web | * Affichage des données (humidité, température et lumière) sur une interface Web | ||
* Régulation automatique de l'arrosage lorsque l'humidité du sol est inférieure à la limite fixée par l'utilisateur | * Régulation automatique de l'arrosage lorsque l'humidité du sol est inférieure à la limite fixée par l'utilisateur | ||
− | * Affichage d'alertes lorsque les conditions de températures/luminosité ne correspondent plus à ce que l'utilisateur | + | * Affichage d'alertes lorsque les conditions de températures/luminosité ne correspondent plus à ce que l'utilisateur a fixé |
* Garder un historique horodaté des alertes et de l'arrosage | * Garder un historique horodaté des alertes et de l'arrosage | ||
* Avertir l'utilisateur lorsque le niveau d'eau est faible | * Avertir l'utilisateur lorsque le niveau d'eau est faible | ||
Ligne 36 : | Ligne 39 : | ||
Nous avons consacré cette première séance à la découverte du FPGA. | Nous avons consacré cette première séance à la découverte du FPGA. | ||
[[Fichier:IMA3P11617TD1_AOPComp.JPG|250px|thumb|right|AOP comparateur]] | [[Fichier:IMA3P11617TD1_AOPComp.JPG|250px|thumb|right|AOP comparateur]] | ||
− | N’en ayant encore jamais utilisé, nous avons commencé par réaliser le tutoriel proposé, celui-ci nous | + | N’en ayant encore jamais utilisé, nous avons commencé par réaliser le tutoriel proposé, celui-ci nous a permis de nous familiariser aussi bien avec la Nanoboard qu’avec la programmation graphique d’Altium qui est assez semblable à celle de logiciel libre Logisim. |
Nous avons constaté qu’il est possible de d’utiliser des interfaces d’Entrée/Sortie afin d’interagir avec notre FPGA, mais aussi qu’il nous est possible d’avoir accès à des instruments virtuels directement sous Altium nous permettant de régler des Entrées/Sorties virtuelles ou encore d’adapter la fréquence de l’horloge du FPGA suivant nos besoins. | Nous avons constaté qu’il est possible de d’utiliser des interfaces d’Entrée/Sortie afin d’interagir avec notre FPGA, mais aussi qu’il nous est possible d’avoir accès à des instruments virtuels directement sous Altium nous permettant de régler des Entrées/Sorties virtuelles ou encore d’adapter la fréquence de l’horloge du FPGA suivant nos besoins. | ||
Ligne 42 : | Ligne 45 : | ||
Pour notre projet nous disposons de 3 capteurs, humidité, température et niveau d’eau. Ces capteurs sont analogiques, il nous faut donc créer un ADC (Analog to Digital Converter ou Convertisseur Analogique Numérique). | Pour notre projet nous disposons de 3 capteurs, humidité, température et niveau d’eau. Ces capteurs sont analogiques, il nous faut donc créer un ADC (Analog to Digital Converter ou Convertisseur Analogique Numérique). | ||
− | Après discussion avec nos professeurs encadrant nous choisissons de nous orienter vers une conversion analogique numérique basée sur une PWM (Pulse Width Modulation ou Modulation des Largeurs d’ Impulsions). Cette technique permet de changer périodiquement le rapport cyclique d’un signal carré | + | Après discussion avec nos professeurs encadrant nous choisissons de nous orienter vers une conversion analogique numérique basée sur une PWM (Pulse Width Modulation ou Modulation des Largeurs d’ Impulsions). Cette technique permet de changer périodiquement le rapport cyclique d’un signal carré. Nous avons utilisé un compteur sur 8bits pour réaliser cette fonction. |
− | |||
− | Cette technique consiste à utiliser un Amplificateur | + | Cette technique consiste à utiliser un Amplificateur Opérationnel (AOP) en mode comparateur afin de comparer la tension d’un signal en dents de scie avec la tension du capteur. |
Quand la tension du signal triangulaire est supérieure à la tension du capteur alors l’AOP envoie un signal. | Quand la tension du signal triangulaire est supérieure à la tension du capteur alors l’AOP envoie un signal. | ||
+ | Cette séance nous a également permis d’apprendre l’utilisation de l’analyseur logique, appareil permettant de visualiser les signaux numériques. | ||
− | + | <br/> | |
− | |||
− | |||
− | |||
==== Prototype Arduino ==== | ==== Prototype Arduino ==== | ||
Ligne 101 : | Ligne 101 : | ||
} | } | ||
− | On initialise les | + | On initialise les pins en mode sortie |
void loop(){ | void loop(){ | ||
Ligne 149 : | Ligne 149 : | ||
[[Fichier:IMA3P11617TD1_PWMetComp.JPG|200px|thumb|right|PWM et comparaison]] | [[Fichier:IMA3P11617TD1_PWMetComp.JPG|200px|thumb|right|PWM et comparaison]] | ||
− | Durant cette seconde séance nous avons | + | Durant cette seconde séance nous avons poursuivi la conception de notre ADC basé sur une PWM. Nous avons généré un signal en dents de scie à partir du signal carré modulé. |
Pour cela, nous avons ajouté un filtre passe bas afin de filtrer la composante alternative dans le but de garder uniquement la composante continue du signal. | Pour cela, nous avons ajouté un filtre passe bas afin de filtrer la composante alternative dans le but de garder uniquement la composante continue du signal. | ||
Ligne 162 : | Ligne 162 : | ||
− | Nous avons choisi une résistance de | + | Nous avons choisi une résistance de 2,2KΩ et un condensateur de 1μF nous donnant une fréquence de coupure d’environ 70Hz. |
Nous avons ensuite utilisé l’analyseur en mode oscilloscope afin de visualiser à la fois notre signal en dents de scie et le signal du capteur. | Nous avons ensuite utilisé l’analyseur en mode oscilloscope afin de visualiser à la fois notre signal en dents de scie et le signal du capteur. | ||
On remarque que le dispositif fonctionne. | On remarque que le dispositif fonctionne. | ||
− | + | <br/> | |
− | + | <br/> | |
− | + | <br/> | |
− | |||
− | |||
==== Prototype Arduino ==== | ==== Prototype Arduino ==== | ||
− | En première partie de matinée, on a finalisé les | + | En première partie de matinée, on a finalisé les essais des capteurs, à savoir celui de température (LM35) et celui d'humidité (VMA303). Les deux capteurs étant analogique, il nous à fallu trouver une correspondance entre la valeur numérique (entre 0 et 1023) et la grandeur physique. On sait pour le LM35 que 10mV = 1°C et que 5V = 5000mv ≡ 1023, par conséquent on obtient : <math> temperature = val\_num\_mesuree * (5.0 / 1023.0 * 100.0) </math>. Pour le VMA303, on souhaite un pourcentage d'humidité, on a donc la formule suivante : <math> humidite = (100 / 1023) * val\_num\_mesuree </math>. On a ensuite écrit un programme Arduino permettant d'afficher ces deux grandeurs sur le moniteur série de l'IDE Arduino. |
void setup() { | void setup() { | ||
Ligne 215 : | Ligne 213 : | ||
=== Partie informatique === | === Partie informatique === | ||
− | + | En dehors des séances, nous avons travaillé sur l'interface web du projet. A l'aide d'un logiciel de création graphique, nous avons imaginé l'apparence souhaitée pour la page. Les images n'ont pas été gardées mais celles-ci ressemblaient fortement au "Croquis de l'interface web" vu plus haut. | |
− | + | Suite à cela, nous avons débuté l'étape de l'intégration en HTML/CSS. Pour ce faire, nous avons choisi d'utiliser le ''Framework'' Bootstrap pour sa modularité d'affichage sur tous les supports. Pour le moment, tous les éléments visibles sont statiques, nous ajouterons par la suite un script JavaScript permettant de modifier les valeurs des barres de progression. | |
− | + | ||
+ | [[Fichier:web_off.jpg|250px|thumb|right|Interface web (v1)]] | ||
+ | |||
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
== Séance 3 == | == Séance 3 == | ||
Ligne 230 : | Ligne 240 : | ||
Nous avons maintenant besoins d’un système de comparaison qui comparera la valeur numérique du capteur avec celle de notre consigne (la pompe devra se déclencher lorsque le taux d’humidité est inférieur à la consigne). | Nous avons maintenant besoins d’un système de comparaison qui comparera la valeur numérique du capteur avec celle de notre consigne (la pompe devra se déclencher lorsque le taux d’humidité est inférieur à la consigne). | ||
− | Pour cela, nous avons réalisé ce programme sous Altium dans le but de mieux comprendre le fonctionnement du bloc « comparator » grâce à 2 entrés virtuelles simulant la valeur du capteur et la consigne. | + | Pour cela, nous avons réalisé ce programme sous Altium (visible sur la photo ci-contre) dans le but de mieux comprendre le fonctionnement du bloc « comparator » grâce à 2 entrés virtuelles simulant la valeur du capteur et la consigne. |
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
+ | |||
+ | === Partie informatique === | ||
+ | |||
+ | Nous avons consacré cette séance à la partie informatique du projet. Tout d'abord, nous avons finalisé l'interface web en incorporant quelques changements visuels. Ensuite, nous avons attaqué la partie la plus intéressante : la dynamisation. N'ayant pas encore la liaison série établie (avec le serveur WebSocket), nous avons écrit nos fonctions en y faisant abstraction. En parallèle, nous avions créé un autre fichier nous permettant de réaliser des tests et de s'affranchir avec la syntaxe du JavaScript. La première fonction que nous avons réalisée été celle de la réception d'un message par le serveur WebSocket. Pour ne pas surcharger de code le wiki, nous ne détaillerons que la sous-partie consacrée à la température. | ||
+ | |||
+ | [[Fichier:web2.jpg|250px|thumb|right|Interface web (v2)]] | ||
+ | websocket.onmessage=function(message){ | ||
+ | |||
+ | console.log(message.data); | ||
+ | On affiche le contenu du message sur la console (pour le débogage) | ||
+ | if((message.data)[0] == 'T') | ||
+ | { | ||
+ | var value = (message.data).substring(1) * (5.0 / 1023.0 * 100.0); | ||
+ | value = Math.round(value*100)/100; | ||
+ | document.getElementById("temp").innerHTML = value; | ||
+ | document.getElementById("temp_lvl").style.width = value +"%"; | ||
+ | La condition ''if'' signifie que le message commence par un 'T'. Ensuite, grâce à la méthode substring, on va découper le message et ne garder que la partie qui contient le nombre auquel on appliquera une formule pour passer d'une valeur analogique entre 0 et 1023 à une température en degré Celsius. La ligne suivante permet simplement d'arrondir le résultat. Finalement, on change la valeur inscrite dans la barre de progression et on modifie sa largeur. | ||
+ | if(value < consigne_temp_inf || value > consigne_temp_sup) | ||
+ | { | ||
+ | document.getElementById("temp_ok").className = "glyphicon glyphicon-remove"; | ||
+ | document.getElementById("temp_lvl").style.backgroundColor = "#e74c3c"; | ||
+ | } | ||
+ | Cette nouvelle condition permet d'avertir l'utilisateur d'un défaut de température. Lorsque celle-ci n'est pas comprise dans l'intervalle fixé, la couleur de la barre passe au rouge et un logo en forme de croix apparaît. | ||
+ | else | ||
+ | { | ||
+ | document.getElementById("temp_ok").className = "glyphicon glyphicon-ok"; | ||
+ | document.getElementById("temp_lvl").style.backgroundColor = "#2ecc71"; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | Dans le cas contraire, la barre passe au vert et un logo "Ok" fait son apparition. | ||
+ | A quelques détails près, ce morceau de fonction est bâti sur le même principe pour le taux d'humidité et le niveau d'eau. | ||
+ | Pour le moment, les informations ne transitent que dans un sens, du serveur WebSocket au client. En dehors de cette séance, nous avons travaillé sur la transmission inverse, du client au serveur. Une seule information doit pouvoir être envoyée, il s'agit du taux d'humidité. En effet, la plage de température ne sert que d'indicateur mais ne joue aucun rôle sur le système contrairement à l'humidité qui peut activer la pompe. Deux fonctions ont été réalisées pour la gestion des consignes : sendTemp() et sendMoisture(). | ||
+ | Commençons par regarder celle pour l'humidité. | ||
+ | |||
+ | function sendMoisture() | ||
+ | { | ||
+ | |||
+ | var consigne = document.getElementById("consigne_moisture_change").value; | ||
+ | On commence par récupérer la valeur de l'''input''. | ||
+ | if(consigne == ""){return;} | ||
+ | Si la valeur est nulle, on sort de la fonction. | ||
+ | document.getElementById("consigne_moisture_change").value = ''; | ||
+ | Sinon, on efface la valeur de l'''input''. | ||
+ | if (parseInt(consigne) > 100) | ||
+ | { | ||
+ | consigne = "100"; | ||
+ | } | ||
+ | websocket.send("M"+consigne); | ||
+ | |||
+ | } | ||
+ | Si la consigne est supérieure à 100, on la fixe à 100 et dans tous les cas on envoie au serveur WebSocket la nouvelle valeur précédée d'un 'M'. | ||
+ | Pour s'assurer de la prise en compte du nouveau seuil, l'Arduino doit envoyer en retour "M[consigne]". Une condition de la fonction ''websocket.onmessage=function(message)'' permet de mettre à jour la variable de la consigne dans le script et de l'afficher. | ||
+ | La fonction consacrée à la température n'a besoin d'aucun envoie, il s'agit simplement d'un affichage. | ||
− | === | + | function sendTemp() |
+ | { | ||
+ | var consigne1 = document.getElementById("consigne1_temp_change").value; | ||
+ | var consigne2 = document.getElementById("consigne2_temp_change").value; | ||
+ | On récupère les valeurs des deux ''inputs'' dans des variables temporaires. | ||
+ | if(consigne1 == "" || consigne2 == ""){return;} | ||
+ | Si l'une des deux est nulle, on sort de la fonction. | ||
+ | var temp_bornes = [parseInt(consigne1),parseInt(consigne2)]; | ||
+ | On crée un tableau d'entier contenant les deux valeurs (permet de gérer plus facilement les cas où la première valeur est supérieur à la seconde). | ||
+ | consigne_temp_inf = Math.min.apply(Math, temp_bornes); | ||
+ | consigne_temp_sup = Math.max.apply(Math, temp_bornes); | ||
+ | document.getElementById("consigne_temp").innerHTML = consigne_temp_inf+"-"+consigne_temp_sup+"°C"; | ||
+ | } | ||
+ | On change les variables globales de consigne de température puis on les affiche. | ||
− | + | L'ensemble du fichier html sera disponible sur notre [https://archives.plil.fr/aduqueno/2017_TD1_1 repository git] en fin de projet. | |
− | |||
== Séance supplémentaire 1 == | == Séance supplémentaire 1 == | ||
Ligne 250 : | Ligne 330 : | ||
Nous avons décidé de mettre en application notre programme de comparaison et nous nous sommes alors rendu compte qu’il nous manquait un élément indispensable à notre ADC : un compteur ! | Nous avons décidé de mettre en application notre programme de comparaison et nous nous sommes alors rendu compte qu’il nous manquait un élément indispensable à notre ADC : un compteur ! | ||
− | En effet, jusque là nous étions en mesure de comparer 2 signaux avec un AOP mais nous ne pouvions pas récupérer la valeur numérique. | + | En effet, jusque-là nous étions en mesure de comparer 2 signaux avec un AOP mais nous ne pouvions pas récupérer la valeur numérique. |
Pour cela, nous avons ajouté un compteur 8bits comptant au même rythme que la PWM, de cette manière, quand l’AOP renverra un signal comme quoi la tension du capteur est supérieure à la tension du signal en dents de scie, nous récupérerons la valeur numérique du compteur sur 8bits. | Pour cela, nous avons ajouté un compteur 8bits comptant au même rythme que la PWM, de cette manière, quand l’AOP renverra un signal comme quoi la tension du capteur est supérieure à la tension du signal en dents de scie, nous récupérerons la valeur numérique du compteur sur 8bits. | ||
Ligne 256 : | Ligne 336 : | ||
Le test de notre ADC avec un capteur de luminosité s’est révélé être un succès ! Notre ADC est donc opérationnel, nous allons donc pouvoir nous concentrer sur la structure globale du FPGA | Le test de notre ADC avec un capteur de luminosité s’est révélé être un succès ! Notre ADC est donc opérationnel, nous allons donc pouvoir nous concentrer sur la structure globale du FPGA | ||
+ | ==== Prototype Arduino ==== | ||
+ | |||
+ | Après s'être assuré du bon fonctionnement de tous les capteurs, il ne nous restait plus qu'à assembler ces différents tests en un seul programme. Ce programme Arduino doit être capable de transmettre par l'intermédiaire de la liaison série les informations provenant des capteurs. De plus, il doit permettre d'allumer ou non la pompe selon la consigne d'humidité qu'il aura reçu par l'utilisateur depuis l'interface. | ||
+ | |||
+ | [[Fichier:PFC_cablage.jpg|250px|thumb|right|Montage du prototype]] | ||
+ | |||
+ | int STBY = 2; | ||
+ | int PWMA = 6; | ||
+ | int AIN1 = 3; | ||
+ | int AIN2 = 4; | ||
+ | int consigne = -1; | ||
+ | String received; | ||
+ | |||
+ | On commence par définir les variables du programme. | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | pinMode(STBY, OUTPUT); | ||
+ | pinMode(PWMA, OUTPUT); | ||
+ | pinMode(AIN1, OUTPUT); | ||
+ | pinMode(AIN2, OUTPUT); | ||
+ | Serial.begin(115200); | ||
+ | } | ||
+ | |||
+ | On initialise les pins en sortie et on active la liaison série avec un baud rate de 115200. | ||
+ | |||
+ | void loop() { | ||
+ | |||
+ | int temp_analog = analogRead(A0); | ||
+ | int water_lvl = analogRead(A1); | ||
+ | int hum_purcent = analogRead(A2)/10.23; | ||
+ | received = Serial.readString(); | ||
+ | |||
+ | On récupère les valeurs analogiques des capteurs et on lit sur le port série une éventuelle consigne. | ||
+ | |||
+ | if(received != "") | ||
+ | { | ||
+ | |||
+ | char c = received.charAt(0); | ||
+ | |||
+ | if(c == 'M') | ||
+ | { | ||
+ | consigne = (received.substring(1)).toInt(); | ||
+ | Serial.print(received); | ||
+ | delay(50); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | Si le message n'est pas vide et qu'il commence par la lettre M alors on change la valeur de la consigne et on envoie le même message que celui reçu pour avertir de la bonne réception. | ||
+ | |||
+ | if(hum_purcent < consigne && consigne != -1 ) | ||
+ | { | ||
+ | move(255); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | stop(); | ||
+ | } | ||
+ | |||
+ | On active la pompe si le taux d'humidité est inférieur à celui demandé (et que la consigne est différente de -1, valeur initiale) sinon on l'éteint. | ||
+ | |||
+ | String water = "N"+String(water_lvl,DEC); | ||
+ | String temp = "T"+String(temp_analog,DEC); | ||
+ | String hum = "H"+String(hum_purcent,DEC); | ||
+ | |||
+ | Serial.print(water); | ||
+ | delay(300); | ||
+ | Serial.print(hum); | ||
+ | delay(300); | ||
+ | Serial.print(temp); | ||
+ | delay(300); | ||
+ | |||
+ | } | ||
+ | |||
+ | Dans cette dernière partie, on envoie les données des capteurs en rajoutant une lettre pour les identifier correctement. | ||
=== Partie informatique === | === Partie informatique === | ||
+ | |||
+ | Parallèlement à la partie Arduino, nous avons commencé le programme en C du serveur WebSocket et celui de la liaison série en se basant sur les exemples fournis. La première étape a été de comprendre le fonctionnement global du serveur et notamment la notion de ''callback''. Pour nous initier, nous avons réalisé un premier programme permettant d'afficher sur la page web un caractère transmis depuis un programme Arduino. Une fois fonctionnel, nous avons expérimenté la transmission inverse. Ces deux petits programmes nous ont permis de combiner à la fois la lecture/écriture sur le port série et l'envoie/réception de données sur le serveur WebSocket. Prochaine étape, adapter l'ensemble à notre système. | ||
== Séance supplémentaire 2 == | == Séance supplémentaire 2 == | ||
Ligne 269 : | Ligne 427 : | ||
Nous disposons de 3 capteurs analogique : Niveau d’eau et Température qui ne servent qu’a donner des informations à l’utilisateur (ie : n’agit sur aucun actionneur), et un capteur d’Humidité qui doit donner des informations à l’utilisateur mais aussi déclencher la pompe lorsque le taux d’humidité de la terre est trop faible. | Nous disposons de 3 capteurs analogique : Niveau d’eau et Température qui ne servent qu’a donner des informations à l’utilisateur (ie : n’agit sur aucun actionneur), et un capteur d’Humidité qui doit donner des informations à l’utilisateur mais aussi déclencher la pompe lorsque le taux d’humidité de la terre est trop faible. | ||
− | Nous aurons donc besoin d’une liaison série. Nous utiliserons les blocs en | + | Nous aurons donc besoin d’une liaison série. Nous utiliserons les blocs en VHDL fournis. |
Notre AOP dispose de 2 comparateurs, nous aurons donc besoin d’un AOP supplémentaire. | Notre AOP dispose de 2 comparateurs, nous aurons donc besoin d’un AOP supplémentaire. | ||
Ligne 276 : | Ligne 434 : | ||
Nous récupérerons donc les valeurs numériques des 3 capteurs et nous allons devoir les envoyer en série. | Nous récupérerons donc les valeurs numériques des 3 capteurs et nous allons devoir les envoyer en série. | ||
− | Dans le but de réunir partie informatique et partie électronique, nous décidons d’établir un protocole de communication : les valeurs des capteurs seront précédées des lettre ‘H’, ‘T’ et ‘N’ en ASCII. De cette manière, lorsque la | + | Dans le but de réunir partie informatique et partie électronique, nous décidons d’établir un protocole de communication : les valeurs des capteurs seront précédées des lettre ‘H’, ‘T’ et ‘N’ en ASCII. De cette manière, lorsque la Raspberry recevra un ‘H’ elle sait que l’octet suivant correspondra à la valeur de l’humidité, idem pour les autres capteurs. |
Nous allons pour cela utiliser un multiplexeur auquel on connectera un compteur allant de 0 à 6 (voir schéma ci-contre) | Nous allons pour cela utiliser un multiplexeur auquel on connectera un compteur allant de 0 à 6 (voir schéma ci-contre) | ||
Ligne 284 : | Ligne 442 : | ||
=== Partie informatique === | === Partie informatique === | ||
+ | Nous avons continué notre travail sur le programme C. Travaillant sur Windows (dans un premier temps) en dehors des séances, il a fallu trouver une alternative au programme ''serial.c''. Après quelques recherches, nous sommes tombés sur le [http://www.teuniz.net/RS-232/ site] d'un ingénieur en électronique proposant une solution compatible à la fois sur les systèmes Unix et Windows. Cette trouvaille nous a permis de travailler facilement chez nous. Après un certain nombre d'essais nous avons su répondre à nos attentes. A nouveau, l’entièreté du code est disponible sur notre git mais nous allons voir ici les blocs composants la fonction principale (static int callback_my()). | ||
+ | |||
+ | 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=NULL; | ||
+ | static int msize=0; | ||
+ | unsigned char buf[4096]; | ||
+ | int i; | ||
+ | int n = RS232_PollComport(cport_nr, buf, 4095); | ||
+ | |||
+ | On récupère le message sur le port série et on le stocke dans buffer (buf) | ||
+ | |||
+ | switch(reason){ | ||
+ | |||
+ | case LWS_CALLBACK_ESTABLISHED: | ||
+ | printf("connection established\n"); | ||
+ | message=NULL; | ||
+ | libwebsocket_callback_on_writable(this,wsi); | ||
+ | break; | ||
+ | |||
+ | Ce premier cas est exécuté à chaque fois que le page web est rechargée et permet de préparer le serveur à envoyer des informations. | ||
+ | |||
+ | case LWS_CALLBACK_RECEIVE: | ||
+ | printf("received data: %s\n",(char *)in); | ||
+ | RS232_cputs(cport_nr, in); | ||
+ | libwebsocket_callback_on_writable(this,wsi); | ||
+ | break; | ||
+ | |||
+ | Ce second cas gère l'arrivé de la consigne d'humidité depuis l'interface et se charge d'envoyer le message reçu sur le port série. | ||
+ | |||
+ | case LWS_CALLBACK_SERVER_WRITEABLE: | ||
+ | if(n > 1) // On ne s'occupe que des messages de taille supérieure à 1 | ||
+ | { | ||
+ | buf[n] = 0; | ||
+ | for(i=0; i < n; i++) | ||
+ | { | ||
+ | if(buf[i] < 32) // On remplace les caractères qui ne sont pas des chiffres ou des lettres par des points | ||
+ | { | ||
+ | buf[i] = '.'; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | printf("received %i bytes: %s\n", n, (char *)buf); | ||
+ | unsigned char * tosend = malloc(LWS_SEND_BUFFER_PRE_PADDING + n + LWS_SEND_BUFFER_POST_PADDING); | ||
+ | memcpy(tosend + LWS_SEND_BUFFER_PRE_PADDING, buf, n); | ||
+ | libwebsocket_write(wsi, tosend + LWS_SEND_BUFFER_PRE_PADDING, n, LWS_WRITE_TEXT); | ||
+ | free(tosend); | ||
+ | } | ||
+ | libwebsocket_callback_on_writable(this,wsi); | ||
+ | break; | ||
+ | |||
+ | Finalement, ce dernier cas permet l'envoie au serveur des informations reçues par la liaison série en provenance de l'Arduino. | ||
+ | |||
+ | default: | ||
+ | break; | ||
+ | |||
+ | } | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | La partie informatique du projet est maintenant opérationnel, il restera néanmoins à configurer le Raspberry Pi pour qu'il puisse accueillir les fichiers de notre système. | ||
+ | En dehors de cette séance, nous avons travaillé sur un début de modèle 3D pour le pot. Il manque cependant la partie inférieure pour contenir l'ensemble du système électronique. | ||
+ | |||
+ | <gallery style="margin: 30px 0;"> | ||
+ | Fichier:PFC_3D1.jpg|Modèle 3D sans rendu | ||
+ | Fichier:PFC_3D2.jpg|Modèle 3D avec rendu | ||
+ | </gallery> | ||
+ | |||
+ | On peut voir sur ces images un trou sur une paroi du pot. Celui-ci traverse toute la paroi et permet de faire passer le tuyau d'arrosage. Le compartiment de forme triangle est le réservoir d'eau. Les pieds du pot devraient s’emboîter sur un support contenant l'électronique. | ||
== Séance supplémentaire 3 == | == Séance supplémentaire 3 == | ||
Ligne 292 : | Ligne 524 : | ||
==== FPGA ==== | ==== FPGA ==== | ||
[[Fichier:IMA3P11617TD1_problemePrecision.jpg|250px|thumb|right|Problème de calibrage de la tension de référence]] | [[Fichier:IMA3P11617TD1_problemePrecision.jpg|250px|thumb|right|Problème de calibrage de la tension de référence]] | ||
+ | [[Fichier:IMA3P11617TD1_Final.JPG|250px|thumb|right|Projet Altium Final]] | ||
+ | |||
Malheureusement, la conversion analogique numérique ne se passe pas comme prévue pour le capteur de température. En effet, le capteur varie de 0V…1,5V -> 0°C…150°C soit 10mV par °C alors que la PWM varie de 0V à 3V. | Malheureusement, la conversion analogique numérique ne se passe pas comme prévue pour le capteur de température. En effet, le capteur varie de 0V…1,5V -> 0°C…150°C soit 10mV par °C alors que la PWM varie de 0V à 3V. | ||
− | |||
− | En lien avec notre cours sur l’ATMEGA nous nous sommes | + | On constate sur l’analyseur qu’a 24,8°C (température de la salle) le capteur renvoi 248mv, valeur bien trop petite comparé aux 3V du capteur et même si le convertisseur fonctionne, la précision est déplorable (la conversion donne 25 à 24,8°C, à 100°C elle donnerait 85). |
+ | |||
+ | |||
+ | En lien avec notre cours sur l’ATMEGA, nous nous sommes rappelés que l'ADC requiert une « tension de référence ». | ||
C’est également ici le cas et la tension de référence correspond à celle du signal en dents de scie. | C’est également ici le cas et la tension de référence correspond à celle du signal en dents de scie. | ||
+ | |||
+ | |||
En effet : ADC = (Vin * 256 (8 Bits))/Vref donc ADC = [0 ,128] nous perdons la moitié de notre précision. | En effet : ADC = (Vin * 256 (8 Bits))/Vref donc ADC = [0 ,128] nous perdons la moitié de notre précision. | ||
Si on s’intéresse uniquement à des températures entre 0°C et 100°C alors on choisira un Vref de 1V. | Si on s’intéresse uniquement à des températures entre 0°C et 100°C alors on choisira un Vref de 1V. | ||
+ | |||
En effet En effet : ADC = (Vin * 256 (8 Bits))/Vref donc ADC = [0 ,256] et à 24,8°C on aura 63 ce qui est bien plus intéressant compte tenu de notre application. | En effet En effet : ADC = (Vin * 256 (8 Bits))/Vref donc ADC = [0 ,256] et à 24,8°C on aura 63 ce qui est bien plus intéressant compte tenu de notre application. | ||
− | Nous | + | |
+ | Nous ne somme pas parvenu à réduire la tension de sortie du FPGA. Nous avons tout de même établi un modèle théorique complet (photo ci-contre) du projet Altium qui fonctionnerait si nous avions résolu le problème de tension. | ||
=== Partie informatique === | === Partie informatique === | ||
+ | [[Fichier:PFC_rclocal.png|250px|thumb|right|Fichier rc.local]] | ||
+ | La configuration du Raspberry Pi s'est faite en dehors de Polytech donc les paramètres réseaux spécifiques à l'établissement n'ont pas été nécessaires. Dans un premier temps il a fallu installer la librairie libwebsockets (1.2.2-1). Après avoir raccordé le Raspberry Pi à internet nous nous sommes rendus sur le [https://packages.debian.org/jessie/armhf/libwebsockets3/download site] des paquets Debian pour télécharger une version compatible avec les architectures armhf. Nous avons également télécharger de la même manière le paquet libwebsockets-dev. Ensuite, via la commande dpkg -i [nom_du_paquet], nous les avons installés. Pour pouvoir accéder à l'interface depuis n'importe quel périphérique du réseau, nous avons installé le serveur HTTP apache2 puis nous avons placé notre fichier dans /var/www/html sous le nom index.html afin d'y accéder plus facilement en tapant simplement l'IP local du Raspberry Pi dans un navigateur. Pour terminer, nous avons automatisé le lancement du serveur WebSocket au démarrage en ajoutant une ligne au fichier /etc/rc.local et en déplaçant notre exécutable dans le répertoire /bin. | ||
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
+ | <br/> | ||
== Conclusion == | == Conclusion == | ||
+ | |||
+ | Ce projet multi-matières nous à permis d'avoir un aperçu de la filière systèmes communicants. Nous avons eu l’occasion de découvrir les FPGA et la manière de les programmer graphiquement. Cette programmation graphique nous à parfois posé problème lors de la recherche d’informations. Nous trouvions beaucoup d’explications en VHDL mais très peu sur Altium en graphique. Nous avons beaucoup appris durant ce projet, nous avons notamment pu mettre en œuvre nos connaissances théoriques en électronique où nous avons concrètement utilisé un AOP et réalisé un filtre. Les nombreux avantages du FPGA (fiabilité, flexibilité, coût) en font une alternative d’avenir face aux microcontrôleurs traditionnels. | ||
+ | |||
+ | La partie informatique et prototype Arduino fut tout aussi intéressante à réaliser. Bien que nous ayons éprouvé quelques difficultés à comprendre le fonctionnement du serveur WebSocket, nous avons tout de même réussi à faire ce que nous voulions sans concessions. | ||
+ | |||
+ | Dans l'ensemble, nous avons respecté le cahier des charges à l'exception de l'historique horodaté des alertes et de l'arrosage (aurait pu être fait en C en enregistrant les valeurs dans un fichier ou éventuellement dans une base de données) et de l'affichage de la luminosité qui est plutôt un oubli volontaire car il y avait assez peu d’intérêt au final. | ||
+ | |||
+ | Nous regrettons tout de même de n'avoir qu'un système à l’état de prototype et non un produit fini que l'on aurait pu imprimer à l'aide de l'imprimante 3D. De plus, le souci au niveau du convertisseur analogique numérique ne nous a pas permis de fusionner les deux parties. Ceci aurait permis au système de gagner en compacité. Hormis ces deux regrets, nous sommes satisfaits du résultat final et du déroulement de ce projet. |
Version actuelle datée du 19 février 2018 à 16:11
Sommaire
Projet IMA3-SC 2016/2017 : Pot de fleur connecté
Lien du projet GIT (vidéo incluse)
Cahier des charges
- Mesure du taux d'humidité de la terre
- Mesure de la température
- Mesure de la luminosité
- Affichage des données (humidité, température et lumière) sur une interface Web
- Régulation automatique de l'arrosage lorsque l'humidité du sol est inférieure à la limite fixée par l'utilisateur
- Affichage d'alertes lorsque les conditions de températures/luminosité ne correspondent plus à ce que l'utilisateur a fixé
- Garder un historique horodaté des alertes et de l'arrosage
- Avertir l'utilisateur lorsque le niveau d'eau est faible
Description du système
Le pot de fleur connecté est un système permettant de faciliter le soin d'une plante.
Il régule automatiquement l’arrosage afin de garder un taux d'humidité propice au développement de la plante et avertit l'utilisateur en cas de réserve d'eau insuffisante.
Il permet également de prévenir l'utilisateur lorsque les conditions de lumière/température ne correspondent plus à ce qu'il avait fixé sur l'interface web.
Le matériel
- 1 hygromètre (certains incluent une sonde de température)
- 1 photorésistance
- 1 sonde de température
- 1 capteur de niveau d'eau
- 1 Rapsberry Pi
- 1 Nanoboard
Séance 1
Partie électronique
FPGA
Nous avons consacré cette première séance à la découverte du FPGA.
N’en ayant encore jamais utilisé, nous avons commencé par réaliser le tutoriel proposé, celui-ci nous a permis de nous familiariser aussi bien avec la Nanoboard qu’avec la programmation graphique d’Altium qui est assez semblable à celle de logiciel libre Logisim.
Nous avons constaté qu’il est possible de d’utiliser des interfaces d’Entrée/Sortie afin d’interagir avec notre FPGA, mais aussi qu’il nous est possible d’avoir accès à des instruments virtuels directement sous Altium nous permettant de régler des Entrées/Sorties virtuelles ou encore d’adapter la fréquence de l’horloge du FPGA suivant nos besoins.
Pour notre projet nous disposons de 3 capteurs, humidité, température et niveau d’eau. Ces capteurs sont analogiques, il nous faut donc créer un ADC (Analog to Digital Converter ou Convertisseur Analogique Numérique).
Après discussion avec nos professeurs encadrant nous choisissons de nous orienter vers une conversion analogique numérique basée sur une PWM (Pulse Width Modulation ou Modulation des Largeurs d’ Impulsions). Cette technique permet de changer périodiquement le rapport cyclique d’un signal carré. Nous avons utilisé un compteur sur 8bits pour réaliser cette fonction.
Cette technique consiste à utiliser un Amplificateur Opérationnel (AOP) en mode comparateur afin de comparer la tension d’un signal en dents de scie avec la tension du capteur.
Quand la tension du signal triangulaire est supérieure à la tension du capteur alors l’AOP envoie un signal. Cette séance nous a également permis d’apprendre l’utilisation de l’analyseur logique, appareil permettant de visualiser les signaux numériques.
Prototype Arduino
On a commencé par faire l'inventaire des différents capteurs et matériels présents dans notre boîte.
- Une pompe accompagnée de son shield permettant le contrôle du moteur par PWM avec un Arduino
- Un capteur d'humidité couplé avec un capteur de température
- Une sonde "chirp" qui émet du son lorsque l'humidité du sol descend en dessous d'un relevé initial "dry point"
- Un phototransistor qui réagit au changement d'intensité lumineuse
- Un microcontrôleur Arduino et une breadboard
Sonde chirp
Cette sonde, bien que fonctionnelle, n'est pas réellement adaptée à notre projet. Il s'agit d'un produit fini et non réellement d'un simple capteur. Nous avons trouvé inutile de perdre du temps à modifier un produit existant sans avoir la certitude d'aboutir à des résultats probants.
Capteur d'humidité / température
Grâce aux librairies fournies par Adafruit, ce capteur est assez simple à prendre en main. Des fonctions comme readTemperature() ou readHumidity() permettent d'accéder à la valeur de ces grandeurs. Seulement, ce capteur est plutôt destiné à un fonctionnement à l'air libre. Or, il va nous falloir la température de l'air mais également l'humidité du sol. De plus, le protocole de communication utilisé (I2C) est bien plus complexe à comprendre qu'une liaison série.
Finalement, nous avons pris la décision de ne travailler qu'avec des capteurs analogiques. En effet, pour le prototype Arduino ce choix n'est pas impactant mais pour la partie FPGA ce choix est préférable. Nous avons demandé une sonde de température (LM35) et nous avons commandé un kit comprenant un détecteur de niveau d'eau ainsi qu'un détecteur d'humidité à planter (lien).
Partie informatique
La distribution d'eau est une fonction essentielle de notre projet. Tout naturellement, on s'est penché en priorité sur le fonctionnement de la pompe. Pour connecter notre pompe, nous nous sommes référé à un schéma du driver TB6612FNG.
Programme du contrôle des moteurs
int STBY = 2; int PWMA = 6; int AIN1 = 4; int AIN2 = 5;
On commence par définir le numéro des pins à utiliser en veillant à prendre un pin compatible PWM pour contrôler la vitesse du moteur
void setup(){ pinMode(STBY, OUTPUT); pinMode(PWMA, OUTPUT); pinMode(AIN1, OUTPUT); pinMode(AIN2, OUTPUT); }
On initialise les pins en mode sortie
void loop(){ move(255); delay(5000); stop(); delay(2000); }
La fonction loop se répète à l'infini. Ici, elle a simplement pour but de faire aspirer la pompe au maximum pendant 5s puis de s’arrêter 2s. C'est durant ce test que nous avons constaté que les valeurs en dessous de 255 ne permettaient pas une vitesse suffisante pour aspirer de l'eau. On fonctionnera donc en tout ou rien.
void move(int speed){ digitalWrite(STBY, HIGH); boolean inPin1 = LOW; boolean inPin2 = HIGH; digitalWrite(AIN1, inPin1); digitalWrite(AIN2, inPin2); analogWrite(PWMA, speed); }
Cette fonction met en marche le moteur et lui applique la consigne de vitesse. Le pin PWN sortira alternativement du 0 et du 5V afin d'obtenir une valeur moyenne correspondant à la consigne émise.
void stop(){ digitalWrite(STBY, LOW); }
Les premiers tests sur la pompe sont concluants. Toutefois, même à une vitesse maximale, le débit est assez faible (goutte à goutte).
En seconde partie de matinée, nous avons commencé à réfléchir à l'interface web et plus précisément à son apparence. Nous avons décidé d'inclure des barres de progression afin de rendre plus graphique l'affichage de la température et de l'humidité. Pour le niveau d'eau, un pictogramme est suffisant pour signaler un manque d'eau ou non dans le réservoir. De même, à coté de la barre figurera sans doute un pictogramme indiquant le respect ou non des conditions de température.
Séance 2
Partie électronique
FPGA
Durant cette seconde séance nous avons poursuivi la conception de notre ADC basé sur une PWM. Nous avons généré un signal en dents de scie à partir du signal carré modulé.
Pour cela, nous avons ajouté un filtre passe bas afin de filtrer la composante alternative dans le but de garder uniquement la composante continue du signal.
Nous avons choisi une résistance de 2,2KΩ et un condensateur de 1μF nous donnant une fréquence de coupure d’environ 70Hz.
Nous avons ensuite utilisé l’analyseur en mode oscilloscope afin de visualiser à la fois notre signal en dents de scie et le signal du capteur.
On remarque que le dispositif fonctionne.
Prototype Arduino
En première partie de matinée, on a finalisé les essais des capteurs, à savoir celui de température (LM35) et celui d'humidité (VMA303). Les deux capteurs étant analogique, il nous à fallu trouver une correspondance entre la valeur numérique (entre 0 et 1023) et la grandeur physique. On sait pour le LM35 que 10mV = 1°C et que 5V = 5000mv ≡ 1023, par conséquent on obtient : . Pour le VMA303, on souhaite un pourcentage d'humidité, on a donc la formule suivante : . On a ensuite écrit un programme Arduino permettant d'afficher ces deux grandeurs sur le moniteur série de l'IDE Arduino.
void setup() { Serial.begin(9600); } void loop() { int valeur_num_temp = analogRead(A0); int valeur_num_hum = analogRead(A1); float temperature = valeur_num_temp * (5.0 / 1023.0 * 100.0); int humidite = valeur_num_hum * (100 / 1023); Serial.print("Température : "); Serial.println(temperature); Serial.print("Humidité : "); Serial.println(humidite); delay(150); }
Les résultats nous ont permis de vérifier la précision du LM35 (+/-0.5°C) et de déterminer un ordre de grandeur des valeurs du capteur d'humidité. Contrairement à notre premier capteur (numérique), celui-ci est prévu uniquement pour les sols (valeur de 0 à l'air).
En seconde partie de matinée, on a cherché des solutions techniques pour mesurer le niveau d'eau dans un réservoir. En premier lieu, on a pensé à un système composé d'un capteur ultrason. Il a l'avantage d'être simple à mettre en place et de retourner un niveau précis en théorie. La mesure est calculée par une fonction Arduino permettant de mesurer une largeur d'impulsion émise par le HC-SR04 qui est proportionnelle à la distance. Cependant, nous n'avons pas réussi à trouver le moyen électronique de reproduire ce procédé. C'est pourquoi après réflexion entre tous les membres du groupe, on a pris la décision de n'utiliser que des capteurs analogiques afin de ne pas complexifier davantage la partie FPGA. On s'est remis à la recherche de dispositifs et on s'est aperçu qu'ils ne permettaient que de détecter des paliers. On a donc opté pour trois niveaux de contrôle : niveau bas / moyen / haut. La plupart étaient basés sur le même procédé : du courant injecté dans l'eau avec des conducteurs disposés dans l'eau à chaque palier. Le faible courant qui passe permet de commander des transistors qui laisseront ou non passer un courant plus important qui représentera l'immersion ou non du câble dans l'eau.
Test du capteur de température et d'humidité Réalisation d'un PCB qui nous permettra de détecter le niveau d'eau dans le réservoir Réflexion sur le design de la page web de supervision
Partie informatique
En dehors des séances, nous avons travaillé sur l'interface web du projet. A l'aide d'un logiciel de création graphique, nous avons imaginé l'apparence souhaitée pour la page. Les images n'ont pas été gardées mais celles-ci ressemblaient fortement au "Croquis de l'interface web" vu plus haut. Suite à cela, nous avons débuté l'étape de l'intégration en HTML/CSS. Pour ce faire, nous avons choisi d'utiliser le Framework Bootstrap pour sa modularité d'affichage sur tous les supports. Pour le moment, tous les éléments visibles sont statiques, nous ajouterons par la suite un script JavaScript permettant de modifier les valeurs des barres de progression.
Séance 3
Partie électronique
FPGA
Nous avons maintenant besoins d’un système de comparaison qui comparera la valeur numérique du capteur avec celle de notre consigne (la pompe devra se déclencher lorsque le taux d’humidité est inférieur à la consigne).
Pour cela, nous avons réalisé ce programme sous Altium (visible sur la photo ci-contre) dans le but de mieux comprendre le fonctionnement du bloc « comparator » grâce à 2 entrés virtuelles simulant la valeur du capteur et la consigne.
Partie informatique
Nous avons consacré cette séance à la partie informatique du projet. Tout d'abord, nous avons finalisé l'interface web en incorporant quelques changements visuels. Ensuite, nous avons attaqué la partie la plus intéressante : la dynamisation. N'ayant pas encore la liaison série établie (avec le serveur WebSocket), nous avons écrit nos fonctions en y faisant abstraction. En parallèle, nous avions créé un autre fichier nous permettant de réaliser des tests et de s'affranchir avec la syntaxe du JavaScript. La première fonction que nous avons réalisée été celle de la réception d'un message par le serveur WebSocket. Pour ne pas surcharger de code le wiki, nous ne détaillerons que la sous-partie consacrée à la température.
websocket.onmessage=function(message){ console.log(message.data);
On affiche le contenu du message sur la console (pour le débogage)
if((message.data)[0] == 'T') { var value = (message.data).substring(1) * (5.0 / 1023.0 * 100.0); value = Math.round(value*100)/100; document.getElementById("temp").innerHTML = value; document.getElementById("temp_lvl").style.width = value +"%";
La condition if signifie que le message commence par un 'T'. Ensuite, grâce à la méthode substring, on va découper le message et ne garder que la partie qui contient le nombre auquel on appliquera une formule pour passer d'une valeur analogique entre 0 et 1023 à une température en degré Celsius. La ligne suivante permet simplement d'arrondir le résultat. Finalement, on change la valeur inscrite dans la barre de progression et on modifie sa largeur.
if(value < consigne_temp_inf || value > consigne_temp_sup) { document.getElementById("temp_ok").className = "glyphicon glyphicon-remove"; document.getElementById("temp_lvl").style.backgroundColor = "#e74c3c"; }
Cette nouvelle condition permet d'avertir l'utilisateur d'un défaut de température. Lorsque celle-ci n'est pas comprise dans l'intervalle fixé, la couleur de la barre passe au rouge et un logo en forme de croix apparaît.
else { document.getElementById("temp_ok").className = "glyphicon glyphicon-ok"; document.getElementById("temp_lvl").style.backgroundColor = "#2ecc71"; } }
Dans le cas contraire, la barre passe au vert et un logo "Ok" fait son apparition.
A quelques détails près, ce morceau de fonction est bâti sur le même principe pour le taux d'humidité et le niveau d'eau. Pour le moment, les informations ne transitent que dans un sens, du serveur WebSocket au client. En dehors de cette séance, nous avons travaillé sur la transmission inverse, du client au serveur. Une seule information doit pouvoir être envoyée, il s'agit du taux d'humidité. En effet, la plage de température ne sert que d'indicateur mais ne joue aucun rôle sur le système contrairement à l'humidité qui peut activer la pompe. Deux fonctions ont été réalisées pour la gestion des consignes : sendTemp() et sendMoisture().
Commençons par regarder celle pour l'humidité.
function sendMoisture() { var consigne = document.getElementById("consigne_moisture_change").value;
On commence par récupérer la valeur de l'input.
if(consigne == ""){return;}
Si la valeur est nulle, on sort de la fonction.
document.getElementById("consigne_moisture_change").value = ;
Sinon, on efface la valeur de l'input.
if (parseInt(consigne) > 100) { consigne = "100"; } websocket.send("M"+consigne); }
Si la consigne est supérieure à 100, on la fixe à 100 et dans tous les cas on envoie au serveur WebSocket la nouvelle valeur précédée d'un 'M'.
Pour s'assurer de la prise en compte du nouveau seuil, l'Arduino doit envoyer en retour "M[consigne]". Une condition de la fonction websocket.onmessage=function(message) permet de mettre à jour la variable de la consigne dans le script et de l'afficher.
La fonction consacrée à la température n'a besoin d'aucun envoie, il s'agit simplement d'un affichage.
function sendTemp() { var consigne1 = document.getElementById("consigne1_temp_change").value; var consigne2 = document.getElementById("consigne2_temp_change").value;
On récupère les valeurs des deux inputs dans des variables temporaires.
if(consigne1 == "" || consigne2 == ""){return;}
Si l'une des deux est nulle, on sort de la fonction.
var temp_bornes = [parseInt(consigne1),parseInt(consigne2)];
On crée un tableau d'entier contenant les deux valeurs (permet de gérer plus facilement les cas où la première valeur est supérieur à la seconde).
consigne_temp_inf = Math.min.apply(Math, temp_bornes); consigne_temp_sup = Math.max.apply(Math, temp_bornes); document.getElementById("consigne_temp").innerHTML = consigne_temp_inf+"-"+consigne_temp_sup+"°C"; }
On change les variables globales de consigne de température puis on les affiche.
L'ensemble du fichier html sera disponible sur notre repository git en fin de projet.
Séance supplémentaire 1
Partie électronique
FPGA
Nous avons décidé de mettre en application notre programme de comparaison et nous nous sommes alors rendu compte qu’il nous manquait un élément indispensable à notre ADC : un compteur !
En effet, jusque-là nous étions en mesure de comparer 2 signaux avec un AOP mais nous ne pouvions pas récupérer la valeur numérique.
Pour cela, nous avons ajouté un compteur 8bits comptant au même rythme que la PWM, de cette manière, quand l’AOP renverra un signal comme quoi la tension du capteur est supérieure à la tension du signal en dents de scie, nous récupérerons la valeur numérique du compteur sur 8bits.
Le test de notre ADC avec un capteur de luminosité s’est révélé être un succès ! Notre ADC est donc opérationnel, nous allons donc pouvoir nous concentrer sur la structure globale du FPGA
Prototype Arduino
Après s'être assuré du bon fonctionnement de tous les capteurs, il ne nous restait plus qu'à assembler ces différents tests en un seul programme. Ce programme Arduino doit être capable de transmettre par l'intermédiaire de la liaison série les informations provenant des capteurs. De plus, il doit permettre d'allumer ou non la pompe selon la consigne d'humidité qu'il aura reçu par l'utilisateur depuis l'interface.
int STBY = 2; int PWMA = 6; int AIN1 = 3; int AIN2 = 4; int consigne = -1; String received;
On commence par définir les variables du programme.
void setup() { pinMode(STBY, OUTPUT); pinMode(PWMA, OUTPUT); pinMode(AIN1, OUTPUT); pinMode(AIN2, OUTPUT); Serial.begin(115200); }
On initialise les pins en sortie et on active la liaison série avec un baud rate de 115200.
void loop() { int temp_analog = analogRead(A0); int water_lvl = analogRead(A1); int hum_purcent = analogRead(A2)/10.23; received = Serial.readString();
On récupère les valeurs analogiques des capteurs et on lit sur le port série une éventuelle consigne.
if(received != "") { char c = received.charAt(0); if(c == 'M') { consigne = (received.substring(1)).toInt(); Serial.print(received); delay(50); } }
Si le message n'est pas vide et qu'il commence par la lettre M alors on change la valeur de la consigne et on envoie le même message que celui reçu pour avertir de la bonne réception.
if(hum_purcent < consigne && consigne != -1 ) { move(255); } else { stop(); }
On active la pompe si le taux d'humidité est inférieur à celui demandé (et que la consigne est différente de -1, valeur initiale) sinon on l'éteint.
String water = "N"+String(water_lvl,DEC); String temp = "T"+String(temp_analog,DEC); String hum = "H"+String(hum_purcent,DEC); Serial.print(water); delay(300); Serial.print(hum); delay(300); Serial.print(temp); delay(300); }
Dans cette dernière partie, on envoie les données des capteurs en rajoutant une lettre pour les identifier correctement.
Partie informatique
Parallèlement à la partie Arduino, nous avons commencé le programme en C du serveur WebSocket et celui de la liaison série en se basant sur les exemples fournis. La première étape a été de comprendre le fonctionnement global du serveur et notamment la notion de callback. Pour nous initier, nous avons réalisé un premier programme permettant d'afficher sur la page web un caractère transmis depuis un programme Arduino. Une fois fonctionnel, nous avons expérimenté la transmission inverse. Ces deux petits programmes nous ont permis de combiner à la fois la lecture/écriture sur le port série et l'envoie/réception de données sur le serveur WebSocket. Prochaine étape, adapter l'ensemble à notre système.
Séance supplémentaire 2
Partie électronique
FPGA
Après avoir passé 4 séances sur l’ADC il nous fallait donc revenir au schéma global de notre FPGA.
Nous disposons de 3 capteurs analogique : Niveau d’eau et Température qui ne servent qu’a donner des informations à l’utilisateur (ie : n’agit sur aucun actionneur), et un capteur d’Humidité qui doit donner des informations à l’utilisateur mais aussi déclencher la pompe lorsque le taux d’humidité de la terre est trop faible.
Nous aurons donc besoin d’une liaison série. Nous utiliserons les blocs en VHDL fournis.
Notre AOP dispose de 2 comparateurs, nous aurons donc besoin d’un AOP supplémentaire.
Nous récupérerons donc les valeurs numériques des 3 capteurs et nous allons devoir les envoyer en série.
Dans le but de réunir partie informatique et partie électronique, nous décidons d’établir un protocole de communication : les valeurs des capteurs seront précédées des lettre ‘H’, ‘T’ et ‘N’ en ASCII. De cette manière, lorsque la Raspberry recevra un ‘H’ elle sait que l’octet suivant correspondra à la valeur de l’humidité, idem pour les autres capteurs.
Nous allons pour cela utiliser un multiplexeur auquel on connectera un compteur allant de 0 à 6 (voir schéma ci-contre) Le schéma de base du FPGA est donc terminé, il ne nous reste plus qu’a tester les capteurs afin de voir si la conversion s’effectue correctement.
Partie informatique
Nous avons continué notre travail sur le programme C. Travaillant sur Windows (dans un premier temps) en dehors des séances, il a fallu trouver une alternative au programme serial.c. Après quelques recherches, nous sommes tombés sur le site d'un ingénieur en électronique proposant une solution compatible à la fois sur les systèmes Unix et Windows. Cette trouvaille nous a permis de travailler facilement chez nous. Après un certain nombre d'essais nous avons su répondre à nos attentes. A nouveau, l’entièreté du code est disponible sur notre git mais nous allons voir ici les blocs composants la fonction principale (static int callback_my()).
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=NULL; static int msize=0; unsigned char buf[4096]; int i; int n = RS232_PollComport(cport_nr, buf, 4095);
On récupère le message sur le port série et on le stocke dans buffer (buf)
switch(reason){ case LWS_CALLBACK_ESTABLISHED: printf("connection established\n"); message=NULL; libwebsocket_callback_on_writable(this,wsi); break;
Ce premier cas est exécuté à chaque fois que le page web est rechargée et permet de préparer le serveur à envoyer des informations.
case LWS_CALLBACK_RECEIVE: printf("received data: %s\n",(char *)in); RS232_cputs(cport_nr, in); libwebsocket_callback_on_writable(this,wsi); break;
Ce second cas gère l'arrivé de la consigne d'humidité depuis l'interface et se charge d'envoyer le message reçu sur le port série.
case LWS_CALLBACK_SERVER_WRITEABLE: if(n > 1) // On ne s'occupe que des messages de taille supérieure à 1 { buf[n] = 0; for(i=0; i < n; i++) { if(buf[i] < 32) // On remplace les caractères qui ne sont pas des chiffres ou des lettres par des points { buf[i] = '.'; } } printf("received %i bytes: %s\n", n, (char *)buf); unsigned char * tosend = malloc(LWS_SEND_BUFFER_PRE_PADDING + n + LWS_SEND_BUFFER_POST_PADDING); memcpy(tosend + LWS_SEND_BUFFER_PRE_PADDING, buf, n); libwebsocket_write(wsi, tosend + LWS_SEND_BUFFER_PRE_PADDING, n, LWS_WRITE_TEXT); free(tosend); } libwebsocket_callback_on_writable(this,wsi); break;
Finalement, ce dernier cas permet l'envoie au serveur des informations reçues par la liaison série en provenance de l'Arduino.
default: break; } return 0; }
La partie informatique du projet est maintenant opérationnel, il restera néanmoins à configurer le Raspberry Pi pour qu'il puisse accueillir les fichiers de notre système.
En dehors de cette séance, nous avons travaillé sur un début de modèle 3D pour le pot. Il manque cependant la partie inférieure pour contenir l'ensemble du système électronique.
On peut voir sur ces images un trou sur une paroi du pot. Celui-ci traverse toute la paroi et permet de faire passer le tuyau d'arrosage. Le compartiment de forme triangle est le réservoir d'eau. Les pieds du pot devraient s’emboîter sur un support contenant l'électronique.
Séance supplémentaire 3
Partie électronique
FPGA
Malheureusement, la conversion analogique numérique ne se passe pas comme prévue pour le capteur de température. En effet, le capteur varie de 0V…1,5V -> 0°C…150°C soit 10mV par °C alors que la PWM varie de 0V à 3V.
On constate sur l’analyseur qu’a 24,8°C (température de la salle) le capteur renvoi 248mv, valeur bien trop petite comparé aux 3V du capteur et même si le convertisseur fonctionne, la précision est déplorable (la conversion donne 25 à 24,8°C, à 100°C elle donnerait 85).
En lien avec notre cours sur l’ATMEGA, nous nous sommes rappelés que l'ADC requiert une « tension de référence ».
C’est également ici le cas et la tension de référence correspond à celle du signal en dents de scie.
En effet : ADC = (Vin * 256 (8 Bits))/Vref donc ADC = [0 ,128] nous perdons la moitié de notre précision.
Si on s’intéresse uniquement à des températures entre 0°C et 100°C alors on choisira un Vref de 1V.
En effet En effet : ADC = (Vin * 256 (8 Bits))/Vref donc ADC = [0 ,256] et à 24,8°C on aura 63 ce qui est bien plus intéressant compte tenu de notre application.
Nous ne somme pas parvenu à réduire la tension de sortie du FPGA. Nous avons tout de même établi un modèle théorique complet (photo ci-contre) du projet Altium qui fonctionnerait si nous avions résolu le problème de tension.
Partie informatique
La configuration du Raspberry Pi s'est faite en dehors de Polytech donc les paramètres réseaux spécifiques à l'établissement n'ont pas été nécessaires. Dans un premier temps il a fallu installer la librairie libwebsockets (1.2.2-1). Après avoir raccordé le Raspberry Pi à internet nous nous sommes rendus sur le site des paquets Debian pour télécharger une version compatible avec les architectures armhf. Nous avons également télécharger de la même manière le paquet libwebsockets-dev. Ensuite, via la commande dpkg -i [nom_du_paquet], nous les avons installés. Pour pouvoir accéder à l'interface depuis n'importe quel périphérique du réseau, nous avons installé le serveur HTTP apache2 puis nous avons placé notre fichier dans /var/www/html sous le nom index.html afin d'y accéder plus facilement en tapant simplement l'IP local du Raspberry Pi dans un navigateur. Pour terminer, nous avons automatisé le lancement du serveur WebSocket au démarrage en ajoutant une ligne au fichier /etc/rc.local et en déplaçant notre exécutable dans le répertoire /bin.
Conclusion
Ce projet multi-matières nous à permis d'avoir un aperçu de la filière systèmes communicants. Nous avons eu l’occasion de découvrir les FPGA et la manière de les programmer graphiquement. Cette programmation graphique nous à parfois posé problème lors de la recherche d’informations. Nous trouvions beaucoup d’explications en VHDL mais très peu sur Altium en graphique. Nous avons beaucoup appris durant ce projet, nous avons notamment pu mettre en œuvre nos connaissances théoriques en électronique où nous avons concrètement utilisé un AOP et réalisé un filtre. Les nombreux avantages du FPGA (fiabilité, flexibilité, coût) en font une alternative d’avenir face aux microcontrôleurs traditionnels.
La partie informatique et prototype Arduino fut tout aussi intéressante à réaliser. Bien que nous ayons éprouvé quelques difficultés à comprendre le fonctionnement du serveur WebSocket, nous avons tout de même réussi à faire ce que nous voulions sans concessions.
Dans l'ensemble, nous avons respecté le cahier des charges à l'exception de l'historique horodaté des alertes et de l'arrosage (aurait pu être fait en C en enregistrant les valeurs dans un fichier ou éventuellement dans une base de données) et de l'affichage de la luminosité qui est plutôt un oubli volontaire car il y avait assez peu d’intérêt au final.
Nous regrettons tout de même de n'avoir qu'un système à l’état de prototype et non un produit fini que l'on aurait pu imprimer à l'aide de l'imprimante 3D. De plus, le souci au niveau du convertisseur analogique numérique ne nous a pas permis de fusionner les deux parties. Ceci aurait permis au système de gagner en compacité. Hormis ces deux regrets, nous sommes satisfaits du résultat final et du déroulement de ce projet.