Skip to content

Instantly share code, notes, and snippets.

@tiehichi
Last active August 28, 2023 07:49
Show Gist options
  • Save tiehichi/aa714f976fab5f608996dc2a27ba94b3 to your computer and use it in GitHub Desktop.
Save tiehichi/aa714f976fab5f608996dc2a27ba94b3 to your computer and use it in GitHub Desktop.
Steam Controller BLE Protocol #steamcontroller

Steam Controller BLE HID Protocol

After connecting the SteamController through BLE, you can see that there are 5 characteristics in the HID Service: three Input Reports, one Output Report, one Feature Report, and Report Map

In lizard mode, the three Input Reports correspond to the following events:

Input Report event
Input Report 0 Keyboard Input
Input Report 1 Mouse Input
Input Report 2 May be battery information, notify every second, after leaving lizard mode, notify all input information of SteamController

Feature Report is used by the host to send commands to device and accept responses.

In addition to HID Service, SteamController also has a Service that cannot be found in the BLE GATT specification, its UUID is 100F6C32-1735-4313-B402-38567131E5F3, there are two characteristic in this service.

Fortunately, I found information about this Service here, as can be seen from this source code, this Service is called SteamControllerService, and the two characteristic in the Service may be Input Report and Feature Report, And the mode that SteamController works as a gamepad is called ValveMode. In the lizard mode, sending {0xC0, 0x87, 0x03, 0x08, 0x07, 0x00} to the Feature Report can enter this mode.

According to the BLE specification, all Service and characteristics must be identified using UUID and handle.

Input Report notify packet format

I don't care about the data packet when SteamController is used as mouse and keyboard, so we only look at Input Report 2.

It is currently found that the length of the data notified by this input report is 19 bytes and all begin with C0.

But C0 is not a fixed value, when establishing a BLE connection, SteamController sets the MTU to 23, which means that if a packet is too long, it needs to be sent multiple times, This C indicates that the packet has ended, there is no subsequent data packet, if there is a data packet behind, it is represented by 8; 0 represents the sequence number of this data packet, If this data packet is not sent once, then the following data packets will use 1, 2......

For example, if a data packet with a length of 5 bytes is sent, it begins with C0. If the length is 28 bytes, it needs to be sent in two times. The first packet begins with 80 and the second begins with C1. If the length is 50, then need to be sent in three times, three packets start with 80, 81, C2.

  • Under Lizard Mode

When the controller is in lizard mode, the Input Report will return a data packet that may be battery information every second. Here are two examples of data packets:

c0 05 55 02 3c 01 00 00 00 00 00 00 2f 09 00 00 00 00 00
c0 05 55 02 ba 01 00 00 00 00 00 00 30 09 00 00 00 00 00

Guess the packet format may be as follows:

C0 05 55 02 3C 01 00 00 00 00 00 00 2F 09 00 00 00 00 00
already explained may represent the type of packet unknown, but it is fixed serial number of the battery information packet unknown Voltage(mV) unknown

to be continue....

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