Skip to content

Instantly share code, notes, and snippets.

@todbot
Last active January 9, 2023 03:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save todbot/7a735d181c75fc01dc27ccf54cbc1c2c to your computer and use it in GitHub Desktop.
Save todbot/7a735d181c75fc01dc27ccf54cbc1c2c to your computer and use it in GitHub Desktop.
pretend to be a Playstation DS4 controller
# ds4_fakey_boot.py -- pretend to be a Playstation DS4 controller, using MacroPad RP2040
# doesn't quite seem to work yet
# 8 Jan 2023 - @todbot / Tod Kurt
import usb_hid
import usb_midi
import supervisor
ds4_vid = 0x054C
ds4_pid = 0x05C4
ds4_mfg_str = "JP Fakey Corp"
ds4_prod_str = "JP Fakey DS4"
usb_midi.disable()
# abbreviated from https://gimx.fr/wiki/index.php?title=DualShock_4
GAMEPAD_DS4_REPORT_DESCRIPTOR = bytes((
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x09, 0x05, # Usage (Game Pad)
0xA1, 0x01, # Collection (Application)
0x85, 0x01, # Report ID (1)
0x09, 0x30, # Usage (X)
0x09, 0x31, # Usage (Y)
0x09, 0x32, # Usage (Z)
0x09, 0x35, # Usage (Rz)
0x15, 0x00, # Logical Minimum (0)
0x26, 0xFF, 0x00, # Logical Maximum (255)
0x75, 0x08, # Report Size (8)
0x95, 0x04, # Report Count (4)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x39, # Usage (Hat switch)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x07, # Logical Maximum (7)
0x35, 0x00, # Physical Minimum (0)
0x46, 0x3B, 0x01, # Physical Maximum (315)
0x65, 0x14, # Unit (System: English Rotation, Length: Centimeter)
0x75, 0x04, # Report Size (4)
0x95, 0x01, # Report Count (1)
0x81, 0x42, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, # Unit (None)
0x05, 0x09, # Usage Page (Button)
0x19, 0x01, # Usage Minimum (0x01)
0x29, 0x0E, # Usage Maximum (0x0E)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x75, 0x01, # Report Size (1)
0x95, 0x0E, # Report Count (14)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, # Usage Page (Vendor Defined 0xFF00)
0x09, 0x20, # Usage (0x20)
0x75, 0x06, # Report Size (6)
0x95, 0x01, # Report Count (1)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x7F, # Logical Maximum (127)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x09, 0x33, # Usage (Rx)
0x09, 0x34, # Usage (Ry)
0x15, 0x00, # Logical Minimum (0)
0x26, 0xFF, 0x00, # Logical Maximum (255)
0x75, 0x08, # Report Size (8)
0x95, 0x02, # Report Count (2)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, # Usage Page (Vendor Defined 0xFF00)
0x09, 0x21, # Usage (0x21)
0x95, 0x36, # Report Count (54)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, # End Collection
))
print("setting new vid/pid")
supervisor.set_usb_identification(vid=ds4_vid, pid=ds4_pid, manufacturer=ds4_mfg_str, product=ds4_prod_str)
print("setting up gamepad hid descriptor")
gamepad_ds4 = usb_hid.Device(
report_descriptor=GAMEPAD_DS4_REPORT_DESCRIPTOR,
usage_page=0x01, # Generic Desktop Control (must match byte1 of above)
usage=0x05, # Gamepad (must match byte3 of above)
report_ids=(1,), # Descriptor uses report ID 1.
in_report_lengths=(64,), # This gamepad sends 64 bytes in its report.
out_report_lengths=(0,), # It does not receive any reports.
)
usb_hid.enable( (gamepad_ds4,) )
print("boot.py done")
# ds4_fakey_code.py -- pretend to be a Playstation DS4 controller, using MacroPad RP2040
# doesn't quite seem to work yet
# 8 Jan 2023 - @todbot / Tod Kurt
# Also need to install accompanying "boot.py" and press RESET to load it
import time
import board, keypad
import neopixel
import usb_hid
import adafruit_hid
leds = neopixel.NeoPixel(board.NEOPIXEL, 12, brightness=0.2, auto_write=True)
leds.fill(0x111111);
key_pins = (board.KEY1, board.KEY2, board.KEY3,
board.KEY4, board.KEY5, board.KEY6,
board.KEY7, board.KEY8, board.KEY9,
board.KEY10, board.KEY11, board.KEY12)
keys = keypad.Keys(key_pins, value_when_pressed=False, pull=True)
gamepad = adafruit_hid.find_device(usb_hid.devices, usage_page=0x1, usage=0x05)
key_U = 7 # aka "board.KEY8" (0-indexed)
key_D = 10
key_L = 9
key_R = 11
key_TRI = 1
key_CIR = 2
key_XXX = 4
key_SQR = 5
for l in (key_U, key_D, key_L, key_R):
leds[l] = 0xFF0000
for l in (key_TRI, key_CIR, key_XXX, key_SQR):
leds[l] = 0x00FF00
# for sending HID report
report = bytearray(64)
buttons = 0x00
dpad = 0x08 # no press
def set_key(keynum,pressed):
global dpad, buttons
dpad = 0x08 # no press
buttons = 0x00 # no press
if not pressed: return # for now
if keynum == key_U: dpad = 0x00
elif keynum == key_L: dpad = 0x06
elif keynum == key_D: dpad = 0x04
elif keynum == key_R: dpad = 0x02
elif keynum == key_TRI: buttons = 0x80
elif keynum == key_CIR: buttons = 0x40
elif keynum == key_XXX: buttons = 0x20
elif keynum == key_SQR: buttons = 0x10
def send_report():
report[0] = 127 # left stick X, 0=left
report[1] = 127 # left stick Y, 0=up
report[2] = 127 # right stick X
report[3] = 127 # right stick Y
report[4] = buttons | dpad # buttons and dpad
report[5] = 0x00 # buttons R3, L3, OPT, Share, R2, L2, R1, L1
report[6] = 0x00 # counter (should go up one per report?)
report[7] = 0x00 # left trigger (0-ff)
report[8] = 0x00 # right trigger
gamepad.send_report(report)
while True:
key = keys.events.get()
if key:
keynum = key.key_number
press = key.pressed
set_key(keynum, press)
print("key:",keynum, press, hex(buttons), hex(dpad))
send_report()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment