-
-
Save bitboy85/cdcd0e7e04082db414b5f1d23ab09005 to your computer and use it in GitHub Desktop.
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, | |
in_report_lengths=(6,), # Number of bytes in the send report = 1 byte for buttons, 2 bytes for x, 2 bytes for y, 1 byte for wheel | |
out_report_lengths=(0,), | |
report_ids=(11,), | |
) | |
usb_hid.enable((usb_hid.Device.KEYBOARD,), boot_device=1) | |
usb_hid.enable((absolute_mouse,), boot_device=0) |
# SPDX-FileCopyrightText: 2017 Dan Halbert for Adafruit Industries | |
# | |
# SPDX-License-Identifier: MIT | |
""" | |
`adafruit_hid.mouse.Mouse` | |
==================================================== | |
* Author(s): Dan Halbert, Bitboy | |
""" | |
import time | |
from . import find_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)) |
import usb_hid | |
from adafruit_hid.mouse_abs import Mouse | |
mouse = Mouse(usb_hid.devices) | |
# Note: Values are NOT pixels! 32767 = 100% (to right or to bottom) | |
mouse.move(x=20000, y=2000) |
I guess that param was added in 7.0 final, i used a cp 7 beta if i remember correctly.
Currently i'm not sure what its good for.
https://circuitpython.readthedocs.io/en/latest/shared-bindings/usb_hid/index.html
https://learn.adafruit.com/custom-hid-devices-in-circuitpython/report-descriptors
Edit: Updated. The parameter from line 50 to 52 in boot.py changed from int to a list. Tested with cp 7.1 Beta
Well, as you can see in the comments of the source code, it is the exact same file :D
You're right. I got the older revision of your gist where the 'report_ids=(11,)' part was missing. Thanks for the hint :-)
I am a newbie. Just started on RP PICO. How to test this on RP pico ? Should i create a boot.py , mouse_abs.py , sample.py in thonny and run it in RP pico circuit python. I tried this and it doesn't work. Can you tell me how to do this ?
You need to add hid from https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases.
Then put mouse_abs.py in the same folder as the adafruit files.
boot.py is in the root of your pico. Sample.py is the one to start which moves the cursor to a specific position.
(edit: No error with Adafruit CircuitPython 7.3.0-beta.1 on 2022-04-07; Adafruit Macropad RP2040 with rp2040
)
I try to replicate the above with Feather RP2040 and CircuitPython 7.2.5 but I encounter an issue.
code.py output:
Traceback (most recent call last):
File "code.py", line 4, in <module>
File "/lib/adafruit_hid/mouse_abs.py", line 47, in __init__
File "/lib/adafruit_hid/mouse_abs.py", line 151, in _send_no_move
ValueError: Buffer incorrect size. Should be 4 bytes.
I believe it is a mismatch on the report size of the normal mouse.py and the new mouse_abs.py.
42 define self.report = bytearray(6)
47 is self._send_no_move()
151 is self._mouse_device.send_report(self.report)
I don't really know where that check and message is coming from ValueError: Buffer incorrect size. Should be 4 bytes.
and what may have change recently either in the firmware or in adafruit_hid
.
Don't have a feather. But in boot.py in line 50 it is specified, that the buffer is 6 bytes. So without further knowledge i would say the feather didn't process boot.py correctly.
I think I know what the problem was, why a change of CircuitPython solved it.
I may not have reset or powered off the board, and so there is a mismatch between what is in the file and what is accepted at runtime in code.py. I had the same error but with 4 when moving back to relative mouse descriptor...
Regards.
I would like to confirm that this "Buffer incorrect size." is occuring when you change the boot.py without powercycling the board.
You need the board to re-negotiate with the host at USB level... unplugging and replugging is the best way to garantee that.
Also I have used your code for this demo: https://github.com/dglaude/CircuitPython_udraw_Absolute_Mouse
A fork of my library is now accepted in CircuitPython Community Bundle and can be found here: https://github.com/Neradoc/CircuitPython_Absolute_Mouse
There is a nice very simple example code there.
Nice! Thanks for mentioning my example :)
Thanks for the example. Unfortunately i can't get it running with CurcuitPython 7.0 for PICO :-(