Skip to content

Instantly share code, notes, and snippets.

@savetheclocktower
Last active March 5, 2024 18:52
Show Gist options
  • Save savetheclocktower/630864d3dbc3dad9ccb6fda4c0151194 to your computer and use it in GitHub Desktop.
Save savetheclocktower/630864d3dbc3dad9ccb6fda4c0151194 to your computer and use it in GitHub Desktop.
ServoStiks & RetroPie
#!/usr/bin/env ruby
require 'io/console'
require 'json'
require 'optparse'
require 'pathname'
$opts = OptionParser.new do |opts|
opts.banner = "Usage: joystick-type [options] system game\nReturns the type of joystick for the given system and game."
opts.separator ""
opts.on_tail('-h', '--help', 'Prints this help message.') do
STDOUT.puts(opts)
exit 0
end
end
begin
$opts.parse!
rescue OptionParser::InvalidArgument => e
STDERR.puts("#{e.message}\n\n")
STDERR.puts($opts)
exit 1
end
SYSTEM, GAME = ARGV
CONTROLS_PATH = Pathname.new('/home/pi/controls')
SYSTEM_PATH = CONTROLS_PATH.join(SYSTEM)
unless SYSTEM_PATH.directory?
STDERR.puts(%Q{System #{SYSTEM} does not exist!})
exit 1
end
GAME_PATH = SYSTEM_PATH.join("#{GAME}.cfg")
if GAME_PATH.file?
puts GAME_PATH.read
exit 0
end
# At this point, if the game doesn't have a config file, we can try to look
# up its joystick type _if_ it's an arcade game. Otherwise we're out of luck.
if SYSTEM != 'arcade'
STDERR.puts(%Q{Could not find joystick config file for game #{GAME} on system #{SYSTEM}})
exit 1
end
def decide_on(type)
GAME_PATH.open('w') { |f| f.write(type) }
puts type
exit 0
end
JSON_PATH = SYSTEM_PATH.join('restructuredControls.json')
# Reluctantly read from the gigantic JSON file. But then store what we find
# so that we never have to do the lookup for that game again.
controls = JSON::load(JSON_PATH.open)
# Each game has control configurations. Each control configuration has
# control sets. Each control set, if it has a joystick, specifies whether
# it's 4-way or 8-way.
#
# 99% of the time, these configurations will not disagree about 4-way versus
# 8-way. The remaining 1% can be figured out manually.
game = controls['gameMap'][GAME]
if game.nil?
STDERR.puts(%Q{Game #{GAME} does not exist!})
exit 1
end
sets = game['controlConfigurations'].map { |cfg| cfg['controlSets'] }.flatten
begin
control_types = sets.map { |s| s['controls'][0]['type'] }
rescue Exception => e
STDERR.puts(%Q{Couldn't figure this game out at all. Maybe it doesn't use a joystick.})
exit 1
end
if control_types.all? { |t| t == 'joy-4way' || t == 'joy-2way-horizontal' || t == 'joy-2way-vertical' }
decide_on(4)
elsif control_types.all? { |t| t == 'joy-8way' }
decide_on(8)
else
STDERR.puts "Can't figure out joystick automatically. Here are the options:"
control_types.uniq.each do |type|
STDERR.puts " #{type}"
end
exit 1
end
# ARGUMENTS, IN ORDER:
# 1. System (e.g., "arcade")
# 2. Emulator (e.g. "lr-fba-next")
# 3. Full path to game (e.g., /home/pi/RetroPie/roms/arcade/wjammers.zip)
if [ -z "$3" ]; then
exit 0
fi
system=$1
emulator=$2
# Gets the basename of the game (e.g., "wjammers")
game=$(basename $3)
game=${game%.*}
###
# Return the joystick to 8-way mode no matter what.
# TODO: I don't know why this needs `sudo`. It seems to work fine without it
# in an interactive shell. Investigate.
sudo /home/pi/bin/set-joystick 8
#!/usr/bin/bash
# ARGUMENTS, IN ORDER:
# 1. System (e.g., "arcade")
# 2. Emulator (e.g. "lr-fba-next")
# 3. Full path to game (e.g., /home/pi/RetroPie/roms/arcade/wjammers.zip)
if [ -z "$3" ]; then
exit 0
fi
system=$1
emulator=$2
# Gets the basename of the game (e.g., "wjammers")
game=$(basename $3)
game=${game%.*}
###
# Figure out the joystick mode of the game.
joystick_mode=`/home/pi/bin/joystick-type "$system" "$game"`
# Strip all spaces. Replace "8" with an empty string so it fails the
# conditional below.
joystick_mode=${joystick_mode// /}
joystick_mode=${joystick_mode//8/}
# If the joystick type needs to be switched to 4-way mode, notify the user.
# We don't care about 8-way mode because that's the default. We don't care
# about when `joystick-mode` returns nothing, because in that case we take
# no action.
if [[ ! -z "$joystick_mode" ]]; then
DIALOGRC="/opt/retropie/configs/all/runcommand-launch-dialog.cfg" dialog --infobox "\nSetting joystick to ${joystick_mode}-way mode." 5 60
sleep 0.5
fi
# TODO: I don't know why this needs `sudo`. It seems to work fine without it
# in an interactive shell. Investigate.
sudo /home/pi/bin/set-joystick "$joystick_mode"
#!/usr/bin/env python3
"""
A script for setting the ServoStik to 4-way mode or 8-way mode.
"""
usage = """
Usage: set-joystick [4|8|0]
Takes a single argument which should be either "4" (four-way), "8" (eight-way),
or "0" (which means "I have no clue what to do and am willing to leave it up to
this script to decide.")
"""
import sys
from RPi import GPIO
GPIO.setmode(GPIO.BCM)
MODE_8 = 6 # white
MODE_4 = 5 # green
GPIO.setup(MODE_4, GPIO.OUT)
GPIO.setup(MODE_8, GPIO.OUT)
def set_joystick_mode(mode):
target_pin = MODE_8
other_pin = MODE_4
if mode == 4:
target_pin = MODE_4
other_pin = MODE_8
GPIO.output(other_pin, GPIO.LOW)
GPIO.output(target_pin, GPIO.HIGH)
mode = sys.argv[1]
if mode not in ("0", "4", "8", ""):
print("Invalid argument!", file=sys.stderr)
print(usage)
GPIO.cleanup()
sys.exit(1)
if mode == "0" or mode == "":
# Zero equals a shrug. Put the joystick in 8-way mode.
set_joystick_mode(8)
elif mode == "4":
set_joystick_mode(4)
elif mode == "8":
set_joystick_mode(8)
GPIO.cleanup()
sys.exit()
@Wilstorm
Copy link

I was curious is this for using an Ultimarc Servostik with RetroPie?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment