Skip to content

Commit 1554ea4

Browse files
authored
Merge pull request #2976 from FoamyGuy/usb_host_gamepad
USB Host gamepad read data example
2 parents 547a331 + d8166ff commit 1554ea4

File tree

1 file changed

+138
-0
lines changed
  • Feather_RP2040_USB_Host/CircuitPython_GamePad_ReadData

1 file changed

+138
-0
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
import array
5+
import time
6+
import usb.core
7+
import adafruit_usb_host_descriptors
8+
9+
# Set to true to print detailed information about all devices found
10+
VERBOSE_SCAN = True
11+
12+
# indexes within the reports to ignore when determining equality.
13+
# some devices send alternating values with each report, this
14+
# allows to ignore those and focus only on bytes that are
15+
# affected by buttons. Value of [19] will ignore data at index 19.
16+
# Check your own output for values that change even when you don't
17+
# do anything on the controller and add their indexes here.
18+
IGNORE_INDEXES = []
19+
20+
DIR_IN = 0x80
21+
controller = None
22+
23+
if VERBOSE_SCAN:
24+
for device in usb.core.find(find_all=True):
25+
controller = device
26+
print("pid", hex(device.idProduct))
27+
print("vid", hex(device.idVendor))
28+
print("man", device.manufacturer)
29+
print("product", device.product)
30+
print("serial", device.serial_number)
31+
print("config[0]:")
32+
config_descriptor = adafruit_usb_host_descriptors.get_configuration_descriptor(
33+
device, 0
34+
)
35+
36+
i = 0
37+
while i < len(config_descriptor):
38+
descriptor_len = config_descriptor[i]
39+
descriptor_type = config_descriptor[i + 1]
40+
if descriptor_type == adafruit_usb_host_descriptors.DESC_CONFIGURATION:
41+
config_value = config_descriptor[i + 5]
42+
print(f" value {config_value:d}")
43+
elif descriptor_type == adafruit_usb_host_descriptors.DESC_INTERFACE:
44+
interface_number = config_descriptor[i + 2]
45+
interface_class = config_descriptor[i + 5]
46+
interface_subclass = config_descriptor[i + 6]
47+
print(f" interface[{interface_number:d}]")
48+
print(
49+
f" class {interface_class:02x} subclass {interface_subclass:02x}"
50+
)
51+
elif descriptor_type == adafruit_usb_host_descriptors.DESC_ENDPOINT:
52+
endpoint_address = config_descriptor[i + 2]
53+
if endpoint_address & DIR_IN:
54+
print(f" IN {endpoint_address:02x}")
55+
else:
56+
print(f" OUT {endpoint_address:02x}")
57+
i += descriptor_len
58+
59+
# get the first device found
60+
device = None
61+
while device is None:
62+
for d in usb.core.find(find_all=True):
63+
device = d
64+
break
65+
time.sleep(0.1)
66+
67+
# set configuration so we can read data from it
68+
device.set_configuration()
69+
print(f"configuration set for {device.manufacturer}, {device.product}, {device.serial_number}")
70+
71+
# Test to see if the kernel is using the device and detach it.
72+
if device.is_kernel_driver_active(0):
73+
device.detach_kernel_driver(0)
74+
75+
# buffer to hold 64 bytes
76+
buf = array.array("B", [0] * 64)
77+
78+
79+
def print_array(arr, max_index=None, fmt="hex"):
80+
"""
81+
Print the values of an array
82+
:param arr: The array to print
83+
:param max_index: The maximum index to print. None means print all.
84+
:param fmt: The format to use, either "hex" or "bin"
85+
:return: None
86+
"""
87+
out_str = ""
88+
if max_index is None or max_index >= len(arr):
89+
length = len(arr)
90+
else:
91+
length = max_index
92+
93+
for _ in range(length):
94+
if fmt == "hex":
95+
out_str += f"{int(arr[_]):02x} "
96+
elif fmt == "bin":
97+
out_str += f"{int(arr[_]):08b} "
98+
print(out_str)
99+
100+
101+
def reports_equal(report_a, report_b):
102+
"""
103+
Test if two reports are equal. Accounting for any IGNORE_INDEXES
104+
105+
:param report_a: First report data
106+
:param report_b: Second report data
107+
:return: True if the reports are equal, otherwise False.
108+
"""
109+
if report_a is None and report_b is not None or \
110+
report_b is None and report_a is not None:
111+
return False
112+
for _ in range(len(report_a)):
113+
if IGNORE_INDEXES is not None and _ not in IGNORE_INDEXES:
114+
if report_a[_] != report_b[_]:
115+
return False
116+
return True
117+
118+
119+
idle_state = None
120+
prev_state = None
121+
122+
while True:
123+
try:
124+
count = device.read(0x81, buf)
125+
# print(f"read size: {count}")
126+
except usb.core.USBTimeoutError:
127+
continue
128+
129+
if idle_state is None:
130+
idle_state = buf[:]
131+
print("Idle state:")
132+
print_array(idle_state, max_index=count)
133+
print()
134+
135+
if not reports_equal(buf, prev_state) and not reports_equal(buf, idle_state):
136+
print_array(buf, max_index=count)
137+
138+
prev_state = buf[:]

0 commit comments

Comments
 (0)