Skip to content

Instantly share code, notes, and snippets.

@cliffrowley
Last active January 16, 2023 08:11
Embed
What would you like to do?
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.

@WitoTV
Copy link

WitoTV commented Aug 1, 2019

Thanks for the info. I'm gonna use this to make my own raspberry pi zero based "fakedeck" working with current Elgato software 👍

@kinsi55
Copy link

kinsi55 commented Jan 5, 2020

@WitoTV soo, anything working you ended up with? :D

@WitoTV
Copy link

WitoTV commented Jan 5, 2020

@kinsi55 sadly nope. I'm getting stuck at windows not seeing the device as stremdeck even though I set vendorID and productID to match the stremdeck ones. It may be my fault becuase I'm not a master of writing usb devices. Sadly I don't have anyone from my circle of firends who can help me with this so I gave up for now.

I based my thing around RP Zero and https://learn.adafruit.com/turning-your-raspberry-pi-zero-into-a-usb-gadget?view=all#other-modules + http://www.linux-usb.org/gadget/

@kinsi55
Copy link

kinsi55 commented Jan 5, 2020

@WitoTV thought about giving this a try with a Teensy, seems like a more sane approach than using a Pi for it (I think an Atmega / Arduino would be too slow for it in regards to updating the LCD). I dont have a Teensy (Atm) but since its inter-compatible w the Arduino ecosystem (Just slower) I might give it a try.

What was your device recognized as anyways? And where did you source the vendor / product ID from?

@WitoTV
Copy link

WitoTV commented Jan 6, 2020

@kinsi55 My device was seen as generic hid interface (no more info was given by the system). I have a friend with streamdeck who found vendor / product ID for me, but they seem to match with the ones provided at the end of this gist.

@WitoTV
Copy link

WitoTV commented Jan 6, 2020

@kinsi55 if you somehow succeed send me some more details.

@kinsi55
Copy link

kinsi55 commented Jan 6, 2020

@WitoTV Sure, if I get around to trying this and have any success I'll let you know. Thanks for now!

@kibix
Copy link

kibix commented Dec 28, 2021

There are some findings i would like to share ..
#1, there is a V1 and V2 of the original Streamdeck. They do not use the same protocol
#2, i received 'V1' style hardware but it has V2 electronics .. so it will need to speak the V2 protocol.
---> easy to detect. if you check the USB capabilities (lsusb -v on linux) the V1 uses 8191 Bytes transfer size out, the V2 uses 1024 Bytes
#3, this 54 bytes 'extra header' is actually well understandable. it is the BMP header of a BMP button file (described for example on wikipedia).
#4, with V2 devices, and the changed protocol, the device will still accept BMP icons but also jpeg icons
#5, i think the number of buttons is returned in the button event message in byte 3 (contains 15 for the original, to be seen on other devices)

I have implemented basic interfacing in lua, using /dev/hidraw0, no special libraries needed for that (but there is no way to send feature reports that way at least not that i am aware of).

@kinsi55
Copy link

kinsi55 commented Dec 28, 2021

Building your own Hardware / Firmware that communicates with the HID protocol of the StreamDeck definitely is possible :P https://twitter.com/Kinsi55/status/1296859058314645505

@ryantheleach
Copy link

@kinsi55 @DDRBoxman Any hints / updates to this?

@kinsi55
Copy link

kinsi55 commented Jul 21, 2022

@ryantheleach Nope not really, I got it to a fully implemented state as seen in the Tweet and thats where its been since

@ryantheleach
Copy link

@kinsi55 I meant, since you have a full implementation, that maybe you had some insight to whether this spec was complete or what to add.

@kinsi55
Copy link

kinsi55 commented Jul 29, 2022

@ryantheleach This is complete yes. For newer streamdeck revisions, the key images are transmitted as jpeg instead of raw rgb but thats it

@kibix
Copy link

kibix commented Oct 11, 2022 via email

@ThePonFarr
Copy link

ThePonFarr commented Jan 16, 2023

How does the elgato work with each packet a total of 8191 bytes, if the high speed bulk endpoint HID spec packet size only goes up to 1024 bytes?

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