Text Editor
Adafruit recommends using the Mu editor for editing your CircuitPython code. You can get more info in this guide.
Alternatively, you can use any text editor that saves simple text files.
Download the Project Bundle
Your project will use a specific set of CircuitPython libraries, and the code.py file. To get everything you need, click on the Download Project Bundle link below, and uncompress the .zip file.
Drag the contents of the uncompressed bundle directory onto your board's CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary. The CIRCUITPY drive appears when you plug the QT Py into the computer via USB.
# SPDX-FileCopyrightText: 2023 John Park w/ Tod Kurt ps2controller library
#
# SPDX-License-Identifier: MIT
# The Takara Game of Life PlayStation roulette wheel controller spinner (TAKC-00001)
# sends two sets of held CIRCLE buttons with randomized hold time periods
# this code turns that into mouse click spamming (the CIRCLE button also spams)
# Tested on QT Py RP2040
import time
import board
import usb_hid
import neopixel
from adafruit_hid.keycode import Keycode
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.mouse import Mouse
from ps2controller import PS2Controller
# turn on neopixel
led = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.1)
led.fill(0x331000) # amber while we wait for controller to connect
mouse = Mouse(usb_hid.devices)
keyboard = Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(keyboard)
# create controller object with QT Py wiring
psx = PS2Controller(
dat=board.A0,
cmd=board.A1,
att=board.A2,
clk=board.A3
)
led.fill(0x0010ee) # a nice PlayStation blue
circle_held = False
spam_speed = 0.001
buttonmap = {
("SELECT"): (0, Keycode.SPACEBAR),
("START"): (0, Keycode.X),
("UP"): (0, Keycode.W),
("DOWN"): (0, Keycode.S),
("RIGHT"): (0, Keycode.D),
("LEFT"): (0, Keycode.A),
("L3"): (0, Keycode.V),
("R3"): (0, Keycode.B),
("L2"): (0, Keycode.R),
("R2"): (0, Keycode.T),
("L1"): (0, Keycode.F),
("R1"): (0, Keycode.G),
("TRIANGLE"): (0, Keycode.I),
("CIRCLE"): (1, Mouse.LEFT_BUTTON), # for mouse clicks
("CROSS"): (0, Keycode.K),
("SQUARE"): (0, Keycode.L),
}
print("PlayStation Roulette Wheel controller")
while True:
events = psx.update()
if events:
print(events)
for event in events:
if buttonmap[event.name][0] == 0: # regular button
if event.pressed:
keyboard.press(buttonmap[event.name][1])
if event.released:
keyboard.release(buttonmap[event.name][1])
if buttonmap[event.name][0] == 1: # mouse button
if event.pressed:
circle_held = True
if event.released:
circle_held = False
if circle_held: # spam the mouse click
mouse.press(buttonmap["CIRCLE"][1])
mouse.release_all()
time.sleep(spam_speed)
How It Works
The code uses the ps2controller library to interpret the PlayStation controller messages, then the adafruit_hid library is used to send USB keyboard commands and mouse clicks.
import time import board import usb_hid import neopixel from adafruit_hid.keycode import Keycode from adafruit_hid.keyboard import Keyboard from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS from adafruit_hid.mouse import Mouse from ps2controller import PS2Controller
NeoPixel Setup
The on-board NeoPixel is set up next to be used as a status indicator -- amber during boot up and blue when a PSX/PS2 controller has been successfully connected.
led = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.1) led.fill(0x331000) # amber while we wait for controller to connect
mouse = Mouse(usb_hid.devices) keyboard = Keyboard(usb_hid.devices) layout = KeyboardLayoutUS(keyboard)
PS2Controller
We'll created an object called psx that is an instance of the PS2Controller with the necessary pin configuration to use it on the QT Py. After the controller is set up the NeoPixel is set to blue.
psx = PS2Controller(
dat=board.A0,
cmd=board.A1,
att=board.A2,
clk=board.A3
)
led.fill(0x0010ee)
Variables
A boolean variable called circle_held is used to hold the state of the spinner -- while the O (circle) button is held (meaning the spinner is spinning) this variable is True and the rapid mouse button spamming will occur. When the spinner stops the mouse button stops spamming.
The spam_speed variable is used to adjust the mouse-click rate.
circle_held = False spam_speed = 0.001
Buttonmap
The buttonmap dictionary is defined to map the controller button names to USB HID keycodes or mouse-clicks. The first item in each key is a 0 or 1 to indicate if keycodes or mouse events are sent, while the second item is the specific keycode or mouse event.
buttonmap = {
("SELECT"): (0, Keycode.SPACEBAR),
("START"): (0, Keycode.X),
("UP"): (0, Keycode.W),
("DOWN"): (0, Keycode.S),
("RIGHT"): (0, Keycode.D),
("LEFT"): (0, Keycode.A),
("L3"): (0, Keycode.V),
("R3"): (0, Keycode.B),
("L2"): (0, Keycode.R),
("R2"): (0, Keycode.T),
("L1"): (0, Keycode.F),
("R1"): (0, Keycode.G),
("TRIANGLE"): (0, Keycode.I),
("CIRCLE"): (1, Mouse.LEFT_BUTTON),
("CROSS"): (0, Keycode.K),
("SQUARE"): (0, Keycode.L),
}
Main Loop
The main loop of the program first checks for any PSX controller events with psx.update().
Each controller event is checked to see if it should trigger a keycode or mouse event.
The events are then sent as per the buttonmap mappings.
Keycodes are pressed and then released along with their corresponding buttons, while the O key that the spinner sends is treated specially -- it rapidly clicks the left mouse button, making it perfect for clicker games!
while True:
events = psx.update()
if events:
print(events)
for event in events:
if buttonmap[event.name][0] == 0: # regular button
if event.pressed:
keyboard.press(buttonmap[event.name][1])
if event.released:
keyboard.release(buttonmap[event.name][1])
if buttonmap[event.name][0] == 1: # mouse button
if event.pressed:
circle_held = True
if event.released:
circle_held = False
if circle_held: # spam the mouse click
mouse.press(buttonmap["CIRCLE"][1])
mouse.release_all()
time.sleep(spam_speed)
Page last edited January 21, 2025
Text editor powered by tinymce.