Skip to content

USB HID #1009

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

Closed
tannewt opened this issue Jul 10, 2018 · 41 comments
Closed

USB HID #1009

tannewt opened this issue Jul 10, 2018 · 41 comments
Assignees
Labels
Milestone

Comments

@tannewt
Copy link
Member

tannewt commented Jul 10, 2018

https://circuitpython.readthedocs.io/en/latest/shared-bindings/usb_hid/__init__.html#module-usb_hid

This is wired USB HID.

@tannewt tannewt added the nordic label Jul 10, 2018
@tannewt tannewt added this to the Bluetooth milestone Jul 10, 2018
@tannewt tannewt modified the milestones: 4.0.0 - Bluetooth, 4.0 Beta Jul 10, 2018
@dhalbert
Copy link
Collaborator

split this into Bluetooth and wired USB support?

@hathach hathach self-assigned this Jul 17, 2018
@tannewt
Copy link
Member Author

tannewt commented Jul 17, 2018

This is USB only. A BLE HID issue would be good too.

@hathach
Copy link
Member

hathach commented Jul 20, 2018

Descriptors to support are

  • keyboard
  • mouse
  • consumer key ( for volume up, down, brightness +/-)

@dhalbert
Copy link
Collaborator

We have code to generate the USB descriptors in https://github.com/adafruit/circuitpython/blob/master/ports/atmel-samd/tools/gen_usb_descriptor.py (most of which should maybe be moved out of the port-specific directory). For HID, that code calls the library code in https://github.com/adafruit/usb_descriptor(which is a submodule further up in the tree), including pre-made HID descriptors in https://github.com/adafruit/usb_descriptor/blob/master/adafruit_usb_descriptor/hid.py. Besides the ones you listed, we also support a generic gamepad. There is latent support for a touchscreen but it does not work on all OS platforms so I turned it off.

@hathach
Copy link
Member

hathach commented Jul 20, 2018

Ah thanks, I am also quite familiar with hid descriptor, so it is not really an issue. Almost forgot the gamepad, I will add it as well

@dhalbert
Copy link
Collaborator

I figured you were an expert on that 😀. Let me know if you think there's something that could be improved in the current descriptors.

@hathach
Copy link
Member

hathach commented Jul 20, 2018

It is not true, I just copy thing here and there and put them together :)) . The whole hid descriptor is too complicated for anyone to digest :) . I will try out the descriptor generator as well. Above descriptors are all that I worked with so far.

@dhalbert
Copy link
Collaborator

If you want, you can use the descriptors in https://github.com/adafruit/usb_descriptor/blob/master/adafruit_usb_descriptor/hid.py. They are all lumped in a single-endpoint composite HID device. I spent a while tuning them up and making sure they work on all platforms.

@hathach
Copy link
Member

hathach commented Jul 20, 2018

Ah yeah, I also plan to have an one composite endpoint for all the hid. Since most the advance host should be able to parse it.

@tannewt
Copy link
Member Author

tannewt commented Jul 24, 2018

Please make it easy to change the descriptor from C at runtime. I want to expose it from Python in the future.

@hathach
Copy link
Member

hathach commented Jul 24, 2018

@tannewt no problem, I will make the hid report descriptor changeable dynamically (you will need to update the configuration descriptor for new length as well). However, to reduce amount of work, and get this done quickly, we will still implement it with fixed descriptor at first. When needed, we will change it with a separated PR.

@tannewt
Copy link
Member Author

tannewt commented Jul 24, 2018 via email

@hathach
Copy link
Member

hathach commented Jul 24, 2018

@tannewt just out of curiousity, we want to change hid report dynamically is to switch on/offf keyboard, mouse, gamepad etc right ? I ask since I always want to have a high level of api as much as possible for tinyusb. Those can be achieved at some level with predefined hid descriptor, though the full hid parser is too much for a device to support.

PS for configuration descriptor it is already dynamic supported by tinyusb, it will reconfigure endpoints based on parsing config desc when enumeration :)

@hathach
Copy link
Member

hathach commented Jul 24, 2018

@tannewt
Copy link
Member Author

tannewt commented Jul 24, 2018

We do want that but we also want the ability to enable and disable other functions such as mass storage, CDC, HID, audio and video. See #231, #672 and #1015 for background.

In general for CircuitPython we want a minimal C API that isn't timing sensitive. For example, for HID we construct the reports in Python because we can load that code dynamically and its not timing sensitive. C: https://github.com/adafruit/circuitpython/blob/master/ports/atmel-samd/common-hal/usb_hid/Device.c Python: https://github.com/adafruit/Adafruit_CircuitPython_HID/blob/master/adafruit_hid/gamepad.py

@hathach
Copy link
Member

hathach commented Jul 25, 2018

@tannewt I see, class dynamic enable/disable is already support by tinyusb. python only need to feed the correct config descriptor. So it is not a problem at all.

For HID, to construct report in Python, I feel like we don't need to dynamically change the report descriptor. I could be wrong, let's me get something running first to see what it turns out to be.

@dhalbert
Copy link
Collaborator

For HID, we mean adding or dropping devices from the multiple report descriptor: https://github.com/adafruit/usb_descriptor/blob/master/adafruit_usb_descriptor/hid.py#L170. E.g. turn off keyboard, or allow a user to add another device with its own Report ID. As you mention, this affects the length field in the composite descriptor. (This stuff was clearly designed by two different committees: it would be a lot easier if the length were in the header of the HID Report Descriptor ☹️)

@hathach
Copy link
Member

hathach commented Jul 25, 2018

@dhalbert thanks, I already got the idea, the C level should only need to implement a generic HID where user need to supply report descriptor and report, which I will added to the stack. Indeed one of issue when changing the wReportLength in the HID descriptor of Config Desc. Though, the stack is flexible enough, so it is not an issue (at least on nrf52 port :D )

FYI, In my stack, originally it attempts to support higher API with both keyboard & mouse API. It is useful to user that is not familiar with HID and only want to use out of the box keyboard/mouse.
https://github.com/hathach/tinyusb/blob/develop/src/class/hid/hid_device.h#L65

@hathach
Copy link
Member

hathach commented Jul 25, 2018

I also try to support boot keyboard at stack C-level as well, so it is a bit confusing with option and things. Trying to nail it now :D

@dhalbert
Copy link
Collaborator

Yeah, I don't think we need boot keyboard support, but it could be moved out of the multi-device HID descriptor to its own endpoint. We have enough endpoints on the SAMD, at least.

@hathach
Copy link
Member

hathach commented Jul 25, 2018

It can still packed within the multiple report one interface. Normally OS that does not support report protocol will SET_PROTOCOL to boot mode, in which the device will behave as a single report keyboard. Though it could be non-consistent if python user e.g does not enable keyboard. I will try to get something running first, I will could tweak things later :D

Boot keyboard is only essential if we want device to work with a simpler host such us SAMD with host stack or bios etc..

PS: It is just me trying to support multiple variant of things at once, which cause some confusing setting :( .

@hathach
Copy link
Member

hathach commented Jul 25, 2018

@dhalbert @tannewt do you have an simple python code, that I could paste into the REPL to run a usb hid test e.g sending 'A' repeatedly with keyboard profile.

@dhalbert
Copy link
Collaborator

Note that you will not see this echoed in the REPL while it's running (if you just do >>> k.send(Keycode.A) you'll see it). But if you move the focus to another window you'll see 'a's being sent.

from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
import time
k = Keyboard()
while True:
    k.send(Keycode.A)
    time.sleep(1.0)

@hathach
Copy link
Member

hathach commented Jul 25, 2018

Superb, thanks. Sorry for silly question, I haven't used cp much :(

@dhalbert
Copy link
Collaborator

You're welcome. https://circuitpython.readthedocs.io/projects/hid/en/latest/index.html has examples for other devices as well.

@hathach
Copy link
Member

hathach commented Jul 25, 2018

👍 👍

@hathach
Copy link
Member

hathach commented Jul 26, 2018

