95 lines
3 KiB
Python
95 lines
3 KiB
Python
#!/usr/bin/python
|
|
|
|
# Somewhat minimal Adafruit Arcade Bonnet handler. Runs in background,
|
|
# translates inputs from MCP23017 port expander to virtual USB keyboard
|
|
# events. Not -quite- as efficient or featuretastic as retrogame, but
|
|
# still reasonably lightweight and may be easier and/or more reliable than
|
|
# retrogame for some users. Supports ONE port expander, no regular GPIO
|
|
# (non-port-expander) or "Vulcan nerve pinch" features.
|
|
# Prerequisites:
|
|
# sudo apt-get install python-pip python-smbus python-dev
|
|
# sudo pip install evdev
|
|
# Be sure to enable I2C via raspi-config. Also, udev rules will need to
|
|
# be set up per retrogame directions.
|
|
# Credit to Pimoroni for Picade HAT scripts as starting point.
|
|
|
|
import os
|
|
import time
|
|
import RPi.GPIO as gpio
|
|
from evdev import uinput, UInput, ecodes as e
|
|
from smbus import SMBus
|
|
|
|
key = [ # EDIT KEYCODES IN THIS TABLE TO YOUR PREFERENCES:
|
|
# See /usr/include/linux/input.h for keycode names
|
|
# Keyboard Bonnet EmulationStation
|
|
e.KEY_LEFTCTRL, # 1A 'A' button
|
|
e.KEY_LEFTALT, # 1B 'B' button
|
|
e.KEY_A, # 1C 'X' button
|
|
e.KEY_S, # 1D 'Y' button
|
|
e.KEY_5, # 1E 'Select' button
|
|
e.KEY_1, # 1F 'Start' button
|
|
0, # Bit 6 NOT CONNECTED on Bonnet
|
|
0, # Bit 7 NOT CONNECTED on Bonnet
|
|
e.KEY_DOWN, # 4-way down D-pad down
|
|
e.KEY_UP, # 4-way up D-pad up
|
|
e.KEY_RIGHT, # 4-way right D-pad right
|
|
e.KEY_LEFT, # 4-way left D-pad left
|
|
e.KEY_L, # Analog right
|
|
e.KEY_H, # Analog left
|
|
e.KEY_J, # Analog down
|
|
e.KEY_K # Analog up
|
|
]
|
|
|
|
addr = 0x26 # I2C Address of MCP23017
|
|
irqPin = 17 # IRQ pin for MCP23017
|
|
|
|
os.system("sudo modprobe uinput")
|
|
|
|
ui = UInput({e.EV_KEY: key}, name="retrogame", bustype=e.BUS_USB)
|
|
bus = SMBus(1)
|
|
IODIRA = 0x00
|
|
IOCONA = 0x0A
|
|
INTCAPA = 0x10
|
|
|
|
# Initial MCP23017 config:
|
|
bus.write_byte_data(addr, 0x05 , 0x00) # If bank 1, switch to 0
|
|
bus.write_byte_data(addr, IOCONA, 0x44) # Bank 0, INTB=A, seq, OD IRQ
|
|
|
|
# Read/modify/write remaining MCP23017 config:
|
|
cfg = bus.read_i2c_block_data(addr, IODIRA, 14)
|
|
cfg[ 0] = 0xFF # Input bits
|
|
cfg[ 1] = 0xFF
|
|
cfg[ 2] = 0x00 # Polarity
|
|
cfg[ 3] = 0x00
|
|
cfg[ 4] = 0xFF # Interrupt pins
|
|
cfg[ 5] = 0xFF
|
|
cfg[12] = 0xFF # Pull-ups
|
|
cfg[13] = 0xFF
|
|
bus.write_i2c_block_data(addr, IODIRA, cfg)
|
|
|
|
# Clear interrupt by reading INTCAP and GPIO registers
|
|
x = bus.read_i2c_block_data(addr, INTCAPA, 4)
|
|
oldState = x[2] | (x[3] << 8)
|
|
|
|
# Callback for MCP23017 interrupt request
|
|
def mcp_irq(pin):
|
|
global oldState
|
|
x = bus.read_i2c_block_data(addr, INTCAPA, 4)
|
|
newState = x[2] | (x[3] << 8)
|
|
for i in range(16):
|
|
bit = 1 << i
|
|
lvl = newState & bit
|
|
if lvl != (oldState & bit):
|
|
ui.write(e.EV_KEY, key[i], 0 if lvl else 1)
|
|
ui.syn()
|
|
oldState = newState
|
|
|
|
# GPIO init
|
|
gpio.setwarnings(False)
|
|
gpio.setmode(gpio.BCM)
|
|
|
|
# Enable pullup and callback on MCP23017 IRQ pin
|
|
gpio.setup(irqPin, gpio.IN, pull_up_down=gpio.PUD_UP)
|
|
gpio.add_event_detect(irqPin, gpio.FALLING, callback=mcp_irq)
|
|
|
|
while True: time.sleep(1)
|