Nota

¡Hola! Bienvenido a la Comunidad de Entusiastas de SunFounder para Raspberry Pi, Arduino y ESP32 en Facebook. Sumérgete junto a otros entusiastas en el fascinante mundo de Raspberry Pi, Arduino y ESP32.

¿Por qué unirte?

  • Soporte experto: Resuelve problemas posventa y desafíos técnicos con ayuda de nuestra comunidad y equipo.

  • Aprende y comparte: Intercambia consejos y tutoriales para mejorar tus habilidades.

  • Preestrenos exclusivos: Accede anticipadamente a anuncios y adelantos de nuevos productos.

  • Descuentos especiales: Aprovecha descuentos exclusivos en nuestros productos más recientes.

  • Promociones y sorteos festivos: Participa en sorteos y promociones especiales en días festivos.

👉 ¿Listo para explorar y crear con nosotros? Haz clic en [Aquí] y únete hoy mismo.

3.1.14 JUEGO – Not Not

Introducción

En esta lección, crearemos un divertido dispositivo de juego llamado «Not Not».

Durante el juego, la matriz de puntos mostrará una flecha en una dirección al azar. Debes presionar el botón en la dirección opuesta a la flecha dentro de un tiempo limitado. Si el tiempo se acaba o presionas el botón en la misma dirección de la flecha, pierdes.

Este juego realmente puede ayudarte a practicar el pensamiento inverso. ¿Listo para intentarlo?

Componentes necesarios

En este proyecto, necesitaremos los siguientes componentes.

../_images/3.1.14_game_not_not_list.png

Diagrama de Esquema

T-Board Name

physical

wiringPi

BCM

GPIO17

Pin 11

0

17

GPIO18

Pin 12

1

18

GPIO27

Pin 13

2

27

GPIO20

Pin 38

28

20

GPIO26

Pin 37

25

26

../_images/3.1.14_game_not_not_schematic.png

Procedimiento Experimental

Paso 1: Monta el circuito.

../_images/3.1.14_game_not_not_circuit.png

Paso 2: Abre el archivo de código.

cd ~/davinci-kit-for-raspberry-pi/python-pi5

Paso 3: Ejecuta el programa.

sudo python3 3.1.14_MotionControl.py

Al iniciar el programa, aparece una flecha en la matriz de puntos apuntando hacia la derecha o la izquierda. Lo que debes hacer es presionar el botón en la dirección opuesta a la flecha dentro de un tiempo límite. Entonces, aparecerá un "" en la matriz de puntos. Si se acaba el tiempo o presionas el botón en la misma dirección que la flecha, pierdes y la matriz de puntos muestra una "x". También puedes agregar 2 botones nuevos o reemplazarlos por los controles de un joystick para tener 4 direcciones: arriba, abajo, izquierda y derecha, aumentando así la dificultad del juego.

Advertencia

Si aparece el mensaje de error RuntimeError: Cannot determine SOC peripheral base address, consulta la sección Si gpiozero no funciona.

Código

Nota

Puedes Modificar/Restablecer/Copiar/Ejecutar/Detener el código a continuación. Sin embargo, antes de hacerlo, dirígete a la ruta de código fuente, como davinci-kit-for-raspberry-pi/python-pi5. Tras realizar cambios, puedes ejecutarlo directamente para ver el efecto.

#!/usr/bin/env python3
from gpiozero import OutputDevice, Button
import time
import threading
import random

# Pines GPIO para el registro de desplazamiento 74HC595
SDI = OutputDevice(17)   # Entrada de Datos Serial
RCLK = OutputDevice(18)  # Reloj de Registro
SRCLK = OutputDevice(27) # Reloj de Registro de Desplazamiento

# Pines GPIO para los botones
AButtonPin = Button(20)  # Botón A
BButtonPin = Button(26)  # Botón B

# Inicialización de variables del juego
timerPlay = 0
timerCheck = 0
waypoint = "NULL"
stage = "NULL"

# Flechas para la matriz LED
arrow = {
    "right": [0xFF, 0xEF, 0xDF, 0x81, 0xDF, 0xEF, 0xFF, 0xFF],
    "left": [0xFF, 0xF7, 0xFB, 0x81, 0xFB, 0xF7, 0xFF, 0xFF]
}

# Iconos de respuesta para respuestas correctas/incorrectas
check = {
    "wrong": [0xFF, 0xBB, 0xD7, 0xEF, 0xD7, 0xBB, 0xFF, 0xFF],
    "right": [0xFF, 0xFF, 0xF7, 0xEB, 0xDF, 0xBF, 0xFF, 0xFF]
}

def hc595_shift(dat):
    """ Shift data to the 74HC595 shift register. """
    for i in range(8):
        SDI.value = 0x80 & (dat << i)
        SRCLK.on()
        SRCLK.off()

def display(glyphCode):
    """ Display a glyph on the LED matrix. """
    for i in range(0, 8):
        hc595_shift(glyphCode[i])
        hc595_shift(0x80 >> i)
        RCLK.on()
        RCLK.off()

def creatGlyph():
    """ Create a new glyph for the game and start the play timer. """
    global waypoint, stage, timerPlay
    waypoint = random.choice(list(arrow.keys()))
    stage = "PLAY"
    timerPlay = threading.Timer(2.0, timeOut)
    timerPlay.start()

def checkPoint(inputKey):
    """ Check player's input and update game state. """
    global waypoint, stage, timerCheck
    if inputKey == "empty" or inputKey == waypoint:
        waypoint = "wrong"
    else:
        waypoint = "right"
    timerPlay.cancel()
    stage = "CHECK"
    timerCheck = threading.Timer(1.0, creatGlyph)
    timerCheck.start()

def timeOut():
    """ Handle game timeout scenario. """
    checkPoint("empty")

def getKey():
    """ Detect button press and trigger checkpoint. """
    if AButtonPin.is_pressed and not BButtonPin.is_pressed:
        checkPoint("right")
    elif not AButtonPin.is_pressed and BButtonPin.is_pressed:
        checkPoint("left")

def main():
    """ Main game loop. """
    creatGlyph()
    while True:
        if stage == "PLAY":
            display(arrow[waypoint])
            getKey()
        elif stage == "CHECK":
            display(check[waypoint])

def destroy():
    """ Clean up resources on program exit. """
    global timerPlay, timerCheck
    timerPlay.cancel()  # Cancela el temporizador de juego
    timerCheck.cancel()  # Cancela el temporizador de verificación

# Ejecuta el juego, maneja la interrupción de teclado para salir limpiamente
try:
    main()
except KeyboardInterrupt:
    destroy()

Explicación del Código

Basado en 1.1.6 Matriz de Puntos LED, en esta lección se añaden 2 botones para crear un dispositivo de juego divertido. Si aún no estás familiarizado con la matriz de puntos, por favor consulta 1.1.6 Matriz de Puntos LED.

  1. El código comienza importando las librerías necesarias. Se utiliza gpiozero para interactuar con pines GPIO como botones y dispositivos de salida. time permite agregar retrasos, threading habilita la ejecución de múltiples tareas simultáneamente, y random es útil para introducir aleatoriedad en el proyecto.

    #!/usr/bin/env python3
    from gpiozero import OutputDevice, Button
    import time
    import threading
    import random
    
  2. Se inicializan los pines GPIO para el registro de desplazamiento (SDI, RCLK, SRCLK) y los botones (AButtonPin, BButtonPin). El registro de desplazamiento se utiliza para controlar múltiples LEDs con menos pines GPIO, lo cual es crucial para la pantalla de la matriz LED.

    # Pines GPIO para el registro de desplazamiento 74HC595
    SDI = OutputDevice(17)   # Entrada de Datos Serial
    RCLK = OutputDevice(18)  # Reloj de Registro
    SRCLK = OutputDevice(27) # Reloj de Registro de Desplazamiento
    
    # Pines GPIO para los botones
    AButtonPin = Button(20)  # Botón A
    BButtonPin = Button(26)  # Botón B
    
  3. Se inicializan variables utilizadas en la lógica del juego, como temporizadores e indicadores de estado del juego.

    # Inicialización de variables del juego
    timerPlay = 0
    timerCheck = 0
    waypoint = "NULL"
    stage = "NULL"
    
  4. Se definen patrones binarios para mostrar flechas y respuestas (correcto/incorrecto) en la matriz LED. Cada elemento del arreglo representa una fila de la matriz LED, donde 1 y 0 indican si el LED está encendido o apagado, respectivamente.

    # Flechas para la pantalla de la matriz LED
    arrow = {
        "right": [0xFF, 0xEF, 0xDF, 0x81, 0xDF, 0xEF, 0xFF, 0xFF],
        "left": [0xFF, 0xF7, 0xFB, 0x81, 0xFB, 0xF7, 0xFF, 0xFF]
    }
    
    # Íconos de respuesta para respuestas correctas/incorrectas
    check = {
        "wrong": [0xFF, 0xBB, 0xD7, 0xEF, 0xD7, 0xBB, 0xFF, 0xFF],
        "right": [0xFF, 0xFF, 0xF7, 0xEB, 0xDF, 0xBF, 0xFF, 0xFF]
    }
    
  5. Esta función desplaza un byte de datos hacia el registro de desplazamiento 74HC595. Itera sobre cada bit del byte dat, configurando el pin SDI alto o bajo según corresponda, y alterna el pin SRCLK para desplazar el bit en el registro.

    def hc595_shift(dat):
        """ Shift data to the 74HC595 shift register. """
        for i in range(8):
            SDI.value = 0x80 & (dat << i)
            SRCLK.on()
            SRCLK.off()
    
  6. Esta función muestra un glifo en la matriz LED. Envía cada fila del glifo (representado por glyphCode) y la dirección de la fila al registro de desplazamiento usando hc595_shift, y luego alterna el pin RCLK para actualizar la pantalla.

    def display(glyphCode):
        """ Display a glyph on the LED matrix. """
        for i in range(0, 8):
            hc595_shift(glyphCode[i])
            hc595_shift(0x80 >> i)
            RCLK.on()
            RCLK.off()
    
  7. Esta función selecciona un glifo al azar del diccionario arrow, inicia el temporizador de juego y establece el estado del juego en «PLAY». Se utiliza threading.Timer para el control del tiempo en el juego.

    def creatGlyph():
        """ Create a new glyph for the game and start the play timer. """
        global waypoint, stage, timerPlay
        waypoint = random.choice(list(arrow.keys()))
        stage = "PLAY"
        timerPlay = threading.Timer(2.0, timeOut)
        timerPlay.start()
    
  8. Esta función verifica la entrada del jugador en comparación con el glifo actual. Si la entrada es correcta, establece el waypoint en «right», de lo contrario en «wrong». Luego cancela el temporizador de juego actual e inicia un nuevo temporizador para el próximo glifo.

    def checkPoint(inputKey):
        """ Check player's input and update game state. """
        global waypoint, stage, timerCheck
        if inputKey == "empty" or inputKey == waypoint:
            waypoint = "wrong"
        else:
            waypoint = "right"
        timerPlay.cancel()
        stage = "CHECK"
        timerCheck = threading.Timer(1.0, creatGlyph)
        timerCheck.start()
    
  9. Esta función se llama cuando se agota el tiempo del juego. Invoca checkPoint con «empty» para indicar que no se presionó ningún botón a tiempo.

    def timeOut():
        """ Handle game timeout scenario. """
        checkPoint("empty")
    
  10. Esta función verifica el estado de los botones. Si AButtonPin está presionado (y BButtonPin no), llama a checkPoint con «right». Si BButtonPin está presionado (y AButtonPin no), llama a checkPoint con «left».

    def getKey():
        """ Detect button press and trigger checkpoint. """
        if AButtonPin.is_pressed and not BButtonPin.is_pressed:
            checkPoint("right")
        elif not AButtonPin.is_pressed and BButtonPin.is_pressed:
            checkPoint("left")
    
  11. La función main controla el flujo del juego. Comienza creando un glifo, luego verifica continuamente el estado del juego. Si está en el estado «PLAY», muestra el glifo actual y verifica las pulsaciones de botón. En el estado «CHECK», muestra la retroalimentación según la acción del jugador.

    def main():
        """ Main game loop. """
        creatGlyph()
        while True:
            if stage == "PLAY":
                display(arrow[waypoint])
                getKey()
            elif stage == "CHECK":
                display(check[waypoint])
    
  12. Esta función cancela cualquier temporizador activo al salir del programa, asegurando un apagado limpio.

    def destroy():
        """ Clean up resources on program exit. """
        global timerPlay, timerCheck
        timerPlay.cancel()  # Cancela el temporizador de juego
        timerCheck.cancel()  # Cancela el temporizador de verificación
    
  13. El juego se ejecuta en un bloque try. Si ocurre una KeyboardInterrupt (como presionar Ctrl+C), captura la excepción y llama a destroy para limpiar antes de salir.

    # Ejecuta el juego y maneja KeyboardInterrupt para salir limpiamente
    try:
        main()
    except KeyboardInterrupt:
        destroy()