Open bmosley opened 6 years ago
I have just downloaded the automatic build and it seems to fix my problems on Windows. The keyboard can be used right after booting. Thanks a lot for the great work.
I just released my keyboard firmware on github: https://github.com/akaenner/picosplit And the building instructions for my Raspberry Pi Pico and CircuitPython based keyboard: https://kaenner.de/picosplit
@akaenner : On a quick look I did not see exception handlers. See PR #1929. Without, on my Windows PCs the software would not re-connect on re-boot or when the BIOS takes a few seconds before Windows is booting. If you have exception handlers, please point me to the positions, as I would like to update the PR with the actual exception types instead of catching all.
@DietmarSchwertberger : I added an exception handler which does a reset. Do you think this is enough? I tested it with a PC running Windows and waking up the computer by pressing a key still does not work. Here is my code: https://github.com/akaenner/picosplit/blob/main/Master/main.py
You really do not want to use a naked "except" like this, ever.
It will catch KeyboardInterrupt
, MemoryError
and even SystemExit
exceptions.
Please use except Exception:
if you really must do pokemon programming, but if possible, find out which exceptions you want to catch and only catch those.
@akaenner : a reset seems a bit hard and unnecessary.
I have tried with sending my Windows PC to Standby. The exception will only be triggered when the keyboard is trying to send the keypress. Then, Keyboard(usb_hid.devices)
will only succeed, once the PC is up and running again. During standby it will constantly fail.
I would guess that the wake-up is done by something outside the normal HID protocol.
I've tried with a Lenovo T410 Notebook. Sadly a keypress from the device completly locks up the BIOS. Even the normal keyboard is not working anymore. This happens at the time where the first keypress is send from the CP-device. Just connecting the CP-device does not trigger this issue.
For whatever reason it worked a single time out of twenty tries but i'm unable to reproduce.
Bus 004 Device 009: ID 239a:80f4 Raspberry Pi Pico Couldn't open device, some information will be missing Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x239a idProduct 0x80f4 bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0029 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 100mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 3 Human Interface Device bInterfaceSubClass 1 Boot Interface Subclass bInterfaceProtocol 1 Keyboard iInterface 4 HID Device Descriptor: bLength 9 bDescriptorType 33 bcdHID 1.11 bCountryCode 0 Not supported bNumDescriptors 1 bDescriptorType 34 Report wDescriptorLength 67 Report Descriptors: ** UNAVAILABLE ** Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 8 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 8
When comparing to http://www.usblyzer.com/reports/usb-properties/usb-keyboard.html there are some differences.
iInterface is 4 guess it should be 0 wMaxPacketSize is 64 but maybe it should be 8
i tried CP 7.1.0 beta 0 and beta 1
Thank you @dhalbert and everyone for everything, I couldn’t get CircuitPython’s HID to work with my KVM either, due to the needed boot keyboard protocol. Special shout-out to @obra and the boot keyboard descriptors in https://github.com/keyboardio/KeyboardioHID, I used it to modify (slightly) the boot descriptor dhalbert graciously provided for a related issue https://github.com/hathach/tinyusb/issues/1129#issuecomment-937756019, and now it works with my KVM!
CP 7.1.0 rc1 on a Feather Express M4 and using this boot.py
```Python import board import digitalio import storage import usb_cdc import usb_midi import usb_hid from time import sleep switch = digitalio.DigitalInOut(board.D5) switch.direction = digitalio.Direction.INPUT switch.pull = digitalio.Pull.UP sleep(0.1) BOOT_KEYBOARD_DESCRIPTOR=bytes(( 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) 0x09, 0x06, # Usage (Keyboard) 0xA1, 0x01, # Collection (Application) 0x05, 0x07, # Usage Page (Kbrd/Keypad) 0x19, 0xE0, # Usage Minimum (0xE0) 0x29, 0xE7, # Usage Maximum (0xE7) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x75, 0x01, # Report Size (1) 0x95, 0x08, # Report Count (8) 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x95, 0x01, # Report Count (1) 0x75, 0x08, # Report Size (8) 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x95, 0x05, # Report Count (3) 0x75, 0x01, # Report Size (1) 0x05, 0x08, # Usage Page (LEDs) 0x19, 0x01, # Usage Minimum (Num Lock) 0x29, 0x05, # Usage Maximum (Kana) 0x91, 0x02, # Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x95, 0x01, # Report Count (1) 0x75, 0x03, # Report Size (5) 0x91, 0x01, # Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x95, 0x06, # Report Count (6) 0x75, 0x08, # Report Size (8) 0x15, 0x00, # Logical Minimum (0) 0x26, 0xFF, # Logical Maximum (255) 0x05, 0x07, # Usage Page (Kbrd/Keypad) 0x19, 0x00, # Usage Minimum (0x00) 0x2A, 0xFF, # Usage Maximum (0xFF) 0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, )) boot_keyboard = usb_hid.Device( report_descriptor=BOOT_KEYBOARD_DESCRIPTOR, usage=0x06, usage_page=0x01, report_ids=(0,), in_report_lengths=(8,), out_report_lengths=(1,), ) maintenance_mode = not switch.value if not maintenance_mode: storage.disable_usb_drive() usb_cdc.disable() usb_midi.disable() usb_hid.enable((boot_keyboard,), boot_device=1 ) else: usb_hid.enable((usb_hid.Device.KEYBOARD,)) ```
@retrospecced Thanks for reporting this. I compared your descriptor above with the one I referenced, and here are the diffs. Your changes are on the right hand side. See the |
in the middle which marks the changed lines.
0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard) 0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application) 0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Kbrd/Keypad) 0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0xE0, // Usage Minimum (0xE0) 0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7) 0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0) 0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1) 0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1) 0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8) 0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Pr 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Pr
0x95, 0x01, // Report Count (1) 0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8) 0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear
0x95, 0x03, // Report Count (3) | 0x95, 0x05, // Report Count (3)
0x75, 0x01, // Report Size (1) 0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Page (LEDs) 0x05, 0x08, // Usage Page (LEDs)
0x19, 0x01, // Usage Minimum (Num Lock) 0x19, 0x01, // Usage Minimum (Num Lock)
0x29, 0x05, // Usage Maximum (Kana) 0x29, 0x05, // Usage Maximum (Kana)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,P 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,P
0x95, 0x01, // Report Count (1) 0x95, 0x01, // Report Count (1)
0x75, 0x05, // Report Size (5) | 0x75, 0x03, // Report Size (5)
0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linea 0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linea
0x95, 0x06, // Report Count (6) 0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8) 0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0) 0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255) | 0x26, 0xFF, // Logical Maximum (255)
0x05, 0x07, // Usage Page (Kbrd/Keypad) 0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0x00, // Usage Minimum (0x00) 0x19, 0x00, // Usage Minimum (0x00)
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF) | 0x2A, 0xFF, // Usage Maximum (0xFF)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear, 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,
0xC0, // End Collection 0xC0, // End Collection
In yours, there are 5 bits of padding and 3 bits of LED's (the comments are incorrect, the 3
and 5
should be reversed. Also, the two-byte 0xFF, 0x00
Maximums are replaced by the one-byte 0xFF
. According to http://eleccelerator.com/usbdescreqparser/, yours does not parse properly. Yet it seems to work, so I am not sure why.
0x26
(0b100110
) means, for instance, that it is a Logical Maximum, 2 bytes long. The value is signed, so 255 has to be represented as two bytes, otherwise the 0xFF
would be interpreted as -1
. The lower two bits are the length, so 10
means 2 two bytes. So if that's the descriptor you got from the other repo, it is slightly incorrect.
The actual descriptor does not necessarily matter, because when you set boot_device=1
, the supplied HID report descriptor is ignored, and a standard one is used instead. See page 59 in https://www.usb.org/sites/default/files/hid1_12.pdf for this standard descriptor.
Nevertheless, yours works, in your situation. If you change the things I mentioned, does it make any difference?
Thank you for the analysis on the descriptor, and your corrections to mine also work with my KVM, as you probably expected!
When I set boot_device=1, the KVM doesn't work unless I supply your descriptor as a parameter. I don't think it's ignoring it.
#usb_hid.enable((usb_hid.Device.KEYBOARD,), boot_device=1 )
# above line does not work with Belkin SOHO KVM
#usb_hid.enable((reference_keyboard,), boot_device=1 )
# above line works fine with Belkin SOHO KVM
```Python """CircuitPython Essentials Storage logging boot.py file""" import board import digitalio import storage import usb_cdc import usb_midi import usb_hid from time import sleep switch = digitalio.DigitalInOut(board.D5) switch.direction = digitalio.Direction.INPUT switch.pull = digitalio.Pull.UP sleep(0.1) REFERENCE_BOOT_KEYBOARD_DESCRIPTOR=bytes(( 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) 0x09, 0x06, # Usage (Keyboard) 0xA1, 0x01, # Collection (Application) 0x05, 0x07, # Usage Page (Kbrd/Keypad) 0x19, 0xE0, # Usage Minimum (0xE0) 0x29, 0xE7, # Usage Maximum (0xE7) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x75, 0x01, # Report Size (1) 0x95, 0x08, # Report Count (8) 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Pr 0x95, 0x01, # Report Count (1) 0x75, 0x08, # Report Size (8) 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear 0x95, 0x03, # Report Count (3) 0x75, 0x01, # Report Size (1) 0x05, 0x08, # Usage Page (LEDs) 0x19, 0x01, # Usage Minimum (Num Lock) 0x29, 0x05, # Usage Maximum (Kana) 0x91, 0x02, # Output (Data,Var,Abs,No Wrap,Linear,P 0x95, 0x01, # Report Count (1) 0x75, 0x05, # Report Size (5) 0x91, 0x01, # Output (Const,Array,Abs,No Wrap,Linea 0x95, 0x06, # Report Count (6) 0x75, 0x08, # Report Size (8) 0x15, 0x00, # Logical Minimum (0) 0x26, 0xFF, 0x00, # Logical Maximum (255) 0x05, 0x07, # Usage Page (Kbrd/Keypad) 0x19, 0x00, # Usage Minimum (0x00) 0x2A, 0xFF, 0x00, # Usage Maximum (0xFF) 0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear, 0xC0, # End Collection )) reference_keyboard = usb_hid.Device( report_descriptor=REFERENCE_BOOT_KEYBOARD_DESCRIPTOR, usage=0x06, usage_page=0x01, report_ids=(0,), in_report_lengths=(8,), out_report_lengths=(1,), ) maintenance_mode = not switch.value if not maintenance_mode: storage.disable_usb_drive() usb_cdc.disable() usb_midi.disable() usb_hid.enable((reference_keyboard,), boot_device=1 ) else: usb_hid.enable((usb_hid.Device.KEYBOARD,)) #usb_hid.enable((usb_hid.Device.KEYBOARD,), boot_device=1 ) # above line does not work with Belkin SOHO KVM #usb_hid.enable((reference_keyboard,), boot_device=1 ) # above line works fine with Belkin SOHO KVM """output from this boot.py Adafruit CircuitPython 7.1.0-rc.1 on 2021-12-25; Adafruit Feather M4 Express with samd51j19 Board ID:feather_m4_express boot.py output: """ ```
Again, thanks for your time, let me know if I can test anything else for you, as I am super-thankful of all of your time and effort spent on making CircuitPython work for us (especially for first-time Python programmers like myself!)
When I set boot_device=1, the KVM doesn't work unless I supply your descriptor as a parameter. I don't think it's ignoring it.
You do need a descriptor. How it works is a bit more complicated than one might expect. If you set boot_device=1
, then CircuitPython says it is capable of being a boot keyboard. But it doesn't do so unless the host asks for it to be a boot keyboard. If the host doesn't ask, then the supplied descriptor is used. If the host does ask, then the supplied descriptor is sent but ignored, and the host assumes the "standard" one.
Did some further testing and i think this is a weird one. I didn't change the descriptor. I just added usb_hid.get_boot_device() to check if the host requests boot mode -> Yes it does.
So i said it locks up bios or grub boot loader. I found this is only half true. After sending the first keypress it locks up. Nether from the cp-device nor from the original keyboard an input is accepted. EXCEPT: pressing and releasing the left CTRL-key does unlock it. The normal keyboard starts working again and even better, the cp-device now acts as a keyboard perfectly fine.
I do not use CTRL key anywhere in my script and i don't have any issues without cp-device so i don't think the error comes from the built-in keyboard.
The behaviour is like a permanently pressed ctrl button. CTRL left button. But only one time after pluggin it in. Pressing ALT + DEL while in bios reboots the pc.
But i can't find a part in my code where i send ctrl without releasing it.
So its fixable with kbd.release(Keycode.CONTROL) at the beginning of my script. But it doesn't explain why it is pressed in the first place. Even more confusing: https://github.com/adafruit/Adafruit_CircuitPython_HID/blob/main/adafruit_hid/keyboard.py does a release_all in the init part. Maybe something is going wrong there. I'll take a closer look tomorrow.
I'm using a KVM, but what you describe is the same behavior I was seeing before I started using the custom descriptor. https://w3c.github.io/uievents/tools/key-event-viewer.html would report left-control was always pressed before my code ran. Also, the KVM wouldn't forward any shift key presses to the host. So an "A" was always showing as control-a (lowercase), and so on.
Thank you for the analysis on the descriptor, and your corrections to mine also work with my KVM, as you probably expected!
When I set boot_device=1, the KVM doesn't work unless I supply your descriptor as a parameter. I don't think it's ignoring it.
#usb_hid.enable((usb_hid.Device.KEYBOARD,), boot_device=1 ) # above line does not work with Belkin SOHO KVM #usb_hid.enable((reference_keyboard,), boot_device=1 ) # above line works fine with Belkin SOHO KVM
boot.py (updated)
Again, thanks for your time, let me know if I can test anything else for you, as I am super-thankful of all of your time and effort spent on making CircuitPython work for us (especially for first-time Python programmers like myself!)
Just want to thank @retrospecced as using his boot.py resolved my issues whereby I couldn't access my ThinkPads BIOS or enter the grub menu on Arch Linux. Now I just need to see if it's possible to change my Pico manufacturer info as lsusb shows Adafruit Pico and I'd like to change it to something else.
@retrospecced thx for your effort. i tried your descriptor (and boot.py) but for some reason the device gets an exclamation mark in the device manager. So i was unable to test with the mentioned startech kvm switch.
@retrospecced's original descriptor has some errors. Did you try the left-hand one in the diff above.
Ah thx. I've thought the provided source does already contain the fix.
Meanwhile i found another descriptor and can confirm working with a startech kvm switch 👍 Other USB functions like storage, midi, and cdc needs to be turned off. I'm using Circuitpython 7.2.0
BOOT_KEYBOARD_DESCRIPTOR=bytes((
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x09, 0x06, # Usage (Keyboard)
0xA1, 0x01, # Collection (Application)
0x75, 0x01, # Report Size (1)
0x95, 0x08, # Report Count (8)
0x05, 0x07, # Usage Page (Kbrd/Keypad)
0x19, 0xE0, # Usage Minimum (0xE0, 224)
0x29, 0xE7, # Usage Maximum (0xE7, 231)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, # Report Count (1)
0x75, 0x08, # Report Size (8)
0x81, 0x03, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x05, # Report Count (5)
0x75, 0x01, # Report Size (1)
0x05, 0x08, # Usage Page (LEDs)
0x19, 0x01, # Usage Minimum (Num Lock)
0x29, 0x05, # Usage Maximum (Kana)
0x91, 0x02, # Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x01, # Report Count (1)
0x75, 0x03, # Report Size (3)
0x91, 0x03, # Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x06, # Report Count (6)
0x75, 0x08, # Report Size (8)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x68, # Logical Maximum (104)
0x05, 0x07, # Usage Page (Kbrd/Keypad)
0x19, 0x00, # Usage Minimum (0)
0x29, 0x68, # Usage Maximum (104)
0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,
))
I have a theory of the various keyboard descriptors discussed and the problem where the left Ctrl key seems to be pressed:
If the device sends reports prefixed with a 1-byte report ID of 0x01
, the host is not expecting the prefixed report ID byte, and it interprets it as the modifiers field, and a modifiers field of 0x01
corresponds to the left Ctrl key. By calling usb_hid.enable((reference_keyboard,)...)
with reference_keyboard
having a report ID of 0, that causes CircuitPython to pass a report ID of 0 to TinyUSB which prevents it from prefixing reports with a report ID, solving the issue. The implication is that perhaps the contents of the keyboard descriptor doesn't matter as much as using a report ID of 0. Conclusion: for boot keyboards, always use a report ID of 0 for simple BIOSes. Maybe something to consider for CircuitPython's defaults, so that users don't need to specify a custom keyboard descriptor.
I may have found a way to keep CDC and possibly other functions enabled while also using a boot keyboard:
Keep the other functions enabled, but make the boot keyboard use an endpoint address of EP 1 IN. By doing this with plain Arduino (no CircuitPython), I was able to use a boot keyboard and CDC with my old PC.
I started putting together a project with my Trinket M0 to use for switching boot volumes on macOS. My goal was to have it select a volume by keystrokes. However, it doesn't seem to be working.Working correctly in macOS once booted
Using the same code for HID keyboard: