Console rétro gaming portable
Projet IUT · SAÉ — Conception et programmation embarquée
Projet : Conception et réalisation d'une console rétrogaming portable basée sur un Raspberry Pi Pico 2, intégrant un écran LCD, des boutons, un joystick analogique et un haut-parleur. Jeux développés en CircuitPython.
Période : 1ère année de BUT GEII (SAÉ)
🔧 Conception électronique
- Analyse de l'existant et étude des contraintes matérielles
- Réalisation du schéma électronique sous Proteus
- Routage du PCB, impression et soudure manuelle
- Intégration de tous les composants sur une seule carte
💻 Programmation embarquée
- Développement des jeux et interfaces en CircuitPython
- Affichage graphique, menus interactifs, animations
- Lecture audio, gestion des collisions, gestion des scores
- Lecture des entrées joystick (axes X/Y) et boutons
🎮 Jeu Simon (mémoire)
- Génération de séquences aléatoires avec vérification des entrées
- Affichage des couleurs, gestion des erreurs et du score
- Synchronisation audio et visuelle
🚀 Jeu Space Rescue
- Déplacement d'un vaisseau avec gestion des collisions
- Système de score et de vies, déplacement dynamique des obstacles
- Animations temps réel et interactions avec des astronautes
🔍 Problèmes rencontrés
- Défauts de routage PCB, connexions audio, pistes de masse
- Correction après vérifications sur la carte réelle et analyse des schémas
💻 Code source — Jeux embarqués
Deux mini-jeux ont été développés en CircuitPython pour valider l’affichage, les boutons, le joystick, l’audio et la logique de jeu de la console rétro gaming.
🎮 Code jeu Simon
Jeu de mémoire basé sur une séquence aléatoire de couleurs : le joueur doit reproduire la suite avec les boutons, avec gestion du score, du timeout et de l’écran de fin.
import time import Ressources_simon import Gestion_controles import random Simon = Ressources_simon.simon() Controles = Gestion_controles.controles() #Variable objects_boutons = [Controles.bouton_haut, Controles.bouton_bas, Controles.bouton_gauche, Controles.bouton_droite, Controles.bouton_start] sequence =[] initialisation = 0 sequence_joueur = [] sequence_int = 1 Audio = [523, 659, 784, 392] nombre_de_couleur_sequence_joueur = 0 nombre_de_couleur_sequence = 0 start_bp = 0 score = 0 end_game = 0 Simon.zone_affichage.append(Simon.ecran_fin) Simon.zone_affichage.append(Simon.label_regles) Simon.zone_affichage.append(Simon.label_rejouer) Simon.zone_affichage.append(Simon.ecran_jeu) Simon.zone_affichage.append(Simon.label_score) Simon.zone_affichage.append(Simon.label_score_final) Simon.label_start.text = "Start" Simon.label_start.x = 4 Simon.label_start.y = 120 Simon.label_score_final.hidden = True Simon.label_score.hidden = True Simon.label_rejouer.hidden = True Simon.ecran_jeu.hidden = True Simon.ecran_fin.hidden = True Simon.label_regles.hidden = True for pad in Simon.illuminations: Simon.zone_affichage.append(pad) #fonction menu de départ def etape_1(active,start): if active ==1: #Menu jeux Simon.zone_affichage.append(Simon.ecran_titre) #importation du text Simon.zone_affichage.append(Simon.label_start) Simon.label_start.text = "Start" Simon.label_start.x = 65 if start == 1: Simon.ecran_jeu.hidden = False Simon.ecran_titre.hidden = True Simon.ecran_fin.hidden = True Simon.label_score.hidden = True #fonction fin du jeux def game_over(): Simon.ecran_jeu.hidden = True Simon.ecran_titre.hidden = True Simon.label_regles.hidden = True Simon.ecran_fin.hidden = False Simon.label_score.hidden = True Simon.label_rejouer.hidden = False etape_1(1,0) #Boucle de jeux while True: while initialisation ==0: Simon.label_rejouer.hidden = True Simon.ecran_fin.hidden = True Simon.label_start.hidden = True time.sleep(0.2) Simon.label_start.hidden = False time.sleep(0.2) for indice, bouton in enumerate(objects_boutons): if bouton.value == Controles.appuye: #bouton de start if indice == 4: start_bp += 1 if start_bp == 1: Simon.ecran_jeu.hidden = True Simon.ecran_titre.hidden = True Simon.ecran_fin.hidden = True Simon.label_regles.hidden = False if start_bp == 2: etape_1(0,1) initialisation = 1 Simon.label_start.hidden = True Simon.label_score.hidden = False time.sleep(0.1) find = True #création séquence aléatoire while sequence_int == 1: sequence.append(random.randint(0,3)) time.sleep(0.4) for number in sequence: Simon.allume_pad(number, Audio[number]) time.sleep(0.3) sequence_int = 0 sequence_joueur = [] # Tour joueur for couleur in sequence: if end_game == 1: break couleur_actif = 1 while couleur_actif == 1: index_bouton_appuye = Controles.attente_appui_bouton(timeout=3.0) #Timeout if index_bouton_appuye is None: print("aucun bouton appuyé") end_game = 1 find = False break Simon.allume_pad(index_bouton_appuye, Audio[index_bouton_appuye]) sequence_joueur.append(index_bouton_appuye) if index_bouton_appuye != couleur: print("faux") end_game = 1 find = False break couleur_actif = 0 if len(sequence_joueur) == len(sequence) and end_game==0: print("sequence suivante") score += 1 Simon.label_score.text = str(score) sequence_int = 1 if find ==False: Simon.label_score_final.hidden = False while end_game == 1: Simon.label_score_final.text = str(f"Score: {score}") game_over() print("GAME OVER") Simon.label_rejouer.hidden = False time.sleep(0.2) Simon.label_rejouer.hidden = True time.sleep(0.2) for indice, bouton in enumerate(objects_boutons): if bouton.value == Controles.appuye: if indice == 4: # bouton start print("Restart") Simon.ecran_titre.hidden = False Simon.label_score_final.hidden = True initialisation = 0 sequence = [] sequence_joueur = [] score = -1 start_bp = 0 end_game = 0 break
⚙ Code CircuitPython pour Raspberry Pi Pico 2 — gestion des boutons, séquence aléatoire, affichage graphique, sons et écran de fin.
⬇ Télécharger le code Simon (.py)🚀 Code jeu Space Saver
Jeu d’arcade spatial avec déplacement au joystick, récupération d’astronautes, collisions avec astéroïde, chronomètre, barre de vie, score et sons.
############################################# # IMPORTATION DES DIFFERENTES BIBLIOTHEQUES # ############################################# import time import Ressources_space import Gestion_controles import random ###################################### # INSTANCIATION DES variable # ###################################### Space = Ressources_space.space() Controles = Gestion_controles.controles() #Variable objects_boutons = [Controles.bouton_haut, Controles.bouton_bas, Controles.bouton_gauche, Controles.bouton_droite, Controles.bouton_start] game = 1 score = 0 vie = 0 collision_asteroide_active = False dernier_degats = 0 cooldown_degats = 2 ############################################# # AJOUT DES ELEMENTS GRAPHIQUES SUR L'ECRAN # ############################################# Space.zone_affichage.append(Space.ecran_fin) Space.zone_affichage.append(Space.ecran_titre) Space.zone_affichage.append(Space.portrait) Space.zone_affichage.append(Space.ecran_jeu) Space.zone_affichage.append(Space.label1_regles) Space.zone_affichage.append(Space.label2_regles) Space.zone_affichage.append(Space.label3_regles) Space.zone_affichage.append(Space.bouton_start) Space.zone_affichage.append(Space.astronaute) Space.zone_affichage.append(Space.label_chrono) Space.zone_affichage.append(Space.label_score) Space.zone_affichage.append(Space.label_score_final) Space.zone_affichage.append(Space.vaisseau) Space.zone_affichage.append(Space.barre_vie) Space.zone_affichage.append(Space.asteroide) Space.label_score_final.hidden = True Space.ecran_titre.hidden = True Space.ecran_fin.hidden = True Space.portrait.hidden = True Space.ecran_jeu.hidden = True Space.label1_regles.hidden = True Space.label2_regles.hidden = True Space.label3_regles.hidden = True Space.bouton_start.hidden = True Space.astronaute.hidden = True Space.label_chrono.hidden = True Space.label_score.hidden = True Space.vaisseau.hidden = True Space.barre_vie.hidden = True Space.asteroide.hidden = True Space.bouton_start.x = 40 Space.bouton_start.y = 110 #label 3 Space.label3_regles.text = "Recuperer un max \n d'astronautes \n avant la fin du \n chrono en evitant \n les astéroides" Space.label3_regles.x = 10 Space.label3_regles.y = 40 Space.label3_regles.scale = 1 Space.label3_regles.color = 0xfffff #Chronomètre duree_partie = 60 # 60 secondes = 1 minute temps_depart = 0 #Bouton start clignotement: temps_clignotement = time.monotonic() intervalle_clignotement = 0.1 # secondes etat_bouton = True #Préparation animation: offset_animation_astronaute = 0 offsets_animation_astronaute = {"REPOS":8} temps_animation_astronaute = time.monotonic() duree_animation_astronaute = 0.1 pause_deplacement_astronaute = 0.09 offset_animation_vaisseau = 0 offsets_animation_vaisseau = {"Av_Ar":3, "Gauche":4, "Droit":5} temps_animation_vaisseau = time.monotonic() duree_animation_vaisseau = 0.1 pause_deplacement_vaisseau = 0.04 pause_deplacement_asteroide = 0.005 temps_animation_asteroide = time.monotonic() def spawn_astronaute(): while True: x = random.randrange(14, 144, 1) y = random.randrange(22, 106, 1) x_vaisseau = Space.vaisseau.x y_vaisseau = Space.vaisseau.y if abs(x - x_vaisseau) > 50 or abs(y - y_vaisseau) > 50: Space.astronaute.x = x Space.astronaute.y = y break asteroide_dx = random.choice([-1, 1]) asteroide_dy = random.choice([-1, 1]) #Boucle de jeux while True: if game ==1: Space.ecran_fin.hidden = True Space.label_score_final.hidden = True Space.portrait.hidden = True Space.ecran_jeu.hidden = True Space.label1_regles.hidden = True Space.label2_regles.hidden = True Space.label3_regles.hidden = True Space.ecran_titre.hidden = False Space.audio_pwm2.play(Space.audio_ecran_titre) temps_actuel = time.monotonic() if temps_actuel - temps_clignotement > intervalle_clignotement: temps_clignotement = temps_actuel etat_bouton = not etat_bouton # inverse ON/OFF Space.bouton_start.hidden = not etat_bouton for indice, bouton in enumerate(objects_boutons): if bouton.value == Controles.appuye: if indice == 4: game = 2 if game==2: Space.audio_pwm1.play(Space.audio_start) Space.bouton_start.hidden = False Space.ecran_titre.hidden = True Space.portrait.hidden = False Space.label1_regles.hidden = False Space.label2_regles.hidden = False Space.label3_regles.hidden = False time.sleep(0.5) game = 3 if game ==3: for indice, bouton in enumerate(objects_boutons): if bouton.value == Controles.appuye: if indice == 4: Space.bouton_start.hidden = False Space.ecran_titre.hidden = True Space.portrait.hidden = True Space.label1_regles.hidden = True Space.label2_regles.hidden = True Space.label3_regles.hidden = True Space.ecran_jeu.hidden = False Space.bouton_start.hidden = True Space.astronaute.hidden = False Space.label_chrono.hidden = False Space.label_score.hidden = False Space.vaisseau.hidden = False Space.barre_vie.hidden = False Space.asteroide.hidden = False spawn_astronaute() x_spawn_vaisseau = random.randrange(14,144,1) y_spawn_vaisseau = random.randrange(22,106,1) Space.vaisseau.x = x_spawn_vaisseau Space.vaisseau.y = y_spawn_vaisseau temps_depart = time.monotonic() game = 4 if game ==4: temps_cpu_actuel = time.monotonic() if (temps_cpu_actuel - temps_animation_astronaute > duree_animation_astronaute) : temps_animation_astronaute = temps_cpu_actuel if (temps_cpu_actuel - temps_animation_vaisseau > duree_animation_vaisseau) : temps_animation_vaisseau = temps_cpu_actuel if (temps_cpu_actuel - temps_animation_vaisseau > pause_deplacement_vaisseau) : temps_animation_vaisseau = temps_cpu_actuel for indice, bouton in enumerate(objects_boutons): # VERS LA GAUCHE if Controles.joystick_horizontal.value <15000 : if Space.vaisseau.x > 0 : Space.vaisseau.x -= 1 Space.vaisseau[0] = offsets_animation_vaisseau["Gauche"] # VERS LA DROITE if Controles.joystick_horizontal.value >50000 : if Space.vaisseau.x< 128 : Space.vaisseau.x += 1 Space.vaisseau[0] = offsets_animation_vaisseau["Droit"] # VERS Le bas if Controles.joystick_vertical.value >50000 : if Space.vaisseau.y > 0 : Space.vaisseau.y -= 1 Space.vaisseau[0] = offsets_animation_vaisseau["Av_Ar"] # VERS Le haut if Controles.joystick_vertical.value <15000 : if Space.vaisseau.y < 108 : Space.vaisseau.y += 1 Space.vaisseau[0] = offsets_animation_vaisseau["Av_Ar"] temps_cpu_actuel = time.monotonic() if (temps_cpu_actuel - temps_animation_astronaute > pause_deplacement_astronaute) : temps_animation_astronaute = temps_cpu_actuel Space.astronaute[0] = offset_animation_astronaute + ((Space.astronaute[0] + 1) %8) #colision if Space.collision(Space.vaisseau,Space.astronaute) == True: score += 1 spawn_astronaute() Space.audio_pwm1.play(Space.audio_astronaute) temps_actuel = time.monotonic() if Space.collision(Space.vaisseau, Space.asteroide): if temps_actuel - dernier_degats > cooldown_degats: Space.audio_pwm1.play(Space.audio_explosion) vie += 1 dernier_degats = temps_actuel Space.label_score.text = str(score) if ((temps_cpu_actuel - temps_animation_asteroide) > pause_deplacement_asteroide): temps_animation_asteroide = temps_cpu_actuel # déplacement Space.asteroide.x += asteroide_dx Space.asteroide.y += asteroide_dy # rebond X if Space.asteroide.x <= 0: Space.asteroide.x = 0 asteroide_dx = random.choice([-2, -1, 1, 2]) elif Space.asteroide.x >= 128: Space.asteroide.x = 128 asteroide_dx = random.choice([-2, -1, 1, 2]) # rebond Y if Space.asteroide.y <= 0: Space.asteroide.y = 0 asteroide_dy = random.choice([-2, -1, 1, 2]) elif Space.asteroide.y >= 108: Space.asteroide.y = 108 asteroide_dy = random.choice([-2, -1, 1, 2]) #Chronomètre: temps_actuel = time.monotonic() temps_ecoule = temps_actuel - temps_depart temps_restant = int(duree_partie - temps_ecoule) Space.label_chrono.text = str(f"0:{temps_restant}") #Vie vaiseau Space.barre_vie[0] = vie if vie==5 or temps_restant==0: Space.bouton_start.hidden = True Space.ecran_titre.hidden = True Space.portrait.hidden = True Space.label1_regles.hidden = True Space.label2_regles.hidden = True Space.label3_regles.hidden = True Space.ecran_jeu.hidden = True Space.bouton_start.hidden = True Space.astronaute.hidden = True Space.label_chrono.hidden = True Space.label_score.hidden = True Space.vaisseau.hidden = True Space.barre_vie.hidden = True Space.asteroide.hidden = True Space.ecran_fin.hidden = False Space.label_score_final.hidden = False Space.label_score_final.text = str(score) game = 5 if game==5: for indice, bouton in enumerate(objects_boutons): if bouton.value == Controles.appuye: if indice == 4: game = 1 vie = 0 score = 0
⚙ Code CircuitPython pour Raspberry Pi Pico 2 — gestion du joystick, collisions, chronomètre, vies, score, animations et effets audio.
⬇ Télécharger le code Space Saver (.py)🎯 Compétences développées
- Conception électronique et routage PCB
- Programmation embarquée en CircuitPython
- Développement de jeux et animation graphique
- Gestion d'entrées analogiques et numériques
- Diagnostic matériel et résolution de problèmes
- Soudure et assemblage électronique
- Validation et tests de systèmes embarqués