Skip to content

Instantly share code, notes, and snippets.

@cliffrowley
Last active August 9, 2024 09:01
Show Gist options
  • Save cliffrowley/d18a9c4569537b195f2b1eb6c68469e0 to your computer and use it in GitHub Desktop.
Save cliffrowley/d18a9c4569537b195f2b1eb6c68469e0 to your computer and use it in GitHub Desktop.
Notes on the Stream Deck HID protocol

Stream Deck Protocol

How to interface with a Stream Deck device.

Synopsis

The device uses the HID protocol to communicate with its software.

Configuration

The number of keys can be determined from the HID device descriptors. There is currently no known provision to determine their layout or size. The buttons on the current version of the device (1.0) are arranged in a grid and numbered from top right through to bottom left as follows:

 ------------------------
| 05 | 04 | 03 | 02 | 01 |
|----|----|----|----|----|
| 10 | 09 | 08 | 07 | 06 |
|----|----|----|----|----|
| 15 | 14 | 13 | 12 | 11 |
 ------------------------

Note: the Stream Deck Mini was recently announced, with six buttons. I'll incorporate information about this device as and when I am able.

Host to Device

Feature reports

0x0B RESET

0B 63 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00

0x05 SET BRIGHTNESS

The brightness on the device is controlled with PWM. This results in a non-linear correlation between set percentage and perceived brightness, and lower values have the most effect.

PC = PERCENTAGE (0-100)

05 55 aa d1 01 PC 00 00 00 00 00 00 00 00 00 00
00

Output reports

0x02 SET KEY IMAGE

Images are 72x72 pixels (5184 pixels total) arranged in a BGR format with 3 bytes per pixel (15552 bytes total).

Images are sent to the device in two packets, the first containing 2583 pixels (7749 bytes) and the second containing the remaining 2601 pixels (7803 bytes). Each packet is a total of 8191 bytes.

Each packet comprises a header and a chunk of image data. The header contains:

  • The index of the key being set (zero based)
  • The packet sequence number (one-based)
  • The previous packet's sequence number (zero if there is no previous packet).

The first packet also contains some extra information that the second does not, the purpose of which is currently unknown.

Including headers, each image packet is exactly 8191 bytes, padded with zeros where appropriate. This leaves up to 8121 bytes available for image data in the first packet and 8175 bytes in the second, but for some reason the official software only sends a maximum of 7749 in the first packet and whatever is left in the second.

I'm assuming that either the device assumes the first packet will contain this amount of data, or that one of the values in the header specifies how long it will be. Experimentation needed.

Header
SE = PACKET SEQUENCE NUMBER
PR = PREVOUS PACKET SEQUENCE NUMBER
KI = KEY INDEX

02 01 SE 00 PR KI 00 00 00 00 00 00 00 00 00 00
First packet

Includes 54 bytes of "extra" information.

.. HEADER
42 4d f6 3c 00 00 00 00 00 00 36 00 00 00 28 00
00 00 48 00 00 00 48 00 00 00 01 00 18 00 00 00
00 00 c0 3c 00 00 13 0e 00 00 13 0e 00 00 00 00
00 00 00 00 00 00
.. 7749 BYTES OF IMAGE
.. ZERO PADDING UP TO 8191 BYTES
Subsequent packets
.. HEADER
.. REMAINING BYTES OF IMAGE
.. ZERO PADDING UP TO 8191 BYTES

Device to Host

Feature reports

0x03 SERIAL

BYTE 6 ONWARDS CONTAINS SERIAL

03 55 aa d3 03 41 4c 31 32 48 31 41 30 37 38 31
36

0x04 VERSION

BYTE 6 ONWARDS CONTAINS STRING VERSION

04 55 aa d4 04 31 2e 30 2e 31 37 30 31 33 33 00
00

Input reports

0x01 KEY STATE CHANGE

When any keys are pressed or released, the device sends an interrupt containing a bit map of the all the current key states. Presumably this means that multiple buttons can be pressed at once, though I haven't yet tested this.

Key 0B pressed (11th)
01 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00
00
Key released
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00

Snooping

Communication between the device and host can be snooped using Wireshark. Please read this page for information on seting up USB capture.

When snooping USB you'll find that there's a lot of chatter from your hubs and other devices, and you'll need to filter it all out to isolate the StreamDeck. To do this, begin by filtering for usb.idVendor == 0x0fd9 && usb.idProduct == 0x0060, which will give you information about the device. Then simply filter "USB device index" to show the relevant packets for your device.

@linusgke
Copy link

linusgke commented May 16, 2024

I am currently utilizing a StreamDeck in a project and implemented the information presented herein in Java by utilizing the hid4j library. Code is published under the MIT License and can therefore be used freely!

https://github.com/FreieWaldorfschuleAugsburg/infoboard/blob/master/src/main/java/de/waldorfaugsburg/infoboard/streamdeck/StreamDeck.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment