Category (Click)
개발보드 덕질하기

[GamePad Bridge] BLE HID Service 삽질기

 차완기 - @10/13/2023, 3:41:00 PM
GamePad Bridge Project : 에오르제아 행복 패드 힐러 라이프를 위하여
Xbox Elite Pad 후면 패들의 하드웨어 매퍼를 만드는 프로젝트입니다.
1.
프로젝트 시작
2.
BLE 및 HID 프로토콜의 이해
프로젝트의 첫 단계는 게임패드의 Bluetooth 프로토콜을 파악하는 것입니다.
이번 포스팅에서는 지난번에 소개한 nRF52840 Dongle을 PC 활용하여 Xbox Elite Pad 2의 BLE 데이터 구조를 파악해보도록 하겠습니다.

Bluetooth 파해치기

nRF52840 Dongle을 PC에 연결한 후 nRF BLE Connect 프로그램을 통해 게임패드와 연결했더니 위와 같이 Human Interface Device Service, 즉 HID Service가 보였습니다.
아무래도 HID 장치를 BLE를 통해 연결하기 위한 정의된 규약이 있는것 같네요.

HID Service Specification

검색 엔진에서 “BLE HID Service” 키워드로 검색했더니 Bluetooth SIG에서 정의한 HID Service 스펙을 확인할 수 있었습니다.
아래로 조금 내려 Characteristic Overview를 확인해보니 게임패드의 실제 Service 구성과 동일한 것을 확인할 수 있었습니다.
그럼 이제 각 Characteristic이 어떤 역할을 하는지 알아보면 되겠네요.
Bluetooth SIG의 Human Interface Device Service Docs 이외에도 연관된 여러 자료를 참고했습니다.

HID Information

HID에 대한 정보가 담겨있다고 합니다. USB HID 스펙의 버전, 지역 코드, 연결과 관련된 flag 정보가 들어있습니다.

HID Control Point

HID 장치를 절전 모드로 진입시킬 때 사용하는 Characteristic 입니다. 00을 보낼 때 절전모드 진입, 01을 보낼 때 해제라고는 하는데, 제가 사용하는 게임패드에 이런 데이터를 보냈을 때 응답이 없는 것을 보아 절전모드가 없는 것 같았습니다.

Report Map

Report Map에서는 이어질 Report Characteristic에 대한 정의, 그리고 HID 장치의 기능에 대한 정보를 담고 있습니다.
아래에서 설명하겠지만 USB-IF의 HID Specification에서 정의하는 Report Descriptor와 대응됩니다.

Report

HID 장치와 Host(컴퓨터 등)간의 데이터 전송을 위해 사용합니다. 예를 든다면 “조이스틱 데이터→PC” 가 있겠네요.
Report Reference Characteristic Descriptor
아래에서 설명하겠지만 USB HID 스펙의 Report ID와 Report Type과 대응됩니다.
나머지 Characteristic들은 제 게임패드에서 사용하지 않는 관계로 생략하였습니다.

(Bluetooth SIG)HID Service Spec (USB-IF)HID Spec

HID Service의 각 Characteristic을 알았으니 다음은 Bluetooth SIG에서 정의한 HID Service Spec과 USB-IF에서 정의한 HID Spec이 어떻게 연결(매핑)되어 있는지 알아볼 차례입니다.
앞서 참고하던 동일한 Docs(HID Service Specification)의 2.3에 이러한 내용이 정리되어 있습니다.
(USB)Report Descriptor → (Bluetooth)Report Map Characteristic
(USB)Report ID, Report Type → (Bluetooth)Report Characteristic의 Report Reference Characteristic Descriptor
이 정도면 블루투스의 데이터 구조를 이해하는데 필요한 정보는 모두 얻은것 같네요!
결론을 짓자면 HID Service의 Report Map Characteristic을 읽은 후 Report Descriptor를 통해 Report Characteristic에서 어떤 정보를 주는지 파악하여 조이스틱 데이터를 읽으면 되겠습니다.

