wip applying GPIO to the game

This commit is contained in:
ohsqueezy 2022-11-04 19:01:26 -04:00
parent f68fb897c5
commit d122f66af4
3 changed files with 112 additions and 19 deletions

65
NS.py
View File

@ -32,6 +32,7 @@ from pygame.draw import aalines, lines
from pygame.gfxdraw import aapolygon, arc, polygon, aaellipse, ellipse, filled_ellipse, filled_circle
from pygame.locals import *
import gpio
from lib.pgfw.pgfw.Game import Game
from lib.pgfw.pgfw.GameChild import GameChild
from lib.pgfw.pgfw.Sprite import Sprite, RainbowSprite, BlinkingSprite
@ -146,11 +147,14 @@ class NS(Game, Animation):
# Specify possible arguments and parse the command line. If the -h flag is passed, the argparse library will print a
# help message and end the program.
parser = argparse.ArgumentParser()
parser.add_argument("--minimize-load-time", action="store_true")
parser.add_argument("--minimize-load-time", action="store_true", help="Disable some graphics loading and effects generation")
parser.add_argument("--serial-port")
parser.add_argument("--audio-buffer-size", type=int, default=1024)
parser.add_argument("--list-serial-ports", action="store_true")
parser.add_argument("--no-serial", action="store_true")
parser.add_argument("--no-serial", action="store_true", help="Force serial (Arduino) mode off.")
parser.add_argument(
"--pi", action="store_true",
help="Force to read input from the GPIO pins of a Raspberry Pi. Must be running on Raspberry Pi. Forces --no-serial.")
parser.add_argument("--show-config", action="store_true")
arguments = parser.parse_known_args()[0]
@ -221,19 +225,37 @@ class NS(Game, Animation):
if arguments.no_serial:
self.get_configuration().set("input", "serial", False)
# Apply the pi flag from the command line if requested. This takes precedence over Arduino, so even if serial is enabled,
# force to no serial mode.
if arguments.pi:
self.get_configuration().set("input", "pi", True)
if get_configuration("input", "serial"):
print("Pi mode was requested, so forcing serial (Arduino) mode to off")
self.get_configuration().set("input", "serial", False)
# Print the configuration if requested on the command line
if arguments.show_config:
print(self.get_configuration())
# Initialize the serial reader and launch a thread for reading from the serial port
if self.serial_enabled():
# init Pi
if self.pi_enabled():
# Initialize GPIO interface
gpio.initialize_gpio()
# init Arduino
elif self.serial_enabled():
# Initialize the serial reader and launch a thread for reading from the serial port
from serial import Serial, SerialException
from serial.tools import list_ports
# If a list of serial ports was requested, print detected ports and exit.
if arguments.list_serial_ports:
for port in list_ports.comports():
print(f"Detected serial port: {port.device}")
exit()
# Open the port specified by the configuration or command line if it is found. If the specified port is not
# found, open the first found serial port. If no serial ports are found, raise an exception.
requested_port = self.get_configuration("input", "arduino-port")
@ -247,6 +269,8 @@ class NS(Game, Animation):
self.serial_kill = False
self.serial_data = 0
self.reset_arduino()
# Launch a separate thread for reading serial data
self.serial_thread = Thread(target=self.read_serial)
self.serial_thread.start()
@ -299,6 +323,9 @@ class NS(Game, Animation):
clear()
def pi_enabled(self):
return self.get_configuration("input", "pi")
def serial_enabled(self):
return self.get_configuration("input", "serial")
@ -351,6 +378,21 @@ class NS(Game, Animation):
if light.pressed:
self.idle_elapsed = 0
def apply_gpio(self):
"""
Check the connection status of the GPIO pins and turn on the appropriate light objects (the pads).
"""
# Get the active (a.k.a. pressed) state of each GPIO pin (a.k.a. pad)
activity = gpio.activity()
for light_id in activity.keys():
# The pressed state is set to the activity state
self.platform.lights[light_id].pressed = activity[light_id]
# Reset idle timer if a light is detected as pressed
if activity[light_id]:
self.idle_elapsed = 0
def reset(self, leave_wipe_running=False):
self.idle_elapsed = 0
self.suppressing_input = False
@ -450,10 +492,22 @@ class NS(Game, Animation):
def update(self):
Animation.update(self)
last_frame_duration = self.time_filter.get_last_frame_duration()
if self.serial_enabled():
# Apply controller input to light (pad) states from either Pi or Arduino if applicable
if self.pi_enabled():
# Translate Raspberry Pi GPIO state into pad states
self.apply_gpio()
elif self.serial_enabled():
# Translate the most recent serial data, being provided by serial/serial2/serial2.ino, into pad states
self.apply_serial()
# Handle auto reset of the Arduino for stablizing serial data
if self.title.active or self.ending.active or self.dialogue.active:
self.no_reset_elapsed += last_frame_duration
# If we received good input, reset the auto reset timer
if 0b11 <= self.serial_data <= 0b1100:
self.no_reset_elapsed = 0
@ -461,6 +515,7 @@ class NS(Game, Animation):
print("auto arduino reset triggered")
self.reset_arduino()
self.no_reset_elapsed = 0
self.title.update()
self.level_select.update()
self.ending.update()

3
config
View File

@ -60,7 +60,8 @@ volume = 1.0
[input]
buffer = 0
arduino-port = /dev/ttyACM0
serial = True
serial = False
pi = True
confirm-quit = False
[time]

View File

@ -6,9 +6,10 @@
#
# Full open source code is available at <https://git.nugget.fun/scrape/scrapeboard>.
#
# This is a utility script for testing if the GPIO interface of a Raspberry Pi is working
# with the four input wires, which correspond to the four metal floor pads in the standard
# Scrapeboard build.
# This module can be imported and used to check on the status of connections between four GPIO
# inputs on the Raspberry Pi for detecting the pads in Scrapeboard.
#
# When run as a script, it prints connections detected between the input GPIO pins.
import time, itertools
import RPi.GPIO as GPIO
@ -22,6 +23,17 @@ pins = {
LSW: 23
}
def initialize_gpio():
"""
Set pin numbering mode to GPIO and initialize all pins to input pullup.
"""
# Use GPIO numbering
GPIO.setmode(GPIO.BCM)
# Set all pins to pullup
for pin_id, pin in pins.items():
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
def connection2(pin_a, pin_b):
"""
Set `pin_a` to output a low signal, and set every other pin to pullup. If `pin_b` reads a low signal even though
@ -55,21 +67,46 @@ def connection(pin_a, pin_b):
def connections():
"""
Look at all six possible connections between the four pins and print a message if one is found connected.
Look at all six possible connections between the four pins and return the state of each as a single dict.
@return A dict with one entry per combination of pins, the key is a tuple of pin IDs, and the value is the state of connection
"""
state = {}
for pin_id_a, pin_id_b in itertools.combinations(pins, 2):
if connection(pins[pin_id_a], pins[pin_id_b]):
print(f"{pin_id_a} ({pins[pin_id_a]}) <-> {pin_id_b} ({pins[pin_id_b]})")
state[(pin_id_a, pin_id_b)] = True
else:
state[(pin_id_a, pin_id_b)] = False
return state
def activity():
"""
Look at all six possible connections between the four pins, and if a pin appears in any active connection, consider it active.
Return the active state of all four pins as a dict.
@return A dict with one entry per pin. The key is the pin ID, and the value is the state of whether it's active (a.k.a. pressed)
"""
# Create a dict of pins all at False
state = pins.copy()
for pin in state.keys():
state[pin] = False
# Check all six combinations. If a pin appears in a connection, update its state to True.
for pin_id_a, pin_id_b in itertools.combinations(pins, 2):
if connection(pins[pin_id_a], pins[pin_id_b]):
state[pin_id_a] = True
state[pin_id_b] = True
return state
if __name__ == "__main__":
# Use GPIO numbering
GPIO.setmode(GPIO.BCM)
# Set all pins to pullup
for pin_id, pin in pins.items():
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
initialize_gpio()
while True:
# Try all connections once each frame
connections()
for connection_ids, connection_state in connections():
# Only print connected combinations of pins
if connection_state:
pin_id_a, pin_id_b = connection_ids
print(f"{pin_id_a} ({pins[pin_id_a]}) <-> {pin_id_b} ({pins[pin_id_b]})")
time.sleep(0.1)