Projet IMA3 P4, 2015/2016, TD2 : Différence entre versions
(→Partie électronique) |
m (a déplacé Projet IMA3 P4, 2014/2015, TD2 vers Projet IMA3 P4, 2015/2016, TD2) |
||
(75 révisions intermédiaires par 2 utilisateurs non affichées) | |||
Ligne 13 : | Ligne 13 : | ||
<br>'''Optionnel:''' | <br>'''Optionnel:''' | ||
<br>• Bouton poussoir | <br>• Bouton poussoir | ||
+ | |||
+ | |||
+ | Voici le schéma du fonctionnement prévu de notre projet : | ||
+ | [[Fichier:schemaprincipe.png|700px|thumb|center|Figure 1 : Schéma de principe]] | ||
== Séance 1 == | == Séance 1 == | ||
Ligne 22 : | Ligne 26 : | ||
− | [[Fichier:Compsignal.png|700px|thumb|center|Figure | + | [[Fichier:Compsignal.png|700px|thumb|center|Figure 2 : Principe de la comparaison]] |
Ligne 28 : | Ligne 32 : | ||
− | [[Fichier:SchémaCAN.png|700px|thumb|center|Figure | + | [[Fichier:SchémaCAN.png|700px|thumb|center|Figure 3 : Schéma de principe]] |
Ce système nous permet de récupérer une valeur numérique proportionnelle à la valeur analogique. Posons '''a''' le coefficient directeur de la rampe tel que <math>V=at</math>, ainsi si le signal analogique à la valeur '''Ve''' est égal à la rampe au temps '''Te''' nous avons <math>Te=\frac{Ve}{a}</math>. Le compteur s'incrémente à une fréquence f<sub>h</sub>, '''N''' est la valeur atteinte avant la raz, nous avons donc <math>N=fhTe</math>, ce qui nous donne <math>N=\frac{fh}{a}Ve</math>. Le tout est envoyé vers un registre à décalage de 8 bits, qui permet de sauvegarder et d'envoyer la valeur de la conversion sur 8 bits vers la liaison série. | Ce système nous permet de récupérer une valeur numérique proportionnelle à la valeur analogique. Posons '''a''' le coefficient directeur de la rampe tel que <math>V=at</math>, ainsi si le signal analogique à la valeur '''Ve''' est égal à la rampe au temps '''Te''' nous avons <math>Te=\frac{Ve}{a}</math>. Le compteur s'incrémente à une fréquence f<sub>h</sub>, '''N''' est la valeur atteinte avant la raz, nous avons donc <math>N=fhTe</math>, ce qui nous donne <math>N=\frac{fh}{a}Ve</math>. Le tout est envoyé vers un registre à décalage de 8 bits, qui permet de sauvegarder et d'envoyer la valeur de la conversion sur 8 bits vers la liaison série. | ||
Ligne 36 : | Ligne 40 : | ||
Nous avons ensuite pris en main le logiciel Altium en suivant le tutoriel proposé. Ce tutoriel consiste en la réalisation d'un compteur 4 bits et de son affichage sur les leds disponibles sur la nanoboard: | Nous avons ensuite pris en main le logiciel Altium en suivant le tutoriel proposé. Ce tutoriel consiste en la réalisation d'un compteur 4 bits et de son affichage sur les leds disponibles sur la nanoboard: | ||
− | [[Fichier:CompteurLed.JPG|500px|thumb|center|Figure | + | [[Fichier:CompteurLed.JPG|500px|thumb|center|Figure 4 : Compteur 4 bits]] |
====Réalisation d'une PWM==== | ====Réalisation d'une PWM==== | ||
Ligne 42 : | Ligne 46 : | ||
Après avoir compris plus clairement le fonctionnement d'Altium et de la nanoboard (compilation du programme et envoi sur la nanoboard), nous avons étudié la façon de réaliser une PWM. Nous avons étudié le fonctionnement du bloc PWM disponible sur Altium afin de comprendre comment l'utiliser. Nous avons donc réalisé le schematic suivant: | Après avoir compris plus clairement le fonctionnement d'Altium et de la nanoboard (compilation du programme et envoi sur la nanoboard), nous avons étudié la façon de réaliser une PWM. Nous avons étudié le fonctionnement du bloc PWM disponible sur Altium afin de comprendre comment l'utiliser. Nous avons donc réalisé le schematic suivant: | ||
− | [[Fichier:PWMV1.JPG|1000px|thumb|center|Figure | + | [[Fichier:PWMV1.JPG|1000px|thumb|center|Figure 5 : Utilisation du bloc PWM]] |
L'entrée '''D[7..0]''' est la valeur du rapport cyclique voulu, l'entrée '''C''' est la fréquence du signal. Notre programme envoie un signal de type MLI sur la sortie HA2. | L'entrée '''D[7..0]''' est la valeur du rapport cyclique voulu, l'entrée '''C''' est la fréquence du signal. Notre programme envoie un signal de type MLI sur la sortie HA2. | ||
Ligne 72 : | Ligne 76 : | ||
Nous avons ensuite testé le programme en en affichant les valeurs numériques de x, y et z envoyées sur le port série de la carte Arduino pour différentes accélérations de l'accéléromètre. Les valeurs numériques de x, y et z sont comprises entre 0 et 500, elles varient bien quand on bouge l'accéléromètre. | Nous avons ensuite testé le programme en en affichant les valeurs numériques de x, y et z envoyées sur le port série de la carte Arduino pour différentes accélérations de l'accéléromètre. Les valeurs numériques de x, y et z sont comprises entre 0 et 500, elles varient bien quand on bouge l'accéléromètre. | ||
− | [[Image:ardui.jpg|500px|thumb|center|alt=Arduino|Figure | + | [[Image:ardui.jpg|500px|thumb|center|alt=Arduino|Figure 6 : Prototype Arduino de l'accéléromètre]] |
====Configuration du Raspberry Pi==== | ====Configuration du Raspberry Pi==== | ||
Ligne 95 : | Ligne 99 : | ||
=== Partie électronique === | === Partie électronique === | ||
− | Lors de cette séance nous avons commencé par tester notre signal PWM et la | + | ====Signal PWM==== |
− | [[Fichier: | + | Lors de cette séance, nous avons commencé par tester notre signal PWM afin de comprendre comment il fonctionne. Comme observé lors de la séance 1, nous faisons varier le rapport cyclique de la PWM à une fréquence plus lente que celle de la PWM, et voici ce que l'on obtient : |
− | + | [[Fichier:pwm.gif|930px|thumb|center|Figure 7 : PWM]] | |
+ | |||
+ | ====Réalisation du montage suiveur==== | ||
+ | Ensuite, nous avons étudié notre partie analogique qui aura pour rôle de convertir le signal PWM en un signal type "dents de scie". Pour commencer, il nous a fallu mettre en place un montage suiveur pour pouvoir transmettre le signal fourni par la nanoboard en toute sécurité et effectuer une adaptation d'impédance. En effet, le courant fourni par la nanoboard étant faible, nous risquons d’endommager les pins de la nanoboard. Ce montage permet de pallier à ce problème et d'augmenter le courant fourni. Nous utilisons le circuit intégré TL082, nous avons cherché dans la datasheet du composant la façon dont les AOPs sont câblés. Voici le schéma de câblage du TL082: | ||
+ | [[Fichier:TL082.jpg|700px|thumb|center|Figure 8 : TL082]] | ||
+ | Nous constatons que le circuit comporte 2 AOPs, le second AOP sera utilisé pour la comparaison entre le signal rampe et la valeur fournie par l'accéléromètre. Voici le montage suiveur proposé et testé: | ||
+ | [[Fichier:Suiveur.png|300px|thumb|center|Figure 9 : Montage suiveur]] | ||
+ | Lorsque nous avons câblé notre circuit, nous avons choisi Vcc=5v, et -Vcc=0v, cependant nous avons rencontré quelques problèmes avec ces valeurs d’alimentation. Ensuite nous avons testé avec -Vcc=-5v, et voici le signal obtenu (entrée en bleu, sortie en jaune): | ||
+ | [[Fichier:Signalsuiveur.jpg|300px|thumb|center|Figure 10 : Signal suiveur]] | ||
+ | Nous constatons donc que le signal en sortie du suiveur est correct. | ||
=== Partie informatique === | === Partie informatique === | ||
Ligne 261 : | Ligne 274 : | ||
Nous avons ensuite testé le fonctionnement de notre serveur WebSocket avec une page HTML qui se connecte dessus. Cette page HTML n'est pas la page définitive de notre interface web. Elle permet d’afficher en temps réel les valeurs des accélérations en X, Y et Z. On connecte notre système arduino sur un des ports USB du Raspberry Pi pour tester. On observe bien la variation des accélérations en X, Y et Z sur le navigateur, notre WebSocket est donc fonctionnel, il ne reste plus qu'a réaliser l'interface web définitive. | Nous avons ensuite testé le fonctionnement de notre serveur WebSocket avec une page HTML qui se connecte dessus. Cette page HTML n'est pas la page définitive de notre interface web. Elle permet d’afficher en temps réel les valeurs des accélérations en X, Y et Z. On connecte notre système arduino sur un des ports USB du Raspberry Pi pour tester. On observe bien la variation des accélérations en X, Y et Z sur le navigateur, notre WebSocket est donc fonctionnel, il ne reste plus qu'a réaliser l'interface web définitive. | ||
− | [[Image:arduino_raspberry.jpg|500px|thumb|center|alt=Arduino|Figure | + | [[Image:arduino_raspberry.jpg|500px|thumb|center|alt=Arduino|Figure 11 : Le système arduino connecté sur le Raspberry Pi]] |
− | [[Image:affiche.png|500px|thumb|center|alt=Arduino|Figure | + | [[Image:affiche.png|500px|thumb|center|alt=Arduino|Figure 12 : Page html pour le test du serveur WebSocket]] |
== Séance 3 == | == Séance 3 == | ||
=== Partie électronique === | === Partie électronique === | ||
+ | ====Circuit analogique : Filtre RC==== | ||
+ | Lors de cette séance, nous avons réalisé la conversion du signal rampe. Pour cela, nous utilisons un filtre RC en intégrateur. Pour que notre circuit RC se comporte comme un intégrateur, il faut que la constante de temps du filtre sois plus grande que la période de notre PWM. | ||
+ | <math>Tau=RC > T </math> et donc <math>\frac{1}{RC} < \frac{1}{T}</math>, nous choisissons donc R=1200 ohm et C=4,7 nF ce qui nous donne une fréquence de coupure de 180 Hz. Nous fixons la fréquence de notre PWM à 200 Hz. Voici le circuit utilisé par la conversion et le signal observé | ||
+ | en sortie de la rampe : | ||
+ | [[Fichier:Circuitconv.jpg|200px|thumb|center|Figure 13 : Circuit conversion]] | ||
+ | [[Fichier:signalrampe.png|200px|thumb|center|Figure 14 : Signal rampe]] | ||
+ | |||
+ | Le signal rampe en sortie est correct, nous avons donc continué et effectué la comparaison. | ||
+ | |||
+ | ====Circuit et résultat final==== | ||
+ | Voici le schéma final et le circuit de la partie analogique : | ||
+ | [[Fichier:Circuitfinal1.png|300px|thumb|center|Figure 15 : Circuit final]] | ||
+ | [[Fichier:Schemafinal.png|400px|thumb|center|Figure 16 : Schéma final]] | ||
+ | |||
+ | Voici le signal observé en entrée et en sortie du comparateur : | ||
+ | [[Fichier:signalcomp1.png|300px|thumb|center|Figure 17 : signal comparaison valeur accéléromètre]] | ||
+ | [[Fichier:signalcomp2.jpg|300px|thumb|center|Figure 18 : signal de sortie]] | ||
+ | Nous obtenons une valeur proportionnelle à celle fournie par l'accéléromètre, ce signal est à récupérer sur la nanoboard pour être ensuite enregistré dans le registre afin d'être envoyé sur la liaison série. Cependant nous n'avons pu finaliser cette partie à cause de nombreux contres temps, avec une séance de plus nous aurions pu la finaliser. | ||
=== Partie informatique === | === Partie informatique === | ||
− | ====Réalisation de la page HTML==== | + | ====Réalisation de la page Web==== |
+ | |||
+ | Lors de cette dernière séance, nous avons réalisé notre interface web. Nous avons décidé de commander la raquette d'un jeu de casse briques à l'aide de l'accéléromètre au lieu de commander le déplacement d'un simple pointeur à l'écran comme indiqué dans le cahier des charges car cela est plus intéressant du point de vu de l'utilisateur et nous permet de gérer plus facilement les valeurs de l'accéléromètre. Après différents tests, nous choisissons les conditions suivantes pour un déplacement à gauche : x<140 et y<60 et pour un déplacement à droite x<140 et y>140. Nous n'utilisons donc que les valeurs fournies sur l'axe y de notre accéléromètre pour différentier le déplacement à gauche du déplacement à droite. | ||
+ | <br><br>Notre interface web se compose de 4 fichiers : | ||
+ | <br>• le fichier jquery.js qui récupère toutes les données de notre Websocket. | ||
+ | <br>• le fichier Cassebriques.js qui gère l'animation de notre page et du jeu. | ||
+ | <br>• le fichier Cassebriques.css qui est notre feuille de style. | ||
+ | <br>• le fichier Cassesbriques.html qui fait appel aux trois autres et affiche notre page Web. | ||
+ | |||
+ | [[Image:Interfaceweb.png|700px|thumb|center|Figure 19 : Interface]] | ||
+ | |||
+ | ====Code de la page Web==== | ||
+ | Voici les fichiers Cassesbriques.html et Cassebriques.css : | ||
+ | <gallery> | ||
+ | Fichier:cassebriqueshtml.jpg|Fichier HTML récupérant les éléments des fichiers javascript et chargeant les canvas | ||
+ | Fichier:cassebriquescss.jpg|Fichier CSS qui permet de gérer les couleurs, les polices... tout ce qui touche à l'aspect graphique de notre page | ||
+ | </gallery> | ||
+ | |||
+ | Le fichier cassesbriques.js étant le plus intéressant, nous décidons de détailler cette partie. | ||
+ | =====Partie websocket===== | ||
+ | <pre> | ||
+ | window.WebSocket=(window.WebSocket||window.MozWebSocket); | ||
+ | |||
+ | var websocket=new WebSocket('ws://172.26.79.9:9000','myprotocol'); | ||
+ | |||
+ | websocket.onopen=function(){ $('h5').css('color','green'); }; | ||
+ | |||
+ | websocket.onerror=function(){ $('h4').css('color','red'); }; | ||
+ | |||
+ | websocket.onmessage=function(message){ | ||
+ | console.log(message.data); | ||
+ | var tableau=message.data.split(' '); | ||
+ | tableau.map(parseInt)//Convertie en entier les caractéres reçus ; | ||
+ | checkDepla(tableau)//envoie le tableau vers la fonction déblacement; | ||
+ | }; | ||
+ | </pre> | ||
+ | Cette partie permet de récupérer les valeurs de l’accéléromètre fournies par le websocket et vérifie aussi si notre interface web est bien connectée à la Raspberry Pi. Nous récupérons les valeurs que l'on converti en entiers et que l'on stocke dans un tableau, ces données serons récupérées plus bas, afin de déplacer la barre. | ||
+ | |||
+ | =====Jeu du casse briques===== | ||
+ | <pre> | ||
+ | // Constantes du jeu | ||
+ | var NBR_LIGNES = 5; | ||
+ | var NBR_BRIQUES_PAR_LIGNE = 8; | ||
+ | var BRIQUE_WIDTH = 48; | ||
+ | var BRIQUE_HEIGHT = 15; | ||
+ | var ESPACE_BRIQUE = 2; | ||
+ | var BARRE_JEU_WIDTH = 80; | ||
+ | var BARRE_JEU_HEIGHT = 10; | ||
+ | var PXL_DEPLA = 10; | ||
+ | var ZONE_JEU_WIDTH = 400; | ||
+ | var ZONE_JEU_HEIGHT = 300; | ||
+ | var COULEURS_BRIQUES = "#0099FF"; | ||
+ | var COULEUR_BALLE = "#FF0101"; | ||
+ | var DIMENSION_BALLE = 8; | ||
+ | var VITESSE_BALLE = 2; | ||
+ | |||
+ | // Variables | ||
+ | var tabBriques; // Tableau virtuel contenant les briques | ||
+ | var barreX; // Position en X de la barre: Valeurs de l'accéléromètre | ||
+ | var barreY; // Position en Y de la barre: Ne bougera pas. | ||
+ | var context; | ||
+ | var balleX = 100; | ||
+ | var balleY = 250; | ||
+ | var dirBalleX = 1; | ||
+ | var dirBalleY = -1; | ||
+ | var boucleJeu; | ||
+ | var limiteBriques = (ESPACE_BRIQUE+BRIQUE_HEIGHT)*NBR_LIGNES; | ||
+ | var aGagne = 0; | ||
+ | </pre> | ||
+ | Nous commençons tout d'abord par initialiser les variables utiles pour notre jeu, telles que les positions X, Y de la balle, sa direction et sa vitesse. Pour les briques, nous utiliserons une matrice, comme dans la plupart des interfaces pour ce type de jeux. | ||
− | + | <pre> | |
+ | window.addEventListener('load', function () { | ||
+ | // On recupere l'objet canvas | ||
+ | var elem = document.getElementById('canvasElem'); | ||
+ | if (!elem || !elem.getContext) { | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | // On recupere le contexte 2D | ||
+ | context = elem.getContext('2d'); | ||
+ | if (!context) { | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | // Initialisations des variables | ||
+ | ZONE_JEU_WIDTH = elem.width; | ||
+ | ZONE_JEU_HEIGHT = elem.height; | ||
+ | barreX = (ZONE_JEU_WIDTH/2)-(BARRE_JEU_WIDTH/2); | ||
+ | barreY = (ZONE_JEU_HEIGHT-BARRE_JEU_HEIGHT); | ||
+ | |||
+ | // Le navigateur est compatible, on peut continuer: On initialise le jeu. | ||
+ | creerBriques(context, NBR_LIGNES, NBR_BRIQUES_PAR_LIGNE, BRIQUE_WIDTH, BRIQUE_HEIGHT, ESPACE_BRIQUE); | ||
+ | |||
+ | // Boucle de rafraichissement du contexte 2D | ||
+ | idInterv = setInterval(refreshGame, 10); | ||
+ | |||
+ | }, false); | ||
+ | </pre> | ||
+ | Une fois l'initialisation des variables faite, nous devons récupérer l'élément canvas de notre page. Si la récupération est réussie, nous initialisons notre jeu avec les valeurs adéquates. Pour finir nous utilisons une boucle qui va se rafraîchir toute les 10 ms et réactualiser notre affichage affin d'avoir une page dynamique. | ||
+ | |||
+ | <pre> | ||
+ | function checkDepla(tableau) { | ||
+ | //Se deplace en fonction des valeurs de l'accélerometre | ||
+ | //deplacement droit | ||
+ | if (tableau[1] > 140) { | ||
+ | if ( (barreX+PXL_DEPLA+BARRE_JEU_WIDTH) <= ZONE_JEU_WIDTH ) barreX += PXL_DEPLA; | ||
+ | } | ||
+ | //deplacement gauche | ||
+ | else if (tableau[1] < 60) { | ||
+ | if ( ((barreX-PXL_DEPLA)) >= 0 ) barreX -= PXL_DEPLA; | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | C'est cette partie qui permet de déplacer notre barre en fonction des valeurs fournies par l’accéléromètre. | ||
+ | |||
+ | <pre> | ||
+ | function perdu() { | ||
+ | clearInterval(boucleJeu); | ||
+ | |||
+ | var r = confirm("voulez vous recommencer ?"); | ||
+ | if (r == true) { | ||
+ | balleX = 100; | ||
+ | balleY = 250; | ||
+ | dirBalleX = 1; | ||
+ | dirBalleY = -1; | ||
+ | creerBriques(context, NBR_LIGNES, NBR_BRIQUES_PAR_LIGNE, BRIQUE_WIDTH, BRIQUE_HEIGHT, ESPACE_BRIQUE); | ||
+ | idInterv = setInterval(refreshGame, 10); | ||
+ | } else { | ||
+ | clearInterval(boucleJeu); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function gagne() { | ||
+ | clearInterval(boucleJeu); | ||
+ | alert("Bravo vous avez gagné !"); | ||
+ | } | ||
+ | </pre> | ||
+ | Ces deux fonctions permettent de gérer la victoire de l'utilisateur. Si l'utilisateur perd, il lui est proposé de recommencer ou non. | ||
+ | |||
+ | <pre> | ||
+ | function clearContexte(ctx, startwidth, ctxwidth, startheight, ctxheight) { | ||
+ | ctx.clearRect(startwidth, startheight, ctxwidth, ctxheight); | ||
+ | } | ||
+ | |||
+ | // Fonction permettant de creer les briques du jeu | ||
+ | function creerBriques(ctx, nbrLignes, nbrParLigne, largeur, hauteur, espace) { | ||
+ | |||
+ | // Tableau virtuel: On initialise les lignes de briques | ||
+ | tabBriques = new Array(nbrLignes); | ||
+ | |||
+ | for (var i=0; i < nbrLignes; i++) { | ||
+ | |||
+ | // Tableau virtuel: On initialise les briques de la ligne | ||
+ | tabBriques[i] = new Array(nbrParLigne); | ||
+ | |||
+ | // Affichage: On attribue une couleur aux briques de la ligne | ||
+ | ctx.fillStyle = COULEURS_BRIQUES[i]; | ||
+ | |||
+ | for (var j=0; j < nbrParLigne; j++) { | ||
+ | |||
+ | // Affichage: On affiche une nouvelle brique | ||
+ | ctx.fillRect((j*(largeur+espace)),(i*(hauteur+espace)),largeur,hauteur); | ||
+ | |||
+ | // Tableau virtuel: On attribue a la case actuelle la valeur 1 = Une brique existe encore | ||
+ | tabBriques[i][j] = 1; | ||
+ | |||
+ | } | ||
+ | } | ||
+ | // Nos briques sont initialisees. | ||
+ | return 1; | ||
+ | } | ||
+ | </pre> | ||
+ | La fonction clearContexte, permet d’effacer tout ce qui est contenu dans la partie où est affiché le jeu, c'est à dire la barre, la balle, et les briques. La fonction creerbriques permet d'initialiser la matrice des briques. | ||
+ | |||
+ | <pre> | ||
+ | function refreshGame() { | ||
+ | |||
+ | // On efface la zone | ||
+ | clearContexte(context, 0, ZONE_JEU_WIDTH, 0, ZONE_JEU_HEIGHT); | ||
+ | |||
+ | // On reaffiche | ||
+ | |||
+ | // Reaffichage des briques | ||
+ | for (var i=0; i < tabBriques.length; i++) { | ||
+ | context.fillStyle = COULEURS_BRIQUES; | ||
+ | for (var j=0; j < tabBriques[i].length; j++) { | ||
+ | if (tabBriques[i][j] == 1) context.fillRect((j*(BRIQUE_WIDTH+ESPACE_BRIQUE)),(i*(BRIQUE_HEIGHT+ESPACE_BRIQUE)),BRIQUE_WIDTH,BRIQUE_HEIGHT); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // On vérifie si le joueur a gagné | ||
+ | if ( aGagne ) gagne(); | ||
+ | |||
+ | // Reaffichage de la barre | ||
+ | context.fillStyle = "#333333"; | ||
+ | context.fillRect(barreX,barreY,BARRE_JEU_WIDTH,BARRE_JEU_HEIGHT); | ||
+ | |||
+ | // Calcul de la nouvelle position de la balle | ||
+ | |||
+ | if ( (balleX + dirBalleX * VITESSE_BALLE) > ZONE_JEU_WIDTH) dirBalleX = -1; | ||
+ | else if ( (balleX + dirBalleX * VITESSE_BALLE) < 0) dirBalleX = 1; | ||
+ | if ( (balleY + dirBalleY * VITESSE_BALLE) > ZONE_JEU_HEIGHT){ | ||
+ | perdu(); | ||
+ | |||
+ | } | ||
+ | else { | ||
+ | if ( (balleY + dirBalleY * VITESSE_BALLE) < 0) dirBalleY = 1; | ||
+ | else { | ||
+ | if ( ((balleY + dirBalleY * VITESSE_BALLE) > (ZONE_JEU_HEIGHT - BARRE_JEU_HEIGHT)) && ((balleX + dirBalleX * VITESSE_BALLE) >= barreX) && ((balleX + dirBalleX * VITESSE_BALLE) <= (barreX+BARRE_JEU_WIDTH))) { | ||
+ | dirBalleY = -1; | ||
+ | dirBalleX = 2*(balleX-(barreX+BARRE_JEU_WIDTH/2))/BARRE_JEU_WIDTH; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Test des collisions avec les briques | ||
+ | if ( balleY <= limiteBriques) { | ||
+ | // On est dans la zone des briques | ||
+ | var ligneY = Math.floor(balleY/(BRIQUE_HEIGHT+ESPACE_BRIQUE)); | ||
+ | var ligneX = Math.floor(balleX/(BRIQUE_WIDTH+ESPACE_BRIQUE)); | ||
+ | if ( tabBriques[ligneY][ligneX] == 1 ) { | ||
+ | tabBriques[ligneY][ligneX] = 0; | ||
+ | dirBalleY = 1; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | balleX += dirBalleX * VITESSE_BALLE; | ||
+ | balleY += dirBalleY * VITESSE_BALLE; | ||
+ | |||
+ | // Affichage de la balle | ||
+ | context.fillStyle = COULEUR_BALLE; | ||
+ | context.beginPath(); | ||
+ | context.arc(balleX, balleY, DIMENSION_BALLE, 0, Math.PI*2, true); | ||
+ | context.closePath(); | ||
+ | context.fill(); | ||
+ | } | ||
+ | </pre> | ||
+ | Voici le cœur de notre jeu, cette partie sert à réactualiser les différentes variables, effacer le canvas et le ré-afficher avec les nouvelles valeurs. | ||
− | [[Image:page_html.png|800px|thumb|center|alt=Arduino|Figure | + | [[Image:page_html.png|800px|thumb|center|alt=Arduino|Figure 20 : Notre interface web]] |
== Démonstration == | == Démonstration == | ||
Ligne 286 : | Ligne 553 : | ||
== Conclusion == | == Conclusion == | ||
+ | Ce projet nous a permi d'aborder une partie importante de la formation IMA, en premier lieu la gestion d'un projet complet avec la nécessité de répartir les tâches. En effet, le projet étant composé de deux grandes parties : l'interface électronique et l'interface informatique, nous ne pouvions pas le mener à terme sans une organisation et un travail de groupe méthodique. La partie électronique nous a permi de nous familiariser avec les FPGA, mais aussi de concevoir un circuit en consultant les datasheet et en choisissant nos composants tout en respectant un cahier des charges. La partie informatique nous a quand à elle permi d'appréhender une partie des protocoles utilisés par les systèmes communicants, mais aussi de réfléchir à la conception d'une interface graphique et à la façon de l'interfacer pour avoir un projet viable. La partie électronique de notre projet n'étant pas aboutie, la communication via la liaison série vers notre serveur ne peut se faire. Cependant, l’utilisation d'un prototype via une carte arduino nous a permi de constater que notre partie informatique est parfaitement fonctionnelle. |
Version actuelle datée du 31 décembre 2016 à 23:39
Sommaire
Projet IMA3-SC 2015/2016 : Accéléromètre
Cahier des charges
Pour notre projet systèmes communicants nous avons choisi de contrôler un pointeur via un accéléromètre. Pour cela, nous utiliserons l'accéléromètre comme un détecteur d'inclinaison. Selon l'angle d'inclinaison nous ferons se déplacer un "pointeur" à l'écran. L'application web sera utilisée pour l'animation du pointeur, avec une page en HTML5 et des canvas pour réaliser notre page web dynamique JavaScript où sera faite l'animation. La communication entre l'application web et le port série (accès matériel) se fera en utilisant un serveur websocket. Via ce serveur nous pourrons avoir une communication en temps réel ce qui permettra d'avoir une animation qui réagira rapidement au mouvement de l'accéléromètre. Pour accéder aux données fournies par notre accéléromètre nous devrons utiliser une conversion analogique numérique. Pour cela, on utilise des signaux PWM. Le filtrage de ces signaux (filtre passe-bas) permet d'obtenir une tension en "dents de scie" (variations de la valeurs moyenne) et ensuite cette tension est comparée aux valeurs de l'accéléromètre.
Matériel nécessaire:
• Une rasberry pi
• Une nanoboard
• Un accéléromètre
• Une résistance et un condensateur (filtre passe-bas)
Optionnel:
• Bouton poussoir
Voici le schéma du fonctionnement prévu de notre projet :
Séance 1
Partie électronique
Analyse des objectifs
Nous avons d'abord commencé par établir les objectifs et rechercher la façon de réaliser notre partie électronique. La partie électronique aura pour rôle de récupérer les données fournies par l'accéléromètre et de les envoyer vers le serveur. L'accéléromètre fournit une tension qui correspond à une valeur d'accélération, pour que cette donnée sois exploitable par notre application web, il nous faut récupérer une valeur numérique c'est pourquoi nous devons réaliser une conversion analogique-numérique (CAN). La méthode proposée est l'utilisation d'un signal PWM pour fournir un signal en dents de scie après filtrage. Cette méthode est appelée conversion simple rampe. Le signal analogique à convertir est comparé à la valeur de la rampe, une fois que la rampe atteint la valeur du signal, la sortie du comparateur passe à 1.
Voici en principe comment devrait fonctionner la conversion. Nous utilisons un compteur qui s'incrémente à une fréquence donnée et est remis à zéro quand le comparateur passe à 1. Nous sauvegardons la valeur du compteur à ce moment là.
Ce système nous permet de récupérer une valeur numérique proportionnelle à la valeur analogique. Posons a le coefficient directeur de la rampe tel que , ainsi si le signal analogique à la valeur Ve est égal à la rampe au temps Te nous avons . Le compteur s'incrémente à une fréquence fh, N est la valeur atteinte avant la raz, nous avons donc , ce qui nous donne . Le tout est envoyé vers un registre à décalage de 8 bits, qui permet de sauvegarder et d'envoyer la valeur de la conversion sur 8 bits vers la liaison série.
Prise en main d'Altium
Nous avons ensuite pris en main le logiciel Altium en suivant le tutoriel proposé. Ce tutoriel consiste en la réalisation d'un compteur 4 bits et de son affichage sur les leds disponibles sur la nanoboard:
Réalisation d'une PWM
Après avoir compris plus clairement le fonctionnement d'Altium et de la nanoboard (compilation du programme et envoi sur la nanoboard), nous avons étudié la façon de réaliser une PWM. Nous avons étudié le fonctionnement du bloc PWM disponible sur Altium afin de comprendre comment l'utiliser. Nous avons donc réalisé le schematic suivant:
L'entrée D[7..0] est la valeur du rapport cyclique voulu, l'entrée C est la fréquence du signal. Notre programme envoie un signal de type MLI sur la sortie HA2.
Partie informatique
Réalisation du prototype Arduino de l'accéléromètre
Lors de cette première séance, nous avons commencé par mettre au point le prototype de notre accéléromètre avec une carte Arduino. Nous avons connecté l'accéléromètre à la carte Arduino (voir figure 1) puis nous avons réalisé un programme permettant de récupérer et convertir les valeurs analogiques que renvoie la carte à l'ordinateur via le port USB en valeurs numériques de type entier. Les valeurs analogiques envoyées par l’accéléromètre à l'ordinateur (des tensions allant de 0 à 3,3 Volts) correspondent aux accélérations selon les axes x, y et z. Le code de notre programme Arduino est donné ci-dessous :
int x, y, z; void setup() { Serial.begin(9600); } void loop() { x=analogRead(0); y=analogRead(1); z=analogRead(2); Serial.print("accelerations are x, y, z: "); Serial.print(x, DEC); Serial.print(" "); Serial.print(y, DEC); Serial.print(" "); Serial.println(z, DEC); delay(100); }
Nous avons ensuite testé le programme en en affichant les valeurs numériques de x, y et z envoyées sur le port série de la carte Arduino pour différentes accélérations de l'accéléromètre. Les valeurs numériques de x, y et z sont comprises entre 0 et 500, elles varient bien quand on bouge l'accéléromètre.
Configuration du Raspberry Pi
Lors de cette première séance nous avons aussi configuré et testé notre Raspberry Pi.
Nous avons tout d'abord connecté le Raspberry Pi à l'ordinateur fixe via un câble série afin de configurer sa carte Ethernet. On utilise le logiciel minicom pour accéder à la Raspberry et l'éditeur de texte nano pour copier les lignes de code suivantes dans le fichier /etc/network/interfaces :
auto eth0 iface eth0 inet static address 172.26.79.9 netmask 255.255.240.0 gateway 172.26.79.254
On indique aussi le serveur DNS dans le fichier /etc/resolv.conf :
nameserver 193.48.57.34
Nous pouvons maintenant accéder à notre Raspberry Pi via un câble ethernet et la commande ssh pi@172.26.79.9 sur la console linux de l'ordinateur fixe.
On installe ensuite le paquetage libwebsockets-dev qui contient une bibliothèque C de WebSocket qui sera utile par la suite à l'aide de la commande apt-get ainsi que le paquetage apache2 qui permet de créer un serveur HTTP sur la Raspberry. Le fichier HTML de notre interface web sera copié dans le répertoire /var/www. On télécharge aussi la bibliothèque Web 2.0 jquery.js sur le site http://jquery.com/. Cette bibliothèque permet de lancer des requêtes HTTP asynchrones mais aussi de faciliter la programmation JavaScript.
Pour vérifier le bon fonctionnement du serveur web HTTP, on le teste avec le serveur WebSocket donné en exemple sur le sujet du projet ainsi que la page HTLM qui se connecte dessus. Les chaînes tapés dans le champ texte de la page HTML sont envoyés au serveur WebSocket puis retournées par le serveur au navigateur Web et affichées dans une balise div. On copie le fichiers jquery.js et le fichier HTML dans /var/www et le fichier C du WebSocket dans /pi/home. On compile et on exécute le fichier C et on tape l’adresse 172.26.79.9 dans un navigateur web pour accéder à l'interface web. Tout fonctionne correctement, nous pourrons utiliser la Raspberry avec notre WebSocket et notre interface web. La commande d'exécution du serveur WebSocket devra être écrite dans le fichier /etc/rc.local pour qu'il s'exécute automatiquement au démarrage du Raspberry.
Séance 2
Partie électronique
Signal PWM
Lors de cette séance, nous avons commencé par tester notre signal PWM afin de comprendre comment il fonctionne. Comme observé lors de la séance 1, nous faisons varier le rapport cyclique de la PWM à une fréquence plus lente que celle de la PWM, et voici ce que l'on obtient :
Réalisation du montage suiveur
Ensuite, nous avons étudié notre partie analogique qui aura pour rôle de convertir le signal PWM en un signal type "dents de scie". Pour commencer, il nous a fallu mettre en place un montage suiveur pour pouvoir transmettre le signal fourni par la nanoboard en toute sécurité et effectuer une adaptation d'impédance. En effet, le courant fourni par la nanoboard étant faible, nous risquons d’endommager les pins de la nanoboard. Ce montage permet de pallier à ce problème et d'augmenter le courant fourni. Nous utilisons le circuit intégré TL082, nous avons cherché dans la datasheet du composant la façon dont les AOPs sont câblés. Voici le schéma de câblage du TL082:
Nous constatons que le circuit comporte 2 AOPs, le second AOP sera utilisé pour la comparaison entre le signal rampe et la valeur fournie par l'accéléromètre. Voici le montage suiveur proposé et testé:
Lorsque nous avons câblé notre circuit, nous avons choisi Vcc=5v, et -Vcc=0v, cependant nous avons rencontré quelques problèmes avec ces valeurs d’alimentation. Ensuite nous avons testé avec -Vcc=-5v, et voici le signal obtenu (entrée en bleu, sortie en jaune):
Nous constatons donc que le signal en sortie du suiveur est correct.
Partie informatique
Réalisation du serveur WebSocket
Lors de cette séance, nous avons réalisé le serveur WebSocket permettant la communication entre le navigateur et le port série. Pour ce projet, le serveur WebSocket doit envoyer les valeurs d'accélérations en X, Y et Z du port série vers le navigateur. Le code en C de notre WebSocket est donné ci-dessous :
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> #include <strings.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/file.h> #include <linux/serial.h> #include <libwebsockets.h> #define MAX_FRAME_SIZE 1024 #define WAIT_DELAY 50 #define MAX_MESSAGE 12 #define SERIAL_READ 0 #define SERIAL_WRITE 1 #define SERIAL_BOTH 2 #define SERIAL_DEVICE "/dev/ttyUSB0" int sd; // // Open serial port device // int serialOpen(char *device,int mode){ int flags=(mode==SERIAL_READ?O_RDONLY:(mode==SERIAL_WRITE?O_WRONLY:O_RDWR)); int fd=open(device,flags|O_NOCTTY|O_NONBLOCK); if(fd<0){ perror(device); exit(-1); } return fd; } // // Serial port configuration // void serialConfig(int fd,int speed){ struct termios new; bzero(&new,sizeof(new)); new.c_cflag=CLOCAL|CREAD|speed|CS8; new.c_iflag=0; new.c_oflag=0; new.c_lflag=0; /* set input mode (non-canonical, no echo,...) */ new.c_cc[VTIME]=0; /* inter-character timer unused */ new.c_cc[VMIN]=1; /* blocking read until 1 char received */ if(tcsetattr(fd,TCSANOW,&new)<0){ perror("serialInit.tcsetattr"); exit(-1); } } // // Serial port termination // void serialClose(int fd){ close(fd); } static int callback_http( struct libwebsocket_context *this, struct libwebsocket *wsi,enum libwebsocket_callback_reasons reason, void *user,void *in,size_t len) { return 0; } static int callback_my( struct libwebsocket_context * this, struct libwebsocket *wsi,enum libwebsocket_callback_reasons reason, void *user,void *in,size_t len) { static char message[MAX_MESSAGE+LWS_SEND_BUFFER_PRE_PADDING+LWS_SEND_BUFFER_POST_PADDING]; unsigned char x,y,z; switch(reason){ case LWS_CALLBACK_ESTABLISHED: printf("connection established\n"); // Declenchement d'un prochain envoi au navigateur unsigned char dummy; while(read(sd,&dummy,1)==1); libwebsocket_callback_on_writable(this,wsi); break; case LWS_CALLBACK_RECEIVE: // Ici sont traites les messages envoyes par le navigateur //RIEN À ENVOYER ! break; case LWS_CALLBACK_SERVER_WRITEABLE: // Ici sont envoyes les messages au navigateur if(read(sd,&x,1)==1) { while(read(sd,&y,1)!=1); while(read(sd,&z,1)!=1); char *out=message+LWS_SEND_BUFFER_PRE_PADDING; sprintf(out,"%03d %03d %03d",x,y,z); libwebsocket_write(wsi,(unsigned char *)out,MAX_MESSAGE,LWS_WRITE_TEXT); } libwebsocket_callback_on_writable(this,wsi); break; default: break; } return 0; } static struct libwebsocket_protocols protocols[] = { { "http-only", // name callback_http, // callback 0, // data size 0 // maximum frame size }, {"myprotocol",callback_my,0,MAX_FRAME_SIZE}, {NULL,NULL,0,0} }; int main(void) { int port=9000; struct lws_context_creation_info info; memset(&info,0,sizeof info); info.port=port; info.protocols=protocols; info.gid=-1; info.uid=-1; struct libwebsocket_context *context=libwebsocket_create_context(&info); if(context==NULL){ fprintf(stderr, "libwebsocket init failed\n"); return -1; } printf("starting server...\n"); sd=serialOpen(SERIAL_DEVICE,SERIAL_BOTH); serialConfig(sd,B9600); while(1){ libwebsocket_service(context,WAIT_DELAY); } serialClose(sd); libwebsocket_context_destroy(context); return 0; }
Les fonctions en rouge permettent d'accéder au port série et de le configurer. On choisit une vitesse de 9600 Bauds pour le port série, ce qui est suffisant ici. La fonction en bleu déclenche les envois vers le navigateur et envoie les accélérations en X, Y et Z sous la forme d'une chaîne de 12 caractéres.
Test du serveur WebSocket avec une page HTML
Nous avons ensuite testé le fonctionnement de notre serveur WebSocket avec une page HTML qui se connecte dessus. Cette page HTML n'est pas la page définitive de notre interface web. Elle permet d’afficher en temps réel les valeurs des accélérations en X, Y et Z. On connecte notre système arduino sur un des ports USB du Raspberry Pi pour tester. On observe bien la variation des accélérations en X, Y et Z sur le navigateur, notre WebSocket est donc fonctionnel, il ne reste plus qu'a réaliser l'interface web définitive.
Séance 3
Partie électronique
Circuit analogique : Filtre RC
Lors de cette séance, nous avons réalisé la conversion du signal rampe. Pour cela, nous utilisons un filtre RC en intégrateur. Pour que notre circuit RC se comporte comme un intégrateur, il faut que la constante de temps du filtre sois plus grande que la période de notre PWM. et donc , nous choisissons donc R=1200 ohm et C=4,7 nF ce qui nous donne une fréquence de coupure de 180 Hz. Nous fixons la fréquence de notre PWM à 200 Hz. Voici le circuit utilisé par la conversion et le signal observé en sortie de la rampe :
Le signal rampe en sortie est correct, nous avons donc continué et effectué la comparaison.
Circuit et résultat final
Voici le schéma final et le circuit de la partie analogique :
Voici le signal observé en entrée et en sortie du comparateur :
Nous obtenons une valeur proportionnelle à celle fournie par l'accéléromètre, ce signal est à récupérer sur la nanoboard pour être ensuite enregistré dans le registre afin d'être envoyé sur la liaison série. Cependant nous n'avons pu finaliser cette partie à cause de nombreux contres temps, avec une séance de plus nous aurions pu la finaliser.
Partie informatique
Réalisation de la page Web
Lors de cette dernière séance, nous avons réalisé notre interface web. Nous avons décidé de commander la raquette d'un jeu de casse briques à l'aide de l'accéléromètre au lieu de commander le déplacement d'un simple pointeur à l'écran comme indiqué dans le cahier des charges car cela est plus intéressant du point de vu de l'utilisateur et nous permet de gérer plus facilement les valeurs de l'accéléromètre. Après différents tests, nous choisissons les conditions suivantes pour un déplacement à gauche : x<140 et y<60 et pour un déplacement à droite x<140 et y>140. Nous n'utilisons donc que les valeurs fournies sur l'axe y de notre accéléromètre pour différentier le déplacement à gauche du déplacement à droite.
Notre interface web se compose de 4 fichiers :
• le fichier jquery.js qui récupère toutes les données de notre Websocket.
• le fichier Cassebriques.js qui gère l'animation de notre page et du jeu.
• le fichier Cassebriques.css qui est notre feuille de style.
• le fichier Cassesbriques.html qui fait appel aux trois autres et affiche notre page Web.
Code de la page Web
Voici les fichiers Cassesbriques.html et Cassebriques.css :
Le fichier cassesbriques.js étant le plus intéressant, nous décidons de détailler cette partie.
Partie websocket
window.WebSocket=(window.WebSocket||window.MozWebSocket); var websocket=new WebSocket('ws://172.26.79.9:9000','myprotocol'); websocket.onopen=function(){ $('h5').css('color','green'); }; websocket.onerror=function(){ $('h4').css('color','red'); }; websocket.onmessage=function(message){ console.log(message.data); var tableau=message.data.split(' '); tableau.map(parseInt)//Convertie en entier les caractéres reçus ; checkDepla(tableau)//envoie le tableau vers la fonction déblacement; };
Cette partie permet de récupérer les valeurs de l’accéléromètre fournies par le websocket et vérifie aussi si notre interface web est bien connectée à la Raspberry Pi. Nous récupérons les valeurs que l'on converti en entiers et que l'on stocke dans un tableau, ces données serons récupérées plus bas, afin de déplacer la barre.
Jeu du casse briques
// Constantes du jeu var NBR_LIGNES = 5; var NBR_BRIQUES_PAR_LIGNE = 8; var BRIQUE_WIDTH = 48; var BRIQUE_HEIGHT = 15; var ESPACE_BRIQUE = 2; var BARRE_JEU_WIDTH = 80; var BARRE_JEU_HEIGHT = 10; var PXL_DEPLA = 10; var ZONE_JEU_WIDTH = 400; var ZONE_JEU_HEIGHT = 300; var COULEURS_BRIQUES = "#0099FF"; var COULEUR_BALLE = "#FF0101"; var DIMENSION_BALLE = 8; var VITESSE_BALLE = 2; // Variables var tabBriques; // Tableau virtuel contenant les briques var barreX; // Position en X de la barre: Valeurs de l'accéléromètre var barreY; // Position en Y de la barre: Ne bougera pas. var context; var balleX = 100; var balleY = 250; var dirBalleX = 1; var dirBalleY = -1; var boucleJeu; var limiteBriques = (ESPACE_BRIQUE+BRIQUE_HEIGHT)*NBR_LIGNES; var aGagne = 0;
Nous commençons tout d'abord par initialiser les variables utiles pour notre jeu, telles que les positions X, Y de la balle, sa direction et sa vitesse. Pour les briques, nous utiliserons une matrice, comme dans la plupart des interfaces pour ce type de jeux.
window.addEventListener('load', function () { // On recupere l'objet canvas var elem = document.getElementById('canvasElem'); if (!elem || !elem.getContext) { return; } // On recupere le contexte 2D context = elem.getContext('2d'); if (!context) { return; } // Initialisations des variables ZONE_JEU_WIDTH = elem.width; ZONE_JEU_HEIGHT = elem.height; barreX = (ZONE_JEU_WIDTH/2)-(BARRE_JEU_WIDTH/2); barreY = (ZONE_JEU_HEIGHT-BARRE_JEU_HEIGHT); // Le navigateur est compatible, on peut continuer: On initialise le jeu. creerBriques(context, NBR_LIGNES, NBR_BRIQUES_PAR_LIGNE, BRIQUE_WIDTH, BRIQUE_HEIGHT, ESPACE_BRIQUE); // Boucle de rafraichissement du contexte 2D idInterv = setInterval(refreshGame, 10); }, false);
Une fois l'initialisation des variables faite, nous devons récupérer l'élément canvas de notre page. Si la récupération est réussie, nous initialisons notre jeu avec les valeurs adéquates. Pour finir nous utilisons une boucle qui va se rafraîchir toute les 10 ms et réactualiser notre affichage affin d'avoir une page dynamique.
function checkDepla(tableau) { //Se deplace en fonction des valeurs de l'accélerometre //deplacement droit if (tableau[1] > 140) { if ( (barreX+PXL_DEPLA+BARRE_JEU_WIDTH) <= ZONE_JEU_WIDTH ) barreX += PXL_DEPLA; } //deplacement gauche else if (tableau[1] < 60) { if ( ((barreX-PXL_DEPLA)) >= 0 ) barreX -= PXL_DEPLA; } }
C'est cette partie qui permet de déplacer notre barre en fonction des valeurs fournies par l’accéléromètre.
function perdu() { clearInterval(boucleJeu); var r = confirm("voulez vous recommencer ?"); if (r == true) { balleX = 100; balleY = 250; dirBalleX = 1; dirBalleY = -1; creerBriques(context, NBR_LIGNES, NBR_BRIQUES_PAR_LIGNE, BRIQUE_WIDTH, BRIQUE_HEIGHT, ESPACE_BRIQUE); idInterv = setInterval(refreshGame, 10); } else { clearInterval(boucleJeu); } } function gagne() { clearInterval(boucleJeu); alert("Bravo vous avez gagné !"); }
Ces deux fonctions permettent de gérer la victoire de l'utilisateur. Si l'utilisateur perd, il lui est proposé de recommencer ou non.
function clearContexte(ctx, startwidth, ctxwidth, startheight, ctxheight) { ctx.clearRect(startwidth, startheight, ctxwidth, ctxheight); } // Fonction permettant de creer les briques du jeu function creerBriques(ctx, nbrLignes, nbrParLigne, largeur, hauteur, espace) { // Tableau virtuel: On initialise les lignes de briques tabBriques = new Array(nbrLignes); for (var i=0; i < nbrLignes; i++) { // Tableau virtuel: On initialise les briques de la ligne tabBriques[i] = new Array(nbrParLigne); // Affichage: On attribue une couleur aux briques de la ligne ctx.fillStyle = COULEURS_BRIQUES[i]; for (var j=0; j < nbrParLigne; j++) { // Affichage: On affiche une nouvelle brique ctx.fillRect((j*(largeur+espace)),(i*(hauteur+espace)),largeur,hauteur); // Tableau virtuel: On attribue a la case actuelle la valeur 1 = Une brique existe encore tabBriques[i][j] = 1; } } // Nos briques sont initialisees. return 1; }
La fonction clearContexte, permet d’effacer tout ce qui est contenu dans la partie où est affiché le jeu, c'est à dire la barre, la balle, et les briques. La fonction creerbriques permet d'initialiser la matrice des briques.
function refreshGame() { // On efface la zone clearContexte(context, 0, ZONE_JEU_WIDTH, 0, ZONE_JEU_HEIGHT); // On reaffiche // Reaffichage des briques for (var i=0; i < tabBriques.length; i++) { context.fillStyle = COULEURS_BRIQUES; for (var j=0; j < tabBriques[i].length; j++) { if (tabBriques[i][j] == 1) context.fillRect((j*(BRIQUE_WIDTH+ESPACE_BRIQUE)),(i*(BRIQUE_HEIGHT+ESPACE_BRIQUE)),BRIQUE_WIDTH,BRIQUE_HEIGHT); } } // On vérifie si le joueur a gagné if ( aGagne ) gagne(); // Reaffichage de la barre context.fillStyle = "#333333"; context.fillRect(barreX,barreY,BARRE_JEU_WIDTH,BARRE_JEU_HEIGHT); // Calcul de la nouvelle position de la balle if ( (balleX + dirBalleX * VITESSE_BALLE) > ZONE_JEU_WIDTH) dirBalleX = -1; else if ( (balleX + dirBalleX * VITESSE_BALLE) < 0) dirBalleX = 1; if ( (balleY + dirBalleY * VITESSE_BALLE) > ZONE_JEU_HEIGHT){ perdu(); } else { if ( (balleY + dirBalleY * VITESSE_BALLE) < 0) dirBalleY = 1; else { if ( ((balleY + dirBalleY * VITESSE_BALLE) > (ZONE_JEU_HEIGHT - BARRE_JEU_HEIGHT)) && ((balleX + dirBalleX * VITESSE_BALLE) >= barreX) && ((balleX + dirBalleX * VITESSE_BALLE) <= (barreX+BARRE_JEU_WIDTH))) { dirBalleY = -1; dirBalleX = 2*(balleX-(barreX+BARRE_JEU_WIDTH/2))/BARRE_JEU_WIDTH; } } } // Test des collisions avec les briques if ( balleY <= limiteBriques) { // On est dans la zone des briques var ligneY = Math.floor(balleY/(BRIQUE_HEIGHT+ESPACE_BRIQUE)); var ligneX = Math.floor(balleX/(BRIQUE_WIDTH+ESPACE_BRIQUE)); if ( tabBriques[ligneY][ligneX] == 1 ) { tabBriques[ligneY][ligneX] = 0; dirBalleY = 1; } } balleX += dirBalleX * VITESSE_BALLE; balleY += dirBalleY * VITESSE_BALLE; // Affichage de la balle context.fillStyle = COULEUR_BALLE; context.beginPath(); context.arc(balleX, balleY, DIMENSION_BALLE, 0, Math.PI*2, true); context.closePath(); context.fill(); }
Voici le cœur de notre jeu, cette partie sert à réactualiser les différentes variables, effacer le canvas et le ré-afficher avec les nouvelles valeurs.
Démonstration
La vidéo de démonstration de notre système arduino commandant la raquette du jeu de casse briques sur l'interface web est disponible en cliquant sur ce lien : https://www.youtube.com/embed/NAXdg80vXPI
Pour tester notre système il faut connecter l'arduino au Raspberry Pi n°4 puis lancer le websocket via la commande ./websocket/web. La page HTML est accessible en tapant 172.26.79.9/Cassebriques.html dans la barre d'adresse de Firefox.
Conclusion
Ce projet nous a permi d'aborder une partie importante de la formation IMA, en premier lieu la gestion d'un projet complet avec la nécessité de répartir les tâches. En effet, le projet étant composé de deux grandes parties : l'interface électronique et l'interface informatique, nous ne pouvions pas le mener à terme sans une organisation et un travail de groupe méthodique. La partie électronique nous a permi de nous familiariser avec les FPGA, mais aussi de concevoir un circuit en consultant les datasheet et en choisissant nos composants tout en respectant un cahier des charges. La partie informatique nous a quand à elle permi d'appréhender une partie des protocoles utilisés par les systèmes communicants, mais aussi de réfléchir à la conception d'une interface graphique et à la façon de l'interfacer pour avoir un projet viable. La partie électronique de notre projet n'étant pas aboutie, la communication via la liaison série vers notre serveur ne peut se faire. Cependant, l’utilisation d'un prototype via une carte arduino nous a permi de constater que notre partie informatique est parfaitement fonctionnelle.