Last active
February 28, 2024 17:51
-
-
Save PaulskPt/60deb9dd3761b2a6486cb4ef40c25070 to your computer and use it in GitHub Desktop.
gamepadqt for CircuitPython
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Platform: Pimoroni Pico DV Demo Base (PIM588) + Pimoroni Pico Lipo board 16MB (PIM560) (RP2040) | |
Date: 2021-08-24 | |
System flashed: adafruit-circuitpython-pimoroni_picolipo_16mb-en_GB-7.0.0-beta.0.uf2 | |
This script is to test the functionality of the 'board.USER_SW' which I, @paulsk, happened to discover | |
during a REPL session, after issuing 'import board' followed by 'dir(board)'. | |
In the list of resulting functions/classes was: 'USER_SW'. | |
The command: 'dir (board.USER_SW)' results in: ['__class__']. | |
The test below shows that board.USER_SW can be defined as an OUTPUT. This test also proves that the value can be set. And the value can be read. | |
See: circuitpython/ports/raspberrypi/boards/pimoroni_picolipo_16mb/mpconfigboard.h for the following 4 defines | |
# MICROPY_HW_BOARD_NAME "Pimoroni Pico Lipo (16 MB)" | |
# MICROPY_HW_MCU_NAME "rp2040" | |
# DEFAULT_I2C_BUS_SCL (&pin_GPIO5) | |
# DEFAULT_I2C_BUS_SDA (&pin_GPIO4) | |
Update 2024-02-12. | |
Modifications to connect to the Pico Lipo, via I2C, an Adafruit Gamepad QT board and use its buttons X, Y, A and B. | |
Firmware flashed into Pico Lipo 16MB: | |
Adafruit CircuitPython 9.0.0-beta.0 on 2024-01-27; Pimoroni Pico dv Base with rp2040 | |
Note: board.USERSW does not exist any longer: now it are: board.BUTTON_A, board.BUTTON_B and board.BUTTON_C. | |
Update 2024-02-28. | |
Today received from Pimoroni a replacement board: Pico DV Demo Base for a faulty board (USERSW Button C not working). | |
Modified the script to use all buttons of the Adafruit Gamepad QT and added functionality to perform a soft reset | |
when pressing the Gamepad QT Start button. | |
""" | |
import board | |
import digitalio | |
import time | |
import sys | |
import busio | |
from micropython import const | |
from adafruit_seesaw.seesaw import Seesaw | |
import gc | |
#import microcontroller | |
import supervisor | |
id = board.board_id | |
print(f"board ID: {id}") | |
my_debug = False | |
selected_group = 0 | |
nr_groups = 2 | |
msg1_shown = False | |
last_x = last_y = 0 | |
BUTTON_SELECT = const(0) | |
BUTTON_B = const(1) | |
BUTTON_Y = const(2) | |
BUTTON_A = const(5) | |
BUTTON_X = const(6) | |
BUTTON_START = const(16) | |
button_mask = const( | |
( 1 << BUTTON_X) # = 1 << 6 = 64 = 0x00040 | |
| (1 << BUTTON_Y) # = 1 << 2 = 4 = 0x00004 | |
| (1 << BUTTON_A) # = 1 << 5 = 32 = 0x00020 | |
| (1 << BUTTON_B) # = 1 << 1 = 2 = 0x00002 | |
| (1 << BUTTON_SELECT) # = 1 << 0 = 1 = 0x00001 | |
| (1 << BUTTON_START) # = 1 << 16 = 65536 = 0x10000 | |
) # = 65639 dec or 0x10067 | |
SCL = board.GP5 | |
SDA = board.GP4 | |
try: | |
i2c = busio.I2C(SCL, SDA) | |
seesaw = Seesaw(i2c, addr=0x50) | |
seesaw.pin_mode_bulk(button_mask, seesaw.INPUT_PULLUP) | |
qt_btns_present = True | |
print(f"global: seesaw.chip_id= {hex(seesaw.chip_id)}") | |
except Exception as e: | |
print(f"global(): Error while creating an instance seesaw class: {e}") | |
qt_btns_present = False | |
raise | |
""" | |
+--------+--------+----------------------------+ | |
| BUTTON | VALUE | Value in CPY when pressed | | |
+--------+--------+----------------------------+ | |
| SELECT | 1 | 65638 | | |
+--------+--------+----------------------------+ | |
| B | 2 | 65637 | | |
+--------+--------+----------------------------+ | |
| Y | 4 | 65635 | | |
+--------+--------+----------------------------+ | |
| ? | 8 | ? | | |
+--------+--------+----------------------------+ | |
| ? | 16 | ? | | |
+--------+--------+----------------------------+ | |
| A | 32 | 65607 | | |
+--------+--------+----------------------------+ | |
| X | 64 | 65637 ! | |
+--------+--------+----------------------------+ | |
| START | 65636 | 103 | | |
+--------+--------+----------------------------+ | |
""" | |
btn_a = digitalio.DigitalInOut(board.BUTTON_A) # was: (GP14) | |
btn_a.direction = digitalio.Direction.INPUT | |
btn_a.pull = digitalio.Pull.DOWN | |
btn_b = digitalio.DigitalInOut(board.BUTTON_B) # GP15) | |
btn_b.direction = digitalio.Direction.INPUT | |
btn_b.pull = digitalio.Pull.DOWN | |
btn_c = digitalio.DigitalInOut(board.BUTTON_C) # GP16) | |
btn_c.direction = digitalio.Direction.INPUT | |
btn_c.pull = digitalio.Pull.DOWN | |
on_off_led = digitalio.DigitalInOut(board.GP23) | |
on_off_led.direction = digitalio.Direction.OUTPUT | |
led = digitalio.DigitalInOut(board.LED) # LED = GPIO25 (see same file: pins.c) | |
led.direction = digitalio.Direction.OUTPUT | |
led_state = 0 | |
# | |
# blink_led | |
# | |
def blink_led(nr_times, bi_led=False, blink_slow=True): | |
global led_state | |
curr_state = led_state | |
# print(f"blink_led(): nr_times: {nr_times}, bi_led= {bi_led}, blink_slow= {blink_slow}") | |
if nr_times is None: | |
nr_times = 1 | |
if blink_slow: | |
delay = 0.5 # was 0.5 | |
else: | |
delay = 0.1 # was 0.1 | |
if curr_state == 1: | |
if bi_led: | |
led.value = False # first switch the led off | |
on_off_led.value = False | |
time.sleep(delay) | |
for _ in range(nr_times): | |
if bi_led: | |
led.value = True | |
#conn_led.value = True | |
on_off_led.value = False | |
time.sleep(delay) | |
if bi_led: | |
led.value = False | |
on_off_led.value = False | |
time.sleep(delay) | |
if curr_state == 1: # if the led originally was on, switch it back on | |
if bi_led: | |
led.value = True | |
on_off_led.value = False | |
def test_msg(): | |
global msg1_shown | |
TAG = "gamepad_test(): " | |
if not msg1_shown: | |
msg1_shown = True | |
print(TAG+f"We\'re going to test the Gamepad QT and the {id}.\n\t\t"+ \ | |
f" Press any of the buttons (A, B or C) on the {id}") | |
print("\t\t or press any of the buttons (X, Y, A, B, Select or Start)\n\t\t"+ \ | |
f" or move the joystick on the Gamepad QT.\n\t\t" + \ | |
f" To soft rerset {id} press Gamepad QT button Start.\n\t\t"+ \ | |
f" To hard reboot {id} press the \'run\' button on the base.") | |
def ck_usr_answr(): | |
ret = False | |
while True: | |
answer = input("Are you sure? (Y/n)+<Enter>: ") | |
print(f"You answered: \'{answer}\'") | |
if answer == "Y" or answer == "y": | |
ret = True | |
break | |
elif answer == "N" or answer == "n": | |
break | |
return ret | |
def reboot(): | |
print("\nSoft reset...") | |
time.sleep(3) | |
# os.system('sudo shutdown -r now') # for Raspberry Pi boards | |
# microcontroller.reset() # for CircuitPython boards | |
supervisor.reload() # do a software reset for CircuitPython boards | |
def pr_btn_name(res): | |
btns = ["X", "Y", "A", "B", "Select", "Start"] | |
if res >= 0 and res < len(btns): | |
print("Button "+btns[res]+" pressed") | |
# Check for button presses on the Gamepad QT | |
def ck_qt_btns(): | |
global selected_group, led_state, msg1_shown, last_x, last_y | |
if not qt_btns_present: | |
return | |
TAG = "ck_qt_btns(): " | |
s_btn = "Gamepad QT button " | |
s_pre = " pressed" | |
nr_btns = 6 | |
res_x = res_y = res_a = res_b = res_sel = res_sta = -1 | |
elapsed_t = None | |
interval_t = 36 | |
interval_cnt = 0 | |
gc.collect() | |
time.sleep(0.2) | |
test_msg() | |
try: | |
x = 1023 - seesaw.analog_read(14) | |
y = 1023 - seesaw.analog_read(15) | |
except Exception as e: | |
if e.errno == 121: # Remote I/O Error | |
print(f"Error: {e}") | |
pass | |
if x >= last_x: | |
diff_x = abs(x - last_x) | |
else: | |
diff_x = abs(last_x - x) | |
if y >= last_y: | |
diff_y = abs(y - last_y) | |
else: | |
diff_y = abs(last_y - y) | |
if (diff_x > 3) or (diff_y > 3): | |
print(f"joystick: (x, y)= ({x}, {y})") | |
# print(TAG+f"diff_x= {diff_x}, diff_y= {diff_y}") | |
last_x = x | |
last_y = y | |
# Get the button presses, if any... | |
buttons = seesaw.digital_read_bulk(button_mask) | |
if my_debug: | |
print("\n"+TAG+f"buttons = {buttons}") | |
if buttons == 65639: | |
if my_debug: | |
print(TAG+f"Gamepad QT: no button pressed") | |
return | |
# time.sleep(0.5) | |
start_t = time.monotonic() | |
if buttons: | |
res = -1 | |
for _ in range(nr_btns): | |
if _ == 0: | |
bz = 1 << BUTTON_X | |
if not buttons & (bz): | |
res = _ | |
if res_x != res: | |
pr_btn_name(res) | |
res_x = res | |
break | |
if _ == 1: | |
bz = 1 << BUTTON_Y | |
if not buttons & (bz): | |
res = _ | |
if res_y != res: | |
pr_btn_name(res) | |
res_y = res | |
break | |
if _ == 2: | |
bz = 1 << BUTTON_A | |
if not buttons & (bz): | |
res = _ | |
if res_a != res: | |
pr_btn_name(res) | |
res_a = res | |
break | |
if _ == 3: | |
bz = 1 << BUTTON_B | |
if not buttons & (bz): | |
res = _ | |
if res_b != res: | |
pr_btn_name(res) | |
res_b = res | |
break | |
if _ == 4: | |
bz = 1 << BUTTON_SELECT | |
if not buttons & (bz): | |
res = _ | |
if res_sel != res: | |
pr_btn_name(res) | |
res_sel = res | |
break | |
if _ == 5: | |
bz = 1 << BUTTON_START | |
if not buttons & (bz): | |
res = _ | |
if res_sta != res: | |
pr_btn_name(res) | |
res_sta = res | |
print(f"About to soft reset the {id}") | |
if ck_usr_answr(): | |
reboot() # Reboot the board | |
else: | |
msg1_shown = False | |
res_sta = -2 | |
test_msg() | |
break | |
curr_t = time.monotonic() | |
elapsed_t = (curr_t - start_t) * 1000 | |
if elapsed_t >= interval_t: | |
interval_cnt += 1 | |
if interval_cnt >= 100: | |
interval_cnt = 0 | |
res_x = res_y = res_a = res_b = res_sel = res_sta = -2 | |
start_t = curr_t | |
time.sleep(0.01) | |
led.value = False | |
led_state = 0 | |
def ck_base_btns(): | |
delay = 0.008 | |
a_val = btn_a.value | |
time.sleep(delay) | |
b_val = btn_b.value | |
time.sleep(delay) | |
c_val = btn_c.value | |
time.sleep(delay) | |
usw = "Base user switch " | |
# print() | |
if a_val: | |
print(usw+f"A: {a_val}") | |
blink_led(3, True, False) | |
elif b_val: | |
print(usw+f"B: {b_val}") | |
blink_led(3, True, False) | |
elif c_val: | |
print(usw+f"C: {c_val}") | |
blink_led(3, True, False) | |
time.sleep(delay) | |
def main(): | |
TAG= "main(): " | |
loopnr = 0 | |
while True: | |
try: | |
loopnr += 1 | |
# print(f"\nLoopnr: {loopnr}") | |
if loopnr >= 100: | |
loopnr = 0 | |
ck_qt_btns() | |
ck_base_btns() | |
#time.sleep(0.5) | |
except KeyboardInterrupt: | |
print(TAG+"KeyboardInterrrupt. Exiting...") | |
break | |
if loopnr >= 1000: | |
break | |
sys.exit() | |
if __name__ == '__main__': | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2024-02-28 14h30 utc | |
Board: Pimoroni pico DV Demo Base + Pimoroni Pico Lipo 16MB | |
Attached hardware: Spakfun qwiic Multiport and an Adafruit Gamepad QT | |
IDE: mu-editor V 1.2.0 | |
>>> | |
soft reboot | |
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable. | |
code.py output: | |
board ID: pimoroni_pico_dv_base | |
global: seesaw.chip_id= 0x87 | |
gamepad_test(): We're going to test the Gamepad QT and the pimoroni_pico_dv_base. | |
Press any of the buttons (A, B or C) on the pimoroni_pico_dv_base | |
or press any of the buttons (X, Y, A, B, Select or Start) | |
or move the joystick on the Gamepad QT. | |
To soft rerset pimoroni_pico_dv_base press Gamepad QT button Start. | |
To hard reboot pimoroni_pico_dv_base press the 'run' button on the base. | |
joystick: (x, y)= (504, 511) | |
Button X pressed | |
Button Y pressed | |
Button A pressed | |
Button B pressed | |
Button Start pressed | |
About to soft reset the pimoroni_pico_dv_base | |
Are you sure? (Y/n)+<Enter>: n | |
You answered: 'n' | |
gamepad_test(): We're going to test the Gamepad QT and the pimoroni_pico_dv_base. | |
Press any of the buttons (A, B or C) on the pimoroni_pico_dv_base | |
or press any of the buttons (X, Y, A, B, Select or Start) | |
or move the joystick on the Gamepad QT. | |
To soft rerset pimoroni_pico_dv_base press Gamepad QT button Start. | |
To hard reboot pimoroni_pico_dv_base press the 'run' button on the base. | |
main(): KeyboardInterrrupt. Exiting... | |
Code done running. | |
Press any key to enter the REPL. Use CTRL-D to reload. | |
Adafruit CircuitPython 9.0.0-beta.0 on 2024-01-27; Pimoroni Pico dv Base with rp2040 | |
>>> [D | |
>>> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Image of the used: hardware