USB HID 파해치기

USB HID Spec 중 Report Descriptor가 무엇인지 알게 되면 조이스틱 데이터를 읽을 수 있겠죠.
인터넷에서 “Report Descriptor parser” 키워드로 검색했더니 Report Descriptor를 파싱해주는 페이지를 찾을 수 있었습니다.
앞서 게임패드와 연결했던 nRF52840 Dongle을 통해 Report Map Characteristic을 읽은 후 페이지에 집어넣었더니 있어보이게(?) 파싱을 해주었습니다.
USB-IF의 “HID Usage Tables for USB” Docs 자료와 함께 파싱된 내용을 해석해보았습니다.

HID Report 이해하기

Report Descriptor의 모든 내용을 해석하기 보다는 구문을 이해하는것에 중점을 맞추었습니다.
파싱된 전체 Report Descriptor (왼쪽 버튼 클릭해 열기)

Usage Page, Usage

HID 장치의 정보를 설명하는 Report Descriptor는 트리 형태의 구조를 가집니다. 모든 요소를 세분화하여 설명한다는 것인데요, 세분화된 각각의 요소들은 Usage Page라는 대분류 속의 Usage라는 소분류로 구분됩니다.
다만, Usage와 실제 기능이 반드시 매칭되는 것은 아닙니다. 예를 들어 게임패드의 LT는 Report ID Usages의 brake Usage를 사용하지만, 실제로는 다양한 목적으로 사용되고는 하죠.
0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x05, // Usage (Game Pad) 0xA1, 0x01, // Collection (Application) 0x85, 0x01, // Report ID (1) 0x09, 0x01, // Usage (Pointer) 0xA1, 0x00, // Collection (Physical) 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x15, 0x00, // Logical Minimum (0) 0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534) 0x95, 0x02, // Report Count (2) 0x75, 0x10, // Report Size (16) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, // End Collection ... // (모든 게임패드 구성요소에 대한 설명이 지난 후) 0xC0, // End Collection
Plain Text
복사
앞서 파싱한 게임패드 Report Descriptor의 시작 부분을 해석해보자면 1~2번 줄을 통해 Generic Desktop Usages 중 Gamepad에 대한 설명이 시작되었음을 알리고 하위 요소를 3번 줄의 Application Collection을 통해 묶어주었습니다.
뒤로는 게임패드의 각 구성요소를 설명하는 내용이 이어지며 마찬가지로 Generic Desktop Usages 중 Pointer Usage에 대한 설명이 이어지는것을 확인할 수 있습니다.
게임패드의 모든 구성요소에 대한 설명이 끝나면 마지막 줄인 0xC0을 통해 모든 설명이 끝났음을 알리게 되는거죠.

Report ID

앞서 BLE HID Service에서 Report ID는 Report Characteristic의 Report Reference Characteristic Descriptor에 대응된다고 했었죠?
Report Reference의 데이터 중 앞쪽 문자열이 Report ID에 해당하며, 이 데이터가 01에 해당하는 Report Characteristic을 찾으면 됩니다.
이 내용을 바탕으로 각 Report ID 데이터를 분석해보았습니다.

Report ID 1 → 입력 데이터

