BLE 开发指南
本文以 /samples/native_samples/ble 为对象,介绍如何使用两块 HiDiTing V100 完成 BLE 广播、扫描、配对、GATT 服务发现、读写和通知验证。协议选型和场景设计见蓝牙开发指南。
BLE 驱动背景知识
Demo 在同一份固件中同时包含中央设备(Central)/GATT Client 和外围设备(Peripheral)/GATT Server 实现,通过 AT+BLEDEMOENABLE=<role> 选择本板角色。这里的 Central 和 Peripheral 是 BLE 协议及 SDK 接口中的连接角色,不是名为 Peripheral 的模块或特性。
| 参数 | 角色 | 本地名称 | 本地地址 |
|---|---|---|---|
0 |
中央设备(Central)+ GATT Client | ble_client |
11:23:34:45:56:61 |
1 |
外围设备(Peripheral)+ GATT Server | ble_server |
11:23:34:45:56:63 |
服务端创建 16 bit UUID 为 0x00AA 的服务和 UUID 为 0x01FF 的特征。UUID 在数组中按低字节在前存放,因此源码对应 {0xAA, 0x00} 和 {0xFF, 0x01}。客户端必须在服务发现结果中按 UUID 匹配句柄,不能使用一个预设句柄。
快速跑通 BLE Demo
准备条件
- 两块 HiDiTing V100 开发板,分别作为服务端和客户端。
- 两个可独立操作的串口,波特率为
750000。 - 一站式 CLI 开发环境;环境安装和串口监视见一站式 CLI 开发环境使用指南。
编译与烧录
完成一站式 CLI 环境配置后执行:
使用一站式 CLI 烧写固件并打开 UART2 串口监视器。以下为 Windows USB DFU 示例;将 COM3 替换为实际日志串口,其他平台和串口烧写参数参见一站式 CLI 开发环境使用指南。
fbb flash -f "$env:FBB_SDK_DIR\output\3322\fwpkg\diting-community.fwpkg" --chip 3322 -d --timeout 180
fbb monitor --port COM3 --baud 750000
服务端操作
在第一块板输入:
命令会完成协议栈初始化、GATT 服务注册和广播启动。日志应包含 ble init success、服务初始化成功和广播启动信息。

客户端扫描与配对
在第二块板依次输入:
扫描回调发现名称为 ble_server 的对端后,Demo 停止扫描并发起配对。配对成功后自动发现全部 GATT 服务,日志会输出目标特征的 value handle。

如需手动对固定地址发起配对,先停止扫描,再使用对端地址:
读写与通知
将下列 <value_handle> 替换为客户端服务发现日志中的实际句柄:
Demo 的写入负载为固定测试数据,服务端写回调会按长度打印内容;读回调会输出句柄、长度和数据。

在服务端使用连接后日志中的实例 ID 和属性 ID 发送通知:

文件结构与代码走读
文件结构
/samples/native_samples/ble
├── CMakeLists.txt # 组件、源文件和编译定义
├── ble_sample_common.c # BLE 设备层、角色切换和 AT 命令
├── ble_sample_common.h # AT 入参结构、校验规则和命令表
├── ble_client.c # GATT Client 注册、服务发现、读写和通知回调
├── ble_client.h
├── ble_server.c # GATT Server 服务模板、读写回调和通知
├── ble_server.h
└── README.md # Demo 命令与编译入口
角色、状态和 AT 入参
ble_sample_common.h 中的角色与状态枚举是业务流程的核心数据:
typedef enum {
BLE_API_BT_AS_CENTRAL,
BLE_API_BT_AS_PERIPHERAL,
BLE_API_BT_AS_MAX,
} ble_api_bt_role_t;
typedef struct {
ble_bt_stack_state_t stack_status;
ble_bt_discovery_state_t scan_status;
ble_bt_discovery_state_t adv_status;
} ble_bt_stack_info_t;
ble_bt_stack_info_t 避免重复启动或停止协议栈、扫描和广播。扩展命令时,应在调用 SDK 前检查状态,在成功回调中更新最终状态。
初始化与回调驱动
bt_ble_init() 先初始化状态互斥锁,再注册设备层回调并使能 BLE。回调集中处理协议栈、连接、配对、扫描结果和移除配对结果:
static bs_ble_callbacks_t ble_cb = {
.stack_state_cbk = process_ble_stack_state_change_cbk,
.connect_state_cbk = process_ble_connect_state_changed_cbk,
.pair_complete_cbk = process_ble_pair_cbk,
.discover_result_cbk = process_ble_discover_cbk,
.pair_remove_cbk = process_ble_remove_device_cbk,
};
外围设备角色使能成功后启动广播并初始化 GATT 服务;中央设备角色初始化 GATT Client。这两条分支由 g_role 决定,自定义业务时可以将单一角色拆成独立组件。
GATT Server 服务模板
ble_server.c 用 bs_gatt_service_t 组织服务 UUID、实例回调和属性数组。业务需重点替换服务/特征 UUID、属性权限、最大长度和读写回调,而不是复制整份示例。注册流程只包含两个关键步骤:
ret = bs_gatts_register(&g_ble_app_uuid, &g_ble_demo_server_id);
if (ret != ERRCODE_BT_SUCCESS) {
return ret;
}
ret = bs_gatts_service_instance_init(
bs_get_ble_service_id(),
(bs_gatt_service_template_t *)&g_ble_demo_gatt_service,
&g_ble_demo_mobile_inst_id);
读写回调对 SDK 传入的指针和长度做校验,数据输出按 data_len 处理,不将二进制数据当作以 \0 结尾的字符串。
GATT Client 服务发现
ble_client.c 注册 Client 后,在配对成功回调中发现全部服务。发现回调遍历 service_count,只保存目标 UUID 对应的特征句柄:
static const uint8_t g_target_service_uuid[UUID16_LEN] = {0xAA, 0x00};
static const uint8_t g_target_character_uuid[UUID16_LEN] = {0xFF, 0x01};
bool is_target_service = service[s].service.uuid.uuid_len == UUID16_LEN &&
memcmp(service[s].service.uuid.uuid,
g_target_service_uuid, UUID16_LEN) == 0;
匹配到服务后再遍历其 character_list。找到目标特征后保存 value_handle,注册读写/通知回调并发起 MTU 协商。如果服务或特征未找到,流程会停止,避免用无效句柄读写。
读写和通知
Client 使用当前 dev_id、client_id 和发现得到的句柄调用 bs_gattc_read()/bs_gattc_write()。Server 发送通知时组装运行时实例和属性 ID:
bs_gatts_ntf_ind_t param = {
.att_id = attribute_id,
.value = g_sample_data,
.value_len = sizeof(g_sample_data),
.instance_id = instance_id,
};
ret = bs_gatts_notify_indicate(dev_id, server_id, ¶m);
接口结构体和返回值见 BLE 连接接口 和 GATT 接口。
基于 BLE Demo 开发自己的应用
- 在 /samples/native_samples 下建立独立目录,复用
ble的CMakeLists.txt组件定义。 - 如产品只需要一个角色,只保留该角色的源文件和回调,减少全局状态。
- 为服务分配正式 UUID,设计特征的读、写、通知/指示权限和最大数据长度。
- 将广播名称、地址和扫描过滤从 Demo 常量替换为产品配置。
- 将回调中的打印替换为状态机事件;需延后使用的数据要按回调长度复制。
- 为业务层提供明确的启动、停止、连接和数据收发入口,从应用任务、服务或 UI 事件调用;不要让业务状态机依赖 AT 参数结构。
- 将组件加入目标配置,用
fbb set-target pack_diting_community和fbb build --clean做全量编译,并通过业务入口验证初始化、连接、收发和反序释放。
如需保留板端串口回归,可再为同一组业务入口增加独立 AT 命令并分配不重复的 cmd_id。AT 层只负责参数校验和调用转发,注册失败不能被忽略;该步骤不是 BLE 业务组件的必需组成。
注意事项
dev_id、GATT Client/Server ID、服务实例 ID 和属性句柄都是运行时值,以当次回调和日志为准。AT+BLEDEMOWRITE是通路测试命令,负载由 Demo 固定;实际业务应将数据和长度作为入参并严格校验。- 去初始化顺序与初始化相反,先停止扫描/广播和业务,再停止自动连接、去使能协议栈并销毁同步资源。
常见错误
| 现象 | 原因与处理 |
|---|---|
BLEDEMOENABLE 失败 |
检查角色参数是否为 0/1,以及上一次协议栈是否已正常释放 |
| 客户端一直扫描 | 确认服务端已启动广播、名称为 ble_server,并确认通信双方未配置成相同角色 |
| 服务发现后没有目标句柄 | 核对对端服务 UUID 0x00AA 和特征 UUID 0x01FF,不要直接使用服务数组的固定下标 |
| 读写失败 | 使用当次发现日志中的 value handle,确认连接未断开且 MTU 协商已完成 |
| 收不到通知 | 确认使用当前 dev_id、实例 ID 和属性 ID,且客户端已注册通知回调 |