基于小智派-LZ3863星闪开发板:OpenHarmony蓝牙开发—UART 透传客户端
1、实验简介
参考网址:https://gitee.com/Lockzhiner-Electronics/lz3863/tree/master/apps/d4_ble_uart_client
1.1、实验目的
本实验旨在帮助学习者掌握 OpenHarmony 轻量系统中 BLE UART 透传客户端 的设计与实现方法。通过本实验,你将学会:
- 理解 BLE GATT 客户端与 UART 串口 结合,实现无线串口透传的基本原理;
- 使用 消息队列 解耦 UART 接收中断与 BLE 发送任务,保障数据传输稳定性;
- 在开发板上实现 BLE 客户端扫描、连接、GATT 服务发现,并将 UART 数据转发至蓝牙服务端;
- 接收服务端 Notify 数据并在串口终端打印,完成双向透传验证;
- 完成案例代码的编译、烧录,并与
d3_ble_uart_server服务端案例进行双板联调。
1.2、实验内容
本案例在 LZ3863-星闪开发板上实现 BLE UART 透传客户端 功能:初始化蓝牙协议栈并注册 GATT 客户端,扫描并连接指定 MAC 地址的服务端;连接建立后完成 GATT 服务发现,将 PC 串口助手发送的数据经 UART 接收后通过 BLE Write 转发至服务端,同时接收服务端 Notify 数据并在串口打印。
| 项目 | 说明 |
|---|---|
| 应用层源文件 | ble_client_app.c(UART 收发任务与消息队列逻辑) |
| 协议栈封装 | ble_client_lib.c / ble_client_lib.h(扫描、连接、GATT 发现、回调) |
| 设备名称 | ble_uart(GAP 本地名) |
| 客户端 MAC 地址 | 12:22:33:44:55:66 |
| 目标服务端 MAC 地址 | 11:22:33:44:55:66(与 d3 服务端一致) |
| 服务 UUID | 0xABCD(由服务端提供,客户端发现所有服务) |
| 写特征 UUID | 0xEFEF(对应服务端 RX 特征,客户端 → 服务端) |
| 接收特征 | 服务端 TX 特征 0xCDEF 的 Notify 通知(服务端 → 客户端) |
| UART 总线 | CONFIG_BLE_UART_BUS = 0 |
| UART 波特率 | 115200,8N1 |
| 接收触发条件 | UART_RX_CONDITION_FULL_OR_IDLE(缓冲区满或空闲) |
| 接收缓冲区 | g_ble_rx_data[256] |
| 核心任务 | ble_uart_client_task(UART → BLE)、ble_uart_tx_server(BLE → 串口打印) |
| 初始化入口 | APP_FEATURE_INIT(ble_client_app) |
典型联调拓扑:
┌─────────────────────┐ BLE 连接 ┌─────────────────────┐
│ 开发板 A(d3 服务端)│ ◄────────────► │ 开发板 B(本案例) │
│ d3_ble_uart_ │ GATT Notify │ d4_ble_uart_ │
│ server(Server) │ GATT Write │ client(Client) │
│ 广播: ble_uart_ │ │ 扫描并连接服务端 │
│ server │ │ │
│ UART0 ↔ BLE │ │ UART0 ↔ BLE │
└─────────┬───────────┘ └─────────┬───────────┘
│ USB 串口 │ USB 串口
▼ ▼
PC 串口助手 A PC 串口助手 B
说明:本案例为 BLE UART 透传客户端,需配合
d3_ble_uart_server服务端案例完成联调。建议先烧录并启动服务端开发板,确认广播就绪后再启动客户端开发板。在 PC 端串口助手向客户端发送任意字符串,数据将经 BLE 转发至服务端并在服务端串口打印;反向同理。
1.3、实验环境
| 项目 | 说明 |
|---|---|
| 硬件 | 两块 LZ3863-星闪开发板(服务端 + 客户端)、USB 数据线 |
| 软件 | OpenHarmony v5.1.0 源码、hb 编译工具 |
| 联调案例 | d3_ble_uart_server(服务端,烧录至第一块开发板) |
| 调试工具 | 串口助手(波特率 115200,8N1)× 2 |
| 案例路径 | applications/sample/wifi-iot/app/d4_ble_uart_client/ |
注意:完整双向透传实验需要 两块开发板,分别烧录服务端固件(
d3_ble_uart_server)和客户端固件(本案例),并各连接一台 PC 串口助手。
2、基础知识
2.1、BLE UART 透传概述
BLE UART 透传 是一种将串口数据通过 BLE 无线链路转发的通信模式,常用于替代物理串口线,实现开发板与 PC/手机之间的无线数据交互。其本质是:
| 方向 | 数据路径 | 本案例实现 |
|---|---|---|
| 串口 → 蓝牙 | PC 串口 → UART 中断 → 消息队列 → GATT Write | ble_uart_read_int_handler → ble_uart_client_task → ble_client_write_cmd() |
| 蓝牙 → 串口 | GATT Notify → 全局缓冲区 → 串口打印 | ble_client_notification_cbk → ble_uart_tx_server → printf |
与 d2_ble_connect_client(基础连接客户端)相比,本案例不再周期性发送固定字符串,而是 按需透传 UART 实际接收的数据,更贴近真实物联网应用场景。
2.2、BLE 协议栈与 GATT 客户端
BLE 协议栈从下到上包括 PHY、LL、HCI、L2CAP、ATT、GATT、GAP 等层次。本案例主要涉及 GAP(扫描与连接)和 GATT Client(服务发现与特征读写)。
GATT 服务结构(与服务端 d3 一致):
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
用途: 客户端向服务端写入数据(客户端发送 / UART 透传)
客户端连接流程:
客户端启动
↓
注册 GAP/GATT 回调 → enable_ble()
↓
设置扫描参数 → 开始扫描
↓
扫描到目标 MAC (11:22:33:44:55:66) → 停止扫描 → 发起连接
↓
连接建立 → MTU 交换 → GATT 服务发现 → 保存 0xEFEF 写句柄
↓
UART 数据就绪 → GATT Write 发送至服务端
↓
收到 Notify → 写入 g_ble_rx_data → 串口打印
2.3、UART 中断回调接收
本案例使用 UART 中断回调模式 接收 PC 串口助手发送的数据:
| 参数 | 值 | 说明 |
|---|---|---|
| UART 总线 | CONFIG_BLE_UART_BUS = 0 |
UART0 |
| 波特率 | 115200 | 与串口助手一致 |
| 触发条件 | UART_RX_CONDITION_FULL_OR_IDLE |
接收缓冲区满或线路空闲时触发 |
| 最小触发字节 | 1 | 收到 1 字节即可能触发回调 |
工作流程:
PC 串口助手发送数据
↓
UART 硬件接收 → 触发中断
↓
ble_uart_read_int_handler() ← 中断上下文,需快速返回
↓
动态分配内存、拷贝数据 → 写入消息队列
↓
ble_uart_client_task() ← 任务上下文,阻塞读取队列
↓
ble_client_write_cmd() → GATT Write → 发送至 BLE 服务端
注意:中断回调中不宜执行耗时操作(如 BLE 发送),因此采用消息队列将 中断上下文 与 任务上下文 解耦。
2.4、消息队列机制
OpenHarmony LiteOS 提供 osal_msg_queue_* 接口,用于任务间异步通信。本案例消息队列设计如下:
| 项目 | 说明 |
|---|---|
| 队列名称 | "task_msg" |
| 消息结构 | msg_data_t(数据指针 + 长度) |
| 写入方 | UART 中断回调 ble_uart_read_int_handler |
| 读取方 | 核心任务 ble_uart_client_task |
| 内存管理 | 中断中 osal_vmalloc 分配,任务中 osal_vfree 释放 |
┌──────────────────┐ write_copy ┌──────────────────┐
│ UART 中断回调 │ ──────────────→ │ 消息队列 │
│ (中断上下文) │ │ (task_msg) │
└──────────────────┘ └────────┬─────────┘
│ read_copy (阻塞)
▼
┌──────────────────┐
│ ble_uart_client │
│ _task (任务) │
└────────┬─────────┘
│ ble_client_write_cmd
▼
┌──────────────────┐
│ BLE 服务端 │
└──────────────────┘
2.5、BLE 事件回调机制
GAP 回调:
| 回调函数 | 触发时机 | 处理逻辑 |
|---|---|---|
ble_client_enable_cbk |
BLE 协议栈启用完成 | 设置设备名/MAC,注册 GATT 客户端,设置扫描参数 |
ble_client_set_scan_param_cbk |
扫描参数设置完成 | 清除配对信息,开始扫描 |
ble_client_scan_result_cbk |
扫描到广播设备 | 匹配目标 MAC,停止扫描并发起连接 |
ble_client_connect_change_cbk |
连接状态变化 | 连接时交换 MTU;断开时重新扫描 |
ble_client_pair_result_cb |
配对完成 | 交换 MTU |
GATT Client 回调:
| 回调函数 | 触发时机 | 处理逻辑 |
|---|---|---|
ble_client_mtu_changed_cbk |
MTU 协商完成 | 开始服务发现 |
ble_client_discover_service_cbk |
发现服务 | 发起特征发现 |
ble_client_discover_character_cbk |
发现特征 | 匹配 0xEFEF 写句柄,发现描述符 |
ble_client_notification_cbk |
收到 Notify 通知 | 拷贝至 g_ble_rx_data,置位 g_ble_new_data_ready |
ble_client_write_cfm_cbk |
写操作完成 | 打印写操作结果 |
2.6、软件调用层次
应用层(ble_client_app.c)
├── ble_client_app() ← APP_FEATURE_INIT 注册入口
├── ble_uart_client_task() ← 核心任务:UART 数据 → BLE 发送
├── ble_uart_tx_server() ← 接收任务:BLE Notify → 串口打印
└── ble_uart_read_int_handler() ← UART 中断回调 → 消息队列
│
封装层(ble_client_lib.c)
├── ble_client_init() ← 初始化:注册回调、启用 BLE
├── ble_client_write_cmd() ← GATT Write 发送
└── ble_get_write_vlaue_handle() ← 获取写特征句柄
│
协议栈 / 驱动层
├── gap_ble_*() ← GAP 扫描与连接管理
├── gattc_*() ← GATT Client 发现/读写
├── uapi_uart_register_rx_callback() ← UART 接收回调注册
└── osal_msg_queue_*() ← 消息队列操作
2.7、核心 API 介绍
2.7.1、头文件
#include "ble_client_lib.h"
#include "cmsis_os2.h"
#include "ohos_init.h"
2.7.2、应用层 API
| API 名称 | 功能说明 |
|---|---|
ble_client_init() |
初始化 BLE 客户端:注册回调、启用 BLE 协议栈 |
ble_get_write_vlaue_handle() |
获取服务端 RX 特征的写句柄(服务发现后有效) |
ble_client_write_cmd(data, len, handle) |
通过 GATT Write 向服务端发送数据 |
uapi_uart_register_rx_callback(bus, cond, min_size, cb) |
注册 UART 接收中断回调 |
osal_msg_queue_create() / osal_msg_queue_write_copy() / osal_msg_queue_read_copy() |
消息队列创建、写入、读取 |
osal_vmalloc() / osal_vfree() |
动态内存分配与释放 |
osThreadNew(func, arg, &attr) |
创建 RTOS 线程 |
APP_FEATURE_INIT(func) |
注册应用特性初始化入口 |
2.7.3、全局变量
| 变量名 | 说明 |
|---|---|
g_ble_rx_data[256] |
BLE 接收数据缓冲区 |
g_ble_rx_len |
当前接收数据长度 |
g_ble_new_data_ready |
BLE 新数据就绪标志,由 Notify 回调置位 |
mouse_msg_queue |
UART 数据消息队列句柄 |
msg_data_t |
消息队列数据结构(指针 + 长度) |
3、程序设计
3.1、程序架构
本案例目录结构:
d4_ble_uart_client/
├── ble_client_app.c # 应用层:UART 收发任务、消息队列与入口
├── ble_client_lib.c # 协议栈封装:扫描、连接、GATT 发现、回调
├── ble_client_lib.h # 头文件:UUID 宏定义与 API 声明
├── BUILD.gn # GN 编译配置
├── README_zh.md # 案例简要说明
└── 实验手册.md # 本实验手册
程序执行流程:
系统启动
│
▼
ble_client_app() ← APP_FEATURE_INIT 注册,自动执行
│
├── osal_msg_queue_create() ← 创建 UART 数据消息队列
│
├── osThreadNew(ble_uart_client_task) ← 创建 UART→BLE 核心任务
└── osThreadNew(ble_uart_tx_server) ← 创建 BLE→串口 接收任务
│
▼
ble_uart_client_task()
├── ble_client_init() ← 延时 3s → 注册回调 → 启用 BLE
│ └── 扫描 → 连接 → MTU → 服务发现 → 保存写句柄
│
└── uapi_uart_register_rx_callback() ← 注册 UART 接收回调
│
▼
┌───────────────────────────────────────────┐
│ UART 中断 → 消息队列 → GATT Write 发送 │
└───────────────────────────────────────────┘
┌───────────────────────────────────────────┐
│ ble_uart_tx_server:轮询 g_ble_new_data │
│ _ready → 打印 BLE 接收数据到串口 │
└───────────────────────────────────────────┘
3.2、源文件说明
| 文件 | 说明 |
|---|---|
ble_client_app.c |
应用层主程序,定义消息队列、UART 中断回调,创建 UART→BLE 与 BLE→串口两个 RTOS 任务 |
ble_client_lib.c |
BLE 协议栈封装,实现扫描、连接、GATT 服务发现与各类回调处理 |
ble_client_lib.h |
UUID 宏定义、全局变量声明与 API 接口 |
BUILD.gn |
编译配置,生成 ble_uart_client 静态库 |
3.3、关键代码分析
(1)消息队列数据结构
typedef struct
{
uint8_t *value; // 数据缓冲区指针
uint16_t value_len; // 数据长度
} msg_data_t;
static unsigned long mouse_msg_queue = 0;
static unsigned int msg_rev_size = sizeof(msg_data_t);
消息队列每条消息携带 动态分配的缓冲区指针 和数据长度,实现 UART 接收数据的异步转发。
(2)UART 接收中断回调 — ble_uart_read_int_handler
static void ble_uart_read_int_handler(const void *buffer, uint16_t length, bool error)
{
printf("ble_uart_read_int_handler client.\r\n");
unused(error);
msg_data_t msg_data = {0};
/* 动态分配内存并拷贝数据,避免中断缓冲区被覆盖 */
void *buffer_cpy = osal_vmalloc(length);
if (memcpy_s(buffer_cpy, length, buffer, length) != EOK)
{
osal_vfree(buffer_cpy);
return;
}
msg_data.value = (uint8_t *)buffer_cpy;
msg_data.value_len = length;
osal_msg_queue_write_copy(mouse_msg_queue, (void *)&msg_data, msg_rev_size, 0);
}
中断回调中只做 内存拷贝 + 入队,不做 BLE 发送,保证中断快速返回。
(3)核心任务 — ble_uart_client_task
void ble_uart_client_task(void *arg)
{
unused(arg);
printf("ble_client_task entry.");
ble_client_init();
errcode_t ret = uapi_uart_register_rx_callback(CONFIG_BLE_UART_BUS,
UART_RX_CONDITION_FULL_OR_IDLE, 1, ble_uart_read_int_handler);
if (ret != ERRCODE_SUCC)
{
printf("Register uart callback fail.");
return;
}
while (1)
{
msg_data_t msg_data = {0};
int msg_ret = osal_msg_queue_read_copy(mouse_msg_queue, &msg_data,
&msg_rev_size, OSAL_WAIT_FOREVER);
if (msg_ret != OSAL_SUCCESS)
{
if (msg_data.value != NULL)
osal_vfree(msg_data.value);
continue;
}
if (msg_data.value != NULL)
{
uint16_t write_handle = ble_get_write_vlaue_handle();
ble_client_write_cmd(msg_data.value, msg_data.value_len, write_handle);
osal_vfree(msg_data.value);
}
}
}
任务阻塞读取消息队列,取到 UART 数据后通过 GATT Write 发送至 BLE 服务端,发送完毕释放动态内存。
(4)BLE 接收任务 — ble_uart_tx_server
void ble_uart_tx_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);
}
}
轮询 g_ble_new_data_ready 标志位,检测到 BLE Notify 新数据后打印至串口终端。
(5)程序入口 — ble_client_app
static void ble_client_app(void)
{
int msg_ret = osal_msg_queue_create("task_msg", msg_rev_size,
&mouse_msg_queue, 0, msg_rev_size);
if (msg_ret != OSAL_SUCCESS)
{
printf("msg queue create fail.");
return;
}
/* 创建 ble_uart_client_task 任务 */
osThreadAttr_t rx_attr = {0};
rx_attr.name = "ble_uart_client_task";
rx_attr.stack_size = 8192;
rx_attr.priority = osPriorityNormal;
osal_kthread_lock();
osThreadNew(ble_uart_client_task, NULL, &rx_attr);
osal_kthread_unlock();
/* 创建 ble_uart_tx_server 任务 */
osThreadAttr_t tx_attr = {0};
tx_attr.name = "ble_uart_tx_server";
tx_attr.stack_size = 8192;
tx_attr.priority = osPriorityNormal;
osal_kthread_lock();
osThreadNew(ble_uart_tx_server, NULL, &tx_attr);
osal_kthread_unlock();
}
APP_FEATURE_INIT(ble_client_app);
先创建消息队列,再分别创建 UART→BLE 与 BLE→串口两个任务。
(6)UUID 与目标地址配置
#define BLE_CHARACTER_CLIENT_UUID_TX 0xEFEF
/* 客户端要连接的服务器地址(对应 MAC 11:22:33:44:55:66) */
static uint8_t g_ble_server_addr_connect[] = {0x66, 0x55, 0x44, 0x33, 0x22, 0x11};
/* 客户端本地 MAC 地址 */
static uint8_t g_ble_client_addr[] = {0x12, 0x22, 0x33, 0x44, 0x55, 0x66};
客户端通过 MAC 地址匹配目标服务端,写特征 UUID 0xEFEF 对应服务端的 RX 特征。
(7)Notify 接收 — ble_client_notification_cbk
void ble_client_notification_cbk(uint8_t client_id, uint16_t conn_id,
gattc_handle_value_t *data, errcode_t status)
{
if ((data->data_len > 0) && data->data)
{
if (data->data_len <= sizeof(g_ble_rx_data))
{
memcpy(g_ble_rx_data, data->data, data->data_len);
g_ble_rx_len = data->data_len;
g_ble_new_data_ready = true;
}
}
}
收到服务端 Notify 后拷贝至全局缓冲区并置位标志,ble_uart_tx_server 任务负责打印。
(8)BUILD.gn — 编译配置
static_library("ble_uart_client") {
sources = [
"ble_client_lib.c",
"ble_client_app.c",
]
defines = [
"CONFIG_UART_SUPPORT_TX",
"CONFIG_UART_SUPPORT_RX",
]
// ...
}
3.4、程序执行流程
4、编译步骤
本实验编译客户端固件并烧录至 开发板 B;服务端固件请参考 d3_ble_uart_server 案例编译烧录至 开发板 A。以下步骤只需在首次编译时完成 4.1~4.3 的配置注册。
4.1、确认案例目录
确认案例已位于 OpenHarmony 源码目录下:
applications/sample/wifi-iot/app/d4_ble_uart_client/
├── ble_client_app.c
├── ble_client_lib.c
├── ble_client_lib.h
├── BUILD.gn
└── 实验手册.md
若从外部复制,请将 d4_ble_uart_client 目录放到上述 app/ 路径下。
4.2、修改 BUILD.gn(注册编译组件)
编辑 applications/sample/wifi-iot/app/BUILD.gn,在 features 列表中添加本案例:
lite_component("app") {
features = [
"startup",
"d4_ble_uart_client:ble_uart_client", // 添加此行
]
}
4.3、修改 SDK 配置文件
步骤 1:编辑 device/soc/hisilicon/ws63v100/sdk/build/config/target_config/ws63/config.py
找到 'ws63-liteos-app' 配置段,在其 'ram_component' 列表中添加:
"ble_uart_client"
步骤 2:编辑 device/soc/hisilicon/ws63v100/sdk/libs_url/ws63/cmake/ohos.cmake
找到 "ws63-liteos-app" 对应的 set(COMPONENT_LIST 部分,添加:
"ble_uart_client"
4.4、编译固件
在 OpenHarmony 源码根目录下执行编译:
rm -rf out
hb set -root .
# 通过上下方向键选择 ws63 对应的编译分支(如 nearlink_txnl63 / nearlink_dk_3863)
hb build -f
编译成功后,将固件烧录到 开发板 B(作为 BLE UART 透传客户端)。
4.5、编译服务端固件(开发板 A)
按照 d3_ble_uart_server 案例的 README,完成服务端的 BUILD.gn 与 SDK 配置注册(组件名 ble_uart_server),编译后将固件烧录到 开发板 A。
4.6、烧录固件
使用开发板配套的烧录工具,将编译生成的固件分别烧写到两块 LZ3863-星闪开发板。具体烧录步骤请参考开发板用户手册。
5、运行结果
5.1、硬件准备
- 将 开发板 A(服务端固件
d3_ble_uart_server)上电或复位,等待 BLE 广播启动; - 将 开发板 B(客户端固件,本案例)上电或复位,开始扫描并连接服务端;
- 两块开发板分别通过 USB 连接 PC,各打开一个串口助手(波特率 115200,8N1)。
建议:先启动服务端开发板,确认广播启动日志输出后,再启动客户端开发板,以提高首次连接成功率。
5.2、串口配置
| 参数 | 值 |
|---|---|
| 波特率 | 115200 |
| 数据位 | 8 |
| 停止位 | 1 |
| 校验位 | 无 |
| 流控 | 无 |
5.3、客户端预期输出(开发板 B)
烧录客户端固件并复位后,串口助手可观察到 BLE 初始化、扫描、连接与服务发现过程:
ble_client_task entry.[SLE Client] try enable1.
ble enable: 0
ble enable end: 0
[ble client] set scan param status: 0
addr: 66: 55: 44: 33: 22: 11:
[ble client] connect state change conn_id: x, status: 2, pair_status:0, disc_reason 0
[ble client] connect change cbk conn_id =x
[ble client] Mtu changed----client:x conn_id:x, mtu size:xx, status:0
[ble client] Discovery service callback client:x conn_id:x
[ble client] discover character declare_handle:xx, value_handle:xx, properties:xx
[ble client] write declare_handle:xx, value_handle:xx, properties:xx
表示 BLE 协议栈启用成功,已扫描到目标服务端并完成连接与 GATT 服务发现。
5.4、UART 透传验证
连接建立且服务发现完成后,进行双向透传测试:
测试 1:PC → 客户端 → BLE → 服务端
- 在 开发板 B 对应的 PC 串口助手中输入字符串(如
Hello from client UART)并发送; - 客户端串口输出:
ble_uart_read_int_handler client.
[ble client] ble_uart_client_write_cmd len: 22, g_client_id: x
48656c6c6f2066726f6d20636c69656e742055415254
[ble client] Write result----client:x conn_id:x handle:xx
[ble client] status:0
- 开发板 A(服务端)串口输出:
ble rev date:Hello from client UART
测试 2:PC → 服务端 → BLE → 客户端
- 在 开发板 A 对应的 PC 串口助手中输入字符串(如
Hello from server UART)并发送; - 服务端串口输出:
ble_uart_read_int_handler server.
ble rev date:Hello from server UART
- 开发板 B(客户端)串口输出:
ble rev date:Hello from server UART
5.5、服务端预期输出(开发板 A)
烧录 d3_ble_uart_server 固件并复位后,串口助手可观察到广播启动与客户端连接过程:
[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
[ble server] connect state change conn_id: x, status: 2, ...
[ble server] send input report indicate_handle:xx
5.6、双向透传验证汇总
| 方向 | 操作 | 预期现象 |
|---|---|---|
| PC B → 服务端 A | 在客户端串口助手发送字符串 | 服务端串口打印 ble rev date:<字符串> |
| PC A → 客户端 B | 在服务端串口助手发送字符串 | 客户端串口打印 ble rev date:<字符串> |
5.7、结果分析
| 现象 | 说明 |
|---|---|
客户端输出 ble enable: 0 |
BLE 协议栈启用成功 |
客户端输出 set scan param status: 0 |
扫描参数配置成功,开始扫描 |
客户端输出 connect state change ... status: 2 |
已成功连接服务端 |
客户端输出 Discovery service / discover character |
GATT 服务发现正常 |
发送 UART 数据后出现 ble_uart_read_int_handler client. |
UART 中断回调正常触发 |
客户端输出 Write result ... status:0 |
GATT Write 发送成功 |
客户端输出 ble rev date:... |
BLE Notify 接收正常 |
| 客户端长时间无连接日志 | 服务端未启动或 MAC 地址不匹配 |
UART 发送后无 ble_uart_read_int_handler |
UART 回调未注册或串口参数不匹配 |
| 有 UART 接收但 Write 失败 | 写特征句柄为 0,服务发现未完成 |
5.8、常见问题排查
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| 客户端扫描不到服务端 | 服务端未启动或广播失败 | 确认服务端输出 adv enable cbk;先启动服务端 |
| 扫描到设备但不连接 | MAC 地址配置不一致 | 确认 g_ble_server_addr_connect 与服务端 MAC 11:22:33:44:55:66 一致 |
| 连接后立即断开 | 信号干扰或协议栈未就绪 | 两块板靠近放置;确认 init 延时 3s 已执行 |
| UART 发送无响应 | 波特率不一致或 USB 串口未连接 | 确认串口助手 115200 8N1;检查 USB 线连接 |
| 有 UART 回调但服务端无数据 | 写句柄为 0 或连接未建立 | 等待服务发现完成;检查 GATT 发现日志 |
| 服务端发送客户端无接收 | Notify 未启用或 CCC 描述符未配置 | 检查服务端 Notify 配置;确认连接已建立 |
编译报错找不到 ble_uart_client |
BUILD.gn 或 config.py 未正确修改 | 逐步核对 4.2、4.3 节的配置项 |
| 两块板烧录了相同固件 | 未区分服务端/客户端固件 | 分别编译烧录 d3(服务端)和 d4(客户端) |
6、实验扩展
完成基本实验后,可尝试以下扩展练习:
- 修改透传方向:在
ble_client_notification_cbk中取消注释uapi_uart_write(),将 BLE 接收数据直接写回 UART,实现真正的串口回环透传; - 添加连接状态判断:参考 d3 服务端,在 UART 中断回调中检查连接状态,未连接时不入队,避免无效发送;
- 修改目标 MAC 地址:更改
g_ble_server_addr_connect,配合修改服务端 MAC,观察扫描连接行为; - 调整消息队列深度:修改
osal_msg_queue_create参数,测试高速 UART 发送时的队列缓冲能力; - 断开重连测试:断开 BLE 连接后观察客户端是否自动重新扫描并成功重连,验证 UART 透传是否恢复;
- 手机联调:使用手机 BLE 调试 App 模拟 GATT 服务端,用本客户端案例连接并进行 UART 透传测试;
- 结合 d2 案例对比:对比本案例(UART 触发发送)与
d2_ble_connect_client(定时发送固定字符串)的设计差异,理解透传模式的工程价值。
更多推荐




所有评论(0)