一、HID over GATT Profile:蓝牙HID的现代之路
说到蓝牙HID设备,很多人第一反应是经典的蓝牙键盘、鼠标。但传统的蓝牙HID走的是BR/EDR(经典蓝牙)通道,功耗高、配对慢。后来BLE(低功耗蓝牙)普及了,Google在Android 4.0之后开始支持HID over GATT(HOGP),说白了就是用BLE来传HID数据。
我个人习惯把HOGP看作「BLE版的HID协议栈」。它定义了一套GATT服务和特征值,让HID设备(比如蓝牙键盘)和Host(比如手机)通过BLE通信。你想想看,传统蓝牙键盘可能几天就没电了,而BLE键盘用一颗纽扣电池能撑半年——这就是HOGP的价值。
1.1 HOGP的核心架构
HOGP协议栈分三层:
- HID Service:暴露HID报告、协议版本、设备信息等
- Report Protocol:定义输入/输出/特性报告的格式
- Boot Protocol:兼容传统USB HID的启动模式(键盘/鼠标基本功能)
我在项目中遇到过一个问题:某款BLE键盘在Android手机上能连上,但按键没反应。后来发现是HID Service的Report Map特征值没配置对——Android Host要求必须包含键盘的Usage Page和Usage ID。嗯,这里要注意,HOGP的GATT Service UUID是固定的0x1812,Report Map特征值是0x2A4B。
关键GATT特征值一览:
| 特征值 | UUID | 作用 |
|---|---|---|
| Report Map | 0x2A4B | 描述HID报告的结构(即报告描述符) |
| Report | 0x2A4D | 传输实际的输入/输出数据 |
| HID Information | 0x2A4A | 包含HID版本、国家代码等 |
| Protocol Mode | 0x2A4E | 切换Boot模式或Report模式 |
二、HID报告描述符:设备与Host的「翻译官」
HID报告描述符(Report Descriptor)是HID设备最核心的部分。它用一套二进制语言告诉Host:我这个设备有几个按键、几个轴、数据怎么组织。说白了,它就是一份「数据说明书」。
我曾经调试过一个游戏手柄,Host始终识别不到摇杆数据。最后发现报告描述符里把摇杆的Logical Minimum写成了0,但实际数据范围是-127到127——Host按照描述符解析,自然全乱了。
2.1 报告描述符的结构
每个报告描述符由一系列Item组成,每个Item包含:
- Tag:比如Input、Output、Feature、Collection
- Usage Page:比如Generic Desktop Controls(0x01)、Keyboard/Keypad(0x07)
- Usage ID:具体功能,比如键盘的A键(0x04)、鼠标的X轴(0x30)
- Logical Minimum/Maximum:数据的取值范围
- Report Size/Count:每个字段的位数和数量
小技巧:写报告描述符时,先用USB HID Descriptor Tool(HUT)生成模板,再根据BLE的MTU大小调整Report Size。BLE的MTU通常只有23字节(默认),所以报告要尽量精简。
2.2 键盘报告描述符示例
下面是一个标准键盘的报告描述符(8字节输入报告):
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Keyboard/Keypad)
0x19, 0xE0, // Usage Minimum (Keyboard LeftControl)
0x29, 0xE7, // Usage Maximum (Keyboard Right GUI)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input (Const) // 填充字节
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Keyboard/Keypad)
0x19, 0x00, // Usage Minimum (Reserved)
0x29, 0x65, // Usage Maximum (Keyboard Application)
0x81, 0x00, // Input (Data,Array)
0xC0 // End Collection
这个描述符定义了:8个修饰键位(Ctrl/Shift等)+ 1个填充字节 + 6个普通按键。Host收到8字节报告后,就能解析出用户按了哪些键。
三、键盘/鼠标模拟:从设备端到Host端
在Android上做HID设备开发,通常有两种场景:
- Android设备作为HID Device:比如把手机模拟成蓝牙键盘,控制电脑
- Android设备作为HID Host:比如手机连接蓝牙键盘,接收输入
我们重点讲第二种——Android作为HID Host。这是Android系统原生支持的,从Android 4.0开始,系统内置了HID Host服务。
3.1 Android HID Host架构
Android的HID Host实现位于packages/apps/Bluetooth模块中,核心类是BluetoothHidHost。它通过BluetoothProfile接口暴露给应用层。
我记得第一次调HID Host时,发现连接成功后收不到按键事件。查了半天,原来是应用没有注册BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED广播。嗯,这个广播是必须的,否则系统不会把HID事件分发给你的应用。
避坑指南:我曾经在Android 9上遇到一个坑——系统默认只支持Boot Protocol模式的键盘。如果你的设备只实现了Report Protocol,需要手动调用setProtocolMode()切换。否则Host会一直等待Boot模式的报告,设备却发Report模式的报告,两边对不上。
3.2 键盘模拟的代码骨架
下面是一个简单的HID键盘模拟代码(设备端):
// 1. 构建HID报告
byte[] report = new byte[8];
report[0] = 0x00; // 修饰键:无
report[1] = 0x00; // 保留
report[2] = 0x04; // 按键:A键(Usage ID 0x04)
// report[3..7] 为其他按键,这里省略
// 2. 通过GATT发送报告
BluetoothGattCharacteristic reportChar =
hidService.getCharacteristic(HID_REPORT_UUID);
reportChar.setValue(report);
bluetoothGatt.writeCharacteristic(reportChar);
Host端接收时,系统会通过InputManager注入按键事件。你不需要自己解析报告——Android的HID Host服务已经帮你做好了。
四、Android设备作为HID Host:实战要点
如果你想让Android手机连接一个自定义的BLE HID设备(比如自制的键盘),需要做以下几步:
4.1 扫描与配对
- 使用
BluetoothLeScanner扫描BLE设备,过滤包含HID Service UUID(0x1812)的设备 - 配对时调用
BluetoothDevice.createBond() - 配对成功后,通过
BluetoothProfile.ServiceListener获取BluetoothHidHost实例
4.2 连接与数据接收
- 调用
BluetoothHidHost.connect(device)建立HID连接 - 注册
BluetoothHidHost.ACTION_HID_DEVICE_REPORT广播接收报告数据 - 或者通过
InputManager直接接收系统注入的按键事件
性能优化建议:BLE HID的延迟通常在10-20ms,比经典蓝牙的5-10ms稍高。如果做游戏外设,建议:
- 使用BLE 5.0的2M PHY提升吞吐量
- 将连接间隔(Connection Interval)设为7.5ms(最小值)
- 报告长度控制在20字节以内,避免分片
五、知识体系总览
下面这张图总结了HID over GATT开发的核心脉络:
这张图从左到右展示了HOGP开发的三个层次:最上层是协议规范,中间是具体设备类型,最下层是Android系统的实现接口。我个人建议初学者先从键盘设备入手,因为它的报告结构最简单,调试起来也最直观。
我的经验:如果你手头没有BLE HID设备,可以用nRF Connect App模拟一个。它支持自定义GATT Service,你可以手动配置Report Map和Report特征值,然后观察Android Host的响应。这是最便宜的调试方式。
好了,关于HID over GATT的基础知识就聊到这里。记住三个核心点:报告描述符是灵魂、协议模式要匹配、Android Host API要注册广播。把这些搞明白了,剩下的就是调参和填坑了。
公众号:蓝海资料掘金营,微信deep3321