-
Notifications
You must be signed in to change notification settings - Fork 66
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
Expose LEDs via USB #322
Comments
I see where you're coming from, ie. it would be great to have full control over each led and their individual brightness over the same API. I agree on that. This seems like a good spec to me. I'm willing to work on an implementation, though I'll note I don't have backlit switches. There are a couple of things that should be considered before settling on this spec. One is a change I'm suggesting, the others are just questions that probably have easy answers. Message splittingA single interrupt message is limited to at most 64 bytes and the minimum interval between messages is 1ms. The command type and The character display has 3*14 = 42 segments. The left half has 31 keys, the right one 33, totaling 64. In practice, you always need two messages to set the character display and three messages to set key backlight leds. My experience in frontend development and communication protocol design says this is bad. I'm a newbie with USB so I'm not sure what could go wrong, but my instinct says it's best to avoid split messages and millisecond delays in the middle of a logical change. My proposed solution is to remove the Other ideas to overcome this issue (that I don't consider as worthwhile) are
Kernel driverThe caps lock LED is usually driven by a kernel driver, with a Set_Report call on the keyboard hid device. Should these calls be left unhandled when the user has overridden the capslock led control? Perhaps some other similar questions arise, eg. should the Agent take over control of the Agent LED? Partial overridesThe proposed spec seems to suggest each individual LED has its own override flag. However, overriding individual LEDs in the character display doesn't make much sense to me. I'd only treat the mod/fn/mouse/capslock/agent/adaptivemode LEDs individually, and the character display and backlights as groups. But maybe there's a good argument for individual LEDs? Returning to non-overridden modeAs the overrides can be set to off, the firmware should know both the firmware-specified value and the user-specified value for each led. Otherwise setting an LED override off would leave it in an inconsistent state. Am I right? |
Can you solder? If so, we can send you a backlight upgrade kit, and you'll be able to upgrade your UHK 60 v1. There are a number of ways to implement USB data transfer and each has its pros and cons. As far as I know, the current implementation has no match when it comes to the lack of driver installation and the general lack of issues, so I don't plan to move away from 64 byte USB interrupt communication packages. I don't foresee the LED display getting changed often via this USB API, and even if it gets changed, it will only take 2 ms. I much rather expect folks to play with per-key backlighting, and RGB LEDs will take far more bandwidth for fancy animations given that one RGB LED contains 3 separate LEDs. Based on the above, I don't think it's worth introducing a dedicated slot for the LED display. I don't suggest to make LEDs individually overridable as I don't think it'd make much practical sense. Treat every one of the 6 fields of the struct I suggested as boolean values. When the override mode is disabled, the firmware should overwrite |
I can, but I'm not sure I'm very eager to. Let's discuss this later when we have something else working.
I agree about staying with interrupt communication, but I'd still like to nitpick a bit. For basic HID, that's true. But for interrupt messages we already need custom software. Yes, it can be userspace software given something like libusb (which actually just uses OS USB drivers under the hood). AFAICT Agent uses node-hid which uses hidapi which uses libusb which uses the USB drivers provided by the OS. So by using anything that libusb supports we're not really adding any dependencies.
I'm already running scrolling texts on the led display so updating once every 100ms is IMHO reasonable. Of course 2ms is not that much, but considering the same bandwidth is used for everything here, hogging it doesn't sound like a good idea. I'm not sure what you mean by slot. I thought the slots would be the same as the six fields of Also, I hadn't considered RGB LEDs at all. That's a lot of bandwidth if every key has three 8-bit brightness indicators. So if there are 3 LEDs per key * 64 keys = 192 backlight leds and you can set 31 in a single interrupt packet, just setting backlight on/off takes 7 interrupt packets. I'm still of the opinion that logical state changes should fit in a single packet. I'll try to think of a protocol and suggest that later. |
I don't think many will use scrolling text with such a fast speed. Regarding slots, see https:/UltimateHackingKeyboard/firmware/blob/master/right/src/slot.h What do you mean by "logical state changes"? |
By logical state changes I mean what happens "at once". It could be setting the backlight color for the whole keyboard. It could be setting a specific text in the display. It could be a single animation frame in backlight animation. These can mostly fit in a single message, and I'll write a protocol proposal soon. |
I'm sorry, but I really don't want to implement additional protocol commands, and I'd rather keep the protocol as simple as possible. I consider implementing an arbitrary set of specialized commands premature optimization at this point. |
SetLedValues protocol proposalFirst, some rationale. I consider splitting messages undesirable if it is not necessary. At the same time, a protocol should be easy to understand. It should also fit use cases as well as possible. If it's necessary to make some use cases difficult, they should be the rare ones. Expected use cases
The general ideaNow, consider backlight animations which are the most difficult and bandwidth intensive. Usually an animation would not require every RGB LED to have a different value. Rather, you could have different kinds of waves going around the keyboard. Typically you'd have maybe up to a dozen different colors for the 64 keys, but often just a few. Therefore we can hugely optimize by setting multiple keys to the same color at once rather than each LED separately. Also, note that keys are the logical single unit, not the three individual LEDs in the RGB LED package. Almost every time the LED value is changed, all color components are changed. A SetLedValues payloadFirst, we define an enum called
Now, the interrupt message for
And the structure of
On the implementation of GetLedValuesThe values for the RGB LEDs do not fit in a single message, no matter what. However, we can fit one color channel (64 bytes) in a single message, so requesting them a channel at a time would make sense. Ie. add three enum values CommentsVoila, we have a simple format (or "protocol"; sorry for the confusion) which we can use to set all the LEDs of a single type ( For purposes other than setting the RGB LEDs this protocol wastes a lot of bytes, but that doesn't matter because a single USB interrupt message is used anyway. |
Actually, it might be better to move Anyway, looking forward for comments on the general concept. |
I can see the value of your suggested protocol, assuming that USB bandwidth is scarce, but it's plentiful. I2C bandwidth is scarce. USB bandwidth is 1,000 * 64 = 64,000 bytes per sec. I2C bandwidth is 100,000 / 8 = 12,500 bytes per sec. The UHK essentially protocol-translates SetLedValues from USB to I2C, and the latter is the bottleneck. The USB protocol isn't used for bandwidth-intensive purposes other than LEDs, so optimizing it merely adds to its complexity. |
That just means we might need a better backlight protocol over I2C (or a faster I2C). If the I2C communication sends two bytes per LED and is used to set 33 RGB LEDs = 99 LEDs, it takes 2B*99 / 12,500 B/s ~= 16ms to complete. That will look weird to the human eye. |
The I2C protocol of the LED drivers is fixed in silicon, and the baud rate cannot be raised by much given the parasitic capacitance of the I2C bus. These are hardware constraints. I think the baud rate is sufficient for reasonable smooth animations. |
Oh, you mean the I2C for IS31FL3731, I thought we're talking about the communication between the right and the left half (I hadn't really looked at the hardware side of the UHK before). The chip seems to support 8 frames in memory that can be switched to or run as an animation. The best animation system would be built around this, but that sounds like a lot of work. |
WRT the ( |
We'll be using 3 different ISSI ICs in our current and upcoming keyboards and modules, and most of them don't support animations, so we can't rely on the animations feature. Interrupt communication has always been reliable, and no dropped messages have been noticed. |
How's the progress on this? I'm a bit late to the party, but it's definitely worth pointing out that there is already a standardized HID usage definition for RGB LED control on things such as keyboards. Check HID usage page 59 Lighting and Illumination. |
There's no progress on this issue yet. If there is any progress, we'll update this issue. Thanks for pointing to the HID page, but I doubt any keyboard uses it, and it'd complicate our USB implementation. |
Good to know! As you can guess from the discussion I lost motivation to work on this. It shouldn't be very difficult to implement but I don't have LEDs on my hardware and don't really care about them, and the way LED control is going to be implemented isn't very good for my use case (animating the segment display). My old closed PR might give hints on how to implement the proper LED control discussed here. |
It's been more than a year, so I hope it's okay to ask if there's some update on this topic, we also have multiple other LED color related tickets that occasionally get updated. I recently got a v2 keyboard ... and I thought the RGB lighting was one of the main new features. But we're mostly stuck with the default coloring, unless one wants to start playing around with other firmwares. I would love to be able to dynamically change colors via the command line, for example to indicate the end of long running processes, or have a visual notification of a meeting about to start. And after work some relaxing animated sea of color, pulsing to the sound of the ambient music. 🚥 |
@Psychopompous I'm as enthusiastic as you about the mentioned possibilities, but we plan to implement per-key LED color settings via Agent first. Chances are we'll also be able to implement this feature in 2023. |
This issue contains a specification for exposing LED states (both of the LED display and the per-key LEDs) via getter and setter USB commands, which is useful for displaying custom information and animations.
First up, let's add the
UsbVariable_LedOverride
struct and expose it via the getter/setter interface ofright/src/usb_commands/usb_command_{get,set}_variable.[ch]
. The following byte fields should be featured:By default, these fields are set to 0, meaning that the UHK firmware is in control of all the LEDs. By setting a field to 1, the relevant LEDs won't be controlled by the UHK firmware anymore, and the user will be able to override their value.
Let's add the
GetLedValues
USB command. The input parameters of this command areslotId
andledIndex
, and based on these parameters,GenericHidOutBuffer
should be filled up with the relevant LED values taken fromLedDriverValues
.Let's also add the
SetLedValues
USB command. The first argument is theslotId
byte. Subsequent arguments are byte-pairs ofledId
(within the slot) and the relatedledValue
to be set. AledId
of 255 value should be ignored, and it marks the end of the slot's LED settings, meaning that a new slot id and led index, value pairs follow. This way, bytes with255
value should be used for padding the unused part ofGenericHidOutBuffer
.I also have a good idea regarding the host-side API, so please open a new Agent issue before starting to implementing it, even if your language of preference is not TypeScript or JavaScript because I'd like to keep libraries of different languages consistent.
The text was updated successfully, but these errors were encountered: