Projet IMA3 P6, 2017/2018, TD2, SEEGA
Sommaire
Projet IMA3-SC 2017-2018 : Projet SEEGA
Description du projet
Le projet consiste en un jeu de plateau connecté. Il permettrait à 2 personnes de jouer l'une contre l'autre (le joueur 1 sur le vrai plateau et le joueur 2 sur ordinateur). Celui-ci se base sur le Seega, un jeu de stratégie qui est apparu il y a plus d'un siècle en Égypte et dont le principe se rapproche du jeu de Dames.
Pour faire une partie de Seega, il faut un plateau de 5x5 cases, 12 pions pour le joueur 1, 12 pions pour le joueur 2. Le but du jeu est de capturer le maximum de pions de l'adversaire.
>Au début de la partie, chaque joueur dispose à tour de rôle 2 de ses pions sur le plateau sur les cases de son choix, sauf case centrale.
>Une fois les 24 pions placés, les joueurs bougent à tour de rôle un de leurs pions respectifs sur le plateau. Chaque pièce peut être déplacée verticalement ou horizontalement et obligatoirement sur une case contiguë.
>Un joueur capture un pion adverse lorsqu'il parvient à l'encadrer avec 2 de ses pions suite à un déplacement. Le pion est donc éliminé du plateau. Cependant, un pion positionné sur la case centrale ne peut pas être capturé.
>La partie se termine quand l'un des joueurs n'a plus de pions ou lorsque les 2 séries de pions forment des barrières qui empêchent d'être capturés. Le gagnant est alors celui à qui il reste le plus de pièces sur le plateau.
Cahier des charges
Nous voulons réaliser un plateau de jeu de taille 5x5 capable de détecter l'emplacement des pions de la personne qui joue sur le plateau et d'afficher l'emplacement des pions adverses. Nous avons besoin d'une application web qui fasse le lien entre les 2 joueurs: les pions captés et déplacés sur le plateau doivent apparaître sur l'écran de la personne qui joue sur ordinateur et vice-versa. Le jeu doit être fonctionnel est respecter les règles. Un indicateur sur le plateau permettra au joueur sur plateau de savoir qui doit jouer et lui signaler lorsqu’il effectue un mouvement incorrecte ou quand un pion est mangé.
Titre du sujet
Projet SEEGA
Description du système
Nous avons comme objectif de détecter les pions du joueur 1 grâce à un capteur de ligne (pour transmettre leurs positions à l'ordinateur) et de simuler les pions du joueur 2 par des leds sous le plateau en plexiglas, allumées aux cases en question.
Matériel nécessaire
- 1 Arduino Uno
- 3 registres à décalage 74HC595
- 25 leds blanches
- 25 capteurs de ligne
- 1 plaque de plexiglass 30*30cm de 3mm d'épaisseur
- 1 plaque de contreplaqué de 5mm usinable pour la découpe du coffre du plateau et des pions
- 12 pions noirs circulaires environ 4 cm de diamètre (découpeuse laser)
- Papier aluminium (optionnel)
- 25 résistances 180 Ohms
- 1 Led rouge + 1 résistance de 220Ohms
- 1 Led verte + 1 résistance de 150Ohms
- 4 entretoises 30mm
Séance 1
Pendant cette première séance, nous avons choisi notre sujet et défini les besoins électroniques et informatiques. Nous nous sommes tout de suite mis d'accord pour réaliser un jeu de société entre un ordinateur et un plateau de jeu réel. Le choix du jeu de Seega est lié aux dimensions du plateau qui permettent de limiter la complexité du projet comparé à un jeu de Dames se jouant sur un plateau de 10*10. On réduit ainsi le nombre de capteurs à utiliser et le coût financier du projet.
Nous avons alors séparé les tâches et partagé le travail à effectuer.
Partie informatique
Du côté informatique lors de la première séance, on nous donne une Raspberry prête à l’utilisation. On commence par lui installer un système d’exploitation adapté pour fonctionner sur Raspberry : on choisit Raspbian sans interface graphique. On a rencontré quelques problèmes qui n’étaient pas prévus par le tutoriel que nous suivions. A la fin de l’installation, on commence alors la configuration de la Raspberry (en port série puis hot-spot wi-fi etc). On sera stoppé par le temps au moment d'entamer la configuration en hot-spot wi-fi.
Partie électronique
Ici, nous avons principalement dédié cette séance à la découverte du FPGA. Au travers d'un tutoriel, nous avons créé un compteur sur Altium et dont la valeur s'affiche sur des LED. Nous nous somme alors familiarisé avec cette carte électronique et avec son fonctionnement.
N'ayant pas trouvé de suite la datasheet, nous avons alors effectué différents tests afin de déterminer les caractéristiques et le fonctionnement de ces modules. Chaque composant est alors alimenté sous une tension de 5V, une led infrarouge émet un rayonnement qui revient vers un capteur si une surface réfléchissante est présente. Le capteur peut alors renvoyer entre 0V et 5V: 0V quand il y a réflexion totale et 5V s'il n'y a aucun obstacle. Afin de maximiser l'efficacité du capteur il nous faut donc une surface la plus réfléchissante possible à disposer sous nos pions. La distance optimale entre récepteur et la surface réfléchissante est de 1mm. Il nous faudra donc un plexiglas peu épais. Pour la surface réfléchissante nous utiliserons du papier aluminium qui permet une réflexion convenable de la lumière.
Séance 2
Préparation
Entre les deux premières séances, nous avons imaginé le circuit électronique et codé en Javascript le jeu de Seega, en y ajoutant un affichage graphique. Nous l'avons d'abord fait en C avant de se rendre compte que notre jeu devait être en html pour fonctionner via la Raspberry. Nous avons alors repris notre code cette fois-ci en Javascript (dans un fichier html). Pour l'affichage graphique, on est passé par un canvas qu'on a soigneusement découpé en 25 cases de mêmes tailles. Ce sera notre plateau de jeu. On récupère les clics qu'on stockent dans des variables, ce sont les choix de déplacements de l'utilisateur sur ordi. On code les différentes fonctions permettant de jouer dans les règles comme la fonction deplacement_possible qui parle d'elle même etc. On décide de simplifier la mise ne place de départ (car fastidie[[Fichier:Rendu_final_seega1.jpg|300px|thumb|right|]use). Ainsi, la partie commence le plateau de jeu est vide et chaque joueur place à son tour 2 pions (sauf sur la case du milieu). Le jeu peut ensuite commencer. Voici quelques exemple de commandes qui nous ont été utiles:
Créer un évènement lorsque l'utilateur effectue un clic gauche: document.addEventListener("mousedown", MouseDownHandler, false);
Créer un évènement lorsque l'utilateur relâche le clic gauche: document.addEventListener("mouseup", MouseUpHandler, false);
Récupérer les coordonnées du clic gauche dans la fenêtre: function MouseDownHandler(e) { var relativeX = e.clientX ; var relativeY = e.clientY ; }
Partie informatique
Lors de cette séance, nous avons continué et finalisé la configuration de la Raspberry Pi. On peut désormais s'y connecter via ssh. On arrive aussi à lancer le redémarrage de la Raspberry depuis la page web (programme très simple donné dans le tutoriel).
Partie électronique
Ddans un premier temps nous nous sommes intéressés à réaliser une matrice de led. Nous avons trouvé 2 solutions permettant de la concevoir:
> La première solution nécessite 5 résistances, 25 leds et occupe 10 pins de l'arduino. 5 pins servent à contrôler chaque ligne (en rose sur l'image), et les 5 autres les colonnes (en bleu).
Pour allumer une seule led, par exemple la première il suffit donc de mettre tous les pins "ligne" à l'état LOW sauf la celui de la première ligne en HIGH.
A cette étape toute la ligne s'allume, pour palier à cela on laisse notre pin "colonne" correspondant à l'état LOW, et les autre en HIGH.
Maintenant pour allumer plusieurs led, on utilisera la persistance rétinienne. C'est à dire que l'on va allumer et éteindre chaque led de manière très rapide. L’œil lui ne va pas percevoir ce clignotement et verra les leds allumées.
> La deuxième solution consiste à utiliser les registres à décalage 74HC595 qui n'occupent que 3 pin de l'arduino.
Vcc : alimentation 6V Max.
QA à QH: sorties Shift Register.
QH': renvoie la même valeur que QH.
SER (Serial): entrée pour le prochain pin qui sera déplacé.
SRCLK (Serial Clock): déplace le registre d'un rang à droite lorsqu’il est mis à 1.
RCLK (Register Clock): mettre à 1 pour valider les modfications.
SRCLR (Serial Clear): le passage de 1 à 0 vide le registre.
OE (Output Enable): ce pin permet d’activer la sortie lorsqu’il est sur la masse et la désactive lorsqu’il est en High.
Le composant 74HC595 dispose de 8 pins de sortie (Qa, Qb, Qc, Qd, Qe, Qf, Qg, Qh) qui peuvent soit avoir la valeur 0 (Low) soit la valeur 1 (High). Lorsque l’on met le pin SRCLK (Serial Clock) en valeur 1, alors les 8 pins de sorties se décalent vers la droite et le premier pin Qa prend la valeur appliquée à SER. La valeur anciennement appliquée à Qh est alors écrasée. Ainsi en 8 étapes nous pouvons enregistrer l'état des 8 leds à contrôler puis valider les modifications en passant RCLK à l'état HIGH.
A présent nous voulons contrôler non pas 8 mais 16 led. Nous allons donc chaîner 2 registres à décalage. Pour cela il suffit de relier le QH' du premier 74HC595 au SER du second. Ainsi le dernier bit, au lieu d'être écrasé, est récupéré par le deuxième registre. Ainsi nous pouvons facilement chaîner plusieurs 74HC595 comme présenté sur l'image.
Nous retenons donc cette solutions moins gourmande en pins de l'arduino. 3 Shift Registers permettront de contrôler 3*8=24 leds. La 25ème led sera directement contrôlée par un 4ème pin.
Cependant nous gardons tout de même la première méthode en tête afin de l'adapter aux capteurs de lignes pour détecter les pions sur les cases. En effet l'arduino uno ne possédant que 6 ports analogique, nous branchons alors les sorties de chaque colonne de capteurs sur une entrée analogiques. Les bornes négatives sont reliées directement à la masse et les bornes positives de chaque lignes de capteurs sont reliées à une sortie digitale. Ainsi pour lire la valeur du capteur de la première ligne et première colonne il suffit d'allumer les capteur de la ligne correspondante et lire la valeur l'entrée analogique de la colonne correspondante. Pour scanner toute la grilles on allumera les lignes successivements en laissant un intervalle de temps suffisant pour la mesure.
Nous avons aussi commencé à réaliser la partie du montage électronique à faire sur Altium sur le FPGA.
Séance 3
Partie électronique
- FPGA:
Nous avons essayé de corriger certains problèmes concernant le schéma sur Altium. En effet, les ports de sortie et le multiplexeur empêchaient la compilation. Nous n'avons malheureusement pas réussi à les résoudre.
- PCB: Média:PCB_SEEGA.zip
Cette séance a été dédiée à la conception du circuit électronique du plateau de jeu afin de rendre les branchements plus faciles. Pour cela nous avons utilisé le logiciel frizing qui permet d'un côté de réaliser un schéma du montage, et d'un autre de dessiner la carte. La tâche s'est avérée plus complexe que nous ne le pensions au vu de la disposition et du nombre de composants en jeu. Il a fallu recommencer plusieurs fois et continuer le travail à la maison pour en arriver au résultat final et envoyer la carte électronique en fabrication.
Partie informatique
Ajout du fichier html contenant le jeu sur la Raspberry Pi. On peut désormais y jouer via internet en se connectant en wifi à la Raspberry (adresse : robot.projetseega.org). Divers essais pour permettre la communication entre la Raspberry et l'Arduino mais sans réussite. On a perdu du temps à cause de certaines intructions du tutoriel qui n'étaient plus valables non plus. On ne baisse pas les bras et on continue d'avancer.
Partie maquette
Avant la séance nous avons réfléchi et créé le logo pour notre projet. Nous avons alors réservé la découpeuse laser dans le but de graver et de fabriquer les jetons pour notre plateau.
Séances complémentaires
Afin de finaliser notre travail, nous nous sommes servis des machines du Fabricarium pour découper le plateau de jeu en bois ainsi que la grille en plexiglas. Nous avons également soudé nos capteurs de ligne et nos diodes qui indiquent les positions des pions du joueur sur ordinateur.
Nous sommes revenus en salle informatique pour réussir à faire communiquer la Raspberry à l'Arduino en utilisant un serveur : le websocket. Après plusieurs heures, ils réussissent à s’échanger des signaux correctement et le jeu peut alors prendre forme. C'était une étape compliquée puisque les nombreux exemples disponibles sur internet étaient codés en python or nous utilisons Javascript pour la gestion du serveur et du jeu.
Transmission de données
La transmission des données entre l'arduino et la Raspberry est un point clé dans le bon fonctionnement du projet. Afin d'optimiser la liaison et d'éviter au maximum la perte de données, nous tentons de coder les information à transmettre sous la forme d'un seul octet.
- Première partie: positionnement des pions: Arduino vers Raspberry.
Ici nous devons ici transmettre le caractère 's' pour marquer le début de la transmission, puis la colonne de la case sur laquelle le pion est positionné ainsi que la ligne.
Nous pouvons encoder la colonne (nombre entre 0 et 4) sur 3 bits, de même pour la ligne. Il nous reste 1 bit pour coder le 's'. Nous l'encodons alors par '11'.
Nous pouvons alors effectuer les commandes suivantes:
Encoder: (0b11<<6)|(ligne<<3)|colonne
Décoder: ligne = (reçu & 0b00111000)>>2
colonne = (reçu & 0b00000111)
- Deuxième partie : mouvement des pions: Arduino vers Raspberry.
Ici nous devons transmettre à la Raspberry quel point a été déplacé sur quelle case il a été positionné. Pour encoder cela sur 1 octet nous enverrons le numéro de la case du pion déplacé (nombre entre 0 et 24 encodé sur les 5 derniers bits). Il nous reste alors 3 bits. Le premier mis à 0 sera un signal de 'start' puis les 2 bits suivants correspondront à la direction prise par le pion (haut ='0b00', bas ='0b01', gauche ='0b10', droite='0b11').
Nous pouvons alors effectuer la commande suivante:
(0b0<<7)|(direction<<5)|case
Ainsi le bit de poids fort permettra de faire la différence entre les 2 phases ( 1 pour la première et 0 pour la seconde).
- Envoie des cases de la Raspberry vers l’Arduino.
Ici nous procédons de la même manière que pour la première partie mais les 2 premiers bit permettrons au lieu de définir un start, d'encoder le type de pion sur la case. Ainsi vide = '0b00', blanc = '0b01' et noir ='0b10'.
La commande devient alors:
(type_pion<<6)|(ligne<<3)|colonne.
L'arduino peut maintenant recevoir les modifications de grille calculées par la raspberry.
Arduino
Dans cette partie nous n'allons pas détailler les quelques 300 lignes code de l'arduino afin de ne pas engorger le wiki. Nous présenterons cependant l'architecture les fonctions principales du programme que vous retrouverez en annexe (Fichier:Code Arduino SEEGA.zip).
- Pour commencer, la grille de jeu est stocké sous la forme d'un tableau à 2 dimensions (5x5) de caractères où 'v' représente une case vide, 'b' une pion blanc et 'b' un pion noir.
- Nous disposons de 3 fonctions qui permettent de gérer les registres à décalages. L'une permet de vider les registres, la seconde, de lire la grille pour mémoriser les leds à allumer, et la dernière pour remplir les registres et allumer les cases.
- Nous avons ensuite une fonction de décodage qui attends la réception d'un ou plusieurs octets, traduit l'octet reçu et actualise le tableau représentant la grille. Nous avons également 2 fonction de transmissions qui permettent d'encoder et d'envoyer les informations d'une part, sur le positionnement d'un pion au départ, et d'une autre part, sur le déplacement d'un pion dans la seconde partie du jeu.
- 2 fonctions sur le traitement des données de capteurs ont également été codés. L'une effectue un scan de la grille et enregistre d'information sur la présence ou non d'un capteur dans un tableau. On pourra ensuite comparer ce tableau à notre grille théorique pour détecter la moindre action de la part du joueur. La seconde fonction permet au au lancement du jeu de faire une moyenne de la valeur captée par chaque capteur à vide. En effet, en plein jour ou la nuit, la lumière infrarouge parasite ambiante n'est pas la même. Cette moyenne servira donc de base au programme pour détecter dans la première fonction si un pion est présent ou non.
- Pour terminer avec la boucle principale,celle-ci est divisées en 2 partie. L'une représente le tour noir, l'autre le tour blanc. Le tour blanc, le plus simple, fait juste appel à la fonction de décodage pour attendre la réception du coup adverse. La partie du tour noir, plus complexe, nécessite 3 fonction essentielles. L'une permet de simuler la mise en place des pions (le début de la partie) tant que les 12 jetons noirs n'ont pas été posés. La seconde est destinée à la partie de déplacement des pions. Enfin, la dernière, permet de contrôler toute erreur de la part du joueur sur plateau, à savoir: pion manquant, pion mangé à enlever, action non autorisé. Elle signale l'erreur à l'utilisateur par le clignotement de la led correspondant à la case et attends que le problème soit résolu. Elle se base donc sur a différence entre la grille théorique et la grille enregistrée par les capteurs. Elle fera office de transition entre le tour blanc et le tour noir, mais aussi de remettre le tableau en ordre après une erreur du joueur.
Raspberry
Côté code de la Raspberry, nous avons procédé de la même manière pour ce qui est de la grille qui est une matrice 5 x 5 et où les pions sont soient vides ('v'), blancs ('b') ou noirs ('n'). En Javascript les variables déclarées en dehors des fonctions sont globales et réutilisables partout. On s'est beaucoup servi de cette particularité du langage.
On a utilisé plusieurs fonctions utiles pour le fonctionnement du jeu :
-Les fonctions MouseUpHandler et MouseDownHandler qui vérifient si les clics et les relâchements de clics sont bien valides, c'est-à-dire présent sur le canvas donc sur une des cases de la grille. Si c'est le cas alors le déplacement est stocké et certaines variables changées pour dire qu'il y a un déplacement.
-Une fonction déplacement_possible qui vérifie si le déplacement enregistré par les clics s'effectue bien horizontalement ou verticalement dans une des 4 cases voisines de la case de départ.
-Une fonction qui_mange_qui (et sa variante qui_mange_qui_arduino) permet de vérifier si suite à un déplacement effectué par l'ordinateur (par l'Arduino dans l'autre cas), des pions sont mangés ou non (appel de la fonction retire_pion si un pion est mangé). On notera qu'on peut manger 3 pions maximum en un seul déplacement.
-Deux fonctions envoie_coord et envoie_voisins qui fonctionnent ensemble pour envoyer à l'Arduino sous la forme d'un octet à chaque fois, les coordonnées des 8 cases voisines de la case qui a été modifiée (+ la case en question) pour ainsi permettre à l'Arduino de mettre à jour la grille, à la fois le déplacement qui a été effectué par l'ordinateur mais aussi les pions à retirer (s'il y en a).
-Une fonction placement_pions qui sert uniquement à la première partie du jeu, le placement des pions. Elle veille à ce que le tour par tour se passe correctement et que chaque joueur place 2 pions à son tour.
-Deux fonctions end_game et start_game. La première vérifie si une équipe n'a plus de pions du tout (défaite et fin de partie). L'autre vérifie si tous les pions ont été placés et met à jour une variable permettant le début de la partie.
On a ensuite la fonction principale de ce programme qui fait cohabiter toutes les autres et permet le bon déroulement du jeu, la fonction draw. Elle a aussi pour rôle de dessiner le canvas à l'écran. Au départ nous appelions cette fonction toutes les 10 millisecondes grâce au setInterval. Cependant ce n'était pas optimal et générait des bugs assez fréquents. Alors nous avons décidé de l'appeler au lancemennt du programme via le onload puis uniquement en cas de déplacement donc dès qu'il y a un clic ou que la Raspberry reçoit un message de l'Arduino.
Maquette
[[Fichier:Boite_SEEGA.png|380px|right|thumb|Média: Découpe_laser_SEEGA.zip ]]
Pour la réalisation du plateau nous avons choisis de réaliser une boite en contreplaqué destinée à recevoir le PCB et l'arduino. Ce dernier est alors fixé à l’intérieur grâce à des entretoises. En fonction de la tailles des entretoises que nous disposions il a fallu ajuster la hauteur de la boite afin que les capteurs de lignes soient au plus proche de la surface afin de détecter au mieux les pions. Le patron a été réalisé grâce au site Générateur de boites qui permet de créer des boites aux dimensions personnalisées facilement. A cela il a fallu modifier le patron afin de découper une fenêtre destiné à accueillir la grille en PCB gravée également. Nous avons également ajouté 2 trous pour laisser passer les leds et un espace pour laisser passer le câble USB. Attention à éviter les trous à la perceuse. En effet le contreplaqué étant un bois tendre,il s’abîme très facilement, privilégier donc au maximum la découpe laser. Nous avons par la suite rajouté le logo du jeu puis les règles directement sur la boite.
Suite à différents tests du jeu nous avons également choisis d'enlever les pastilles d’aluminium en dessous des pions. En effet le bois étant claire, la différence réflexion entre l'aluminium et le bois brut est très minime. Mais l'aluminium se décollait facilement et s’abîmait rapidement ce qui détériorait son action réfléchissante. Nous avons alors décidé de les enlever.
Concernant les leds rouges et vertes, pour avoir une perception de la lumières des led de manière égale, nous utiliserons un résistance de 220 Ohms pour la rouge et 150 Ohms pour la verte. En effet une led rouge a une tension de fonctionnement plus faible que la led verte.
Bilan
Vidéo de présentation
Tutoriel de mise en marche
La mise en marche doit se faire dans un ordre bien précis. En effet, au démarrage de la Raspberry et seulement à ce moment la, l'executable "webserial" disponible sur la Raspberry dans le répertoire /home/ se lance. Il cherche automatiquement ce qui est connecté en série à la Raspberry via le port USB0.
Il est donc important que tous les branchements soient faits avant la mise sous tension de la Raspberry. On procède alors de la manière suivante :
-On connecte un cable ethernet à la Raspberry (encore éteinte) pour qu'elle puisse être disponible en wi-fi.
/!\ Avant de connecter le plateau à la Raspberry, retirez la plaque en bois de la grille de jeu et ne posez aucun pion dessus afin de laisser l'arduino initialiser ses capteurs.
-On connecte l'Arduino à la Raspberry sur un des 4 ports USB disponibles, le choix du port n'a pas d'importance car il n'y aura que l'Arduino branché en USB.
-On termine par brancher la Raspberry à l'ordinateur et ainsi la démarrer.
Si toutes ces étapes ont été réalisées avec rigueur et attention, vous verrez alors la Raspberry apparaître après quelques instants dans les spots wi-fi à portée sous le nom de "SEEGA".
Il faut vous y connecter et entrer le mot de passe : "seega2018".
Une fois la connexion faite, vous pouvez accéder à l'adresse internet du jeu qui est la suivante : robot.projetseega.org (sinon via l'adresse ip : 172.26.145.140)
Vous verrez alors le plateau, matérialisé par un canvas sous la forme d'une grille 5 x 5 apparaître à l'écran.
C'est alors à vous de jouer (l’ordinateur en premier). La partie endiablée peut commencer !
Conclusion
Ce projet de système communiquant nous a permis d'allier l'informatique à l'électronique au travers de la manipulation de l'Arduino et de la Raspberry Pi. Ainsi, nous avons pu découvrir plusieurs langages de programmation, à savoir C++ et JavaScript. Nous avons également dû réfléchir en terme de logique pour parvenir à transmettre toutes les données d'un déplacement, entre le plateau et l'ordinateur, sur seulement 8 bits.
Nous sommes satisfaits d'être parvenus à concevoir un jeu de société qui fonctionne, et d'avoir pu enjoliver le plateau grâce aux machines du Fabricarium. Toutefois, nous avons rencontré des problèmes lors de la réalisation du FPGA, et nous aurions aimé améliorer certains points du jeu comme la sensibilité des capteurs de ligne, ou encore l'ajout d'une animation de fin de partie avec les leds du plateau, et la résolutions de quelques bugs mineurs (ex: perte d'octets) qui peuvent perturber le fonctionnement optimal du jeu.
Annexes
PCB surFrizing: Fichier:PCB SEEGA.zip
Découpe laser en .svg : Fichier:Découpe laser SEEGA.zip
Code sur l'arduino: Fichier:Code Arduino SEEGA.zip
Code sur la Raspberry: Fichier:Index.zip