1、实验简介

参考网址:https://gitee.com/Lockzhiner-Electronics/lz3863/tree/master/apps/d3_ble_uart_server

1.1、实验目的

本实验旨在帮助学习者掌握 OpenHarmony 轻量系统中 BLE(Bluetooth Low Energy,低功耗蓝牙)UART 透传服务端 的实现方法。通过本实验,你将学会:

  1. 理解 BLE GATT Server(通用属性服务端)UART 串口 协同工作的透传机制;
  2. 在开发板上实现 BLE UART 透传服务端,完成 GATT 服务注册、广播启动与双向数据转发;
  3. 使用 消息队列 解耦 UART 中断回调与 BLE 发送任务,掌握异步数据转发设计;
  4. 掌握 UART 中断接收、BLE Notify 发送与 Write 接收的完整处理流程;
  5. 完成案例代码的编译、烧录,并与 d4_ble_uart_client 客户端案例进行双板 UART 透传联调。

1.2、实验内容

本案例在 LZ3863-星闪开发板上实现 BLE UART 透传服务端 功能:初始化蓝牙协议栈并注册 UART 服务,启动可连接广播;连接建立后,将 UART 串口接收的数据通过 BLE Notify 转发给客户端,同时将客户端写入的数据打印到串口终端。

项目 说明
应用层源文件 ble_server_app.c(UART 收发任务与入口)
协议栈封装 ble_server_lib.c / ble_server_lib.h(GATT 服务、广播、回调)
设备名称 ble_uart(GAP 本地名)/ ble_uart_server(广播名)
设备 MAC 地址 11:22:33:44:55:66
UART 总线 CONFIG_BLE_UART_BUS = 0(UART0)
服务 UUID 0xABCD
TX 特征 UUID 0xCDEF(读 / 写 / Notify,服务端 → 客户端)
RX 特征 UUID 0xEFEF(写 / 无应答写,客户端 → 服务端)
数据转发方式 UART 接收 → 消息队列 → BLE Notify;BLE Write → 串口打印
接收缓冲区 g_ble_rx_data[256]
初始化入口 APP_FEATURE_INIT(ble_server_app)

典型联调拓扑:

┌─────────────────────────┐    BLE 连接     ┌─────────────────────────┐
│  开发板 A(本案例)        │ ◄────────────► │  开发板 B(d4 客户端)    │
│  d3_ble_uart_server     │  GATT Notify   │  d4_ble_uart_client     │
│  (Server)              │  GATT Write    │  (Client)              │
│  广播: ble_uart_server  │                │  扫描并连接服务端         │
│  UART0 ←→ 串口助手       │                │  UART0 ←→ 串口助手       │
│  串口输入 → BLE 转发     │                │  串口输入 → BLE 转发     │
│  BLE 数据 → 串口打印     │                │  BLE 数据 → 串口打印     │
└─────────────────────────┘                 └─────────────────────────┘

说明:本案例为 BLE UART 透传服务端,需配合 d4_ble_uart_client 客户端案例完成双板透传联调。建议先烧录并启动服务端开发板,确认广播就绪后再启动客户端开发板。

1.3、实验环境

项目 说明
硬件 两块 LZ3863-星闪开发板(服务端 + 客户端)、USB 数据线
软件 OpenHarmony v5.1.0 源码、hb 编译工具
联调案例 d4_ble_uart_client(客户端,烧录至第二块开发板)
调试工具 串口助手(波特率 115200,8N1)
案例路径 applications/sample/wifi-iot/app/d3_ble_uart_server/

注意:完整 UART 透传实验需要 两块开发板,分别烧录服务端固件(本案例)和客户端固件(d4_ble_uart_client),并分别通过 USB 连接 PC 串口助手。


2、基础知识

2.1、BLE UART 透传概述

BLE UART 透传 是一种将串口数据通过 BLE 无线链路透明传输的通信模式,广泛应用于物联网网关、无线串口模块等场景。其核心思想是:

方向 数据路径 本案例实现
串口 → BLE UART 接收数据,通过 GATT Notify 发送给客户端 ble_uart_read_int_handler → 消息队列 → ble_server_send_input_report()
BLE → 串口 客户端 Write 数据,服务端接收后输出到串口 ble_receive_write_req_cbkg_ble_rx_datable_uart_tx_server 打印

d1_ble_connect_server 基础连接案例的区别在于:本案例不再周期性发送固定字符串,而是 由 UART 串口输入驱动数据转发,实现真正的透传功能。

2.2、BLE 协议栈概述

BLE(Bluetooth Low Energy) 是一种面向低功耗、短距离通信的无线技术。BLE 协议栈从下到上可分为:

层次 英文名称 主要功能
物理层 PHY 2.4 GHz 射频收发
链路层 LL 连接建立、数据包封装
主机控制器接口 HCI 控制器与主机间通信
逻辑链路控制与适配 L2CAP 多路复用、分段重组
属性协议 ATT 属性读写、句柄管理
通用属性配置文件 GATT 基于 ATT 的服务/特征抽象
通用访问配置文件 GAP 广播、扫描、连接、配对
安全管理协议 SMP 加密与配对安全

本案例主要涉及 GAP(广播与连接)和 GATT Server(服务与特征)两层。

2.3、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.4、GATT:服务与特征

GATT 采用 Client/Server 模型。本案例实现了一个 BLE UART 透传服务,结构如下:

Service: 0xABCD (BLE UART Service)
    ├── Characteristic TX: 0xCDEF
    │       属性: Read | Write | Write No Response
    │       描述符: Client Characteristic Configuration (0x2902)
    │       用途: 服务端通过 Notify 向客户端发送 UART 数据
    │
    └── Characteristic RX: 0xEFEF
            属性: Write | Write No Response
            用途: 客户端向服务端写入数据(打印到串口)
角色 数据方向 本案例实现方式
服务端 → 客户端 TX 特征 Notify UART 数据 → ble_server_send_input_report()
客户端 → 服务端 RX 特征 Write ble_receive_write_req_cbk() → 写入 g_ble_rx_data

2.5、UART 中断回调与消息队列

本案例采用 中断回调 + 消息队列 架构处理 UART 数据,避免在中断上下文中执行耗时的 BLE 发送操作:

UART 硬件接收数据
    ↓
触发接收中断 → ble_uart_read_int_handler()
    ↓
检查 BLE 连接状态(仅连接时处理)
    ↓
动态分配内存,拷贝 UART 数据
    ↓
写入消息队列(osal_msg_queue_write_copy)
    ↓
ble_uart_rx_server 任务阻塞读取队列
    ↓
ble_server_send_input_report() → BLE Notify 发送
    ↓
释放动态内存(osal_vfree)

消息队列的优势:

对比项 直接在中断中发送 BLE 消息队列异步转发
中断耗时 中断中执行 BLE 协议栈操作,可能超时 中断仅做数据拷贝和入队,耗时极短
数据安全 中断缓冲区可能被覆盖 动态分配独立缓冲区,数据完整
任务解耦 UART 中断与 BLE 发送紧耦合 收发任务独立调度,互不阻塞

UART 接收触发条件:

参数 说明
触发条件 UART_RX_CONDITION_FULL_OR_IDLE 接收缓冲区满或空闲时触发
超时阈值 1 空闲超时参数

2.6、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.7、软件调用层次

本案例的软件调用层次如下:

应用层(ble_server_app.c)
    ├── ble_server_app()              ← APP_FEATURE_INIT 注册入口
    ├── ble_uart_rx_server()          ← UART 转发任务:队列读取 → BLE Notify 发送
    ├── ble_uart_tx_server()          ← BLE 接收任务:打印客户端写入的数据
    └── ble_uart_read_int_handler()   ← UART 中断回调:数据入队
            │
封装层(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 服务/特征管理
    ├── uapi_uart_register_rx_callback() ← UART 中断回调注册
    ├── osal_msg_queue_*()            ← 消息队列操作
    └── enable_ble()                  ← 启用 BLE 协议栈

2.8、核心 API 介绍

2.8.1、头文件
#include "ble_server_lib.h"
#include "cmsis_os2.h"
#include "ohos_init.h"
2.8.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 向已连接客户端发送数据
uapi_uart_register_rx_callback(bus, condition, threshold, handler) 注册 UART 接收中断回调函数
osal_msg_queue_create(name, msg_size, &queue, flags, max_msgs) 创建消息队列
osal_msg_queue_write_copy(queue, msg, size, timeout) 向消息队列写入数据(拷贝模式)
osal_msg_queue_read_copy(queue, msg, &size, timeout) 从消息队列读取数据(阻塞模式)
osal_vmalloc(size) / osal_vfree(ptr) 动态内存分配与释放
osThreadNew(func, arg, &attr) 创建 RTOS 线程
APP_FEATURE_INIT(func) 注册应用特性初始化入口,系统启动后自动执行
2.8.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、程序架构

本案例目录结构

d3_ble_uart_server/
├── ble_server_app.c      # 应用层:UART 收发任务、消息队列与入口
├── ble_server_lib.c      # 协议栈封装:GATT 服务、广播、回调
├── ble_server_lib.h      # 头文件:UUID 宏定义与 API 声明
├── BUILD.gn              # GN 编译配置
├── README_zh.md          # 案例简要说明
└── 实验手册.md            # 本实验手册

程序执行流程:

系统启动
    │
    ▼
ble_server_app()                    ← APP_FEATURE_INIT 注册,自动执行
    │
    ├── osal_msg_queue_create()       ← 创建消息队列
    │
    ├── osThreadNew(ble_uart_rx_server)  ← 创建 UART 转发任务
    └── osThreadNew(ble_uart_tx_server)  ← 创建 BLE 接收打印任务
            │
            ▼
    ┌───────────────────────────────────────────────────┐
    │  ble_uart_rx_server(UART 转发任务)                │
    │  ble_server_init() → 注册 UART 回调               │
    │  阻塞读取消息队列 → BLE Notify 发送 → 释放内存       │
    └───────────────────────────────────────────────────┘
    ┌───────────────────────────────────────────────────┐
    │  ble_uart_tx_server(BLE 接收任务)                 │
    │  轮询 g_ble_new_data_ready → 打印客户端写入数据      │
    └───────────────────────────────────────────────────┘
    ┌───────────────────────────────────────────────────┐
    │  ble_uart_read_int_handler(UART 中断回调)          │
    │  连接已建立 → 拷贝数据 → 写入消息队列               │
    └───────────────────────────────────────────────────┘

3.2、源文件说明

文件 说明
ble_server_app.c 应用层主程序,实现消息队列、UART 中断回调、两个 RTOS 任务及程序入口
ble_server_lib.c BLE 协议栈封装,实现 GATT 服务注册、广播配置、GAP/GATT 回调处理
ble_server_lib.h UUID 宏定义、数据结构、全局变量声明与 API 接口
BUILD.gn 编译配置,生成 ble_uart_server 静态库

3.3、关键代码分析

(1)UART 总线与消息队列数据结构
#define CONFIG_BLE_UART_BUS 0

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);

CONFIG_BLE_UART_BUS 指定 UART0 作为透传串口;msg_data_t 结构体封装 UART 接收数据的指针和长度,作为消息队列的传输载体。

(2)UART 接收中断回调 — ble_uart_read_int_handler

UART 串口接收到数据后触发中断,回调函数将数据异步写入消息队列:

static void ble_uart_read_int_handler(const void *buffer, uint16_t length, bool error)
{
    unused(error);
    /* 检查连接状态,只有在连接状态下才进行数据处理 */
    if (ble_get_connection_state() != 0)
    {
        msg_data_t msg_data = {0};
        void *buffer_cpy = osal_vmalloc(length);
        if (buffer_cpy == NULL)
        {
            printf("%s vmalloc failed for UART data\r\n", BLE_SERVER_ERROR);
            return;
        }
        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_get_connection_state() 非 0(BLE 已连接)时才处理 UART 数据,避免无连接时的无效转发。

(3)UART 转发任务 — ble_uart_rx_server

该任务负责 BLE 服务端初始化、UART 回调注册,以及从消息队列读取数据并通过 BLE 发送:

void ble_uart_rx_server(void *arg)
{
    unused(arg);
    ble_server_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)
        {
            ble_server_send_input_report(msg_data.value, msg_data.value_len);
            osal_vfree(msg_data.value);
        }
    }
}

任务以 OSAL_WAIT_FOREVER 阻塞等待消息队列数据,收到数据后立即通过 ble_server_send_input_report() 转发,并释放动态内存。

(4)BLE 接收打印任务 — ble_uart_tx_server

该任务轮询全局标志位,检测客户端通过 BLE Write 发送的新数据并打印到串口:

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 由 GATT 写请求回调 ble_receive_write_req_cbk 在收到客户端数据时置位。

(5)程序入口 — ble_server_app
static void ble_server_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;
    }

    osThreadAttr_t rx_attr = {0};
    rx_attr.name = "ble_uart_rx_server";
    rx_attr.stack_size = 8192;
    rx_attr.priority = osPriorityNormal;
    osal_kthread_lock();
    osThreadNew(ble_uart_rx_server, NULL, &rx_attr);
    osal_kthread_unlock();

    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_server_app);

系统启动后自动调用 ble_server_app():先创建消息队列,再创建 UART 转发任务和 BLE 接收打印任务。BLE 初始化在 ble_uart_rx_server 任务中执行。

(6)服务端初始化 — ble_server_init
void ble_server_init(void)
{
    (void)osal_msleep(3000);   /* 延时 3s,等待 BLE 协议栈初始化完毕 */

    ble_server_register_callbacks();
    enable_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;
    param.value_len = len;
    param.value = data;
    gatts_notify_indicate(BLE_SERVER_ID, g_ble_conn_id, &param);
    return ERRCODE_BT_SUCCESS;
}

Notify 发送依赖 TX 特征的值句柄(在 ble_server_characteristic_add_cbk 中保存)和当前连接 ID。

(8)BUILD.gn — 编译配置
static_library("ble_uart_server") {
  sources = [
    "ble_server_lib.c",
    "ble_server_app.c",
  ]
  defines = [
    "CONFIG_UART_SUPPORT_TX",
    "CONFIG_UART_SUPPORT_RX",
  ]
  // ...
}

编译宏 CONFIG_UART_SUPPORT_TXCONFIG_UART_SUPPORT_RX 启用 UART 收发功能支持。

3.4、程序执行流程

d4 客户端 GATT Server ble_server_lib 消息队列 ble_server_app UART0 PC 串口助手 d4 客户端 GATT Server ble_server_lib 消息队列 ble_server_app UART0 PC 串口助手 osal_msg_queue_create() 创建 ble_uart_rx_server / ble_uart_tx_server ble_server_init() gatts_register_server() + ble_add_service() 服务启动 → 开始广播 uapi_uart_register_rx_callback() 扫描并连接服务端 connect_change_cbk (CONNECTED) 发送串口数据 ble_uart_read_int_handler() osal_msg_queue_write_copy() ble_server_send_input_report() gatts_notify_indicate() Notify 转发数据 串口打印接收数据 客户端串口发送数据 Write RX 特征 write_request_cbk → g_ble_rx_data ble_uart_tx_server 打印接收数据

4、编译步骤

本实验编译服务端固件并烧录至 开发板 A;客户端固件请参考 d4_ble_uart_client 案例编译烧录至 开发板 B。以下步骤只需在首次编译时完成 4.1~4.3 的配置注册。

4.1、确认案例目录

确认案例已位于 OpenHarmony 源码目录下:

applications/sample/wifi-iot/app/d3_ble_uart_server/
├── ble_server_app.c
├── ble_server_lib.c
├── ble_server_lib.h
├── BUILD.gn
└── 实验手册.md

若从外部复制,请将 d3_ble_uart_server 目录放到上述 app/ 路径下。

4.2、修改 BUILD.gn(注册编译组件)

编辑 applications/sample/wifi-iot/app/BUILD.gn,在 features 列表中添加本案例:

lite_component("app") {
  features = [
    "startup",
    "d3_ble_uart_server:ble_uart_server",   // 添加此行
  ]
}

4.3、修改 SDK 配置文件

步骤 1:编辑 device/soc/hisilicon/ws63v100/sdk/build/config/target_config/ws63/config.py

找到 'ws63-liteos-app' 配置段,在其 'ram_component' 列表中添加:

"ble_uart_server"

步骤 2:编辑 device/soc/hisilicon/ws63v100/sdk/libs_url/ws63/cmake/ohos.cmake

