Projet IMA3 P7, 2017/2018, TD2 : Différence entre versions
(→Présentation de notre code final) |
(→Présentation de notre code final) |
||
(36 révisions intermédiaires par 3 utilisateurs non affichées) | |||
Ligne 55 : | Ligne 55 : | ||
Nous avons constaté qu’il était possible d’utiliser des interfaces d’Entrée/Sortie afin d’interagir avec notre FPGA, mais aussi qu’il nous était 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 était possible d’utiliser des interfaces d’Entrée/Sortie afin d’interagir avec notre FPGA, mais aussi qu’il nous était 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 decidé de configurer le servomoteur en fpga pour répondre au cahier de charge, mais lors de la présentation du projet, le servomoteur sera configuré grâce à notre code arduino. | ||
+ | |||
+ | |||
+ | Dans notre cas le servomoteur fera office de la barrière du parking. Il faut donc que le sens de rotation soit anti-horraire pour l'ouverture de la barrière et sens horraire pour la fermeture. De plus, le servo moteur doituniquement faire un quart de tour, conditions à prendre en compte lors de sa configuration. | ||
+ | |||
+ | Le sens de rotation, dépend du signe de l'alimentation si on applique 5V, le sens de rotation sera le sens inverse des aiguilles d'une montre, et lorsqu'on applique -5V, le servomoteur troune dans le sens horraire. Donc pour passer de l'un à l'autre, il faut changer la polarité du moteur. En pratique, le pont en H est un bonne façon de changer le sens du courant dans un montage. | ||
+ | La période du moteur est de 20ms et le rapport ccyclique est compris entre 1 et 2 ms, en fontion de la position de la barrière. Dans notre cas, nous voullons que la barrière fasse un quart de tour vers le haut, donc le rapport cyclique sera de 2ms et pour la position initiale le rapport cyclique sera de 1,5ms. | ||
+ | |||
+ | Afin de gerer le servo moteur avec l'outil FPGA, il suffit de régler cette valeur du rapport cyclique pour ouvrir et refermer la barrière. | ||
+ | On aura besoin d'une horloge, pour avoir une certaine periodicité; d'un compteur qui compte jusqu'à 100 (temps que met le servo moteur à se mettre en position 90°) et d'un comparateur, pour verifier la valeur du compteur. | ||
+ | |||
+ | Notre système se met en position 90° et 0° avec une periode de 4ms | ||
=== Séance 2 === | === Séance 2 === | ||
Ligne 78 : | Ligne 91 : | ||
'''Partie informatique''' : Nous avons continuer à effectuer des recherches afin de mettre en place la configuration de notre futur site web, où les données du parking seraient envoyées. Nous avions quelques idées en tête: | '''Partie informatique''' : Nous avons continuer à effectuer des recherches afin de mettre en place la configuration de notre futur site web, où les données du parking seraient envoyées. Nous avions quelques idées en tête: | ||
− | + | * permettre aux usagers de pouvoir vérifier en temps réel les données du parking (si il y a des places libres ou non et combien) | |
− | + | * réaliser une interface attractive (dans la mesure du possible et avec nos connaissances limitées) | |
− | + | * réaliser 2 grandes parties: l'une pour l'affichage du nombre de places disponibles, l'autre composée de deux boutons pour l'ouverture et la fermeture de la barrière | |
− | |||
− | |||
Ligne 89 : | Ligne 100 : | ||
'''Partie maquette''' : [[Fichier:Schema_maquette.png|thumb|right|400px|schéma de notre future maquette]]Durant cette dernière séance, il nous a fallut passer par la case "Réalisation de la maquette". Nous voulions créer une maquette afin de permettre aux enseignants et autres personnes intéressées de comprendre notre projet de façon ludique. C'est dans cette perspective que nous avons commencé notre travail. Dans un premier temps nous avons essayer de rassembler des idées pour mettre en place la configuration de la maquette. Nous avons décidé de mettre 4 places sur notre maquette, donc trois équipées de capteur de détection de place (nous avions que 3 capteurs en stock et cela nous suffisait). | '''Partie maquette''' : [[Fichier:Schema_maquette.png|thumb|right|400px|schéma de notre future maquette]]Durant cette dernière séance, il nous a fallut passer par la case "Réalisation de la maquette". Nous voulions créer une maquette afin de permettre aux enseignants et autres personnes intéressées de comprendre notre projet de façon ludique. C'est dans cette perspective que nous avons commencé notre travail. Dans un premier temps nous avons essayer de rassembler des idées pour mettre en place la configuration de la maquette. Nous avons décidé de mettre 4 places sur notre maquette, donc trois équipées de capteur de détection de place (nous avions que 3 capteurs en stock et cela nous suffisait). | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
=== Séances supplémentaires === | === Séances supplémentaires === | ||
Ligne 109 : | Ligne 106 : | ||
− | '''Partie | + | '''Partie éléctronique ''' : |
+ | [[Fichier:Branchement2018.png|thumb|center|700px|Branchement partie Electronique]] | ||
Ligne 116 : | Ligne 114 : | ||
Voici le résultat obtenu pour notre maquette: | Voici le résultat obtenu pour notre maquette: | ||
− | [[Fichier:Maquettepark.jpg|thumb| | + | [[Fichier:Maquettepark.jpg|thumb|center|400px|rendu final de notre maquette Smart Car Park]] |
=== Présentation de notre code final=== | === Présentation de notre code final=== | ||
+ | |||
'''Partie Arduino :''' | '''Partie Arduino :''' | ||
Ligne 137 : | Ligne 136 : | ||
#define PLACES_TOTALES 3 | #define PLACES_TOTALES 3 | ||
#define SERVOMOTEUR 40 | #define SERVOMOTEUR 40 | ||
− | + | ||
LiquidCrystal LCD(13,12,11,10,9,8); // on crée l'objet écran | LiquidCrystal LCD(13,12,11,10,9,8); // on crée l'objet écran | ||
Servo servo; | Servo servo; | ||
Ligne 151 : | Ligne 150 : | ||
Une fois les initialisations faites, nous allimentons notre programme de plusieurs sous fonctio | Une fois les initialisations faites, nous allimentons notre programme de plusieurs sous fonctio | ||
− | 1.Fonction lever: | + | '''1.Fonction lever: ''' |
Elle permet de lever la barrière du parking, pour cela il suffit d'initialiser une variablie Position qui à l'aide d'une " boucle for" , va modifier la position du servo-moteur en incrementant la variable. | Elle permet de lever la barrière du parking, pour cela il suffit d'initialiser une variablie Position qui à l'aide d'une " boucle for" , va modifier la position du servo-moteur en incrementant la variable. | ||
− | void lever(void){ | + | void lever(void){ |
for (int pos = 0; pos <= 100; pos++){ | for (int pos = 0; pos <= 100; pos++){ | ||
servo.write(pos); | servo.write(pos); | ||
Ligne 161 : | Ligne 160 : | ||
− | 2.Fonction descendre: | + | '''2.Fonction descendre: ''' |
Dans la même optique, cette fonction permet de descendre la barrière du parking, mais cette fois-ci en décrementant la variable. | Dans la même optique, cette fonction permet de descendre la barrière du parking, mais cette fois-ci en décrementant la variable. | ||
− | void descendre(void){ | + | void descendre(void){ |
for (int pos = 100; pos >= 1; pos--){ | for (int pos = 100; pos >= 1; pos--){ | ||
servo.write(pos); | servo.write(pos); | ||
Ligne 171 : | Ligne 170 : | ||
− | 3.Fonction EcritureSérie: | + | '''3.Fonction EcritureSérie: ''' |
Cette fonction nous permettra de communiquer en liaison série, lorsqu'on reçoit un caractère on le renvoit grâce à un Serial.Write | Cette fonction nous permettra de communiquer en liaison série, lorsqu'on reçoit un caractère on le renvoit grâce à un Serial.Write | ||
− | void ecritureSerie(char caractere){ | + | void ecritureSerie(char caractere){ |
Serial.write(caractere);} | Serial.write(caractere);} | ||
− | 4.Fonction Occupée: | + | '''4.Fonction Occupée:''' |
Permet d'obetenir des informations sur une place du parking, si la distance renvoyée par le capteur ultrason est comprise entre 0 non inclu et 3, alors la place est occupée. | Permet d'obetenir des informations sur une place du parking, si la distance renvoyée par le capteur ultrason est comprise entre 0 non inclu et 3, alors la place est occupée. | ||
− | bool occuped(long distance){ | + | bool occuped(long distance){ |
return (distance <= 3) and (distance != 0);} | return (distance <= 3) and (distance != 0);} | ||
− | 5.Fonction Get_distance: | + | '''5.Fonction Get_distance: ''' |
Cette fonction permet de récuperer la distance reçue grace aux capteurs à ultrason. Elle prend en paramètre les deux broches du capteurs, et retourne la distance entre le capteur et l'obstacle (en loccurence la voiture dans notre cas). | Cette fonction permet de récuperer la distance reçue grace aux capteurs à ultrason. Elle prend en paramètre les deux broches du capteurs, et retourne la distance entre le capteur et l'obstacle (en loccurence la voiture dans notre cas). | ||
Si aucun obstacle n'a été trouvé, le capteur renvoit 0. | Si aucun obstacle n'a été trouvé, le capteur renvoit 0. | ||
− | long get_distance(int trigPin, int echoPin){ | + | long get_distance(int trigPin, int echoPin){ |
long dist, duration; | long dist, duration; | ||
digitalWrite(trigPin, LOW); | digitalWrite(trigPin, LOW); | ||
Ligne 200 : | Ligne 199 : | ||
return dist;} | return dist;} | ||
− | 6.Fonction VerifPlace: | + | '''6.Fonction VerifPlace: ''' |
Cette fonction permet d'incrementer une variable place_occup, qui nous sera utile pour le site. Dans cette fonction, nous affichons également le nombre de place disponible, et les numéros de ces places sur l'affficheur. Si aucune place n'est disponible, alors on affiche FULL. | Cette fonction permet d'incrementer une variable place_occup, qui nous sera utile pour le site. Dans cette fonction, nous affichons également le nombre de place disponible, et les numéros de ces places sur l'affficheur. Si aucune place n'est disponible, alors on affiche FULL. | ||
− | void verifplace(){ | + | void verifplace(){ |
distance = get_distance(TRIGPIN_1, ECHOPIN_1); | distance = get_distance(TRIGPIN_1, ECHOPIN_1); | ||
distance2 = get_distance(TRIGPIN_2, ECHOPIN_2); | distance2 = get_distance(TRIGPIN_2, ECHOPIN_2); | ||
Ligne 218 : | Ligne 217 : | ||
pl2 = 0; | pl2 = 0; | ||
} | } | ||
− | + | place_ocup = pl1 + pl2; | |
− | place_ocup = pl1 + pl2 | ||
− | |||
− | |||
− | |||
− | |||
− | |||
LCD.setCursor(0, 0); | LCD.setCursor(0, 0); | ||
LCD.write("Parking"); | LCD.write("Parking"); | ||
Ligne 246 : | Ligne 239 : | ||
else if (pl3 == 1) LCD.print(" ");} | else if (pl3 == 1) LCD.print(" ");} | ||
− | 7.Fonction Setup: | + | '''7.Fonction Setup: ''' |
− | void setup(){ | + | void setup(){ |
pinMode(TRIGPIN_1, OUTPUT); | pinMode(TRIGPIN_1, OUTPUT); | ||
pinMode(ECHOPIN_1, INPUT); | pinMode(ECHOPIN_1, INPUT); | ||
Ligne 262 : | Ligne 255 : | ||
− | 8.Fonction Loop: | + | '''8.Fonction Loop: ''' |
Fonction qui sera réalisée en boucle, et qui appel nos sous-fonctions. | Fonction qui sera réalisée en boucle, et qui appel nos sous-fonctions. | ||
− | void loop() { | + | void loop() { |
verifplace(); | verifplace(); | ||
while(Serial.available()>0){ | while(Serial.available()>0){ | ||
Ligne 286 : | Ligne 279 : | ||
''' Partie Websocket :''' | ''' Partie Websocket :''' | ||
− | En un premier temps nous avons décidé de nous mettre dans un environnement | + | En un premier temps nous avons décidé de nous mettre dans un environnement Linux afin de nous faciliter la tâche car nos essais sous Windows nous ont parus plus complexes à adapter. |
− | La difficulté ici était de faire | + | La difficulté ici était de faire communiquer en série l'Arduino et la Raspberry Pi, pour cela nous avons commencé par des exercices simplifiés : allumer la led du pin 13 si l'on reçoit le caractère 'a' (par exemple) de la Raspberry. |
− | Pour ce qui est de la liaison entre le navigateur et notre Raspberry, nous avons opté pour utiliser un | + | Une fois cette étape franchie et le principe assimilé , il nous suffisait de déchiffrer le code du fichier webserial.c qui nous a été fourni, et effectuer nos modifications dessus afin de l'adapter à nos besoins. |
− | + | Pour ce qui est de la liaison entre le navigateur et notre Raspberry, nous avons opté pour utiliser un commutateur, car nous avons essentiellement travaillé en dehors de Polytech, il était donc impossible de disposer de son réseau. Nous avons donc pensé à implémenter un réseau local "personnel", tout d'abord en wifi, sans succès (ie : impossible d'inter-ping entre les appareils), puis nous nous sommes tournés vers un commutateur de type "plug and play". | |
− | |||
− | |||
− | |||
− | + | ''' Partie HTML''' | |
+ | la configuration des adresses IP de la Raspberry et de l'ordinateur (sur lequel le navigateur tourne), dans les lignes de code du fichier html suivantes : | ||
− | + | var localhost = '127.0.0.1:9000' | |
+ | var ip_raspberry = '172.26.145.162:9000' | ||
+ | var websocket = new WebSocket('ws://'+ip_raspberry,'serial'); | ||
− | + | Nous avons établi un code couleur qui indique si la liaison est faite ou pas : | |
− | + | websocket.onopen=function(){ $('h1').css('color','green'); }; | |
− | + | websocket.onerror=function(){ $('h1').css('color','red'); }; | |
− | function | ||
− | websocket. | ||
+ | La fonction suivante permet au navigateur d'envoyer ses données à la Raspberry : | ||
+ | function sendMessage(data){ | ||
+ | websocket.send(data); | ||
+ | } | ||
La création des boutons que l'on utilise sur l'interface et qui répondent à nos fonctionnalités : | La création des boutons que l'on utilise sur l'interface et qui répondent à nos fonctionnalités : | ||
− | <button onclick="sendMessage('a');">Ouvrir barrière</button> | + | <button onclick="sendMessage('a');">Ouvrir barrière</button> |
− | <button onclick="sendMessage('b');">Descendre barrière</button> | + | <button onclick="sendMessage('b');">Descendre barrière</button> |
− | <button onclick="sendMessage('c');">Afficher nbr places </button> | + | <button onclick="sendMessage('c');">Afficher nbr places </button> |
− | Dans notre code C les fonctions principales sont les suivantes (étant donné que les autres fonctions sont explicités sous forme de commentaires sur le code disponible sur notre gitlab) | + | '''Partie C''' |
− | Ce qui permet d'envoyer et recevoir les données série entre la | + | |
+ | Dans notre code C les fonctions principales sont les suivantes (étant donné que les autres fonctions sont explicités sous forme de commentaires sur le code disponible sur notre gitlab). | ||
+ | |||
+ | Ce qui permet d'envoyer et recevoir les données série entre la Raspberry et l'Arduino : | ||
+ | |||
la fonction envoie_serie envoie une chaine de caractère sur la liaison série | la fonction envoie_serie envoie une chaine de caractère sur la liaison série | ||
− | int envoie_serie(int fd, char *str){ | + | |
− | + | int envoie_serie(int fd, char *str){ | |
− | + | return write(fd, str, strlen(str)); | |
} | } | ||
La fonction lecture_serie Lit n caractères sur la liaison série (données reçues de l'Arduino à la Raspberry) : | La fonction lecture_serie Lit n caractères sur la liaison série (données reçues de l'Arduino à la Raspberry) : | ||
− | int lecture_serie(int fd, char *str, int n){ | + | int lecture_serie(int fd, char *str, int n){ |
− | + | return read(fd, str, n); | |
− | } | + | } |
− | Ce qui permet d'envoyer et recevoir les données entre la | + | Ce qui permet d'envoyer et recevoir les données entre la Raspberry et le serveur : |
− | static int callback_serial(struct lws *wsi, | + | static int callback_serial(struct lws *wsi, |
− | + | enum lws_callback_reasons reason, | |
− | + | void *user, | |
− | + | void *in, | |
− | + | size_t len) | |
− | { | + | { |
− | + | unsigned char byte; | |
− | + | char message[LWS_SEND_BUFFER_PRE_PADDING+LWS_SEND_BUFFER_POST_PADDING+3]; | |
− | + | switch(reason){ | |
− | + | case LWS_CALLBACK_ESTABLISHED: | |
− | + | printf("connection established\n"); | |
− | + | lws_callback_on_writable(wsi); | |
− | + | break; | |
− | + | case LWS_CALLBACK_RECEIVE: | |
− | + | // Ici sont traites les messages envoyes par le navigateur | |
− | + | printf("received data: %s\n",(char *)in); | |
− | + | if(envoie_serie(sd, (char*)in) == -1){ | |
− | + | /* En cas d'erreur d'envoie sur la laison série */ | |
− | + | perror("callback_serial.envoie_serie"); | |
− | + | } | |
− | + | if(sscanf(in,"%hhd",&byte)==1) write(sd,&byte,1); | |
− | + | break; | |
− | + | case LWS_CALLBACK_SERVER_WRITEABLE: | |
− | + | // Ici sont envoyes les messages au navigateur | |
− | + | if(read(sd,&byte,1)==1){ | |
− | + | /* Recup sur la laison série d'un byte */ | |
− | + | char *out=message+LWS_SEND_BUFFER_PRE_PADDING; | |
− | + | printf("Caractère recu par Arduino : %s\n",out); | |
− | + | printf("Envoie du caractère au navigateur\n"); | |
− | + | sprintf(out,"%c",byte); | |
− | + | //On copie l'entier contenu de byte dans la viariable out | |
− | + | lws_write(wsi,(unsigned char *)out,strlen(out),LWS_WRITE_TEXT); | |
− | + | /* | |
Renvoie au navigateur la variable out (ie : Ce qui a été | Renvoie au navigateur la variable out (ie : Ce qui a été | ||
renvoyé par l'Arduino) | renvoyé par l'Arduino) | ||
*/ | */ | ||
− | + | } | |
− | + | lws_callback_on_writable(wsi); | |
− | + | break; | |
− | + | default: | |
− | + | break; | |
+ | } | ||
+ | return 0; | ||
} | } | ||
− | |||
− | |||
Ligne 390 : | Ligne 389 : | ||
Nous avons eu l’occasion de découvrir les FPGA et la manière de les programmer graphiquement.Mais cette partie n'a pas pu être aboutit malheureusement, et c'est dommage, car c'est une technologie intéressante, et plus optimisé et rapide qu'une Arduino (mais nous supposons que nous aurions l'occasion de mieux le comprendre et l'élaborer en IMA4). Nous avons constaté tout de même les nombreux avantages du FPGA (fiabilité, flexibilité, coût) qui font que c'est une excellente alternative d’avenir face aux microcontrôleurs traditionnels. | Nous avons eu l’occasion de découvrir les FPGA et la manière de les programmer graphiquement.Mais cette partie n'a pas pu être aboutit malheureusement, et c'est dommage, car c'est une technologie intéressante, et plus optimisé et rapide qu'une Arduino (mais nous supposons que nous aurions l'occasion de mieux le comprendre et l'élaborer en IMA4). Nous avons constaté tout de même les nombreux avantages du FPGA (fiabilité, flexibilité, coût) qui font que c'est une excellente alternative d’avenir face aux microcontrôleurs traditionnels. | ||
− | En outre, ce projet nous a permis d'avoir un aperçu sur la filière systèmes communicants | + | En outre, ce projet nous a permis d'avoir un aperçu sur la filière systèmes communicants. |
− | En effet, | + | |
+ | En effet, le projet nous a été très enrichissant, alliant la pratique à la théorie notamment quelques cours de modules vu cette année, nous avons beaucoup cherché pour comprendre et réaliser nos fonctions, nous n'avions jamais utilisé de Raspberry Pi ni fait une configuration dessus (cela nous a aidé à nous développer en autonomie). Nous avons donc eu l'occasion de voir la capacité et la force de ce petit objet! et bon nombre d'entre nous a pour objectif cet été de faire des petits projets et tutoriels dessus afin de se familiariser davantage avec les objets et les technologies connectés et pourquoi pas choisir la filière SC l'an prochain. | ||
+ | |||
+ | De plus, ce projet nous a permis de confronter la difficulté du débogage. Mais en pensant au "Voyager 1" que l'on peut localiser depuis plusieurs milliards de kilomètres, nous avons admis que faire communiquer une Arduino avec une Raspberry pi ne devrait pas être si difficile pour un ingénieur ! | ||
+ | |||
+ | Le git du projet se trouve à l'adresse suivante : https://archives.plil.fr/hmalti/Projet_SC.git | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | [[Fichier:Image lien video.png|center|400px|]] | ||
− | |||
− | La | + | '''La vidéo de notre projet se trouve à l'adresse suivante:''' https://drive.google.com/file/d/1S3H0lWYVez-IOR8IS-GYRhNrQQHRCI9G/view?usp=sharing |
Version actuelle datée du 19 juin 2018 à 15:27
Sommaire
Smart Car Park
Cahier des charges
Titre du projet
Maquette Smart Car Park
Description du système
Pour notre projet, nous avons pensé à réaliser une maquette sur le thème de la ville intelligente. En effet, notre sujet consiste à concevoir un parking intelligent: le Smart Car Park.
Tout d'abord, le parking sera composé de 3 places de parking qui détecteraient la présence de voitures grâce à 3 capteurs ultrasons et qui indiqueraient si les places sont libres ou non sur un écran LCD positionné à l'entrée du parking et relié aux capteurs. De plus, nous avons pensé à ajouter un système de barrière automatique qui détecterait une voiture et s'ouvrirait ensuite pour la laisser passer. Nous y avons ajouté quelques conditions comme le fait qu'elle ne s'ouvre plus quand le parking affiche complet.
Tout ce projet sera présenté sous forme de maquette, qui semble être un moyen ludique et efficace de présenter ce genre de système.
Matériels nécessaires
- 1 servo-moteur
- 1 résistance
- 1 potentiometre
- 1 afficheur LCD alphanumérique
- 3 capteurs ultrason
- 1 capteurs de mouvements
- 2 breadboard
- 1 lot de jumpers en tout genre (males/femelles)
- 1 Raspberry Pi
- 1 Arduino Mega
- matériels en tout genre permettant de faire une barrière automatique
- matériels en tout genre pour concevoir la maquette
Séance 1
Durant cette séance, nous avons eu l'idée de travailler sur le thème de la ville intelligente, thème en rapport avec la société actuelle mais également celle de demain. Nos premières idées ont été quelque peu ambitieuses. En effet, nous voulions, à la base, créer une ville entière, composée d'un parking intelligent, d'un éclairage automatique de rue et d'une maquette de maison composée d'un volet automatique. Nous l'avions explicité en précisant les éléments dont nous aurions plus tard besoin : des leds (pour l'éclairage des rues et peut être du parking), des capteurs ultrasons (pour détecter si la place de parking est occupé par une voiture ou non ), d'un écran LCD (pour afficher le nombre de places libres du parking), des servo moteurs (pour la barrière automatique du parking et pour le volet automatique), des capteurs de mouvement (pour détecter les piétons dans la rue ou les voitures) , une Arduino UNO, une rapsberry Pi. ( cela nous a pris deux bonnes heures )
La photo ci-dessus résume de façon schématique notre première idée.
A partir du temps qu'il nous restait, nous nous sommes séparées en deux groupes: l'un travaillant sur la configuration de la Rapsberry Pi et l'autre sur le tutoriel compteur sur ALTIUM. Cela nous a pris beaucoup de temps de réflexion et de compréhension.
A la fin de la séance, nous nous sommes rendu compte que notre temps été compté et que la réalisation de la ville entière nous paraissait impossible.Nous nous sommes donc demandés si le fait de réaliser seulement le parking intelligent ne serait pas plus judicieux, du fait du manque de temps.
Activité électronique, découverte du FPGA
Nous avons consacré une partie de la première séance à la découverte du FPGA. Ceci étant tout nouveau pour nous et n’en ayant encore jamais utilisé, nous avons commencé par réaliser le tutoriel proposé par nos professeurs. Ce tutoriel nous a permit de nous familiariser avec les différents éléments électroniques proposés: la Nanoboard ainsi que la programmation graphique et la réalisation de schéma électronique d’Altium.
Nous avons constaté qu’il était possible d’utiliser des interfaces d’Entrée/Sortie afin d’interagir avec notre FPGA, mais aussi qu’il nous était 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 decidé de configurer le servomoteur en fpga pour répondre au cahier de charge, mais lors de la présentation du projet, le servomoteur sera configuré grâce à notre code arduino.
Dans notre cas le servomoteur fera office de la barrière du parking. Il faut donc que le sens de rotation soit anti-horraire pour l'ouverture de la barrière et sens horraire pour la fermeture. De plus, le servo moteur doituniquement faire un quart de tour, conditions à prendre en compte lors de sa configuration.
Le sens de rotation, dépend du signe de l'alimentation si on applique 5V, le sens de rotation sera le sens inverse des aiguilles d'une montre, et lorsqu'on applique -5V, le servomoteur troune dans le sens horraire. Donc pour passer de l'un à l'autre, il faut changer la polarité du moteur. En pratique, le pont en H est un bonne façon de changer le sens du courant dans un montage. La période du moteur est de 20ms et le rapport ccyclique est compris entre 1 et 2 ms, en fontion de la position de la barrière. Dans notre cas, nous voullons que la barrière fasse un quart de tour vers le haut, donc le rapport cyclique sera de 2ms et pour la position initiale le rapport cyclique sera de 1,5ms.
Afin de gerer le servo moteur avec l'outil FPGA, il suffit de régler cette valeur du rapport cyclique pour ouvrir et refermer la barrière. On aura besoin d'une horloge, pour avoir une certaine periodicité; d'un compteur qui compte jusqu'à 100 (temps que met le servo moteur à se mettre en position 90°) et d'un comparateur, pour verifier la valeur du compteur.
Notre système se met en position 90° et 0° avec une periode de 4ms
Séance 2
Partie informatique : Durant cette séance, nous nous sommes occupées de finir la configuration de la Rapsberry Pi, cela n'a pas été finalisé lors de la 1 ère séance car nous avons été retardées par l'installation de Raspbian ainsi que la configuration du système. Pour ce, il fallait exécuter quelques commandes non-renseignées sur le tuto que nous suivions (ce qui explique les conflits entre les paquets que nous rencontrions). A la fin de la séance, notre Raspberry Pi était opérationnelle, nous pouvions désormais entamer la partie programmation de notre application !
Partie électronique : Nous nous sommes occupées de réaliser notre circuit Arduino, composé tout d'abord d'un seul capteur ultrason (une seule place de parking) relié à l'Arduino et à l'écran LCD, lui même relié a l'Arduino, ainsi que notre programme. Ayant eu rapidement un résultat positif avec un capteur ultrasons, nous avons réalisé le même circuit avec 3 capteurs ultrasons (3 places de parking). Pour cela, nous avons du changé de carte Arduino et opter pour une Arduino Mega, avec plus de ports entrées afin de pouvoir brancher tous nos capteurs.
En remarquant une bonne réussite dans notre partie électronique, nous avons décidé de rajouter une option, en plus de l'affichage des places libres et des capteurs détectant si une place était occupée ou non. Cette option consistait à réaliser une barrière automatique à l'entrée du parking. Celle-ci s'ouvrirait dès que le capteur de mouvement aurait détectée une voiture. La barrière resterait fermée si le parking affiche complet.
Séance 3
Durant cette séance, nous avons du accélérer le pas pour réussir à finir dans les temps.
Partie informatique : Nous avons continuer à effectuer des recherches afin de mettre en place la configuration de notre futur site web, où les données du parking seraient envoyées. Nous avions quelques idées en tête:
- permettre aux usagers de pouvoir vérifier en temps réel les données du parking (si il y a des places libres ou non et combien)
- réaliser une interface attractive (dans la mesure du possible et avec nos connaissances limitées)
- réaliser 2 grandes parties: l'une pour l'affichage du nombre de places disponibles, l'autre composée de deux boutons pour l'ouverture et la fermeture de la barrière
Partie électronique : Nous avons fini de réaliser notre circuit Arduino avec notre option supplémentaire: la barrière automatique.
Séances supplémentaires
Afin de mener à bien notre projet, nous avons du effectuer des séances supplémentaires, notamment pour finaliser notre site web mais également pour réaliser notre maquette.
Partie éléctronique :
Partie maquette : A l'aide des machines du Fabricarium notamment la découpe laser, nous avons réaliser des voitures qui permettront la détection d’obstacles pour les capteurs. Nous avons ensuite acheté différents éléments indispensables comme la plaque en bois support de notre maquette (40*40cm) et des planches de bois pour réaliser les petits abris des places de voitures. Après assemblage, nous avons remarqué que placer l'arduino mega sous la plaque support était une bonne idée.
Voici le résultat obtenu pour notre maquette:
Présentation de notre code final
Partie Arduino :
Voici a quoi ressemble notre code arduino à la fin de notre projet:
Nous commencons par initialiser les différentes variables
#include <Servo.h> // Permet d'utiliser le servo-moteur #include <LiquidCrystal.h> // Permet d'utiliser l'afficheur
Il faut maintenant préciser les Pins des différentes broches des capteurs, et du servo-moteur
#define TRIGPIN_1 42 #define ECHOPIN_1 43 #define TRIGPIN_2 45 #define ECHOPIN_2 44 #define TRIGPIN_3 47 #define ECHOPIN_3 46 #define PLACES_TOTALES 3 #define SERVOMOTEUR 40 LiquidCrystal LCD(13,12,11,10,9,8); // on crée l'objet écran Servo servo; int ledPin = 26; int pirState = LOW; int val = 0; char place_ocup = 0; int inputPin = 10; long duration, distance, duration2, distance2, duration3, distance3; int pl1 = 0, pl2 = 0, pl3 = 0; // 0 vrai // 1 faux
Une fois les initialisations faites, nous allimentons notre programme de plusieurs sous fonctio
1.Fonction lever: Elle permet de lever la barrière du parking, pour cela il suffit d'initialiser une variablie Position qui à l'aide d'une " boucle for" , va modifier la position du servo-moteur en incrementant la variable.
void lever(void){ for (int pos = 0; pos <= 100; pos++){ servo.write(pos); delay(50); }}
2.Fonction descendre:
Dans la même optique, cette fonction permet de descendre la barrière du parking, mais cette fois-ci en décrementant la variable.
void descendre(void){ for (int pos = 100; pos >= 1; pos--){ servo.write(pos); delay(20); }}
3.Fonction EcritureSérie:
Cette fonction nous permettra de communiquer en liaison série, lorsqu'on reçoit un caractère on le renvoit grâce à un Serial.Write
void ecritureSerie(char caractere){ Serial.write(caractere);}
4.Fonction Occupée:
Permet d'obetenir des informations sur une place du parking, si la distance renvoyée par le capteur ultrason est comprise entre 0 non inclu et 3, alors la place est occupée.
bool occuped(long distance){ return (distance <= 3) and (distance != 0);}
5.Fonction Get_distance:
Cette fonction permet de récuperer la distance reçue grace aux capteurs à ultrason. Elle prend en paramètre les deux broches du capteurs, et retourne la distance entre le capteur et l'obstacle (en loccurence la voiture dans notre cas).
Si aucun obstacle n'a été trouvé, le capteur renvoit 0.
long get_distance(int trigPin, int echoPin){ long dist, duration; digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); dist = (duration / 2) / 29.1; return dist;}
6.Fonction VerifPlace: Cette fonction permet d'incrementer une variable place_occup, qui nous sera utile pour le site. Dans cette fonction, nous affichons également le nombre de place disponible, et les numéros de ces places sur l'affficheur. Si aucune place n'est disponible, alors on affiche FULL.
void verifplace(){ distance = get_distance(TRIGPIN_1, ECHOPIN_1); distance2 = get_distance(TRIGPIN_2, ECHOPIN_2); if (occuped(distance)){ pl1 = 1; } else{ pl1 = 0; } if (occuped(distance2)){ pl2 = 1; } else{ pl2 = 0; } place_ocup = pl1 + pl2; LCD.setCursor(0, 0); LCD.write("Parking"); LCD.setCursor(0, 1); LCD.write("Nbplac"); LCD.setCursor(7, 1); LCD.print(PLACES_TOTALES - place_ocup); LCD.setCursor(13, 0); LCD.print("PL:"); LCD.setCursor(8, 0); if (pl1 + pl2 + pl3 == 3) LCD.print("FULL"); else LCD.print(" "); LCD.setCursor(9, 1); if (pl1 == 0) LCD.print("1"); else if (pl1 == 1) LCD.print(" "); LCD.setCursor(12, 1); if (pl2 == 0) LCD.print("2"); else if (pl2 == 1) LCD.print(" "); LCD.setCursor(15, 1); if (pl3 == 0) LCD.print("3"); else if (pl3 == 1) LCD.print(" ");}
7.Fonction Setup:
void setup(){ pinMode(TRIGPIN_1, OUTPUT); pinMode(ECHOPIN_1, INPUT); pinMode(TRIGPIN_2, OUTPUT); pinMode(ECHOPIN_2, INPUT); LCD.begin(16, 2); Serial.begin(9600); pinMode(ledPin, OUTPUT); // on accroche notre servomoteur branché sur la broche 9 servo.attach(SERVOMOTEUR); // positionne la barrière horizontalement servo.write(0);}
8.Fonction Loop:
Fonction qui sera réalisée en boucle, et qui appel nos sous-fonctions.
void loop() { verifplace(); while(Serial.available()>0){ char data = Serial.read(); char nbPlace; if(data == 'a'){ lever(); } if(data == 'b'){ descendre(); } if(data == 'c'){ verifplace(); ecritureSerie(PLACES_TOTALES - place_ocup + '0'); } } delay(200);}
Partie Websocket :
En un premier temps nous avons décidé de nous mettre dans un environnement Linux afin de nous faciliter la tâche car nos essais sous Windows nous ont parus plus complexes à adapter.
La difficulté ici était de faire communiquer en série l'Arduino et la Raspberry Pi, pour cela nous avons commencé par des exercices simplifiés : allumer la led du pin 13 si l'on reçoit le caractère 'a' (par exemple) de la Raspberry. Une fois cette étape franchie et le principe assimilé , il nous suffisait de déchiffrer le code du fichier webserial.c qui nous a été fourni, et effectuer nos modifications dessus afin de l'adapter à nos besoins. Pour ce qui est de la liaison entre le navigateur et notre Raspberry, nous avons opté pour utiliser un commutateur, car nous avons essentiellement travaillé en dehors de Polytech, il était donc impossible de disposer de son réseau. Nous avons donc pensé à implémenter un réseau local "personnel", tout d'abord en wifi, sans succès (ie : impossible d'inter-ping entre les appareils), puis nous nous sommes tournés vers un commutateur de type "plug and play".
Partie HTML
la configuration des adresses IP de la Raspberry et de l'ordinateur (sur lequel le navigateur tourne), dans les lignes de code du fichier html suivantes :
var localhost = '127.0.0.1:9000' var ip_raspberry = '172.26.145.162:9000' var websocket = new WebSocket('ws://'+ip_raspberry,'serial');
Nous avons établi un code couleur qui indique si la liaison est faite ou pas :
websocket.onopen=function(){ $('h1').css('color','green'); }; websocket.onerror=function(){ $('h1').css('color','red'); };
La fonction suivante permet au navigateur d'envoyer ses données à la Raspberry :
function sendMessage(data){ websocket.send(data); }
La création des boutons que l'on utilise sur l'interface et qui répondent à nos fonctionnalités :
<button onclick="sendMessage('a');">Ouvrir barrière</button> <button onclick="sendMessage('b');">Descendre barrière</button> <button onclick="sendMessage('c');">Afficher nbr places </button>
Partie C
Dans notre code C les fonctions principales sont les suivantes (étant donné que les autres fonctions sont explicités sous forme de commentaires sur le code disponible sur notre gitlab).
Ce qui permet d'envoyer et recevoir les données série entre la Raspberry et l'Arduino :
la fonction envoie_serie envoie une chaine de caractère sur la liaison série
int envoie_serie(int fd, char *str){ return write(fd, str, strlen(str)); }
La fonction lecture_serie Lit n caractères sur la liaison série (données reçues de l'Arduino à la Raspberry) :
int lecture_serie(int fd, char *str, int n){ return read(fd, str, n); }
Ce qui permet d'envoyer et recevoir les données entre la Raspberry et le serveur :
static int callback_serial(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { unsigned char byte; char message[LWS_SEND_BUFFER_PRE_PADDING+LWS_SEND_BUFFER_POST_PADDING+3]; switch(reason){ case LWS_CALLBACK_ESTABLISHED: printf("connection established\n"); lws_callback_on_writable(wsi); break; case LWS_CALLBACK_RECEIVE: // Ici sont traites les messages envoyes par le navigateur printf("received data: %s\n",(char *)in); if(envoie_serie(sd, (char*)in) == -1){ /* En cas d'erreur d'envoie sur la laison série */ perror("callback_serial.envoie_serie"); } if(sscanf(in,"%hhd",&byte)==1) write(sd,&byte,1); break; case LWS_CALLBACK_SERVER_WRITEABLE: // Ici sont envoyes les messages au navigateur if(read(sd,&byte,1)==1){ /* Recup sur la laison série d'un byte */ char *out=message+LWS_SEND_BUFFER_PRE_PADDING; printf("Caractère recu par Arduino : %s\n",out); printf("Envoie du caractère au navigateur\n"); sprintf(out,"%c",byte); //On copie l'entier contenu de byte dans la viariable out lws_write(wsi,(unsigned char *)out,strlen(out),LWS_WRITE_TEXT); /* Renvoie au navigateur la variable out (ie : Ce qui a été renvoyé par l'Arduino) */ } lws_callback_on_writable(wsi); break; default: break; } return 0; }
La partie la plus fun et simpliste était la conception de l'interface du navigateur en HTML faire son style en css, car il suffisait de faire un peu de recherche personnelle et suivre quelques tutoriels.
Bilan
En conclusion, notre Smart Car Park est terminé et est fonctionnel. En effet le serveur Websocket envoie des caractères permettant d'ouvrir fermer ou donner le nombres de places disponibles à l’Arduino, et elle à son tour entraîne le servo moteur ou renvoie la valeur des places disponibles. L’objectif principal de notre projet est donc réalisé avec succès.
Contrairement à la partie du serveur WebSocket où nous avions eu du mal à comprendre l’enchaînement des taches et la manière de l'utiliser même si nous avons tout de même réussi à faire ce que nous voulions et nous en étions très satisfaites et contentes.
Nous avons eu l’occasion de découvrir les FPGA et la manière de les programmer graphiquement.Mais cette partie n'a pas pu être aboutit malheureusement, et c'est dommage, car c'est une technologie intéressante, et plus optimisé et rapide qu'une Arduino (mais nous supposons que nous aurions l'occasion de mieux le comprendre et l'élaborer en IMA4). Nous avons constaté tout de même les nombreux avantages du FPGA (fiabilité, flexibilité, coût) qui font que c'est une excellente alternative d’avenir face aux microcontrôleurs traditionnels.
En outre, ce projet nous a permis d'avoir un aperçu sur la filière systèmes communicants.
En effet, le projet nous a été très enrichissant, alliant la pratique à la théorie notamment quelques cours de modules vu cette année, nous avons beaucoup cherché pour comprendre et réaliser nos fonctions, nous n'avions jamais utilisé de Raspberry Pi ni fait une configuration dessus (cela nous a aidé à nous développer en autonomie). Nous avons donc eu l'occasion de voir la capacité et la force de ce petit objet! et bon nombre d'entre nous a pour objectif cet été de faire des petits projets et tutoriels dessus afin de se familiariser davantage avec les objets et les technologies connectés et pourquoi pas choisir la filière SC l'an prochain.
De plus, ce projet nous a permis de confronter la difficulté du débogage. Mais en pensant au "Voyager 1" que l'on peut localiser depuis plusieurs milliards de kilomètres, nous avons admis que faire communiquer une Arduino avec une Raspberry pi ne devrait pas être si difficile pour un ingénieur !
Le git du projet se trouve à l'adresse suivante : https://archives.plil.fr/hmalti/Projet_SC.git
La vidéo de notre projet se trouve à l'adresse suivante: https://drive.google.com/file/d/1S3H0lWYVez-IOR8IS-GYRhNrQQHRCI9G/view?usp=sharing