返回首页

BLE HID 学习笔记

概述

BLE HID(HID over GATT Profile, HOGP)是蓝牙低功耗协议中用于实现人机接口设备的规范。它允许 BLE 设备作为无线键盘、鼠标等 HID 设备。

HOGP 架构

┌─────────────────────────────────────────┐
│            HID Application              │
│  - 键盘/鼠标逻辑                         │
├─────────────────────────────────────────┤
│            HOGP Profile                 │
│  - HID Service                          │
│  - Battery Service                      │
│  - Device Information Service           │
├─────────────────────────────────────────┤
│            GATT Layer                   │
│  - 服务发现 / 特征读写                   │
├─────────────────────────────────────────┤
│            GAP Layer                    │
│  - 广播 / 连接 / 配对                    │
├─────────────────────────────────────────┤
│            BLE Controller               │
│  - PHY / Link Layer                     │
└─────────────────────────────────────────┘

HID Service (HIDS)

服务 UUID

#define BT_UUID_HIDS_VAL       0x1812
#define BT_UUID_HIDS           BT_UUID_DECLARE_16(BT_UUID_HIDS_VAL)

必需特征

特征UUID用途
Report Map0x2A4BHID 报告描述符
Report0x2A4D输入/输出/特征报告
HID Information0x2A4AHID 版本信息
HID Control Point0x2A4C控制命令(挂起/恢复)

可选特征

特征UUID用途
Protocol Mode0x2A4E报告协议/引导协议
Battery Level0x2A19电池电量
Boot Keyboard Input0x2A22引导协议键盘输入
Boot Keyboard Output0x2A32引导协议键盘输出
Boot Mouse Input0x2A33引导协议鼠标输入

HID 报告描述符

键盘报告描述符

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,
};

GATT 服务定义

Zephyr BLE HID 服务

#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;
}

连接和配对

BLE 广播

/* 广播数据 */
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);
    }
}

常见问题

1. 无法连接

2. 配对失败

3. 按键无响应

调试技巧

启用 BLE 日志

CONFIG_BT_LOG_LEVEL_DBG=y
CONFIG_BT_HIDS_LOG_LEVEL_DBG=y

nRF Connect App

使用 nRF Connect 手机 App:

  1. 扫描设备
  2. 连接并查看服务
  3. 订阅 HID 通知
  4. 测试按键

参考链接


*学习笔记创建时间: 2026-03-19*