BLE HID(HID over GATT Profile, HOGP)是蓝牙低功耗协议中用于实现人机接口设备的规范。它允许 BLE 设备作为无线键盘、鼠标等 HID 设备。
┌─────────────────────────────────────────┐
│ HID Application │
│ - 键盘/鼠标逻辑 │
├─────────────────────────────────────────┤
│ HOGP Profile │
│ - HID Service │
│ - Battery Service │
│ - Device Information Service │
├─────────────────────────────────────────┤
│ GATT Layer │
│ - 服务发现 / 特征读写 │
├─────────────────────────────────────────┤
│ GAP Layer │
│ - 广播 / 连接 / 配对 │
├─────────────────────────────────────────┤
│ BLE Controller │
│ - PHY / Link Layer │
└─────────────────────────────────────────┘
#define BT_UUID_HIDS_VAL 0x1812
#define BT_UUID_HIDS BT_UUID_DECLARE_16(BT_UUID_HIDS_VAL)
| 特征 | UUID | 用途 |
|---|---|---|
| Report Map | 0x2A4B | HID 报告描述符 |
| Report | 0x2A4D | 输入/输出/特征报告 |
| HID Information | 0x2A4A | HID 版本信息 |
| HID Control Point | 0x2A4C | 控制命令(挂起/恢复) |
| 特征 | UUID | 用途 |
|---|---|---|
| Protocol Mode | 0x2A4E | 报告协议/引导协议 |
| Battery Level | 0x2A19 | 电池电量 |
| Boot Keyboard Input | 0x2A22 | 引导协议键盘输入 |
| Boot Keyboard Output | 0x2A32 | 引导协议键盘输出 |
| Boot Mouse Input | 0x2A33 | 引导协议鼠标输入 |
static const uint8_t hids_report_map[] = {
/* Usage Page (Generic Desktop) */
0x05, 0x01,
/* Usage (Keyboard) */
0x09, 0x06,
/* Collection (Application) */
0xA1, 0x01,
/* Modifier Keys (Ctrl, Shift, Alt, GUI) */
0x05, 0x07, /* Usage Page (Keyboard) */
0x19, 0xE0, /* Usage Minimum (Left Ctrl) */
0x29, 0xE7, /* Usage Maximum (Right GUI) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1 bit) */
0x95, 0x08, /* Report Count (8) */
0x81, 0x02, /* Input (Data, Var, Abs) */
/* Reserved Byte */
0x75, 0x08, /* Report Size (8 bits) */
0x95, 0x01, /* Report Count (1) */
0x81, 0x01, /* Input (Const) */
/* Key Codes */
0x75, 0x08, /* Report Size (8 bits) */
0x95, 0x06, /* Report Count (6) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x65, /* Logical Maximum (101) */
0x05, 0x07, /* Usage Page (Keyboard) */
0x19, 0x00, /* Usage Minimum (0) */
0x29, 0x65, /* Usage Maximum (101) */
0x81, 0x00, /* Input (Data, Array) */
/* End Collection */
0xC0,
};
#include <zephyr/bluetooth/services/hids.h>
/* HID Information */
static struct hids_info hids_info = {
.bcd_hid = 0x0111, /* HID Version 1.11 */
.b_country_code = 0, /* Not localized */
.flags = HIDS_REMOTE_WAKE | HIDS_NORMALLY_CONNECTABLE,
};
/* HID Input Report */
static struct hids_input_report input_report = {
.id = 0x01,
.size = 8, /* 8 bytes report */
};
/* HIDS Instance */
static struct hids hids;
/* Initialize HIDS */
int init_hids(void)
{
int ret = bt_hids_init(&hids, &hids_info);
if (ret < 0) {
LOG_ERR("HIDS init failed: %d", ret);
return ret;
}
return 0;
}
/* 广播数据 */
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_HIDS_VAL)),
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, sizeof(DEVICE_NAME) - 1),
};
/* 开始广播 */
int start_advertising(void)
{
return bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), NULL, 0);
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
};
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
LOG_ERR("Connection failed: %u", err);
return;
}
LOG_INF("Connected");
/* 启用 HID 服务通知 */
bt_hids_notify(&hids);
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
LOG_INF("Disconnected: %u", reason);
/* 重新开始广播 */
bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), NULL, 0);
}
struct keyboard_report {
uint8_t modifiers; /* 修饰键 */
uint8_t reserved; /* 保留 */
uint8_t keycodes[6]; /* 按键码 */
} __packed;
int send_key(struct bt_conn *conn, uint8_t keycode, uint8_t modifiers)
{
struct keyboard_report report = {
.modifiers = modifiers,
.reserved = 0,
.keycodes = {keycode, 0, 0, 0, 0, 0},
};
return bt_hids_send_input_report(&hids, conn, &report, sizeof(report));
}
int send_key_release(struct bt_conn *conn)
{
struct keyboard_report report = {0};
return bt_hids_send_input_report(&hids, conn, &report, sizeof(report));
}
/* 安全参数 */
static struct bt_conn_auth_cb auth_callbacks = {
.pairing_confirm = pairing_confirm,
.passkey_display = passkey_display,
.passkey_entry = passkey_entry,
.cancel = auth_cancel,
};
/* 配对确认 */
static void pairing_confirm(struct bt_conn *conn)
{
LOG_INF("Pairing confirm");
bt_conn_auth_pairing_confirm(conn);
}
/* 显示配对码 */
static void passkey_display(struct bt_conn *conn, unsigned int passkey)
{
LOG_INF("Passkey: %06u", passkey);
}
# BLE 配置
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="BLE Keyboard"
CONFIG_BT_DEVICE_APPEARANCE=961 # Keyboard
# HID Service
CONFIG_BT_HIDS=y
CONFIG_BT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y
# 安全配置
CONFIG_BT_SMP=y
CONFIG_BT_SMP_ENFORCE_MITM=y
# 电池服务
CONFIG_BT_BAS=y
# 设备信息服务
CONFIG_BT_DIS=y
CONFIG_BT_DIS_MODEL="BLE Keyboard"
CONFIG_BT_DIS_MANUF="OpenClaw"
void main(void)
{
int ret;
/* 初始化 BLE */
ret = bt_enable(NULL);
if (ret) {
LOG_ERR("BLE init failed: %d", ret);
return;
}
/* 注册连接回调 */
bt_conn_cb_register(&conn_callbacks);
/* 初始化 HID 服务 */
ret = init_hids();
if (ret) {
return;
}
/* 开始广播 */
ret = start_advertising();
if (ret) {
LOG_ERR("Advertising failed: %d", ret);
return;
}
LOG_INF("BLE HID Keyboard ready");
while (1) {
k_sleep(K_FOREVER);
}
}
CONFIG_BT_LOG_LEVEL_DBG=y
CONFIG_BT_HIDS_LOG_LEVEL_DBG=y
使用 nRF Connect 手机 App:
*学习笔记创建时间: 2026-03-19*