Report ID 1에는 Generic Desktop Ctrls 중 Game Pad Usage의 데이터가 존재하며, 이는 버튼, 스틱, 트리거 등 게임패드의 모든 입력요소를 보고하는 데이터입니다.
위치별로 각 데이터의 용도를 분석해보았습니다. (이어지는 데이터가 있다면 Little Endian으로 읽습니다)
1A-7A-F7-7D-4D-7A-50-82-00-00-00-00-00-00-00-00-00-00-00
1~2: 16 bit 왼쪽 스틱, 좌(0)→우(65534)
3~4: 16 bit 오른쪽 스틱, 상(0)→하(65534)
5~6: 16 bit 오른쪽 스틱, 좌(0)→우(65534)
7~8: 16 bit 오른쪽 스틱, 상(0)→하(65534)
9~10: 10 bit LT 0~1023 (뒷쪽 6비트는 더미)
11~12: 10 bit RT 0~1023 (뒷쪽 6비트는 더미)
13: 4 bit D패드 시계방향으로 0도부터 315도까지 45도 단위로 1→8 (뒷쪽 4비트는 더미)
14~15: 16 bit 나머지 버튼 (중간에 더미 있음)
16 bit를 읽은 후 이를 2진수로 변환했을 때 각 버튼은 LSB 기준으로 n번째 순서의 비트에 해당합니다. (건너뛴 비트는 더미입니다)
버튼을 하나씩 누르며 노가다하다가 좋은 자료를 발견해 이를 활용하였습니다.
1번: A
2번: B
4번: X
5번: Y
7번: TL
8번: TR
10번: View
11번: Manu
13번: Xbox 로고
14번: LS
15번: RS
17: 2 bit 프로필 번호, 0~3 (뒷쪽 6비트는 더미)
18: 4 bit 트리거 락 단계 0→2, 1~2번 비트 왼쪽, 3~4번 비트 오른쪽 (뒷쪽 4비트는 더미)
19: 4 bit Pn→n번째 비트 (뒷쪽 4비트는 더미)

Report ID 3 → 진동

Report ID 3은 PID Page 중 ID 0x21인 Set Effect Report Usage에 대한 데이터입니다. 게임패드의 진동을 담당하는 것으로 보입니다.
00-00-00-00-00-00-00-00
1: 4bit 엑추에이터 활성화 설정 (뒷쪽 4비트는 더미)
Xbox 게임패드에는 총 4개의 진동모터가 존재합니다. LT, RT에 하나씩, 그리고 각 손잡이에 서로 다른 진동수를 가진 두 개의 모터가 있습니다. 하나씩 켜보며 어떤 비트에 어떤 모터가 대응하는지 확인해보았습니다.
각 비트별로 LSB→MSB 순으로 다음과 같습니다.
1번: 오른쪽 손잡이의 높은 주파수 진동
2번: 왼쪽 손잡이의 낮은 주파수 진동
3번: RT
4번: LT
2~5: 32 bit 엑추에이터별 강도, 0~100
Big Endian으로 읽은 후 LSB부터 8 bit씩 끊어 앞선 1번의 진동모터 별 순서와 대응됩니다.
예를 들어 64-00-00-00 으로 설정하는 경우 LT는 100, 나머지는 0의 강도가 되는거죠.
6: 8 bit, 진동을 유지할 시간, x10 ms 단위 (0~255) → (0~2.5 초)
7: 8 bit, 진동을 시작하기 전 딜레이, x10 ms 단위 (0~255) → (0~2.5 초)
8: 8 bit, 반복 횟수, (0~255)
예시를 위해 0F-64-64-64-64-19-19-0A 를 전송하면 아래와 같습니다.
모든 모터 켬, 모든 모터 강도 100, 유지 시간 0.25초, 딜레이 0.25초, 10회 반복 (총 11번 진동)

Report ID 12 → ?

어떤 데이터인지 전혀 모르겠습니다..

Report ID 5 → 가상 키보드

Xbox 패드에 키보드 액세서리를 부착하는 것을 본 적이 있는데요, 아마 이를 위한게 아닌가 싶습니다. 이 데이터로 뭔가 할게 아니기 때문에 깊게 파지는 않았습니다.

마무리

여기까지 게임패드의 BLE 연결과 관련해 데이터의 형태를 파보았습니다.
그냥 Central에서 연결해 Service만 읽으면 될거라 생각했는데, HID 스펙까지 분석하게 될 줄은 몰랐네요. 덕분에 신기한걸 하나 더 알아갑니다
다음 포스팅에서는 ESP32를 BLE GATT Client로 설정한 후 게임패드와 연결해 데이터를 읽는 과정을 소개할 예정입니다.