Projet IMA3 P7, 2017/2018, TD2
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.
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 :
Fichier:Branchement2018.jpg
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; } pl3 = 1; place_ocup = pl1 + pl2 + pl3; /*if ((distance3 <= 2) and (distance3 != 0) ){ place_ocup++; pl3 = 1; } */ 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 communiqué 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 faire 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 switch, car nous ne travaillions pas souvent sur le réseau Polytech et nous ne parvenions pas à les faire communiquer via le réseau wifi du campus (à cause de sa sécurité et les restrictions mises dessus). Ceci explique 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>
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, il nous a été très enrichissant, en 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 et communiquer avec depuis plusieurs millions 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