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.

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 |

Procedimiento Experimental
Paso 1: Monta el circuito.

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.
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, yrandom
es útil para introducir aleatoriedad en el proyecto.#!/usr/bin/env python3 from gpiozero import OutputDevice, Button import time import threading import random
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
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"
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
y0
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] }
Esta función desplaza un byte de datos hacia el registro de desplazamiento 74HC595. Itera sobre cada bit del byte
dat
, configurando el pinSDI
alto o bajo según corresponda, y alterna el pinSRCLK
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()
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 usandohc595_shift
, y luego alterna el pinRCLK
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()
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 utilizathreading.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()
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()
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")
Esta función verifica el estado de los botones. Si
AButtonPin
está presionado (yBButtonPin
no), llama acheckPoint
con «right». SiBButtonPin
está presionado (yAButtonPin
no), llama acheckPoint
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")
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])
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
El juego se ejecuta en un bloque
try
. Si ocurre unaKeyboardInterrupt
(como presionar Ctrl+C), captura la excepción y llama adestroy
para limpiar antes de salir.# Ejecuta el juego y maneja KeyboardInterrupt para salir limpiamente try: main() except KeyboardInterrupt: destroy()