找到 "ws63-liteos-app" 对应的 set(COMPONENT_LIST 部分,添加:

"ble_uart_server"

4.4、编译固件

在 OpenHarmony 源码根目录下执行编译:

rm -rf out
hb set -root .
# 通过上下方向键选择 ws63 对应的编译分支(如 nearlink_txnl63 / nearlink_dk_3863)
hb build -f

编译成功后,将固件烧录到 开发板 A(作为 BLE UART 透传服务端)。

4.5、编译客户端固件(开发板 B)

按照 d4_ble_uart_client 案例的 README 或实验手册,完成客户端的 BUILD.gn 与 SDK 配置注册,编译后将固件烧录到 开发板 B

4.6、烧录固件

使用开发板配套的烧录工具,将编译生成的固件分别烧写到两块 LZ3863-星闪开发板。具体烧录步骤请参考开发板用户手册。


5、运行结果

5.1、硬件准备

  1. 开发板 A(服务端固件,本案例)上电或复位,等待 BLE 广播启动;
  2. 开发板 B(客户端固件 d4_ble_uart_client)上电或复位,开始扫描并连接服务端;
  3. 两块开发板分别通过 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, ...

status: 2 表示 BLE 连接已建立(GAP_BLE_STATE_CONNECTED)。

5.4、UART 透传验证

连接建立后,可通过两块开发板的串口助手进行双向透传测试:

测试 1:服务端串口 → 客户端串口

  1. 开发板 A(服务端)的串口助手中输入任意字符串,如 hello from server uart,点击发送;
  2. 观察 开发板 B(客户端)的串口助手,应输出:
ble rev date:hello from server uart

同时服务端串口会打印 UART 中断回调日志:

ble_uart_read_int_handler server.
[ble server] send input report indicate_handle:xx

测试 2:客户端串口 → 服务端串口

  1. 开发板 B(客户端)的串口助手中输入任意字符串,如 hello from client uart,点击发送;
  2. 观察 开发板 A(服务端)的串口助手,应输出:
ble rev date:hello from client uart

5.5、双向透传验证汇总

方向 操作 预期结果
服务端 UART → 客户端 开发板 A 串口发送数据 开发板 B 串口打印 ble rev date:...
客户端 UART → 服务端 开发板 B 串口发送数据 开发板 A 串口打印 ble rev date:...

5.6、结果分析

现象 说明
服务端输出 start adv / adv enable cbk GATT 服务就绪,广播已成功启动
服务端输出 connect state change ... status: 2 客户端已成功连接
服务端输出 ble_uart_read_int_handler server. UART 接收到数据,中断回调已触发
服务端输出 send input report UART 数据已通过 BLE Notify 转发
服务端输出 ble rev date:... 客户端 Write 数据接收正常
客户端输出 ble rev date:... Notify 接收正常,UART 透传成功
串口发送后无 ble_uart_read_int_handler 日志 BLE 未连接,UART 数据被忽略
客户端长时间无连接日志 服务端未启动或广播未就绪,检查服务端日志

5.7、常见问题排查

问题 可能原因 解决方法
客户端扫描不到服务端 服务端未启动或广播失败 确认服务端输出 adv enable cbk;先启动服务端
连接后串口发送无转发 BLE 未连接或 UART 回调未注册 确认 connect state change ... status: 2;检查 UART 回调注册日志
服务端有中断日志但客户端无接收 Notify 未启用或 MTU 不足 检查 CCC 描述符;确认 MTU 协商日志
客户端发送后服务端无打印 客户端未完成服务发现 确认客户端输出 discover character 日志
消息队列创建失败 系统资源不足 检查 msg queue create fail. 日志,重启开发板
内存分配失败 系统堆内存不足 检查 vmalloc failed 日志,减小发送数据量
编译报错找不到 ble_uart_server BUILD.gn 或 config.py 未正确修改 逐步核对 4.2、4.3 节的配置项
两块板烧录了相同固件 未区分服务端/客户端固件 分别编译烧录 d3(服务端)和 d4(客户端)

6、实验扩展

完成基本实验后,可尝试以下扩展练习:

  1. 修改设备名称:更改 g_local_nameg_ble_name_value,观察客户端扫描结果的变化;
  2. 修改 UART 总线:将 CONFIG_BLE_UART_BUS 改为其他 UART 端口(如 UART2),配合引脚复用实现外接串口透传;
  3. BLE 数据写回 UART:在 ble_uart_tx_server 中调用 uapi_uart_write() 将 BLE 接收数据写回 UART,实现完整的双向硬件透传(参考 vendor 示例 19_ble_uart);
  4. 调整消息队列深度:修改 osal_msg_queue_create 的队列容量参数,测试高速串口数据下的队列缓冲能力;
  5. 未连接时缓存数据:修改 ble_uart_read_int_handler,在 BLE 未连接时也将数据入队,连接建立后自动发送;
  6. 手机 BLE 调试:使用手机 BLE 调试 App(如 nRF Connect)扫描连接服务端,手动写入 RX 特征值,观察服务端串口输出;
  7. 断开重连测试:断开 BLE 连接后观察服务端是否自动重新广播,重连后透传功能是否恢复正常;
  8. 结合后续案例:在 UART 透传基础上,配合 Wi-Fi 或 SLE 通信案例实现多协议网关应用。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