-
Notifications
You must be signed in to change notification settings - Fork 1.3k
USB HID Report - USB Busy #5461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Here is a post that I found some times ago: |
@ThomasAtBBTF Thanks! Why would you say is the thread related? Is the USB Busy error sent by the host? |
The Report ID is no longer set at runtime. that line should be: + (0x85, 0x0B) # Report ID (11 decimal) Do not put the |
@dhalbert Thanks, just tried it but I get the same error as before (USB Busy). The |
@JaggerJo The devices are not set up until just before This is explained in https://learn.adafruit.com/customizing-usb-devices-in-circuitpython/usb-setup-timing . |
@dhalbert works. Thank you! |
Could you please describe a little bit in more detail what "works" ? |
Sure, I'm simulating a Mouse over USB. Tried absolute positioning on a linux host. Did not test it on windows/macOS yet. |
I also got "it working" on a ItsyBitsy nRF52840 as Bluetooth HID device. But only on MAC OS ! Under Windows and under IOS absolute HID mouse reports do not seem to be accepted. I contacted someone at Apple with a feature request to make it working also under IOS. |
I tested some "Digitizer"-usage absolute positioning HID devices a long time ago, considering whether to include them in the standard devices we provided. I could not find a descriptor that would work on all three major operating systems, so I omitted it. At that time I did include a gamepad, but even it did not work on all three. That's why I dropped it when I added custom HID device capability. |
Just tested it on MacOS (Big Sur) and absolute mouse positioning works fine. |
Do you mind posting your code example? Thanks |
This should move you mouse to an absolute position once. # boot.py
import time
import usb_hid
# https://stackoverflow.com/questions/36750287/two-byte-report-count-for-hid-report-descriptor
absolute_mouse = usb_hid.Device(
report_descriptor=bytes(
# Absolute mouse
(0x05, 0x01) # Usage Page (Generic Desktop)
+ (0x09, 0x02) # Usage (Mouse)
+ (0xA1, 0x01) # Collection (Application)
+ (0x09, 0x01) # Usage (Pointer)
+ (0xA1, 0x00) # Collection (Physical)
+ (0x85, 0x0B) # Report ID [11 is SET at RUNTIME]
# Buttons
+ (0x05, 0x09) # Usage Page (Button)
+ (0x19, 0x01) # Usage Minimum (0x01)
+ (0x29, 0x05) # Usage Maximum (0x05)
+ (0x15, 0x00) # Logical Minimum (0)
+ (0x25, 0x01) # Logical Maximum (1)
+ (0x95, 0x05) # Report Count (5)
+ (0x75, 0x01) # Report Size (1)
+ (0x81, 0x02) # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ (0x75, 0x03) # Report Size (3)
+ (0x95, 0x01) # Report Count (1)
+ (0x81, 0x03) # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
# Movement
+ (0x05, 0x01) # Usage Page (Generic Desktop Ctrls)
+ (0x09, 0x30) # Usage (X)
+ (0x09, 0x31) # Usage (Y)
+ (0x15, 0x00) # LOGICAL_MINIMUM (0) ; Note: 0x15 = 1 Byte; 0x16 = 2 Byte; 0x17 = 4 Byte
+ (0x26, 0xFF, 0x7F) # LOGICAL_MAXIMUM (32767) ; Note: 0x25 = 1 Byte, 0x26 = 2 Byte; 0x27 = 4 Byte Report
# + (0x35, 0x00) # Physical Minimum (0)
# + (0x46, 0xff, 0x7f) # Physical Maximum (32767)
+ (0x75, 0x10) # REPORT_SIZE (16)
+ (0x95, 0x02) # REPORT_COUNT (2)
+ (0x81, 0x02) # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
# Wheel
+ (0x09, 0x38) # Usage (Wheel)
+ (0x15, 0x81) # Logical Minimum (-127)
+ (0x25, 0x7F) # Logical Maximum (127)
# + (0x35, 0x81) # Physical Minimum (same as logical)
# + (0x45, 0x7f) # Physical Maximum (same as logical)
+ (0x75, 0x08) # Report Size (8)
+ (0x95, 0x01) # Report Count (1)
+ (0x81, 0x06) # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+ (0xC0,) # End Collection
+ (0xC0,) # End Collection
),
usage_page=1,
usage=2,
report_ids=(11,),
in_report_lengths=(6,),
out_report_lengths=(0,)
)
# usb_hid.enable((usb_hid.Device.KEYBOARD, absolute_mouse))
usb_hid.enable((absolute_mouse,)) # Make sure to have double brackets and comma\ import usb_hid
import time
def find_device(devices, *, usage_page, usage):
"""Search through the provided list of devices to find the one with the matching usage_page and
usage."""
if hasattr(devices, "send_report"):
devices = [devices]
for device in devices:
if (
device.usage_page == usage_page
and device.usage == usage
and hasattr(device, "send_report")
):
return device
raise ValueError("Could not find matching HID device.")
class Mouse:
"""Send USB HID mouse reports."""
LEFT_BUTTON = 1
"""Left mouse button."""
RIGHT_BUTTON = 2
"""Right mouse button."""
MIDDLE_BUTTON = 4
"""Middle mouse button."""
def __init__(self, devices):
"""Create a Mouse object that will send USB mouse HID reports.
Devices can be a list of devices that includes a keyboard device or a keyboard device
itself. A device is any object that implements ``send_report()``, ``usage_page`` and
``usage``.
"""
self._mouse_device = find_device(devices, usage_page=0x1, usage=0x02)
#print(dir(devices))
# Reuse this bytearray to send mouse reports.
# report[0] buttons pressed (LEFT, MIDDLE, RIGHT)
# report[1] x1 movement
# report[2] x2 movement
# report[3] y1 movement
# report[4] y2 movement
# report[5] wheel movement
self.report = bytearray(6)
# Do a no-op to test if HID device is ready.
# If not, wait a bit and try once more.
try:
self._send_no_move()
except OSError:
time.sleep(1)
self._send_no_move()
def press(self, buttons):
"""Press the given mouse buttons.
:param buttons: a bitwise-or'd combination of ``LEFT_BUTTON``,
``MIDDLE_BUTTON``, and ``RIGHT_BUTTON``.
Examples::
# Press the left button.
m.press(Mouse.LEFT_BUTTON)
# Press the left and right buttons simultaneously.
m.press(Mouse.LEFT_BUTTON | Mouse.RIGHT_BUTTON)
"""
self.report[0] |= buttons
self._send_no_move()
def release(self, buttons):
"""Release the given mouse buttons.
:param buttons: a bitwise-or'd combination of ``LEFT_BUTTON``,
``MIDDLE_BUTTON``, and ``RIGHT_BUTTON``.
"""
self.report[0] &= ~buttons
self._send_no_move()
def release_all(self):
"""Release all the mouse buttons."""
self.report[0] = 0
self._send_no_move()
def click(self, buttons):
"""Press and release the given mouse buttons.
:param buttons: a bitwise-or'd combination of ``LEFT_BUTTON``,
``MIDDLE_BUTTON``, and ``RIGHT_BUTTON``.
Examples::
# Click the left button.
m.click(Mouse.LEFT_BUTTON)
# Double-click the left button.
m.click(Mouse.LEFT_BUTTON)
m.click(Mouse.LEFT_BUTTON)
"""
self.press(buttons)
self.release(buttons)
def move(self, x=0, y=0, wheel=0):
"""Move the mouse and turn the wheel as directed.
:param x: Set pointer on x axis. 32767 = 100% to the right
:param y: Set pointer on y axis. 32767 = 100% to the bottom
:param wheel: Rotate the wheel this amount. Negative is toward the user, positive
is away from the user. The scrolling effect depends on the host.
Examples::
# Move 100 to the left. Do not move up and down. Do not roll the scroll wheel.
m.move(1000, 3000, 0)
# Same, with keyword arguments.
m.move(x=1000, y=3000, wheel=0)
# Roll the mouse wheel away from the user.
m.move(wheel=1)
"""
# Wheel
while wheel != 0:
partial_wheel = self._limit(wheel)
print(wheel)
self.report[5] = partial_wheel & 0xFF
self._mouse_device.send_report(self.report)
wheel -= partial_wheel
# Coordinates
x = self._limit_coord(x)
y = self._limit_coord(y)
# HID reports use little endian
x1, x2 = (x & 0xFFFFFFFF).to_bytes(2, 'little')
y1, y2 = (y & 0xFFFFFFFF).to_bytes(2, 'little')
#print(x1)
#print(x2)
#print(y1)
#print(y2)
self.report[1] = x1
self.report[2] = x2
self.report[3] = y1
self.report[4] = y2
self._mouse_device.send_report(self.report)
def _send_no_move(self):
"""Send a button-only report."""
self.report[1] = 0
self.report[2] = 0
self.report[3] = 0
self.report[4] = 0
self._mouse_device.send_report(self.report)
@staticmethod
def _limit(dist):
return min(127, max(-127, dist))
@staticmethod
def _limit_coord(coord):
return min(32767, max(0, coord))
mouse = Mouse(usb_hid.devices)
# Note: Values are NOT pixels! 32767 = 100% (to right or to bottom)
mouse.move(x=20000, y=2000) |
Thanks a lot. This works for me under Windows 10 :-)
|
@JaggerJo + @hdo Does this work with CuircuitPython 8.x too? It would help me a lot if you could just point out the procedure roughly. |
As I am interested in this, I wrote a sample. https://github.com/jins-tkomoda/circuitpython-custom-hid-reportmap-sample |
CircuitPython version
Code/REPL
Behavior
Adafruit CircuitPython 7.0.0 on 2021-09-20; Raspberry Pi Pico with rp2040
Board ID:raspberry_pi_pico
boot.py output:
booting...
Traceback (most recent call last):
File "boot.py", line 209, in
File "boot.py", line 55, in init
File "boot.py", line 142, in _send_no_move
OSError: USB busy
Description
Copied the code from https://gist.github.com/bitboy85/cdcd0e7e04082db414b5f1d23ab09005 and slightly changed it. It appears like the
usb_hid.Device
function has changed and now takes different arguments.Additional information
The goal is to send absolute mouse HID events with a raspberry pi pico.
There might be something wrong with the Report, can't figure it out tho.
The text was updated successfully, but these errors were encountered: