蓝牙开发指南
本文面向蓝牙业务的选型和流程设计,说明 HiDiTing V100 中 BLE 和 BR/EDR 的角色、数据模型与常见场景。可运行示例的编译、AT 命令和核心代码走读分别见 BLE 开发指南 和 BR 开发指南。
文档导航
| 文档 | 适用场景 | 配套实现 |
|---|---|---|
| 本文 | 了解 BLE、BR/EDR、GATT、SPP、PBAP、HFP、A2DP 和 AVRCP,完成业务选型与流程设计 | 概念和场景说明 |
| BLE 开发指南 | 广播、扫描、配对、GATT 服务发现、读写和通知 | /samples/native_samples/ble |
| BR 开发指南 | 经典蓝牙发现、配对、SPP 连接和双向数传 | /samples/native_samples/br |
建议先在本文的场景选型中确认协议与角色,再按 BLE 开发指南或 BR 开发指南完成示例联调。需要确认入参、返回值或结构体字段时,再查阅文档站内的蓝牙接口参考。
蓝牙基础知识
模块职责与软件关系

开发者通常在业务层组织设备状态、连接策略和数据处理,通过 BS 服务接口使用协议栈。不建议业务直接跨过服务层依赖协议栈内部实现,否则后续升级时会将内部变化传导给业务。
| 能力 | 主要职责 | 常用场景 |
|---|---|---|
| BLE GAP | 本地信息、广播、扫描、连接、配对 | 低功耗配件、传感器、近场配网 |
| GATT Server | 定义服务、特征和描述符,响应读写,发送通知/指示 | 手表暴露状态或控制点 |
| GATT Client | 发现对端服务,读写特征,接收通知/指示 | 手表主动访问外设 |
| BR/EDR GAP | 可发现、扫描、配对和链路管理 | 经典蓝牙配件或手机连接 |
| SPP Port | 建立面向端口的双向字节流 | 串口替代、私有指令和诊断数据 |
| PBAP/HFP | 电话簿、通话记录和通话控制 | 穿戴设备通讯录与通话 |
| A2DP/AVRCP | 音频流与媒体控制 | 蓝牙音频播放、暂停和曲目控制 |
角色不是固定绑定的
BLE 的中央设备(Central)和外围设备(Peripheral)描述连接建立时的协议角色,GATT Client/Server 描述属性数据的访问方向。处于外围设备角色的设备也可以同时作为 GATT Client,不要根据设备类型推导 GATT 角色。BR/EDR 的 SPP Client 主动连接远端端口,SPP Server 创建端口并决定是否接受连接。
通用调用顺序
不同 Profile 的具体函数不同,但生命周期应保持如下顺序:
- 注册设备层和 Profile 回调。
- 配置本地地址、名称、设备类型或服务 UUID。
- 使能协议栈及所需 Profile。
- 启动广播/可发现或扫描,再建立连接和配对。
- 在回调中记录
dev_id、端口或属性句柄,业务使用这些运行时标识收发数据。 - 退出时先停止业务和设备发现,再断开连接、去使能 Profile 和协议栈。
场景选型
| 业务需求 | 建议能力 | 设计要点 |
|---|---|---|
| 周期上报少量传感数据 | BLE + GATT | 以特征表达数据,上报使用 Notification,关键状态可使用 Indication |
| 手机向设备下发配置 | BLE + GATT Server | 将配置分成可写特征,回调中做长度、取值范围和权限校验 |
| 设备主动读取 BLE 外设 | BLE Central + GATT Client | 过滤广播,连接后按 UUID 发现服务,不要固定写死句柄 |
| 连续字节流或私有串口协议 | BR/EDR + SPP | 定义分帧、长度、校验和超时,不将一次回调等同于一帧 |
| 同步手机通讯录 | PBAP | 先完成基础连接与鉴权,再分页拉取和解析 vCard |
| 控制手机通话 | HFP | 用状态机统一处理铃声、接听、挂断和音频链路 |
| 蓝牙音乐与按键控制 | A2DP + AVRCP | A2DP 承载音频,AVRCP 承载播放控制和元数据 |
BLE 连接与数据通信
广播与扫描

外围设备角色应先准备广播参数和广播数据,再启动广播。广播数据容量有限,建议只放置用于识别和过滤的服务 UUID、设备名等必要信息。中央设备角色从扫描回调取得对端地址和地址类型,应同时校验广播内容,避免只根据一个可变的设备名连接。
| 环节 | 需要保存的数据 | 失败处理 |
|---|---|---|
| 广播 | 广播 ID、广播状态 | 启动失败时不进入等待连接状态 |
| 扫描 | 扫描 ID、过滤条件 | 超时后停止扫描,按策略退避重试 |
| 发现结果 | 地址、地址类型、RSSI、服务 UUID | 校验回调指针和数据长度 |
连接、配对与断开
主动连接前停止或降低扫描占用;连接完成回调中保存 dev_id;业务需要加密或持久信任时再启动配对。配对、连接和 GATT 服务发现是异步过程,后续步骤必须由成功回调驱动,不能仅根据发起函数的成功返回值认定连接已建立。
GATT 服务端设计
GATT 服务由 Service、Characteristic 和 Descriptor 组成。业务先定义稳定的 UUID 和属性权限,再注册服务模板。读回调返回当前值;写回调先检查长度、偏移和取值范围,再更新业务状态;主动上报时根据可靠性选择 Notification 或 Indication。
| 上报方式 | 协议层确认 | 适用数据 |
|---|---|---|
| Notification | 无 | 高频、允许个别丢失的状态或传感数据 |
| Indication | 有 | 必须确认送达的关键事件,吞吐量低于 Notification |
GATT 客户端设计
连接成功后按 UUID 发现服务和特征,保存本次连接返回的句柄。句柄可因对端固件升级而改变,不要将一次调试得到的句柄写死在产品代码中。服务发现后再注册读写和通知回调,然后开始业务收发。
接口入参与回调类型见 BLE 连接接口 和 GATT 接口。
BR/EDR 连接与数据通信
可发现、扫描与配对
SPP Server 一侧将设备设为可发现,SPP Client 一侧扫描并根据地址、名称和 Class of Device 过滤目标。配对完成后保存信任关系;如果产品要支持“忘记设备”,还应将移除配对记录、清理业务状态和断开当前连接组成一个完整操作。
SPP 端口和数据流

SPP Server 先使能 Port 服务、创建服务端口并注册连接请求、数据接收和状态回调。SPP Client 使能 Port 服务并创建远端端口描述,配对后主动连接。Server 在连接请求回调中明确接受或拒绝,双方只能在 Port 连接状态为已连接时发送数据。
SPP 提供字节流,业务帧边界必须由上层协议定义。建议至少包含版本、命令字、负载长度和校验字段,接收回调应支持半包和粘包重组。
BR 设备和 Port 的函数定义见 BR/EDR 连接接口 和 Port 接口。
电话簿、通话与媒体
PBAP 电话簿
PBAP 适合从手机拉取联系人和通话记录。建议将“连接、选择电话簿、分页拉取、vCard 解析、断开”建模为状态机,每次回调仅驱动下一个状态。大型电话簿必须分页,同时限制单条记录和累计缓存大小。
HFP 通话控制
HFP 业务不应只根据本地按键直接切换界面,而应以协议回调上报的实际通话状态为准。拨号、来电、接听、挂断、保持和音频链路是相关但不同的状态,错误回调和超时必须回收中间状态。
A2DP 与 AVRCP
A2DP 负责媒体音频流,AVRCP 负责播放/暂停、上下曲和媒体元数据。两者可以处于不同连接状态,业务层应分别记录 Profile 状态,在界面或按键处理前综合判断。音频缓冲、时钟和编解码属于数据面,媒体控制属于控制面,不应在同一回调中执行耗时操作。
设计自己的蓝牙业务
- 从需求确定 BLE 或 BR/EDR,以及本端的链路角色和 Profile 角色。
- 列出业务状态和异步事件,将连接、配对、服务发现和数据收发设计为状态机。
- 定义稳定的 UUID、特征权限或 SPP 业务帧,不把运行时
dev_id、句柄和 Port ID 当作固定配置。 - 从 /samples/native_samples/ble 或 /samples/native_samples/br 复用注册、回调和错误处理骨架,替换业务数据与过滤条件。
- 对所有外部数据校验指针、长度、枚举范围和当前状态;回调中不执行长时间阻塞操作。
- 至少验证正常连接、主动断开、异常掉线、重连、重复配对、超长数据和资源释放。
注意事项
- 地址字节序以接口结构体和回调数据为准,日志展示顺序与内存排布可能不同。
- 所有建立连接、发现服务和数据收发接口都要检查立即返回值,最终结果以对应回调为准。
- 广播、扫描和可发现都需要明确的超时与退避策略,避免长时间高功耗运行。
- 配对信息属于持久状态,版本升级时需考虑兼容、迁移和清除策略。
- 不在回调中保存 SDK 短生命周期指针;需要异步处理时按实际长度复制数据。
常见问题
| 现象 | 优先检查 |
|---|---|
| 扫描不到设备 | 角色、广播/可发现状态、地址类型、过滤条件和扫描超时 |
| 接口返回成功但没有连接 | 是否收到连接状态回调,对端是否可连接,配对记录是否冲突 |
| GATT 读写失败 | 服务发现是否完成,句柄是否来自当前连接,属性权限和长度是否匹配 |
| 收不到 Notification/Indication | 对端是否订阅,服务端 dev_id、实例 ID 和属性 ID 是否为当前值 |
| SPP 收到半包或多帧粘包 | 在业务层按帧头和长度累积重组,不用一次回调作为一帧边界 |