Adafruit_Learning_System_Gu.../Super_Nintendo_USB_Controller/code.py

133 lines
3.8 KiB
Python
Executable file

# SPDX-FileCopyrightText: 2023 Robert Dale Smith for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Simple Super Nintendo controller to standard USB HID gamepad with DirectInput button mapping.
# Tested on KB2040
import time
import board
import digitalio
import usb_hid
# Update the SNES Controller pins based on your input
LATCH_PIN = board.D6
CLOCK_PIN = board.D5
DATA_PIN = board.D7
# Set up pins for SNES Controller
latch = digitalio.DigitalInOut(LATCH_PIN)
latch.direction = digitalio.Direction.OUTPUT
clock = digitalio.DigitalInOut(CLOCK_PIN)
clock.direction = digitalio.Direction.OUTPUT
data = digitalio.DigitalInOut(DATA_PIN)
data.direction = digitalio.Direction.INPUT
data.pull = digitalio.Pull.UP # pull-up as a default
# SNES to USB button mapping
buttonmap = {
"B": (0, 0, 0x2), # Button 1
"Y": (1, 0, 0x1), # Button 3
"Select": (2, 1, 0x01), # Button 9
"Start": (3, 1, 0x02), # Button 10
"Up": (4, 0, 0), # D-pad North
"Down": (5, 0, 0), # D-pad South
"Left": (6, 0, 0), # D-pad East
"Right": (7, 0, 0), # D-pad West
"A": (8, 0, 0x4), # Button 2
"X": (9, 0, 0x8), # Button 4
"L": (10, 0, 0x10), # Button 5
"R": (11, 0, 0x20) # Button 6
}
# D-pad buttons to hat-switch value
dpad_state = {
"Up": 0,
"Down": 0,
"Left": 0,
"Right": 0,
}
hat_map = {
(0, 0, 0, 0): 0x08, # Released
(1, 0, 0, 0): 0x00, # N
(1, 1, 0, 0): 0x01, # NE
(0, 1, 0, 0): 0x02, # E
(0, 1, 0, 1): 0x03, # SE
(0, 0, 0, 1): 0x04, # S
(0, 0, 1, 1): 0x05, # SW
(0, 0, 1, 0): 0x06, # W
(1, 0, 1, 0): 0x07, # NW
}
def read_snes_controller():
data_bits = []
latch.value = True
time.sleep(0.000012) # 12µs
latch.value = False
for _ in range(16):
time.sleep(0.000006) # Wait 6µs
data_bits.append(data.value)
clock.value = True
time.sleep(0.000006) # 6µs
clock.value = False
time.sleep(0.000006) # 6µs
return data_bits
# Find the gamepad device in the usb_hid devices
gamepad_device = None
for device in usb_hid.devices:
if device.usage_page == 0x01 and device.usage == 0x05:
gamepad_device = device
break
if gamepad_device is not None:
print("Gamepad device found!")
else:
print("Gamepad device not found.")
report = bytearray(19)
report[2] = 0x08 # default released hat switch value
report[3] = 127 # default x center value
report[4] = 127 # default y center value
report[5] = 127 # default z center value
report[6] = 127 # default rz center value
prev_report = bytearray(report)
while True:
button_state = read_snes_controller()
all_buttons = list(buttonmap.keys())
for idx, button in enumerate(all_buttons):
index, byte_index, button_value = buttonmap[button]
is_pressed = not button_state [index] # True if button is pressed
if button in dpad_state: # If it's a direction button
dpad_state[button] = 1 if is_pressed else 0
else:
if is_pressed:
report[byte_index] |= button_value
else: # not pressed, reset button state
report[byte_index] &= ~button_value
# SOCD (up priority and neutral horizontal)
if (dpad_state["Up"] and dpad_state["Down"]):
dpad_state["Down"] = 0
if (dpad_state["Left"] and dpad_state["Right"]):
dpad_state["Left"] = 0
dpad_state["Right"] = 0
# Extract the dpad_state to a tuple and get the corresponding hat value
dpad_tuple = (dpad_state["Up"], dpad_state["Right"], dpad_state["Left"], dpad_state["Down"])
report[2] = hat_map[dpad_tuple] # Assuming report[2] is the byte for the hat switch
if prev_report != report:
gamepad_device.send_report(report)
prev_report[:] = report
time.sleep(0.1)