提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
HID(Human Interface Device)人体学接口设备。
由其名称可以了解HID设备是直接与人交互的设备,比如键盘、鼠标、游戏手柄、翻页笔、蓝牙自拍杆等等,其既可以使用usb接口来实现,也可以使用在蓝牙作为传输层,实现无线HID设备,也就是说,BLE HID协议规范其实是以USB HID协议规范为蓝本的,可以认为是USB HID的无线方式。
蓝牙HID设备的实现一般需要两个条件
1.在广播数据中广播HID的UUID,设备外观,设备名称等信息
2.在GATT中实现HID要求的服务和特性。
比如:
HID服务的UUID是
00001812-0000-1000-8000-00805f9b34fb
0x1812,是短uuid
键盘外观的标识符是0x03c1,
鼠标外观是0x03c2,
游戏手柄的外观是0x03c3
直接搜索下面这个文档就可以查看,更多资料放在最后最下面
Bluetooth Assigned Numbers
广播数据中广播的UUID,设备外观,设备名称
Length | AD Type | Date | 含义 |
---|---|---|---|
0x02 | 0x01 | 0x05 | 设备标识(flags)低功耗设备,有限发现模式 |
0x03 | 0x03 | 0x1218 | HID服务UUID |
0xn | 0x09 | 设备名称,自由设置 | 完整的设备名称(utf-8编码) |
0x03 | 0x19 | 0xc203 | 设备外观 |
在GATT中实现HID要求的服务和特性。
类型 | UUID | 功能 | 读写权限 |
---|---|---|---|
服务 | 0x1812 | HID服务的UUid | |
特性 | 0x2a4a | HID设备信息 | READ |
特性 | 0x2a4b | HID Report Map(上报给主机的数据描述) | read |
特性 | 0x2a4c | HID Control Point(主机状态指示) | write |
特性 | 0x2a4d | 数据上报(必须2908描述) | 读 通知 |
特性 | 0x2a4d | 数据上报(必须2908描述) | 读 写 |
特性 | 0x2a4e | 协议模式控制 | 读 写 |
关于上面的服务和特性(也可以说是HID profile介绍,配置)详细介绍如下
0x1812是HID 服务的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,该特性一定要可写,HID主机通过该特性告知HID设备主机的状态,比如电脑休眠后会告知蓝牙鼠标也进入低功耗模式。
0x2a4d是HID设备与HID主机之间交互数据(Report)的特性UUID。对于键盘设备,当某个按键按下时,HID设备发送数据到HID主句;当开关CapsLock和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模式。
蓝牙键盘代码
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\x1e\x00\x00\x00\x00\x00')#按键1按下
ble.gatts_notify(0, h_repin, b'\x00\x00\x00\x00\x00\x00\x00\x00')#按键1抬起
蓝牙键盘,按下设备上IO0对应的按键,发送按键1到HID主机。
按键对应的代码可搜索HID KeyCode,这里是部分
#define _KEY_Z 0x1d // Keyboard z and Z
#define _KEY_1 0x1e // Keyboard 1 and !
#define _KEY_ENTER 0x28 // Keyboard Return (ENTER)
#define _KEY_ESC 0x29 // Keyboard ESCAPE
b'\x00\x00\x1e\x00\x00\x00\x00\x00'
这个叫ReportMap,为8个字节,是键盘发给主机的数据
第一个字节代表一些特殊的按键(Modifier按键),比如ctrl,gui
第二字节保留,第三到第八字节标示6个按键,数据值为零表示无按键按下.
杰理鼠标报表分析(c)
static const u8 mouse_report_map[] = {
#if TCFG_KEYBOARD_EN
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, KEYBOARD_REPORT_ID,// Report ID (1)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
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,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x03, // Report Count (3)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Page (LEDs)
0x19, 0x01, // Usage Minimum (Num Lock)
0x29, 0x03, // Usage Maximum (Scroll Lock)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0x00, // Usage Minimum (0x00)
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, COUSTOM_CONTROL_REPORT_ID,// Report ID (3)
0x75, 0x10, // Report Size (16)
0x95, 0x01, // Report Count (1)
0x15, 0x00, // Logical Minimum (0)
0x26, 0x8C, 0x02, // Logical Maximum (652)
0x19, 0x00, // Usage Minimum (Unassigned)
0x2A, 0x8C, 0x02, // Usage Maximum (AC Send)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
// Dummy mouse collection starts here
//
0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0
0x09, 0x02, // USAGE (Mouse) 2
0xa1, 0x01, // COLLECTION (Application) 4
0x85, MOUSE_POINT_REPORT_ID, // REPORT_ID (Mouse) 6
0x09, 0x01, // USAGE (Pointer) 8
0xa1, 0x00, // COLLECTION (Physical) 10
0x05, 0x09, // USAGE_PAGE (Button) 12
0x19, 0x01, // USAGE_MINIMUM (Button 1) 14
0x29, 0x02, // USAGE_MAXIMUM (Button 2) 16
0x15, 0x00, // LOGICAL_MINIMUM (0) 18
0x25, 0x01, // LOGICAL_MAXIMUM (1) 20
0x75, 0x01, // REPORT_SIZE (1) 22
0x95, 0x02, // REPORT_COUNT (2) 24
0x81, 0x02, // INPUT (Data,Var,Abs) 26
0x95, 0x06, // REPORT_COUNT (6) 28
0x81, 0x03, // INPUT (Cnst,Var,Abs) 30
0x05, 0x01, // USAGE_PAGE (Generic Desktop) 32
0x09, 0x30, // USAGE (X) 34
0x09, 0x31, // USAGE (Y) 36
0x15, 0x81, // LOGICAL_MINIMUM (-127) 38
0x25, 0x7f, // LOGICAL_MAXIMUM (127) 40
0x75, 0x08, // REPitORT_SIZE (8) 42
0x95, 0x02, // REPORT_COUNT (2) 44
0x81, 0x06, // INPUT (Data,Var,Rel) 46
0xc0, // END_COLLECTION 48
0xc0 // END_COLLECTION
#else
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x05, // Usage Maximum (0x05)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x38, // Usage (Wheel)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x0C, // Usage Page (Consumer)
0x0A, 0x38, 0x02, // Usage (AC Pan)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x85, 0x02, // Report ID (2)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x00, // Collection (Physical)
0x75, 0x0C, // Report Size (12)
0x95, 0x02, // Report Count (2)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x16, 0x01, 0xF8, // Logical Minimum (-2047)
0x26, 0xFF, 0x07, // Logical Maximum (2047)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xC0, // End Collection
#if 0
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x03, // Report ID (3)
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,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x0A, 0x83, 0x01, // Usage (AL Consumer Control Configuration)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0xB5, // Usage (Scan Next Track)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0xB6, // Usage (Scan Previous Track)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0xEA, // Usage (Volume Decrement)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0xE9, // Usage (Volume Increment)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x0A, 0x25, 0x02, // Usage (AC Forward)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x0A, 0x24, 0x02, // Usage (AC Back)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x05, // Usage (Headphone)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
#endif
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x04, // Report ID (4)
0x75, 0x10, // Report Size (16)
0x95, 0x01, // Report Count (1)
0x15, 0x00, // Logical Minimum (0)
0x26, 0x8C, 0x02, // Logical Maximum (652)
0x19, 0x00, // Usage Minimum (Unassigned)
0x2A, 0x8C, 0x02, // Usage Maximum (AC Send)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, 0x05, // Report ID (5)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0x00, // Usage Minimum (0x00)
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
// 149 bytes
#endif
};
这是一个描述鼠标和键盘混合输入报告的HID(Human Interface Device)报告描述符。HID报告描述符用于定义设备与主机之间的通信格式,以便主机能够正确解释设备发送的数据。这段描述符描述了鼠标、消费者控制和键盘三个功能。
以下是这个报告描述符的主要部分:
1.鼠标报告(Report ID: 1):
该部分定义了鼠标的基本行为,包括鼠标按钮、坐标和滚轮。
Usage Page (Generic Desktop Ctrls) 表示使用通用桌面控制器的控制。
Usage (Mouse) 表示这是一个鼠标。
鼠标的报告数据包括5个字节,其中包括按钮状态和坐标信息。
2.用户控制报告(Report ID: 4):
该部分定义了消费者控制,如音量、播放/暂停等。
Usage Page (Consumer) 表示使用消费者控制的控制。
Usage (Consumer Control) 表示这是一个消费者控制设备。
报告数据包括一个16位的值,用于表示不同的消费者控制功能。
3.键盘报告(Report ID: 5):
该部分定义了键盘的基本行为,包括按键。
Usage Page (Generic Desktop Ctrls) 表示使用通用桌面控制器的控制。
Usage (Keyboard) 表示这是一个键盘。
报告数据包括6个字节,其中包括按键的状态。
4.Report ID: 3
这段代码块描述了一些消费者控制的相关信息,包括播放/暂停、配置、下一曲、上一曲、音量调节、前进、后退等操作。
5.Report ID: 2
这部分代码是 HID(Human Interface Device)报告描述的一部分,描述了一个具有 Report ID 为 2 的 Consumer Control 类型的输入报告。
具体描述如下:
Report ID (0x85, 0x02): 报告的唯一标识符,这里是 2。
Usage (0x09, 0x01): 使用 Consumer Control 功能。
Collection (0xA1, 0x00): 定义数据的集合,这里是 Physical(物理集合)。
Report Size (0x75, 0x0C): 每个数据项的位数,这里是 12 位。
Report Count (0x95, 0x02): 数据项的数量,这里是 2。
Usage Page (0x05, 0x01): 使用 Generic Desktop 控制页。
Usage (0x09, 0x30): 使用 X 轴。
Usage (0x09, 0x31): 使用 Y 轴。
Logical Minimum (0x16, 0x01, 0xF8): 逻辑最小值为 -2047。
Logical Maximum (0x26, 0xFF, 0x07): 逻辑最大值为 2047。
Input (0x81, 0x06): 表示这是一个输入项,具体是 Data、Var(变量)、Rel(相对),无 Wrap(不回绕),Linear(线性),Preferred State,No Null Position。
End Collection (0xC0): 结束此集合。
总体来说,这个报告描述了一个 Consumer Control 类型的输入报告,包含 X 和 Y 两个轴的信息,每个轴占用 12 位,取值范围是 -2047 到 2047。这样的报告通常用于描述类似于鼠标或者控制台遥控器等设备的输入。
一些资料:
Bluetooth Core Specification:
这是蓝牙技术的核心规范,提供了关于蓝牙协议栈、服务、特征以及UUID的详细信息。
这个规范通常包含多个卷,其中特定的UUID信息可能分布在不同的卷中。
Bluetooth Assigned Numbers:
这个文档列出了由Bluetooth SIG分配的所有标准UUID和其他数字标识符。你可以在这里找到各种服务、特征和配置文件的UUID。
GATT Specifications:
GATT(Generic Attribute Profile)是蓝牙协议栈中用于设备之间通信的一部分,包括定义了一些通用的服务和特征。相关的UUID信息可能在这里找到。
Profile Specifications:
不同的蓝牙设备和应用领域通常有特定的配置文件(Profile)规范,其中包含了定义的服务和特征以及相应的UUID。你可能需要查看与你的应用领域相关的配置文件规范。