基于小智派-LZ3863星闪开发板:OpenHarmony 蓝牙开发—基础连接服务端
1.1、实验目的
本实验旨在帮助学习者掌握 OpenHarmony 轻量系统中 BLE(Bluetooth Low Energy,低功耗蓝牙)GATT 服务端 的基本使用方法。通过本实验,你将学会:
- 理解 BLE GAP(Generic Access Profile) 广播与连接机制,以及 GATT(Generic Attribute Profile) 服务/特征模型;
- 在开发板上实现 BLE UART 服务端,完成 GATT 服务注册、特征添加与广播启动;
- 使用 CMSIS-RTOS v2 创建独立的收发任务,实现与客户端的双向数据通信;
- 掌握 BLE 连接状态回调、Notify 通知发送与 Write 写请求接收的处理流程;
- 完成案例代码的编译、烧录,并与
d2_ble_connect_client客户端案例进行双板联调。
1.2、实验内容
本案例在 LZ3863-星闪开发板上实现 BLE GATT 服务端 功能:初始化蓝牙协议栈并注册 UART 服务,启动可连接广播;连接建立后,通过 Notify 周期性向客户端发送数据,同时接收客户端写入的数据并打印到串口。
| 项目 | 说明 |
|---|---|
| 应用层源文件 | ble_server_app.c(任务与收发逻辑) |
| 协议栈封装 | ble_server_lib.c / ble_server_lib.h(GATT 服务、广播、回调) |
| 设备名称 | ble_uart(GAP 本地名)/ ble_uart_server(广播名) |
| 设备 MAC 地址 | 11:22:33:44:55:66 |
| 服务 UUID | 0xABCD |
| TX 特征 UUID | 0xCDEF(读 / 写 / Notify,服务端 → 客户端) |
| RX 特征 UUID | 0xEFEF(写 / 无应答写,客户端 → 服务端) |
| 发送数据 | "hello ble:from server" |
| 发送周期 | 约 1 秒(osDelay(100),tick = 10 ms) |
| 接收缓冲区 | g_ble_rx_data[256] |
| 初始化入口 | APP_FEATURE_INIT(ble_server_app) |
典型联调拓扑:
┌─────────────────────┐ BLE 连接 ┌─────────────────────┐
│ 开发板 A(本案例) │ ◄────────────► │ 开发板 B(d2 客户端)│
│ d1_ble_connect_ │ GATT Notify │ d2_ble_connect_ │
│ server(Server) │ GATT Write │ client(Client) │
│ 广播: ble_uart_ │ │ 扫描并连接服务端 │
│ server │ │ 发送: hello ble: │
│ 发送: hello ble: │ │ from client │
│ from server │ │ │
└─────────────────────┘ └─────────────────────┘
说明:本案例为 BLE 服务端,需配合
d2_ble_connect_client客户端案例或手机 BLE 调试工具完成联调。建议先烧录并启动服务端,再启动客户端进行扫描连接。
1.3、实验环境
| 项目 | 说明 |
|---|---|
| 硬件 | 两块 LZ3863-星闪开发板(服务端 + 客户端)、USB 数据线 |
| 软件 | OpenHarmony v5.1.0 源码、hb 编译工具 |
| 联调案例 | d2_ble_connect_client(客户端,烧录至第二块开发板) |
| 调试工具 | 串口助手(波特率 115200,8N1) |
| 案例路径 | applications/sample/wifi-iot/app/d1_ble_connect_server/ |
注意:完整双向通信实验需要 两块开发板,分别烧录服务端固件(本案例)和客户端固件(
d2_ble_connect_client)。
2、基础知识
2.1、BLE 协议栈概述
BLE(Bluetooth Low Energy) 是一种面向低功耗、短距离通信的无线技术,广泛应用于可穿戴设备、传感器和物联网场景。BLE 协议栈从下到上可分为:
| 层次 | 英文名称 | 主要功能 |
|---|---|---|
| 物理层 | PHY | 2.4 GHz 射频收发 |
| 链路层 | LL | 连接建立、数据包封装 |
| 主机控制器接口 | HCI | 控制器与主机间通信 |
| 逻辑链路控制与适配 | L2CAP | 多路复用、分段重组 |
| 属性协议 | ATT | 属性读写、句柄管理 |
| 通用属性配置文件 | GATT | 基于 ATT 的服务/特征抽象 |
| 通用访问配置文件 | GAP | 广播、扫描、连接、配对 |
| 安全管理协议 | SMP | 加密与配对安全 |
本案例主要涉及 GAP(广播与连接)和 GATT Server(服务与特征)两层。
2.2、GAP:广播与连接
GAP 定义了 BLE 设备的发现与连接方式。本案例服务端采用 可连接非定向广播(Connectable Undirected Advertising):
服务端启动
↓
注册 GATT 服务与特征
↓
配置广播数据(Flags + Appearance + Local Name)
↓
gap_ble_start_adv() → 开始广播,等待客户端扫描连接
↓
客户端发起连接 → GAP_BLE_STATE_CONNECTED
↓
连接断开 → 重新广播
本案例广播参数:
| 参数 | 值 | 说明 |
|---|---|---|
| 广播类型 | BLE_ADV_TYPE_CONNECTABLE_UNDIRECTED |
可连接、非定向 |
| 广播间隔 | min=0x30, max=0x60 | 单位 0.625 ms |
| 广播信道 | 37 / 38 / 39(默认全信道) | BLE_ADV_CHANNEL_MAP_CH_DEFAULT |
| 过滤策略 | 任意设备可扫描、可连接 | SCAN_ANY_CONNECT_ANY |
| 广播时长 | 永久 | BTH_GAP_BLE_ADV_FOREVER_DURATION |
2.3、GATT:服务与特征
GATT 采用 Client/Server 模型。服务端维护一个或多个 Service(服务),每个服务包含若干 Characteristic(特征),特征可附带 Descriptor(描述符)。
本案例实现了一个 BLE UART 透传服务,结构如下:
Service: 0xABCD (BLE UART Service)
├── Characteristic TX: 0xCDEF
│ 属性: Read | Write | Write No Response | Notify
│ 描述符: Client Characteristic Configuration (0x2902)
│ 用途: 服务端通过 Notify 向客户端发送数据
│
└── Characteristic RX: 0xEFEF
属性: Write | Write No Response
用途: 客户端向服务端写入数据
| 角色 | 数据方向 | 本案例实现方式 |
|---|---|---|
| 服务端 → 客户端 | TX 特征 Notify | ble_server_send_input_report() → gatts_notify_indicate() |
| 客户端 → 服务端 | RX 特征 Write | ble_receive_write_req_cbk() → 写入 g_ble_rx_data |
2.4、BLE 事件回调机制
BLE 协议栈通过回调函数异步通知应用层状态变化。本案例注册了 GAP 与 GATT Server 两类回调:
GAP 回调:
| 回调函数 | 触发时机 | 处理逻辑 |
|---|---|---|
ble_server_adv_enable_cbk |
广播启动成功 | 打印日志 |
ble_server_adv_disable_cbk |
广播停止 | 打印日志 |
ble_server_connect_change_cbk |
连接状态变化 | 更新连接 ID/状态;断开时重新广播 |
ble_server_pair_result_cb |
配对完成 | 打印配对结果 |
GATT Server 回调:
| 回调函数 | 触发时机 | 处理逻辑 |
|---|---|---|
ble_server_service_add_cbk |
服务添加完成 | 添加 TX/RX 特征与描述符,启动服务 |
ble_server_characteristic_add_cbk |
特征添加完成 | 保存 TX 特征值句柄(用于 Notify) |
ble_server_descriptor_add_cbk |
描述符添加完成 | 打印日志 |
ble_server_service_start_cbk |
服务启动完成 | 所有服务就绪后启动广播 |
ble_receive_write_req_cbk |
收到客户端写请求 | 将数据拷贝至 g_ble_rx_data,置位 g_ble_new_data_ready |
ble_receive_read_req_cbk |
收到客户端读请求 | 打印日志 |
ble_mtu_changed_cbk |
MTU 协商完成 | 打印 MTU 大小 |
2.5、软件调用层次
本案例的软件调用层次如下:
应用层(ble_server_app.c)
├── ble_server_app() ← APP_FEATURE_INIT 注册入口
├── ble_rx_server() ← 接收任务:打印客户端写入的数据
└── ble_tx_server() ← 发送任务:周期性 Notify 发送数据
│
封装层(ble_server_lib.c)
├── ble_server_init() ← 初始化:注册回调、启用 BLE、添加服务
├── ble_set_adv_data() ← 配置广播数据
├── ble_start_adv() ← 启动广播
├── ble_server_send_input_report() ← GATT Notify 发送
└── ble_get_connection_state() ← 查询连接状态
│
协议栈 / 驱动层
├── gap_ble_*() ← GAP 广播与连接管理
├── gatts_*() ← GATT Server 服务/特征管理
└── enable_ble() ← 启用 BLE 协议栈
2.6、核心 API 介绍
2.6.1、头文件
#include "ble_server_lib.h"
#include "cmsis_os2.h"
#include "ohos_init.h"
2.6.2、应用层 API
| API 名称 | 功能说明 |
|---|---|
ble_server_init() |
初始化 BLE 服务端:注册回调、设置设备名/MAC、注册 GATT Server、添加 UART 服务 |
ble_get_connection_state() |
获取连接状态,返回 0 表示未连接,非 0 表示已连接 |
ble_server_send_input_report(data, len) |
通过 GATT Notify 向已连接客户端发送数据 |
osThreadNew(func, arg, &attr) |
创建 RTOS 线程 |
osDelay(ticks) |
线程延时,100 ticks ≈ 1 秒(tick = 10 ms) |
APP_FEATURE_INIT(func) |
注册应用特性初始化入口,系统启动后自动执行 |
2.6.3、协议栈 API(ble_server_lib.c 内部调用)
| API 名称 | 功能说明 |
|---|---|
enable_ble() |
启用 BLE 协议栈 |
gap_ble_set_local_name() |
设置 GAP 本地设备名称 |
gap_ble_set_local_addr() |
设置 BLE 设备 MAC 地址 |
gap_ble_set_adv_data() |
设置广播数据与扫描响应数据 |
gap_ble_set_adv_param() / gap_ble_start_adv() |
配置并启动广播 |
gatts_register_server() |
注册 GATT Server |
gatts_add_service() |
添加 GATT 服务 |
gatts_add_characteristic() |
添加特征 |
gatts_add_descriptor() |
添加描述符(如 CCC) |
gatts_start_service() |
启动 GATT 服务 |
gatts_notify_indicate() |
发送 Notify/Indicate 通知 |
3、程序设计
3.1、程序架构
本案例目录结构:
d1_ble_connect_server/
├── ble_server_app.c # 应用层:收发任务与入口
├── ble_server_lib.c # 协议栈封装:GATT 服务、广播、回调
├── ble_server_lib.h # 头文件:UUID 宏定义与 API 声明
├── BUILD.gn # GN 编译配置
├── README_zh.md # 案例简要说明
└── 实验手册.md # 本实验手册
程序执行流程:
系统启动
│
▼
ble_server_app() ← APP_FEATURE_INIT 注册,自动执行
│
├── ble_server_init() ← 延时 3s → 注册回调 → 启用 BLE
│ ├── 设置设备名/MAC
│ ├── gatts_register_server()
│ ├── ble_add_service() → 添加 0xABCD 服务
│ └── 回调链:添加特征 → 启动服务 → 开始广播
│
├── osThreadNew(ble_rx_server) ← 创建接收任务
└── osThreadNew(ble_tx_server) ← 创建发送任务
│
▼
┌───────────────────────────────────────────┐
│ ble_rx_server(接收任务) │
│ 轮询 g_ble_new_data_ready → 打印接收数据 │
└───────────────────────────────────────────┘
┌───────────────────────────────────────────┐
│ ble_tx_server(发送任务) │
│ 连接建立后 → 每 1s Notify 发送 send_date │
└───────────────────────────────────────────┘
3.2、源文件说明
| 文件 | 说明 |
|---|---|
ble_server_app.c |
应用层主程序,定义收发数据缓冲区,创建 RX/TX 两个 RTOS 任务 |
ble_server_lib.c |
BLE 协议栈封装,实现 GATT 服务注册、广播配置、GAP/GATT 回调处理 |
ble_server_lib.h |
UUID 宏定义、数据结构、全局变量声明与 API 接口 |
BUILD.gn |
编译配置,生成 ble_connect_server 静态库 |
3.3、关键代码分析
(1)UUID 与服务配置
#define BLE_UUID_SERVER_SERVICE 0xABCD
#define BLE_CHARACTERISTIC_UUID_TX 0xCDEF
#define BLE_CHARACTERISTIC_UUID_RX 0xEFEF
#define BLE_CLIENT_CHARACTERISTIC_CONFIGURATION 0x2902
TX 特征支持 Read、Write 和 Notify,并附带 CCC 描述符(默认启用 Notify);RX 特征支持 Write 和 Write No Response,用于接收客户端数据。
(2)全局发送数据
uint8_t send_date[20] = "hello ble:from server";
服务端连接建立后,发送任务每隔约 1 秒通过 Notify 向客户端发送该字符串。
(3)接收任务 — ble_rx_server
接收任务通过轮询全局标志位 g_ble_new_data_ready 检测客户端写入的新数据(由 GATT 写请求回调 ble_receive_write_req_cbk 置位):
void ble_rx_server(void *arg)
{
unused(arg);
while (1)
{
if (g_ble_new_data_ready == true)
{
printf("ble rev date:%s", g_ble_rx_data);
g_ble_new_data_ready = false;
}
osDelay(1);
}
}
(4)发送任务 — ble_tx_server
发送任务在检测到 BLE 连接已建立后,周期性调用 Notify 发送接口:
void ble_tx_server(void *arg)
{
unused(arg);
while (1)
{
if (ble_get_connection_state() != 0)
{
ble_server_send_input_report(send_date, sizeof(send_date));
}
osDelay(100);
}
}
注意:仅在
ble_get_connection_state()非 0(已连接)时才发送数据,避免无连接时的无效 Notify 调用。
(5)程序入口 — ble_server_app
static void ble_server_app(void)
{
ble_server_init();
osThreadAttr_t rx_attr = {0};
rx_attr.name = "ble_rx_server";
rx_attr.stack_size = 8192;
rx_attr.priority = osPriorityNormal;
osal_kthread_lock();
if (osThreadNew(ble_rx_server, NULL, &rx_attr) == NULL)
{
printf("[LedTest] Failed to create led_pwm_main thread!\n");
}
osal_kthread_unlock();
osThreadAttr_t tx_attr = {0};
tx_attr.name = "ble_tx_server";
tx_attr.stack_size = 8192;
tx_attr.priority = osPriorityNormal;
osal_kthread_lock();
if (osThreadNew(ble_tx_server, NULL, &tx_attr) == NULL)
{
printf("[LedTest] Failed to create led_pwm_main thread!\n");
}
osal_kthread_unlock();
}
APP_FEATURE_INIT(ble_server_app);
系统启动后自动调用 ble_server_app(),先完成 BLE 服务端初始化,再创建收发两个独立任务。收发任务优先级相同,通过 RTOS 时间片轮转调度,互不阻塞。
(6)服务端初始化 — ble_server_init
void ble_server_init(void)
{
(void)osal_msleep(3000); /* 延时 3s,等待 BLE 协议栈初始化完毕 */
ble_server_register_callbacks(); /* 注册 GAP/GATT 回调 */
enable_ble(); /* 启用 BLE */
gap_ble_set_local_name(g_ble_name_value, sizeof(g_ble_name_value));
gap_ble_set_local_addr(&ble_addr); /* MAC: 11:22:33:44:55:66 */
gatts_register_server(&app_uuid, &g_server_id);
ble_add_service(); /* 添加 0xABCD UART 服务 */
bth_ota_init();
}
初始化完成后,GATT 回调链自动执行:添加特征 → 启动服务 → 配置广播数据 → 开始广播。
(7)GATT Notify 发送 — ble_server_send_input_report
errcode_t ble_server_send_input_report(uint8_t *data, uint16_t len)
{
gatts_ntf_ind_t param = {0};
param.attr_handle = g_notify_indicate_handle; /* TX 特征值句柄 */
param.value_len = len;
param.value = data;
gatts_notify_indicate(BLE_SERVER_ID, g_ble_conn_id, ¶m);
return ERRCODE_BT_SUCCESS;
}
Notify 发送依赖 TX 特征的值句柄(在 ble_server_characteristic_add_cbk 中保存至 g_notify_indicate_handle)和当前连接 ID(在 ble_server_connect_change_cbk 中保存至 g_ble_conn_id)。
(8)BUILD.gn — 编译配置
static_library("ble_connect_server") {
sources = [
"ble_server_lib.c",
"ble_server_app.c",
]
// ...
}
3.4、程序执行流程
4、编译步骤
本实验编译服务端固件并烧录至 开发板 A;客户端固件请参考 d2_ble_connect_client 案例编译烧录至 开发板 B。以下步骤只需在首次编译时完成 4.1~4.3 的配置注册。
4.1、确认案例目录
确认案例已位于 OpenHarmony 源码目录下:
applications/sample/wifi-iot/app/d1_ble_connect_server/
├── ble_server_app.c
├── ble_server_lib.c
├── ble_server_lib.h
├── BUILD.gn
└── 实验手册.md
若从外部复制,请将 d1_ble_connect_server 目录放到上述 app/ 路径下。
4.2、修改 BUILD.gn(注册编译组件)
编辑 applications/sample/wifi-iot/app/BUILD.gn,在 features 列表中添加本案例:
lite_component("app") {
features = [
"startup",
"d1_ble_connect_server:ble_connect_server", // 添加此行
]
}
4.3、修改 SDK 配置文件
步骤 1:编辑 device/soc/hisilicon/ws63v100/sdk/build/config/target_config/ws63/config.py
找到 'ws63-liteos-app' 配置段,在其 'ram_component' 列表中添加:
"ble_connect_server"
步骤 2:编辑 device/soc/hisilicon/ws63v100/sdk/libs_url/ws63/cmake/ohos.cmake
找到 "ws63-liteos-app" 对应的 set(COMPONENT_LIST 部分,添加:
"ble_connect_server"
4.4、编译固件
在 OpenHarmony 源码根目录下执行编译:
rm -rf out
hb set -root .
# 通过上下方向键选择 ws63 对应的编译分支(如 nearlink_txnl63 / nearlink_dk_3863)
hb build -f
编译成功后,将固件烧录到 开发板 A(作为 BLE 服务端)。
4.5、编译客户端固件(开发板 B)
按照 d2_ble_connect_client 案例的 README 或实验手册,完成客户端的 BUILD.gn 与 SDK 配置注册,编译后将固件烧录到 开发板 B。
4.6、烧录固件
使用开发板配套的烧录工具,将编译生成的固件分别烧写到两块 LZ3863-星闪开发板。具体烧录步骤请参考开发板用户手册。
5、运行结果
5.1、硬件准备
- 将 开发板 A(服务端固件)上电或复位,等待 BLE 广播启动;
- 将 开发板 B(客户端固件)上电或复位,开始扫描并连接服务端;
- 两块开发板分别通过 USB 连接 PC,打开串口助手查看日志(波特率 115200,8N1)。
建议:先启动服务端开发板,确认广播启动日志输出后,再启动客户端开发板,以提高首次连接成功率。
5.2、串口配置
| 参数 | 值 |
|---|---|
| 波特率 | 115200 |
| 数据位 | 8 |
| 停止位 | 1 |
| 校验位 | 无 |
| 流控 | 无 |
5.3、服务端预期输出(开发板 A)
烧录服务端固件并复位后,串口助手可观察到 BLE 初始化与服务注册过程:
[ble server] beginning add service
[ble server] add characters_and_descriptors cbk service:1, srv_handle:xx, ...
[ble server] TX characters:1 srv_handle:xx
[ble server] RX characters:1 srv_handle: xx
[ble server] start service cbk , start adv
[ble uart adv] ble_uart_start_adv adv_id 1
[ble server] adv enable cbk adv_id:1 status:0
表示 GATT UART 服务注册成功,广播已启动,设备名 ble_uart_server 可被客户端扫描发现。
客户端连接成功后,服务端还会输出:
[ble server] connect state change conn_id: x, status: 2, ...
[ble server] send input report indicate_handle:xx
ble rev date:hello ble:from client
其中:
connect state change ... status: 2表示 BLE 连接已建立(GAP_BLE_STATE_CONNECTED);send input report表示正在通过 Notify 向客户端发送数据;ble rev date:hello ble:from client表示收到客户端通过 RX 特征写入的数据。
5.4、客户端预期输出(开发板 B)
烧录 d2_ble_connect_client 固件并复位后,串口助手可观察到扫描、连接与数据收发过程:
ble enable: 0
[ble client] set scan param status: 0
[ble client] connect state change conn_id: x, status: 2, ...
[ble client] Discovery service callback client:x conn_id:x
[ble client] discover character ... properties:xx
ble rev date:hello ble:from server
其中:
ble enable: 0表示 BLE 协议栈启用成功;connect state change ... status: 2表示已成功连接服务端;Discovery service/discover character表示 GATT 服务发现完成;ble rev date:hello ble:from server表示收到服务端 Notify 发送的数据。
5.5、双向通信验证
连接建立后,两块开发板应持续(约每秒一次)交换数据:
| 方向 | 发送内容 | 接收端串口输出 |
|---|---|---|
| 服务端 → 客户端 | hello ble:from server |
客户端:ble rev date:hello ble:from server |
| 客户端 → 服务端 | hello ble:from client |
服务端:ble rev date:hello ble:from client |
5.6、结果分析
| 现象 | 说明 |
|---|---|
服务端输出 start adv / adv enable cbk |
GATT 服务就绪,广播已成功启动 |
服务端输出 connect state change ... status: 2 |
客户端已成功连接 |
服务端周期性输出 send input report |
Notify 发送正常执行 |
服务端输出 ble rev date:hello ble:from client |
客户端 Write 数据接收正常 |
客户端输出 ble rev date:hello ble:from server |
Notify 接收正常,双向通信成功 |
| 客户端长时间无连接日志 | 服务端未启动或广播未就绪,检查服务端日志 |
| 连接成功但无数据收发 | GATT 服务发现未完成,检查 UUID 是否匹配 |
5.7、常见问题排查
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| 客户端扫描不到服务端 | 服务端未启动或广播失败 | 确认服务端输出 adv enable cbk;先启动服务端 |
| 连接后立即断开 | 信号干扰或协议栈未就绪 | 两块板靠近放置;确认服务端 init 延时 3s 已执行 |
服务端无 ble rev date 输出 |
客户端未写入或 RX 特征 UUID 不匹配 | 确认客户端已连接并完成服务发现 |
客户端无 ble rev date 输出 |
Notify 未启用或 TX 特征句柄错误 | 检查 CCC 描述符是否默认启用 Notify |
编译报错找不到 ble_connect_server |
BUILD.gn 或 config.py 未正确修改 | 逐步核对 4.2、4.3 节的配置项 |
| 两块板烧录了相同固件 | 未区分服务端/客户端固件 | 分别编译烧录 d1(服务端)和 d2(客户端) |
6、实验扩展
完成基本实验后,可尝试以下扩展练习:
- 修改设备名称:更改
g_local_name和g_ble_name_value,观察客户端扫描结果的变化; - 修改发送内容:更改
send_date字符串,验证 Notify 发送自定义数据; - 调整发送频率:修改
ble_tx_server中osDelay(100)的参数,观察收发频率变化; - 手机 BLE 调试:使用手机 BLE 调试 App(如 nRF Connect)扫描连接服务端,手动读写特征值;
- 断开重连测试:断开 BLE 连接后观察服务端是否自动重新广播并成功重连;
- 结合后续案例:在 BLE 连接基础上,配合其他蓝牙或网络通信案例实现更复杂的物联网应用。
更多推荐



所有评论(0)