-
2022-05-13 08:25:50
同学,别退出呀,我可是全网最牛逼的 Android 蓝牙分析博主,我写了上百篇蓝牙文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。
HID/HOGP完整安卓源码分析(word文件)请点击下面链接:Android 蓝牙低功耗ble hogp 广播、扫描、连接、数据读写源码流程分析大全,详细的从btif-bta-btm-hci
L2CAP完整安卓源码分析(word文件)请点击下面链接:Android 蓝牙L2cap协议源码分析大全,非常详细的从btif-bta-btm-hci 数据流程走向,以及从control
一、简述
在工作中,我发现大多数同事对HID/HOGP的概念不是很清楚,尤其是HOGP。网上也没有人将这两个协议的概念和安卓源码流程总结出来,今天我将用
更多相关内容 -
低功耗蓝牙hid协议中文版.zip
2022-01-04 19:26:44低功耗蓝牙hid协议中文版 -
安卓wifi蓝牙相关-android蓝牙hid协议开发实现手机连接蓝牙鼠标键盘扫描枪.rar
2019-07-29 16:35:39android 蓝牙hid协议开发,实现手机连接蓝牙鼠标、键盘、扫描枪.rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。 -
蓝牙HID协议
2011-12-06 17:29:08完整的蓝牙HID协议,好不容易才找到的。 -
bt蓝牙hid协议.zip
2022-01-04 19:34:09标准蓝牙hid协议中文版 -
【低功耗蓝牙】⑤ 蓝牙HID协议
2022-05-26 20:31:48本文章主要介绍了蓝牙HID协议的实现方法,基于ESP32平台实现了蓝牙键盘,蓝牙鼠标,蓝牙自拍杆和蓝牙游戏手柄等设备,是初学者学习BLE HID协议很好的参考文章。 HID设备 HID(Human Interface Device)人体学接口...摘要
本文章主要介绍了蓝牙HID协议的实现方法,基于ESP32平台实现了蓝牙键盘,蓝牙鼠标,蓝牙自拍杆和蓝牙游戏手柄等设备,是初学者学习BLE HID协议很好的参考文章。
HID设备
HID(Human Interface Device)人体学接口设备,是生活中常见的输入设备,比如键盘鼠标游戏手柄等等。早期的HID是设备大部分都是通过USB接口来实现,蓝牙技术出现后,通过蓝牙作为传输层,实现了无线HID设备。通过低功耗蓝牙实现的HID功能一般简称为
HOGP
(HID over Gatt Profile)。蓝牙HID设备的实现
任何低功耗蓝牙模块都可以通过软件开发实现HID功能,一个蓝牙模块要实现HID的功能,一般需满足如下两个条件:
①、在广播数据中广播HID的UUID,设备外观,设备名称等信息。
HID服务的UUID是0x1812
,键盘的外观是0x03C1
,鼠标的外观是0x03C2
,游戏手柄的外观是0x03C3
。
UUID参考资料:https://btprodspecificationrefs.blob.core.windows.net/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf
设备外观参考资料:https://specificationrefs.bluetooth.com/assigned-values/Appearance%20Values.pdf②、在GATT中实现HID要求的服务和特性。
0x1812
是HID Service的UUID,必须要使用该UUID实现服务。0x2A4A
是HID Information 的特性UUID,主要功能是展示HID的信息,其值为4个字节:
前两个字节是HID版本,一般填入0x01,0x01,表示版本号为1.1
第三个字节是Country Code,一般填入0x00
第四个字节是HID Flags,一般填入0x02,表示Normally Connectable。0x2A4B
是Report Map的特性UUID,主要功能是描述HID设备与HID主机数据交互的方式,即二者之间所发送的数据每一位的含义。0x2A4C
是Control Point的特性UUID,该特性一定要可Write ,HID主机通过该特性告知HID设备主机的状态,比如电脑休眠后会告知蓝牙键盘也进入低功耗模式。0x2A4D
是HID设备与HID主机之间交互数据(Report)的特性UUID。
对于键盘设备,当某个按键按下时,HID设备发送数据到HID主机;当开关CapsLock,NumsLook和ScrollLook功能时,HID主机将相关指示灯的状态发送给HID键盘。所以键盘设备需要两个UUID为0x2A4D的特性,一个用于发送数据,另一个用于接收数据(使用UUID为0x2908的描述来区分)。
而对于鼠标,游戏手柄这种只发送数据给电脑的设备,只需要定义一个0x2A4D的特性用来发送数据就可以了(当然定义两个也不会出错)。0x2A4E
是协议模式的特性UUID,对于键盘和鼠标这两种设备,可能也会在电脑BIOS阶段使用,此阶段的计算机没有进入系统,难以支持复杂的设备。所以键盘鼠标就有两种模式,分别是Report
模式和Boot
模式,系统启动前使用的是Boot模式,HID设备与HID主机之间使用固定的数据格式进行交互。系统启动完成后,HID主机会通过该特性发数据给HID设备,通知HID设备切换成Report模式,在Report模式下,数据格式由 Report Map 决定。
该特性的数据值为0x00表示Boot模式,0x01表示Report模式。在Boot模式下,Keyboard Input 的特性UUID是
0x2A22
,Output 的特性UUID是0x2A32
;Mous Input 的特性UUID是0x2A33
。本文章只讨论Report模式,暂不讨论Boot模式!
蓝牙键盘
下列代码基于ESP32芯片MicroPython固件(V1.16以上)给出了简单的蓝牙键盘案例:
from machine import Pin import ubluetooth #导入BLE功能模块 from bluetooth import UUID ble = ubluetooth.BLE() #创建BLE设备 ble.active(True) #打开BLE ble.config(gap_name="ESP Keyboard") ble.config(mtu=23) HIDS = ( # Service description: describes the service and how we communicate UUID(0x1812), # Human Interface Device ( (UUID(0x2A4A), ubluetooth.FLAG_READ), # HID information (UUID(0x2A4B), ubluetooth.FLAG_READ), # HID report map (UUID(0x2A4C), ubluetooth.FLAG_WRITE), # HID control point (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, ((UUID(0x2908), 1),)), # HID report / reference (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE, ((UUID(0x2908), 1),)), # HID report / reference (UUID(0x2A4E), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE), # HID protocol mode ), ) services = (HIDS,) handles = ble.gatts_register_services(services) KEYBOARD_REPORT = bytes([ # Report Description: describes what we communicate 0x05, 0x01, # USAGE_PAGE (Generic Desktop) 0x09, 0x06, # USAGE (Keyboard) 0xa1, 0x01, # COLLECTION (Application) 0x85, 0x01, # REPORT_ID (1) 0x75, 0x01, # Report Size (1) 0x95, 0x08, # Report Count (8) 0x05, 0x07, # Usage Page (Key Codes) 0x19, 0xE0, # Usage Minimum (224) 0x29, 0xE7, # Usage Maximum (231) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x81, 0x02, # Input (Data, Variable, Absolute); Modifier byte 0x95, 0x01, # Report Count (1) 0x75, 0x08, # Report Size (8) 0x81, 0x01, # Input (Constant); Reserved byte 0x95, 0x05, # Report Count (5) 0x75, 0x01, # Report Size (1) 0x05, 0x08, # Usage Page (LEDs) 0x19, 0x01, # Usage Minimum (1) 0x29, 0x05, # Usage Maximum (5) 0x91, 0x02, # Output (Data, Variable, Absolute); LED report 0x95, 0x01, # Report Count (1) 0x75, 0x03, # Report Size (3) 0x91, 0x01, # Output (Constant); LED report padding 0x95, 0x06, # Report Count (6) 0x75, 0x08, # Report Size (8) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x65, # Logical Maximum (101) 0x05, 0x07, # Usage Page (Key Codes) 0x19, 0x00, # Usage Minimum (0) 0x29, 0x65, # Usage Maximum (101) 0x81, 0x00, # Input (Data, Array); Key array (6 bytes) 0xc0 # END_COLLECTION ]) #设置BLE广播数据并开始广播 ble.gap_advertise(100, adv_data = b'\x02\x01\x05' + b'\x03\x03\x12\x18' #HID UUID + b'\x03\x19\xC1\x03' #设备外观为键盘 + b'\x0D\x09' + "ESP Keyboard".encode("UTF-8")) (h_info, h_map, _, h_repin, h_d1, h_repout, h_d2, h_model,) = handles[0] # Write service characteristics ble.gatts_write(h_info, b"\x01\x01\x00\x02") # HID info: ver=1.1, country=0, flags=normal ble.gatts_write(h_map, KEYBOARD_REPORT) # HID input report map ble.gatts_write(h_d1, b"\x01\x01") # HID reference: id=1, type=input ble.gatts_write(h_d2, b"\x01\x02") # HID reference: id=1, type=output ble.gatts_write(h_model, b"\x01") # HID Protocol Model: 0=Boot Model, 1=Report Model key = Pin(0,Pin.IN)#IO 0 用作按键 while True: if key.value() == 0: while key.value() == 0: pass ble.gatts_notify(0, h_repin, b'\x00\x00\x04\x00\x00\x00\x00\x00')#按键A按下 ble.gatts_notify(0, h_repin, b'\x00\x00\x00\x00\x00\x00\x00\x00')#按键A抬起
上述代码实现了一个简单的蓝牙键盘,按下设备上
IO0
对应的按键,发送按键A
到HID主机。
上述代码的ReportMap中定义了HID设备发给HID主机的数据为8个字节,定义如下图:
第一个字节是Modifier
按键,相应的位为1表示对应的按键按下(GUI在Windows下是Win键);第二个字节保留,默认为零。第三到第八字节可表示六个按键,数据值为零表示无按键按下,按键对应的代码可搜索HID KeyCode
,比如按键A对应的代码是0x04
,按键B对应的代码是0x05
,Enter键对应的代码是0x28
。比如:
0x80,0x00,0x06,0x00,0x00,0x00,0x00,0x00 //表示同时按下Ctrl键和字母C键。 0x40,0x00,0x04,0x00,0x00,0x00,0x00,0x00 //表示同时按下Shift键和字母A键。
需要注意的是,发送某个按键按下的数据后,需及时发送按键抬起的数据,否则系统会认为按键未抬起,从而触发长按操作。
蓝牙自拍杆
自拍杆是最近几年出现的产品,其实蓝牙自拍杆的本质就是一个超简易的蓝牙键盘。我们知道在手机拍照页面,
音量调节
按键可以触发快门拍照,蓝牙自拍杆本质上就是一个只能调节音量的蓝牙键盘。调节音量是键盘的高级功能,可通过如下代码试下:from machine import Pin import ubluetooth #导入BLE功能模块 from bluetooth import UUID ble = ubluetooth.BLE() #创建BLE设备 ble.active(True) #打开BLE ble.config(gap_name="ESP Keyboard") ble.config(mtu=23) HIDS = ( # Service description: describes the service and how we communicate UUID(0x1812), # Human Interface Device ( (UUID(0x2A4A), ubluetooth.FLAG_READ), # HID information (UUID(0x2A4B), ubluetooth.FLAG_READ), # HID report map (UUID(0x2A4C), ubluetooth.FLAG_WRITE), # HID control point (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, ((UUID(0x2908), 1),)), # HID report / reference (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE, ((UUID(0x2908), 1),)), # HID report / reference (UUID(0x2A4E), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE), # HID protocol mode ), ) services = (HIDS,) handles = ble.gatts_register_services(services) MEDIA_REPORT = bytes([ # Report Description: describes what we communicate # Report ID 1: Advanced buttons 0x05, 0x0C, # Usage Page (Consumer) 0x09, 0x01, # Usage (Consumer Control) 0xA1, 0x01, # Collection (Application) 0x85, 0x01, # Report Id (1) 0x15, 0x00, # Logical minimum (0) 0x25, 0x01, # Logical maximum (1) 0x75, 0x01, # Report Size (1) 0x95, 0x01, # Report Count (1) 0x09, 0xCD, # Usage (Play/Pause) 0x81, 0x06, # Input (Data,Value,Relative,Bit Field) 0x0A, 0x83, 0x01, # Usage (AL Consumer Control Configuration) 0x81, 0x06, # Input (Data,Value,Relative,Bit Field) 0x09, 0xB5, # Usage (Scan Next Track) 0x81, 0x06, # Input (Data,Value,Relative,Bit Field) 0x09, 0xB6, # Usage (Scan Previous Track) 0x81, 0x06, # Input (Data,Value,Relative,Bit Field) 0x09, 0xEA, # Usage (Volume Down) 0x81, 0x06, # Input (Data,Value,Relative,Bit Field) 0x09, 0xE9, # Usage (Volume Up) 0x81, 0x06, # Input (Data,Value,Relative,Bit Field) 0x0A, 0x25, 0x02, # Usage (AC Forward) 0x81, 0x06, # Input (Data,Value,Relative,Bit Field) 0x0A, 0x24, 0x02, # Usage (AC Back) 0x81, 0x06, # Input (Data,Value,Relative,Bit Field) 0xC0 # End Collection ]) #设置BLE广播数据并开始广播 ble.gap_advertise(100, adv_data = b'\x02\x01\x05' + b'\x03\x03\x12\x18' #HID UUID + b'\x03\x19\xC1\x03' #设备外观为键盘 + b'\x0D\x09' + "ESP Keyboard".encode("UTF-8")) (h_info, h_map, _, h_repin, h_d1, h_repout, h_d2, h_model,) = handles[0] # Write service characteristics ble.gatts_write(h_info, b"\x01\x01\x00\x02") # HID info: ver=1.1, country=0, flags=normal ble.gatts_write(h_map, MEDIA_REPORT) # HID input report map ble.gatts_write(h_d1, b"\x01\x01") # HID reference: id=1, type=input ble.gatts_write(h_d2, b"\x01\x02") # HID reference: id=1, type=output ble.gatts_write(h_model, b"\x01") # HID Protocol Model: 0=Boot Model, 1=Report Model key = Pin(0,Pin.IN)#IO 0 用作按键 while True: if key.value() == 0: while key.value() == 0: pass ble.gatts_notify(0, h_repin, b'\x10')#音量- 按下 ble.gatts_notify(0, h_repin, b'\x00')#音量- 抬起
上述代码试下了媒体控制的功能,按下设备上
IO0
对应的按键,执行音量-
的动作。上述Report Map中定义了键盘的高级按键,媒体控制的功能,发送给HID主机的数据为一个字节,每一位定义如下:0x10表示音量-,0x04表示下一曲。
蓝牙鼠标
下列代码给出了简单的蓝牙鼠标的示例代码:
from machine import Pin import ubluetooth #导入BLE功能模块 from bluetooth import UUID ble = ubluetooth.BLE() #创建BLE设备 ble.active(True) #打开BLE ble.config(gap_name="ESP Mouse") ble.config(mtu=23) HIDS = ( # Service description: describes the service and how we communicate UUID(0x1812), # Human Interface Device ( (UUID(0x2A4A), ubluetooth.FLAG_READ), # HID information (UUID(0x2A4B), ubluetooth.FLAG_READ), # HID report map (UUID(0x2A4C), ubluetooth.FLAG_WRITE), # HID control point (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, ((UUID(0x2908), 1),)), # HID report / reference (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE, ((UUID(0x2908), 1),)), # HID report / reference (UUID(0x2A4E), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE), # HID protocol mode ), ) services = (HIDS,) handles = ble.gatts_register_services(services) MOUSE_REPORT = bytes([ # Report Description: describes what we communicate 0x05, 0x01, # USAGE_PAGE (Generic Desktop) 0x09, 0x02, # USAGE (Mouse) 0xa1, 0x01, # COLLECTION (Application) 0x85, 0x01, # REPORT_ID (1) 0x09, 0x01, # USAGE (Pointer) 0xa1, 0x00, # COLLECTION (Physical) 0x05, 0x09, # Usage Page (Buttons) 0x19, 0x01, # Usage Minimum (1) 0x29, 0x03, # Usage Maximum (3) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x95, 0x03, # Report Count (3) 0x75, 0x01, # Report Size (1) 0x81, 0x02, # Input(Data, Variable, Absolute); 3 button bits 0x95, 0x01, # Report Count(1) 0x75, 0x05, # Report Size(5) 0x81, 0x03, # Input(Constant); 5 bit padding 0x05, 0x01, # Usage Page (Generic Desktop) 0x09, 0x30, # Usage (X) 0x09, 0x31, # Usage (Y) 0x09, 0x38, # Usage (Wheel) 0x15, 0x81, # Logical Minimum (-127) 0x25, 0x7F, # Logical Maximum (127) 0x75, 0x08, # Report Size (8) 0x95, 0x03, # Report Count (3) 0x81, 0x06, # Input(Data, Variable, Relative); 3 position bytes (X,Y,Wheel) 0xc0, # END_COLLECTION 0xc0 # END_COLLECTION ]) #设置BLE广播数据并开始广播 ble.gap_advertise(100, adv_data = b'\x02\x01\x05' + b'\x03\x03\x12\x18' #HID UUID + b'\x03\x19\xC2\x03' #设备外观为鼠标 + b'\x0A\x09' + "ESP Mouse".encode("UTF-8")) (h_info, h_map, _, h_repin, h_d1, h_repout, h_d2, h_model,) = handles[0] # Write service characteristics ble.gatts_write(h_info, b"\x01\x01\x00\x02") # HID info: ver=1.1, country=0, flags=normal ble.gatts_write(h_map, MOUSE_REPORT) # HID input report map ble.gatts_write(h_d1, b"\x01\x01") # HID reference: id=1, type=input ble.gatts_write(h_d2, b"\x01\x02") # HID reference: id=1, type=output ble.gatts_write(h_model, b"\x01") # HID Protocol Model: 0=Boot Model, 1=Report Model key = Pin(0,Pin.IN)#IO 0 用作按键 while True: if key.value() == 0: while key.value() == 0: pass ble.gatts_notify(0, h_repin, b'\x00\x0A\xF6\x00')#X正方向和Y的负方向各移动10像素 ble.gatts_notify(0, h_repin, b'\x00\x00\x00\x00')#
上述代码实现了简单的蓝牙鼠标的功能,按下设备上
IO0
对应的按键,鼠标指针将向右和向上各移动10个像素单位。上述代码中的Report Map定义了HID设备发给HID主机的数据为4个字节,每个字节含义如下:一个标准的鼠标上面有三个按键(左键右键中间键)一个滑轮和一个光标。上述数据中,第一个字节的第三位表示三个按键按下与否(具体如何对应未知),第二个字节表示鼠标指针在X方向的移动量,取值范围-127 ~ 127,第三字节表示鼠标指针在Y方向的移动量,取值范围是-127 ~ 127,第四个字节表示鼠标滑轮的滚动量,取值范围是-127 ~ 128。
比如:
0x01,0x00,0x00,0x00 表示鼠标上的某个按键按下。
0x00,0x09,0x09,0x00 表示鼠标指针向X正方向和Y正方向个移动9个像素单位。
0x00,0x00,0x00,0x01 表示鼠标滑轮滚动一格。
蓝牙游戏手柄
下列代码给出了简单的蓝牙游戏手柄案例:
from machine import Pin from time import sleep_ms import ubluetooth #导入BLE功能模块 from bluetooth import UUID import struct ble = ubluetooth.BLE() #创建BLE设备 ble.active(True) #打开BLE ble.config(gap_name="ESP Joystick") ble.config(mtu=23) HIDS = ( # Service description: describes the service and how we communicate UUID(0x1812), # Human Interface Device ( (UUID(0x2A4A), ubluetooth.FLAG_READ), # HID information (UUID(0x2A4B), ubluetooth.FLAG_READ), # HID report map (UUID(0x2A4C), ubluetooth.FLAG_WRITE), # HID control point (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, ((UUID(0x2908), 1),)), # HID report / reference (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE, ((UUID(0x2908), 1),)), # HID report / reference (UUID(0x2A4E), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE), # HID protocol mode ), ) services = (HIDS,) handles = ble.gatts_register_services(services) JOY_REPORT = bytes([ # Report Description: describes what we communicate 0x05, 0x01, # USAGE_PAGE (Generic Desktop) 0x09, 0x04, # USAGE (Joystick) 0xa1, 0x01, # COLLECTION (Application) 0x85, 0x01, # REPORT_ID (1) 0xa1, 0x00, # COLLECTION (Physical) 0x09, 0x30, # USAGE (X) 0x09, 0x31, # USAGE (Y) 0x15, 0x81, # LOGICAL_MINIMUM (-127) 0x25, 0x7f, # LOGICAL_MAXIMUM (127) 0x75, 0x08, # REPORT_SIZE (8) 0x95, 0x02, # REPORT_COUNT (2) 0x81, 0x02, # INPUT (Data,Var,Abs) 0x05, 0x09, # USAGE_PAGE (Button) 0x29, 0x08, # USAGE_MAXIMUM (Button 8) 0x19, 0x01, # USAGE_MINIMUM (Button 1) 0x95, 0x08, # REPORT_COUNT (8) 0x75, 0x01, # REPORT_SIZE (1) 0x25, 0x01, # LOGICAL_MAXIMUM (1) 0x15, 0x00, # LOGICAL_MINIMUM (0) 0x81, 0x02, # Input (Data, Variable, Absolute) 0xc0, # END_COLLECTION 0xc0 # END_COLLECTION ]) # Report Size (1) #设置BLE广播数据并开始广播 ble.gap_advertise(100, adv_data = b'\x02\x01\x05' + b'\x03\x03\x12\x18' #HID UUID + b'\x03\x19\xC3\x03' #设备外观为手柄 + b'\x0D\x09' + "ESP Joystick".encode("UTF-8")) (h_info, h_map, _, h_repin, h_d1, h_repout, h_d2, h_model,) = handles[0] # Write service characteristics ble.gatts_write(h_info, b"\x01\x01\x00\x02") # HID info: ver=1.1, country=0, flags=normal ble.gatts_write(h_map, JOY_REPORT) # HID input report map ble.gatts_write(h_d1, struct.pack("<BB", 1, 1)) # HID reference: id=1, type=input ble.gatts_write(h_d2, struct.pack("<BB", 1, 2)) # HID reference: id=1, type=output ble.gatts_write(h_model, b"\x01") # HID Protocol Model: 0=Boot Model, 1=Report Model key = Pin(0,Pin.IN)#IO 0 用作按键 while True: if key.value() == 0: while key.value() == 0: pass ble.gatts_notify(0, h_repin, b'\xf5\xf5\x00') ble.gatts_notify(0, h_repin, b'\x00\x00\x00')
上述代码实现了一个简单的蓝牙游戏手柄的功能,ReportMap中定义了HID设备发给HID主机的数据为3个字节,每个字节的含义如下:
游戏手柄上一般由推杆和按键组成,上面数据的第一字节表示推杆在X方向的偏移量,范围是-127 ~ 127,第二字节表示推杆在Y方向的偏移量,范围是-127 ~ 128;第三字节表示是否有按键按下,最多支持八个按键。上述代码中的BUG
一般的蓝牙设备在配对成功后,下次可以自动连接,上述代码运行后,需要在电脑或手机上删除该设备,再次连接才能生效,只有首次连接可以使用。断开连接后需要删除设备重新配对。
上述文档中的数据格式和取值范围和代码中的
ReportMap
一一对应,修改ReportMap后,数据格式和取值返回也要做对应的变化。一般正常的蓝牙HID设备也要包含DIS(Device Information Service)设备信息服务,和BAS(Battery Service)电池电量服务,读者可自行添加。
上一章节:【低功耗蓝牙】④ 蓝牙MIDI协议
作者:我是鹏老师
-
蓝牙HID协议笔记
2022-05-16 14:45:02The Human Interface Device (HID)定义了蓝牙在人机接口设备中的协议、特征和使用规程。典型的应用包括蓝牙鼠标、蓝牙键盘、蓝牙游戏手柄等。该协议改编自USB HID Protocol。 2.一些概念 (1)HID Reports:...1.概述
The Human Interface Device (HID)定义了蓝牙在人机接口设备中的协议、特征和使用规程。典型的应用包括蓝牙鼠标、蓝牙键盘、蓝牙游戏手柄等。该协议改编自USB HID Protocol。
2.一些概念
(1)HID Reports:Bluetooth HID devices支持三种Report:Input, Output, and Feature。
(2)HID建立Control Channel和Interrupt Channel两个通道,report可以在这两条channel上传输,在Control channel上传输的report称为synchronous reports ;在Interrupt channel上传输的report称为asynchronous reports。
(3)Feature reports are always transferred synchronously using GET_REPORT or SET_REPORT requests。
(4)Report Protocol Mode和Boot Protocol Mode。Bluetooth HID Hosts至少支持一种,Bluetooth HID Device则需要支持Report Protocol Mode,并且Report Protocol Mode是Bluetooth HID Device的默认Mode。
3.Bluetooth HID Protocol Messages
这些message不能超过L2CAP的MTU,大小超过MTU的message将被忽略。
Message Header的格式如下:
(1)HANDSHAKE该Message用来acknowledgeSET_REPORT, SET_IDLE and SET_PROTOCOL等request。只在Control Channel上传输,只由Bluetooth HID device。
Parameter部分定义如下:
(2)HID_CONTROL
控制Bluetooth HID device改变状态。
Parameter部分定义如下:
(3)GET_REPORT
Bluetooth HID Host用来请求Bluetooth HID device的传输。
定义如下:
(4)SET_REPORT
Bluetooth HID Host用来向Bluetooth HID device发起传输。
格式如下:
(5)GET_PROTOCOL
用来获取Bluetooth HID device的Protocol Mode,然后Bluetooth HID device response一个DATA payload说明当前的Protocol Mode。
格式如下:
GET_PROTOCOL Data Definition格式如下:
(6)SET_PROTOCOL
用来设置Bluetooth HID device的Bluetooth HID device。格式如下:
(7)DATA
代表一个a HID payload。格式如下:
4.Transfers
以HID Protocol messages的形式传输。
(1)Control Channel Transfers
分为Acknowledged和Unacknowledged两种,格式如下:
(2)Interrupt Channel Transfers
Interrupt IN和Interrupt OUT两种,可以在任意时刻发送。【中断嘛】
5.其余各层对于HID的要求
HID与L2CAP的交互如下:
-
蓝牙标准HID协议
2018-08-06 13:24:15蓝牙标准的HID协议,英文原版。可以用于蓝牙产品开发。 -
蓝牙hid协议源码解析
2017-01-11 21:47:031.1 HID协议 HID协议: Hunman Interface Device Profile人机交互设备协议 使用场景:支持人机交互设备之间的控制 市场产品:蓝牙键盘,蓝牙鼠标,蓝牙游戏手柄等。 1.2 代码路径 客户端: frameworks\base\core\...1,概述
1.1 HID协议
HID协议: Hunman Interface Device Profile人机交互设备协议
使用场景:支持人机交互设备之间的控制
市场产品:蓝牙键盘,蓝牙鼠标,蓝牙游戏手柄等。
1.2 代码路径
客户端: frameworks\base\core\java\android\bluetooth
服务端: packages\apps\Bluetooth\src\com\android\bluetooth\ hid
HidDevService.java hid协议的服务端
开发流程和健康设备类似,但是稍微麻烦
2,接口
接口如下
3,开发步骤
在官方文档中有一个建立通信的流程:
1、调用getProfileProxy(Context,BluetoothProfile.ServiceListener, int)来获取代理对象的连接。
2、创建BluetoothHidDeviceAppSdpSettings, BluetoothHidDeviceAppQosSettings对象,创建BluetoothHidDeviceCallback回调,调用registerApp方法注册
3、将手机与设备配对,并且进行连接。
4、实现BluetoothHidDeviceCallback的7个回调方法
5、调用sendReport方法分别实现蓝牙鼠标,蓝牙键盘等。
3.1 获取客户端代理对象
一般在oncreate方法中,直接调用getProfileProxy方法,这个没什么好说的。
BluetoothAdapter.getDefaultAdapter().getProfileProxy(getApplicationContext(), mProfileServiceListener,BluetoothProfile. HID_DEVICE);
private BluetoothProfile.ServiceListener mProfileServiceListener = new BluetoothProfile.ServiceListener() { @Override public void onServiceDisconnected(int profile) { if (profile == BluetoothProfile.HEALTH){ mBluetoothHealth = null; } } @SuppressLint("NewApi") @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { if (profile == BluetoothProfile.HEALTH) { mHidDevice = (BluetoothHidDevice) proxy; // 获取代理对象之后就进行注册 ... } } };
一般经过这个步骤,客户端的BluetoothHidDevice对象已经和服务端的HidDevService对象绑定了。
3.2 注册registerApp
BluetoothHidDeviceAppSdpSettings sdp = new BluetoothHidDeviceAppSdpSettings( HidConsts.NAME, HidConsts.DESCRIPTION, HidConsts.PROVIDER, BluetoothHidDevice.SUBCLASS1_COMBO, HidConsts.DESCRIPTOR); BluetoothHidDeviceAppQosSettings inQos = new BluetoothHidDeviceAppQosSettings( BluetoothHidDeviceAppQosSettings.SERVICE_GUARANTEED, 200, 2, 200, 10000 /* 10 ms */, 10000 /* 10 ms */); BluetoothHidDeviceAppQosSettings outQos = new BluetoothHidDeviceAppQosSettings( BluetoothHidDeviceAppQosSettings.SERVICE_GUARANTEED, 900, 9, 900, 10000 /* 10 ms */, 10000 /* 10 ms */); boolean result = mHidDevice.registerApp(sdp, inQos, outQos, mCallback);
HidConsts类的定义如下:
public class HidConsts { public final static String NAME = "HID Device Testapp"; public final static String DESCRIPTION = ""; public final static String PROVIDER = "Codeaurora"; /* @formatter:off */ public final static byte[] DESCRIPTOR = { (byte) 0x05, (byte) 0x01, // USAGE_PAGE (Generic Desktop) (byte) 0x09, (byte) 0x02, // USAGE (Mouse) (byte) 0xa1, (byte) 0x01, // COLLECTION (Application) (byte) 0x09, (byte) 0x01, // USAGE (Pointer) (byte) 0xa1, (byte) 0x00, // COLLECTION (Physical) (byte) 0x85, (byte) 0x02, // REPORT_ID (2) (byte) 0x05, (byte) 0x09, // USAGE_PAGE (Button) (byte) 0x19, (byte) 0x01, // USAGE_MINIMUM (Button 1) (byte) 0x29, (byte) 0x03, // USAGE_MAXIMUM (Button 3) (byte) 0x15, (byte) 0x00, // LOGICAL_MINIMUM (0) (byte) 0x25, (byte) 0x01, // LOGICAL_MAXIMUM (1) (byte) 0x95, (byte) 0x03, // REPORT_COUNT (3) (byte) 0x75, (byte) 0x01, // REPORT_SIZE (1) (byte) 0x81, (byte) 0x02, // INPUT (Data,Var,Abs) (byte) 0x95, (byte) 0x01, // REPORT_COUNT (1) (byte) 0x75, (byte) 0x05, // REPORT_SIZE (5) (byte) 0x81, (byte) 0x03, // INPUT (Cnst,Var,Abs) (byte) 0x05, (byte) 0x01, // USAGE_PAGE (Generic Desktop) (byte) 0x09, (byte) 0x30, // USAGE (X) (byte) 0x09, (byte) 0x31, // USAGE (Y) (byte) 0x15, (byte) 0x81, // LOGICAL_MINIMUM (-127) (byte) 0x25, (byte) 0x7f, // LOGICAL_MAXIMUM (127) (byte) 0x75, (byte) 0x08, // REPORT_SIZE (8) (byte) 0x95, (byte) 0x02, // REPORT_COUNT (2) (byte) 0x81, (byte) 0x06, // INPUT (Data,Var,Rel) (byte) 0x09, (byte) 0x38, // USAGE (Wheel) (byte) 0x15, (byte) 0x81, // LOGICAL_MINIMUM (-127) (byte) 0x25, (byte) 0x7f, // LOGICAL_MAXIMUM (127) (byte) 0x75, (byte) 0x08, // REPORT_SIZE (8) (byte) 0x95, (byte) 0x01, // REPORT_COUNT (1) (byte) 0x81, (byte) 0x06, // INPUT (Data,Var,Rel) (byte) 0xc0, // END_COLLECTION (byte) 0xc0, // END_COLLECTION // battery strength (byte) 0x05, (byte) 0x0c, (byte) 0x09, (byte) 0x01, (byte) 0xa1, (byte) 0x01, (byte) 0x85, (byte) 0x20, // REPORT_ID (32) (byte) 0x05, (byte) 0x01, (byte) 0x09, (byte) 0x06, (byte) 0xa1, (byte) 0x02, (byte) 0x05, (byte) 0x06, // USAGE_PAGE (Generic Device Controls) (byte) 0x09, (byte) 0x20, // USAGE (Battery Strength) (byte) 0x15, (byte) 0x00, // LOGICAL_MINIMUM (0) (byte) 0x26, (byte) 0xff, (byte) 0x00, // LOGICAL_MAXIMUM (100) (byte) 0x75, (byte) 0x08, // REPORT_SIZE (8) (byte) 0x95, (byte) 0x01, // REPORT_COUNT (1) (byte) 0x81, (byte) 0x02, // INPUT (Data,Var,Abs) (byte) 0xc0, (byte) 0xc0, (byte) 0x05, (byte) 0x01, // USAGE_PAGE (Generic Desktop) (byte) 0x09, (byte) 0x06, // USAGE (Keyboard) (byte) 0xa1, (byte) 0x01, // COLLECTION (Application) (byte) 0x85, (byte) 0x01, // REPORT_ID (1) (byte) 0x05, (byte) 0x07, // USAGE_PAGE (Keyboard) (byte) 0x19, (byte) 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) (byte) 0x29, (byte) 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) (byte) 0x15, (byte) 0x00, // LOGICAL_MINIMUM (0) (byte) 0x25, (byte) 0x01, // LOGICAL_MAXIMUM (1) (byte) 0x75, (byte) 0x01, // REPORT_SIZE (1) (byte) 0x95, (byte) 0x08, // REPORT_COUNT (8) (byte) 0x81, (byte) 0x02, // INPUT (Data,Var,Abs) (byte) 0x05, (byte) 0x0c, // USAGE_PAGE (Consumer Devices) (byte) 0x15, (byte) 0x00, // LOGICAL_MINIMUM (0) (byte) 0x25, (byte) 0x01, // LOGICAL_MAXIMUM (1) (byte) 0x95, (byte) 0x07, // REPORT_COUNT (7) (byte) 0x75, (byte) 0x01, // REPORT_SIZE (1) (byte) 0x09, (byte) 0xb6, // USAGE (Scan Previous Track) (byte) 0x09, (byte) 0xb5, // USAGE (Scan Next Track) (byte) 0x09, (byte) 0xb7, // USAGE (Stop) (byte) 0x09, (byte) 0xcd, // USAGE (Play/Pause) (byte) 0x09, (byte) 0xe2, // USAGE (Mute) (byte) 0x09, (byte) 0xe9, // USAGE (Volume Up) (byte) 0x09, (byte) 0xea, // USAGE (Volume Down) (byte) 0x81, (byte) 0x02, // INPUT (Data,Var,Abs) (byte) 0x95, (byte) 0x01, // REPORT_COUNT (1) (byte) 0x75, (byte) 0x01, // REPORT_SIZE (1) (byte) 0x81, (byte) 0x03, // INPUT (Constant,Var,Abs) (byte) 0x05, (byte) 0x07, // USAGE_PAGE (Keyboard) (byte) 0x95, (byte) 0x05, // REPORT_COUNT (5) (byte) 0x75, (byte) 0x01, // REPORT_SIZE (1) (byte) 0x85, (byte) 0x01, // REPORT_ID (1) (byte) 0x05, (byte) 0x08, // USAGE_PAGE (LEDs) (byte) 0x19, (byte) 0x01, // USAGE_MINIMUM (Num Lock) (byte) 0x29, (byte) 0x05, // USAGE_MAXIMUM (Kana) (byte) 0x91, (byte) 0x02, // OUTPUT (Data,Var,Abs) (byte) 0x95, (byte) 0x01, // REPORT_COUNT (1) (byte) 0x75, (byte) 0x03, // REPORT_SIZE (3) (byte) 0x91, (byte) 0x03, // OUTPUT (Cnst,Var,Abs) (byte) 0x95, (byte) 0x06, // REPORT_COUNT (6) (byte) 0x75, (byte) 0x08, // REPORT_SIZE (8)
连接很简单,直接调用connect方法就可以了。
public void connect() { if (mHidDevice == null) return; mHidDevice.connect(); }
3.4 BluetoothHidDeviceCallback
BluetoothHidDeviceCallback这个抽象类有7个回调方法,
private byte[] mBuffer = new byte[1];
private final BluetoothHidDeviceCallback mCallback = new BluetoothHidDeviceCallback() { @Override public void onAppStatusChanged(BluetoothDevice pluggedDevice, BluetoothHidDeviceAppConfiguration config, boolean registered) { // 一般在registerApp和unregisterApp方法之后回调 // registered 表示是否注册上 } @Override public void onConnectionStateChanged(BluetoothDevice device, int state) { // device 远程蓝牙设备 state连接状态 mBuffer = (byte) 63 mHidDevice.sendReport(32, mBuffer); // 不知道为啥子这样写? } // 其他5个方法就可以不管了。 @Override public void onIntrData(byte reportId, byte[] data) { Log.v(TAG, "intr data: reportId=" + reportId + " data=" + Arrays.toString(data)); } @Override public void onSetProtocol(byte protocol) { Log.d(TAG, "protocol set to " + protocol); } @Override public void onVirtualCableUnplug() { } @Override public void onGetReport(byte type, byte id, int bufferSize) { } @Override public void onSetReport(byte type, byte id, byte[] data) { } };
BluetoothHidDeviceCallback7个方法都是C/C++等通过JNI机制的回调。
4,蓝牙鼠标
4.1 蓝牙鼠标滑动
实现鼠标在整个界面前后左右上下滑动
mTouchpad = view.findViewById(R.id.touchpad); mTouchpad.setOnTouchListener(new OnTouchListener() { private int mPrevX; private int mPrevY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mPrevX = (int) (event.getX() * mSpeed); mPrevY = (int) (event.getY() * mSpeed); break; case MotionEvent.ACTION_MOVE: int x = (int) (event.getX() * mSpeed); int y = (int) (event.getY() * mSpeed); mouseMove((byte) (x – mPrevX), (byte) (y - mPrevY)); mPrevX = x; mPrevY = y; break; } return true; } });
private int mSpeed = 3;
mSpeed的值控制鼠标移动的速度。
private byte[] mBuffer = new byte[4]; byte id = 2;
public synchronized void move(byte dx, byte dy) { // leave buttons state unchanged mBuffer[1] = dx; mBuffer[2] = dy; mHidDevice.sendReport(id, mBuffer); }
4.2 蓝牙鼠标点击
将鼠标滑动到目标后,点击按钮可以选中目标。
button.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int which = 0; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mouseButtonDown(which); break; case MotionEvent.ACTION_UP: mouseButtonUp(which); break; } return false; } });
Which的值有三种,分别是0,1,2 之间好像没什么差别。
public synchronized void buttonDown(int which) { mBuffer[0] |= (1 << which); mBuffer[1] = 0; mBuffer[2] = 0; mHidDevice.sendReport(id, mBuffer); } public synchronized void buttonUp(int which) { mBuffer[0] &= ~(1 << which); mBuffer[1] = 0; mBuffer[2] = 0; mHidDevice.sendReport(id, mBuffer); }
4.3 蓝牙鼠标翻页
比如在手机中,有时候界面不止一页,这样就需要翻页来显示了。
private int mScrollSpeed = 3; // 控制翻页的速度 mScrollZone.setOnTouchListener(new OnTouchListener() { private int mPrevY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mPrevY = (int) (event.getY() * mScrollSpeed); break; case MotionEvent.ACTION_MOVE: int y = (int) (event.getY() * mScrollSpeed); mouseScroll((byte) (mPrevY - y)); mPrevY = y; break; } return true; } }); public synchronized void scroll(byte delta) { mBuffer[3] = delta; mHidDevice.sendReport(id, mBuffer); mBuffer[3] = 0x00; }
5,蓝牙键盘
市面上的键盘类型较多,实现了3种蓝牙键盘,
标准键盘
lable以及对应keyCode如下:
<Keyboard> <Layout> <Key keyLabel="Esc" keyCode="0x29"/> <Key keyLabel="F1" keyCode="0x3A"/> <Key keyLabel="F2" keyCode="0x3B"/> <Key keyLabel="F3" keyCode="0x3C"/> <Key keyLabel="F4" keyCode="0x3D"/> <Key keyLabel="F5" keyCode="0x3E"/> <Key keyLabel="F6" keyCode="0x3F"/> <Key keyLabel="F7" keyCode="0x40"/> <Key keyLabel="F8" keyCode="0x41"/> <Key keyLabel="F9" keyCode="0x42"/> <Key keyLabel="F10" keyCode="0x43"/> <Key keyLabel="F11" keyCode="0x44"/> <Key keyLabel="F12" keyCode="0x45"/> <Key keyLabel="Del" keyCode="0x4C"/> </Layout> <Layout> <Key keyLabel="'" shiftLabel="~" keyCode="0x35"/> <Key keyLabel="1" shiftLabel="!" keyCode="0x1E"/> <Key keyLabel="2" shiftLabel="\@" keyCode="0x1F"/> <Key keyLabel="3" shiftLabel="\#" keyCode="0x20"/> <Key keyLabel="4" shiftLabel="$" keyCode="0x21"/> <Key keyLabel="5" shiftLabel="%" keyCode="0x22"/> <Key keyLabel="6" shiftLabel="^" keyCode="0x23"/> <Key keyLabel="7" shiftLabel="&" keyCode="0x24"/> <Key keyLabel="8" shiftLabel="*" keyCode="0x25"/> <Key keyLabel="9" shiftLabel="(" keyCode="0x26"/> <Key keyLabel="0" shiftLabel=")" keyCode="0x27"/> <Key keyLabel="-" shiftLabel="_" keyCode="0x2D"/> <Key keyLabel="=" shiftLabel="+" keyCode="0x2E"/> <Key keyLabel="Backspace ←" keyCode="0x2A" weight="1.5"/> </Layout> <Layout> <Key keyLabel="Tab ↹" keyCode="0x2B" weight="1.5"/> <Key keyLabel="Q" keyCode="0x14"/> <Key keyLabel="W" keyCode="0x1A"/> <Key keyLabel="E" keyCode="0x08"/> <Key keyLabel="R" keyCode="0x15"/> <Key keyLabel="T" keyCode="0x17"/> <Key keyLabel="Y" keyCode="0x1C"/> <Key keyLabel="U" keyCode="0x18"/> <Key keyLabel="I" keyCode="0x0C"/> <Key keyLabel="O" keyCode="0x12"/> <Key keyLabel="P" keyCode="0x13"/> <Key keyLabel="[" keyCode="0x2F" shiftLabel="{"/> <Key keyLabel="]" keyCode="0x30" shiftLabel="}"/> <Key keyLabel="\\" keyCode="0x31" shiftLabel="|"/> </Layout> <Layout> <Key keyLabel="Caps Lock" keyCode="0x39" weight="1.5"/> <Key keyLabel="A" keyCode="0x04"/> <Key keyLabel="S" keyCode="0x16"/> <Key keyLabel="D" keyCode="0x07"/> <Key keyLabel="F" keyCode="0x09"/> <Key keyLabel="G" keyCode="0x0A"/> <Key keyLabel="H" keyCode="0x0B"/> <Key keyLabel="J" keyCode="0x0D"/> <Key keyLabel="K" keyCode="0x0E"/> <Key keyLabel="L" keyCode="0x0F"/> <Key keyLabel=";" keyCode="0x33" shiftLabel=":"/> <Key keyLabel="'" keyCode="0x34" shiftLabel="""/> <Key keyLabel="Enter ↵" keyCode="0x28" weight="3.0"/> </Layout> <Layout> <Key keyLabel="Shift ⇧" keyCode="0xE1" keyFunc="Shift" weight="1.5"/> <Key keyLabel="Z" keyCode="0x1D"/> <Key keyLabel="X" keyCode="0x1B"/> <Key keyLabel="C" keyCode="0x06"/> <Key keyLabel="V" keyCode="0x19"/> <Key keyLabel="B" keyCode="0x05"/> <Key keyLabel="N" keyCode="0x11"/> <Key keyLabel="M" keyCode="0x10"/> <Key keyLabel="," keyCode="0x36" shiftLabel="<"/> <Key keyLabel="." keyCode="0x37" shiftLabel=">"/> <Key keyLabel="/" keyCode="0x38" shiftLabel="\?"/> <Key keyLabel="Shift ⇧" keyCode="0xE5" keyFunc="Shift" weight="1.5"/> </Layout> <Layout> <Key keyLabel="Ctrl" keyCode="0xE0" weight="1.5"/> <Key keyLabel="Win" keyCode="0xE3"/> <Key keyLabel="Alt" keyCode="0xE2"/> <Key keyLabel=" " keyCode="0x2C" weight="10.0"/> <Key keyLabel="Alt Gr" keyCode="0xE6"/> <Key keyLabel="Win" keyCode="0xE7"/> <Key keyLabel="Menu" keyCode="0x76"/> <Key keyLabel="Ctrl" keyCode="0xE4" weight="1.5"/> </Layout> </Keyboard>
左边是导航键盘 右边是数字键盘
导航键盘的lable以及对应keyCode如下:
<Keyboard> <Layout> <Key keyLabel="Vol Down ⇩" keyCode="0x86"/> <Key keyLabel="Mute" keyCode="0x84"/> <Key keyLabel="Vol Up ⇧" keyCode="0x85"/> </Layout> <Layout> <Key keyLabel="Print Screen" keyCode="0x46"/> <Key keyLabel="Scroll Lock" keyCode="0x47"/> <Key keyLabel="Pause" keyCode="0x48"/> </Layout> <Layout> <Key keyLabel="Insert" keyCode="0x49"/> <Key keyLabel="Home" keyCode="0x4A"/> <Key keyLabel="Page Up" keyCode="0x4B"/> </Layout> <Layout> <Key keyLabel="Delete" keyCode="0x4C"/> <Key keyLabel="End" keyCode="0x4D"/> <Key keyLabel="Page Down" keyCode="0x4E"/> </Layout> <Layout> <Key visible="false"/> <Key keyLabel="↑" keyCode="0x52"/> <Key visible="false"/> </Layout> <Layout> <Key keyLabel="←" keyCode="0x50"/> <Key keyLabel="↓" keyCode="0x51"/> <Key keyLabel="→" keyCode="0x4F"/> </Layout> </Keyboard>
数字键盘的lable以及对应keyCode如下:
<Keyboard> <Layout> <Key keyLabel="Prev\n⇦" keyCode="0x80"/> <Key keyLabel="Next\n⇨" keyCode="0x81"/> <Key keyLabel="Stop" keyCode="0x82"/> <Key keyLabel="Play\nPause" keyCode="0x83"/> <Key visible="false"/> </Layout> <Layout> <Key keyLabel="Num\nLock" keyCode="0x53"/> <Key keyLabel="/" keyCode="0x54"/> <Key keyLabel="*" keyCode="0x55"/> <Key keyLabel="-" keyCode="0x56"/> </Layout> <Layout> <Key keyLabel="7\nHome" keyCode="0x5F"/> <Key keyLabel="8 ↑" keyCode="0x60"/> <Key keyLabel="9\nPgUp" keyCode="0x61"/> <Key keyLabel="+" keyCode="0x57"/> </Layout> <Layout> <Key keyLabel="4\n←" keyCode="0x5C"/> <Key keyLabel="5" keyCode="0x5D"/> <Key keyLabel="6\n→" keyCode="0x5E"/> <Key visible="false"/> </Layout> <Layout> <Key keyLabel="1\nEnd" keyCode="0x59"/> <Key keyLabel="2 ↓" keyCode="0x5A"/> <Key keyLabel="3\nPgDn" keyCode="0x5B"/> <Key visible="false"/> </Layout> <Layout> <Key keyLabel="0\nIns" keyCode="0x62" weight="2.0"/> <Key keyLabel=".\nDel" keyCode="0x63"/> <Key keyLabel="Enter" keyCode="0x58"/> </Layout> </Keyboard>
按键事件都是一样的,主要就是keyCode不同,
@Override public void onKeyUp(byte keyCode) { keyboardKeyUp(keyCode); } @Override public void onKeyDown(byte keyCode) { keyboardKeyDown(keyCode); } private final static byte MODIFIER_BASE = (byte) 0xe0; private final static byte MODIFIER_COUNT = 8; /* left ctrl -> right gui */ private byte[] mBuffer = new byte[8]; byte id = 1; public synchronized void keyDown(byte key) { if (key >= MODIFIER_BASE && key <= MODIFIER_BASE + MODIFIER_COUNT) { mBuffer[0] |= (1 << (key - MODIFIER_BASE)); } else if ((key & 0x80) != 0) { mBuffer[1] |= (1 << (key & 0x07)); } else { for (int i = 2; i < 8; i++) { if (mBuffer[i] == 0x00) { mBuffer[i] = key; break; } } } mHidDevice.sendReport(id, mBuffer); } public synchronized void keyUp(byte key) { if (key >= MODIFIER_BASE && key <= MODIFIER_BASE + MODIFIER_COUNT) { mBuffer[0] &= ~(1 << (key - MODIFIER_BASE)); } else if ((key & 0x80) != 0) { mBuffer[1] &= ~(1 << (key & 0x07)); } else { for (int i = 2; i < 8; i++) { if (mBuffer[i] == key) { mBuffer[i] = 0x00; break; } } } mHidDevice.sendReport(id, mBuffer); }
这样就可以了,很简单吧。
6,源码解析
客户端的BluetoothHidDevice和服务端的HidDevService都比较简单,很直接,完全没有拐弯抹角的地方, sendReport方法从客户端到服务端的过程如下,
回调的7个方法流程如下,以onAppStatusChanged回调为例,
7,小节
问题:1,回调的几个方法以及2个类还未弄清楚其作用。
2,客户端的BluetoothInputDevice以及客户端的HidService和HID协议的文件在一个文件夹下,那么这2个类的作用是什么呢?如何使用。
-
HID 协议官方文档 蓝牙5.0协议
2018-10-26 17:30:01HID协议官方文档,蓝牙5.0协议,存点C币下东西用,见谅。 -
蓝牙HID协议介绍
2015-12-18 10:26:28The Human Interface Device (HID)定义了蓝牙在人机接口设备中的协议、特征和使用规程。典型的应用包括蓝牙鼠标、蓝牙键盘、蓝牙游戏手柄等。该协议改编自USB HID Protocol。 2.一些概念 (1)HID ... -
Android 蓝牙 hid hogp协议分析大全- 全网最详细
2022-05-07 20:15:55android-蓝牙A2dp-avrcp-hfp-opp-...HID:human interface device :基于经典蓝牙实现的。 HOGP:human over gatt prifile :基于ble gatt协议实现的。 HID/HOGP设备,即人机交互设备,常见的有鼠标,键盘,游戏...... -
请教 BLE 蓝牙HID协议开发
2021-09-14 19:36:35没有接触过蓝牙开发,最近需要开发蓝牙相关的项目,需要用到HID实现蓝牙解锁,求指点 -
蓝牙人机接口设备协议(HID)概念以及封包格式介绍
2020-12-29 08:33:09The Human Interface Device (HID)定义了蓝牙在人机接口设备中的协议、特征和使用规程。该协议改编自USB HID Protocol。 13.1.1 HID角色 A Bluetooth HID deviceis a device providing the service of human or ... -
【无线通信协议笔记】蓝牙篇:BLE HID协议
2022-01-17 11:24:22BLE HID协议规范是以USB HID协议规范为蓝本的,可以认为是USB HID的无线方式。那么作为人机交互设备(HID),我们常见产品有:蓝牙鼠标,蓝牙键盘,蓝牙游戏手柄,蓝牙自拍杆等。另外有一点就是,对于Android系统的... -
STM32 USB-HID 协议源码
2016-03-10 18:21:34STM32 USB-HID 协议源代码 上位机和下位机 -
BLE HID协议-----蓝牙鼠标代码流分析
2021-06-08 05:34:38隐藏了蓝牙写在前面: 使用SDK版本NRF 12.3.0,nRF52 \ nRF52832包\ nRF5_SDK_12.3.0_d7731ad \ examples \ ble_peripheral \ ble_app_hids_mouse,本文仅用于代码分析,如果没有足够的理解,欢迎交流,QQ: 993650814... -
蓝牙HID——android利用手机来解锁电脑/平板/iPhone
2022-06-03 01:35:55蓝牙HID——将android设备变成蓝牙键盘(BluetoothHidDevice) 可先了解上篇文章的内容再来浏览此篇。首先需要建立HID键盘连接,参考上篇。 其次要进行密码设置并保存,这里设置了3种类型的密码:4位数字、6位数字、... -
Android 蓝牙 BLE 连接(connect)数据流程图(协议栈),非常详细的从btif-bta-btm-hci 数据流程
2022-04-30 15:43:27Android 蓝牙 BLE 连接(connect)数据流程图(协议栈),非常详细的从btif-bta-btm-hci 数据流程走向,以及从controller收到数据到btm层,将Android 源码使用流程图的形式画了出来,使Android 蓝牙开发者更清楚数据... -
TouchScreen协议让你的安卓手机变成蓝牙HID触摸屏
2021-04-15 17:43:49接到这个任务,我还是挺有把握的,因为在研究HID鼠标协议的时候听说有绝对位置的鼠标这个么东西,心想只要实现绝对定位的鼠标不就行了,于是当时就答应了那哥们,说没问题。经过一段时间的折腾后,我搞出了符合绝对... -
蓝牙HID协议笔记【转】
2015-04-19 19:10:00蓝牙HID协议笔记 转自:http://blog.sina.com.cn/s/blog_69b5d2a50101emll.html 1.概述 The Human Interface Device (HID)定义了蓝牙在人机接口设备中的协议、特征和使用规程。典型的应用包括蓝牙鼠标、蓝牙键盘... -
iOS HID蓝牙协议
2021-12-17 14:12:37蓝牙HID协议笔记【转】- 阿里云开发社区 资料蓝牙开发 - 51博客 -
【转载】蓝牙人机接口设备协议(HID)概念以及封包格式介绍
2022-05-08 15:19:04HID协议有很多应用,比如蓝牙鼠标,键盘,手柄,自拍杆等都会用到HID协议! 一. 声明 本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下: 第一篇:蓝牙综合介绍 ,主要介绍蓝牙的一些概念... -
蓝牙单模双协议(SPP、HID)透传模组-BM4342
2018-08-07 15:07:45BM4342 模块是一款符合蓝牙标准的蓝牙产品,单模双协议, 基于BK方案开发的.目前支持的协议包括 BR/SPP/HID, 支持 UART 通信( 流控功能可定制),主要应用于IOT、无线扫描枪、无线测量监控、医疗、蓝牙POS机. -
bluetooth hid协议
2011-08-02 10:25:21开发蓝牙HID设备的必备资料啊,吐血奉献