btw, @tannewt do you decide the API to change the usb descriptor dynamically (enable/disable classes). If it is decided already, I will make it into the next PR as well.

@tannewt
Copy link
Member Author

tannewt commented Jul 26, 2018

Not sure what it will be. Static is ok for now. Thanks!

@hathach
Copy link
Member

hathach commented Jul 26, 2018

@dhalbert the readthedoc link should list Adafruit_CircuitPython_HID as dependency instead of the core repo right ?

@hathach
Copy link
Member

hathach commented Jul 26, 2018

never mind, I think you mean the dependency of the HID library. Sorry, misunderstand there.

@hathach
Copy link
Member

hathach commented Jul 31, 2018

@dhalbert @tannewt I would like to light up an LED when receiving SET REPORT request from host e.g CAPSLOCK. But couldn't find where to add it, could you please give me a hint ?

@hathach
Copy link
Member

hathach commented Jul 31, 2018

keyboard and mouse works quite well, cleaning it up for full list of devices support

@dhalbert
Copy link
Collaborator

@dhalbert @tannewt I would like to light up an LED when receiving SET REPORT request from host e.g CAPSLOCK. But couldn't find where to add it, could you please give me a hint ?

We haven't provided a way to do that reverse-direction dataflow at all yet in the HID module, so it would be noticeable work and an API addition. Fine to skip it for now. You could open a long-term issue

@hathach
Copy link
Member

hathach commented Jul 31, 2018

Ah, thanks. Since I don't know how to organize the cp API and my obj things. I will just turn on/off an LED0 for that, does it sound OK, if not I will leave it as stub.

@dhalbert
Copy link
Collaborator

A stub is fine too, since we don't want to reserve an LED just for this. It's more likely LED state would be sent on to some peripheral, (e.g. a pin) for someone who's building a keyboard. Right now CircuitPython doesn't have an of handling callbacks which we might want for this case (since SET REPORT can arrive spontaneously).

@hathach
Copy link
Member

hathach commented Jul 31, 2018

@dhalbert everything seems works, except for the gamepad, I got this error. I have no idea about this, am I missing something

Adafruit CircuitPython 4.0.0-alpha-40-g2c85f42-dirty on 2018-07-31; PCA10056 with nRF52840                                                                                                                                                                                 
                                                                                                                                                                                                                                                                           
>>> from adafruit_hid.gamepad import Gamepad                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                           
>>> gp = Gamepad()                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                           
Traceback (most recent call last):                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                           
  File "<stdin>", line 1, in <module>                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                           
  File "adafruit_hid/gamepad.py", line 89, in __init__                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                           
  File "adafruit_hid/gamepad.py", line 86, in __init__                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                           
  File "adafruit_hid/gamepad.py", line 149, in reset_all                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                           
  File "adafruit_hid/gamepad.py", line 163, in _send                                                                                                                                                                                                                       
                                                                                                                                                                                                                                                                           
TypeError: 'bytearray' object does not support item assignment                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                           

@hathach
Copy link
Member

hathach commented Jul 31, 2018

btw @tannewt this line should be declaration only, and is missing extern right ?

https://github.com/adafruit/circuitpython/blob/master/ports/atmel-samd/common-hal/usb_hid/Device.h#L48

I am basing nrf52 port on samd, just to make sure I am not missing something.

@dhalbert
Copy link
Collaborator

TypeError: 'bytearray' object does not support item assignment

In mpconfigport.h, try changing these from 0 to 1:

#define MICROPY_PY_ARRAY_SLICE_ASSIGN            (0)
...
#define MICROPY_PY_BUILTINS_SLICE_ATTRS (0)

There are probably some other changes to the feature on/off flags that I should make match up with atmel-samd and esp8266. I'll do some diff'ing.

@hathach
Copy link
Member

hathach commented Jul 31, 2018

Superb !! It is all working now, thanks. I will do some clean up and submit an PR for review

@tannewt
Copy link
Member Author

tannewt commented Jul 31, 2018

@hathach Whatever works. :-) Feel free to change it.

@dhalbert
Copy link
Collaborator

Fixed by #1074.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants