scrapeboard/gpio.py

130 lines
4.8 KiB
Python
Raw Permalink Normal View History

2022-11-04 17:49:08 -04:00
# [SCRAPEBOARD] is an arcade game in development by [@diskmem] & [@snakesandrews]
#
# It requires custom hardware to play but can be tested in keyboard mode without
# the hardware. For more information on setting up and running the game, see
2023-12-21 23:49:40 -05:00
# README.md. For information on the game in general, see <https://scrape.nugget.fun/>.
2022-11-04 17:49:08 -04:00
#
# Full open source code is available at <https://git.nugget.fun/scrape/scrapeboard>.
#
2022-11-04 19:01:26 -04:00
# 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.
#
2023-12-21 23:49:40 -05:00
# It can also be run as a diagnostic script, in which case it prints connections detected between
# the input GPIO pins. Use the `-h` flag to print the options.
#
2023-12-21 23:49:40 -05:00
# $ python3 gpio.py -h
#
# Original algorithm by Dr. Clement Shimizu is taken from his Arduino code in serial/serial.ino
2022-11-04 17:49:08 -04:00
2022-11-05 01:33:05 -04:00
import time, itertools, argparse
2022-11-04 17:49:08 -04:00
import RPi.GPIO as GPIO
# These represent the game pads and the GPIO pins they're connected to
LNW, LNE, LSE, LSW = range(4)
pins = {
2023-01-27 17:22:06 -05:00
LNW: 26,
LNE: 19,
LSE: 13,
LSW: 6
2022-11-04 17:49:08 -04:00
}
2022-11-04 19:01:26 -04:00
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)
2022-11-04 17:49:08 -04:00
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
it's set to pullup, that means `pin_a` is connected to it, so return `True`.
@param pin_a Pin which will be set to output LOW
@param pin_b Pin which will be read
@return `True` if a low signal is sent from `pin_a` to `pin_b`, `False` otherwise
"""
for pin_id, pin in pins.items():
if pin == pin_a:
# Set pin_a to output LOW
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, GPIO.LOW)
else:
# Set all other pins to input pullup
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# Force 25ns delay between comparisons
time.sleep(0.025)
2022-11-04 17:49:08 -04:00
# pin_b can now be tested to see if if reads the LOW signal from pin_a
return GPIO.input(pin_b) == GPIO.LOW
def connection(pin_a, pin_b):
"""
Tests low signals connect from both A to B and from B to A
@param pin_a Test if this pin is connected to `pin_b`
@param pin_b Test if this pin is connected to `pin_a`
@return `True` if `pin_a` and `pin_b` are connected, `False` otherwise
"""
return connection2(pin_a, pin_b) and connection2(pin_b, pin_a)
def connections():
"""
2022-11-04 19:01:26 -04:00
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
2022-11-04 17:49:08 -04:00
"""
2022-11-04 19:01:26 -04:00
state = {}
2022-11-04 17:49:08 -04:00
for pin_id_a, pin_id_b in itertools.combinations(pins, 2):
if connection(pins[pin_id_a], pins[pin_id_b]):
2022-11-04 19:01:26 -04:00
state[(pin_id_a, pin_id_b)] = True
else:
state[(pin_id_a, pin_id_b)] = False
return state
2022-11-04 17:49:08 -04:00
2022-11-04 19:01:26 -04:00
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.
2022-11-04 17:49:08 -04:00
2022-11-04 19:01:26 -04:00
@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
2022-11-04 17:49:08 -04:00
2022-11-04 19:01:26 -04:00
if __name__ == "__main__":
2022-11-05 01:33:05 -04:00
# Set up and parse CLI
parser = argparse.ArgumentParser()
parser.add_argument("--connections", action="store_true")
parser.add_argument("--activity", action="store_true")
arguments = parser.parse_args()
2022-11-04 19:01:26 -04:00
initialize_gpio()
2022-11-04 17:49:08 -04:00
while True:
2022-11-05 01:33:05 -04:00
if arguments.connections or not arguments.activity:
# Try all connections once each frame
for connection_ids, connection_state in connections().items():
# 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]})")
else:
pin_activity = activity()
print(f"LNW {int(pin_activity[LNW])} LNE {int(pin_activity[LNE])} LSE {int(pin_activity[LSE])} LSW {int(pin_activity[LSW])}")