← Retour aux projets
🎮 Prototype de la console rétro gaming — carte électronique assemblée autour du Raspberry Pi Pico 2, avec écran TFT, boutons de commande, joystick, haut-parleur et alimentation intégrée.
🕹️ Console en fonctionnement — affichage d’un mini-jeu rétro sur l’écran couleur, validation des entrées utilisateur, de l’affichage graphique et de la logique embarquée en CircuitPython.

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É)

Raspberry Pi Pico 2CircuitPythonProteus PCBÉcran LCDJoystickHaut-parleur Oscilloscope

🔧 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.

console_simon.py
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.

console_space_saver.py
#############################################
# 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