1、实验简介

本实验参考网址:https://gitee.com/Lockzhiner-Electronics/lz3863/tree/master/apps/e4_sle_uart_client

准备事项:

  • 小智派-LZ3863开发板
  • USB烧录线
  • Windows电脑

其中,小智派-LZ3863开发板是福州市凌睿智捷电子有限公司研发的一款基于海思WS63(Hi3863)芯片、高度集成2.4GHz Wi-Fi6、BLE(低功耗蓝牙)和SLE(星闪低功耗接入技术,简称SLE)、专用于短距离数据传输的新兴无线通信技术模块。
在这里插入图片描述
在这里插入图片描述

1.1、实验目的

本实验旨在帮助学习者掌握 OpenHarmony 轻量系统中 SLE UART 透传客户端 的设计与实现方法。通过本实验,你将学会:

  1. 理解 SLE SSAP 客户端与 UART 串口 结合,实现无线串口透传的基本原理;
  2. 使用 消息队列 解耦 UART 接收中断与 SLE 发送任务,保障数据传输稳定性;
  3. 在开发板上实现 SLE 客户端扫描、连接、SSAP 服务发现,并将 UART 数据转发至星闪服务端;
  4. 接收服务端 Notify 数据并在串口终端打印,完成双向透传验证;
  5. 完成案例代码的编译、烧录,并与 e3_sle_uart_server 服务端案例进行双板联调。

1.2、实验内容

本案例在 LZ3863-星闪开发板上实现 SLE UART 透传客户端 功能:初始化星闪协议栈并注册 SSAP 客户端,扫描并连接广播名称为 sle_uart_server 的服务端;连接建立后完成 SSAP 服务发现,将 PC 串口助手发送的数据经 UART 接收后通过 SLE Write 转发至服务端,同时接收服务端 Notify 数据并在串口打印。

项目 说明
应用层源文件 sle_client_app.c(UART 收发任务与消息队列逻辑)
协议栈封装 sle_client_lib.c / sle_client_lib.h(扫描、连接、SSAP 发现、回调)
目标服务端名称 sle_uart_server(与 e3 服务端一致)
客户端 MAC 地址 13:67:5c:07:00:51
服务端 MAC 地址 78:70:60:88:96:46(由 e3 服务端配置)
服务 UUID 0x2222(由服务端提供,客户端发现所有服务)
属性 UUID 0x2323(双向数据收发属性)
UART 总线 CONFIG_SLE_UART_BUS = 0
UART 波特率 115200,8N1
接收触发条件 UART_RX_CONDITION_FULL_OR_IDLE(缓冲区满或空闲)
接收缓冲区 g_sle_rx_data[256]
核心任务 sle_uart_rx_client(UART → SLE)、sle_uart_tx_client(SLE → 串口打印)
初始化入口 APP_FEATURE_INIT(sle_client_app)

典型联调拓扑:

┌─────────────────────┐    SLE 连接     ┌─────────────────────┐
│  开发板 A(e3 服务端)│ ◄────────────► │  开发板 B(本案例)   │
│  e3_sle_uart_       │  SSAP Notify   │  e4_sle_uart_       │
│  server(Server)    │  SSAP Write    │  client(Client)    │
│  广播: sle_uart_    │                │  扫描并连接服务端     │
│  server             │                │                     │
│  UART0 ↔ SLE        │                │  UART0 ↔ SLE        │
└─────────┬───────────┘                 └─────────┬───────────┘
          │ USB 串口                               │ USB 串口
          ▼                                        ▼
     PC 串口助手 A                            PC 串口助手 B

说明:本案例为 SLE UART 透传客户端,需配合 e3_sle_uart_server 服务端案例完成联调。建议先烧录并启动服务端开发板,确认广播就绪后再启动客户端开发板。在 PC 端串口助手向客户端发送任意字符串,数据将经 SLE 转发至服务端并在服务端串口打印;反向同理。

1.3、实验环境

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

注意:完整双向透传实验需要 两块开发板,分别烧录服务端固件(e3_sle_uart_server)和客户端固件(本案例),并各连接一台 PC 串口助手。


2、基础知识

2.1、SLE UART 透传概述

SLE UART 透传 是一种将串口数据通过 SLE 无线链路转发的通信模式,常用于替代物理串口线,实现开发板与 PC/手机之间的无线数据交互。其本质是:

方向 数据路径 本案例实现
串口 → 星闪 PC 串口 → UART 中断 → 消息队列 → SSAP Write sle_uart_read_int_handlersle_uart_rx_clientsle_client_send_report_by_handle()
星闪 → 串口 SSAP Notify → 全局缓冲区 → 串口打印 ssapc_notification_callbackssle_uart_tx_clientprintf

e2_sle_connect_client(基础连接客户端)相比,本案例不再周期性发送固定字符串,而是 按需透传 UART 实际接收的数据,更贴近真实物联网应用场景。

2.2、SLE 协议栈与 SSAP 客户端

SLE(Smart Link Extension,星闪) 是面向物联网场景的短距离无线通信技术。本案例主要涉及 设备发现(Seek)SSAP Client(客户端服务发现与属性读写) 两层。

SSAP 服务结构(与服务端 e3 一致):

Service: 0x2222 (SLE UART Service)
    └── Property: 0x2323
            权限: Read | Write
            操作指示: Read | Write
            描述符: Client Configuration (0x2902)
            用途: 双向数据收发(Notify 接收 + Write 发送)

客户端连接流程:

客户端启动
    ↓
注册 SSAP 客户端回调 → EnableSle()
    ↓
设置扫描参数 → SleStartSeek()
    ↓
扫描到 sle_uart_server → 停止扫描 → SleConnectRemoteDevice()
    ↓
连接建立 → MTU 交换 → SSAP 服务发现 → 保存属性句柄
    ↓
UART 数据就绪 → SSAP Write 发送至服务端
    ↓
收到 Notify → 写入 g_sle_rx_data → 串口打印

2.3、UART 中断回调接收

本案例使用 UART 中断回调模式 接收 PC 串口助手发送的数据:

参数 说明
UART 总线 CONFIG_SLE_UART_BUS = 0 UART0
波特率 115200 与串口助手一致
触发条件 UART_RX_CONDITION_FULL_OR_IDLE 接收缓冲区满或线路空闲时触发
最小触发字节 1 收到 1 字节即可能触发回调

工作流程:

PC 串口助手发送数据
    ↓
UART 硬件接收 → 触发中断
    ↓
sle_uart_read_int_handler()  ← 中断上下文,需快速返回
    ↓
动态分配内存、拷贝数据 → 写入消息队列
    ↓
sle_uart_rx_client()  ← 任务上下文,阻塞读取队列
    ↓
sle_client_send_report_by_handle() → SSAP Write → 发送至 SLE 服务端

注意:中断回调中不宜执行耗时操作(如 SLE 发送),因此采用消息队列将 中断上下文任务上下文 解耦。

2.4、消息队列机制

OpenHarmony LiteOS 提供 osal_msg_queue_* 接口,用于任务间异步通信。本案例消息队列设计如下:

项目 说明
队列名称 "task_msg"
消息结构 msg_data_t(数据指针 + 长度)
写入方 UART 中断回调 sle_uart_read_int_handler
读取方 核心任务 sle_uart_rx_client
内存管理 中断中 osal_vmalloc 分配,任务中 osal_vfree 释放
┌──────────────────┐   write_copy    ┌──────────────────┐
│ UART 中断回调     │ ──────────────→ │   消息队列        │
│ (中断上下文)      │                 │  (task_msg)      │
└──────────────────┘                 └────────┬─────────┘
                                            │ read_copy (阻塞)
                                            ▼
                                   ┌──────────────────┐
                                   │ sle_uart_rx_     │
                                   │ client (任务)    │
                                   └────────┬─────────┘
                                            │ sle_client_send_report_by_handle
                                            ▼
                                   ┌──────────────────┐
                                   │   SLE 服务端      │
                                   └──────────────────┘

2.5、SLE 事件回调机制

设备发现回调:

回调函数 触发时机 处理逻辑
sle_client_sample_sle_enable_cbk SLE 协议栈启用完成 延时 1s 后开始扫描
sle_client_sample_seek_enable_cbk 扫描启动成功 打印日志
sle_client_sample_seek_result_info_cbk 扫描到广播设备 匹配 sle_uart_server,保存地址并停止扫描
sle_client_sample_seek_disable_cbk 扫描停止 发起连接 SleConnectRemoteDevice()

连接管理回调:

回调函数 触发时机 处理逻辑
sle_client_sample_connect_state_changed_cbk 连接状态变化 连接时交换 MTU 并配对;断开时移除配对并重新扫描

SSAP Client 回调:

回调函数 触发时机 处理逻辑
sle_client_sample_exchange_info_cbk MTU 协商完成 发起服务发现 ssapc_find_structure()
sle_client_sample_find_structure_cbk 发现服务 保存服务句柄范围与 UUID
sle_client_sample_find_property_cbk 发现属性 保存属性句柄至 g_sle_send_param
sle_client_sample_find_structure_cmp_cbk 服务发现完成 打印发现结果
sle_client_sample_write_cfm_cb 写操作完成 打印写操作结果
ssapc_notification_callbacks 收到 Notify 通知 将数据拷贝至 g_sle_rx_data,置位 g_sle_new_data_ready
ssapc_indication_callbacks 收到 Indicate 指示 预留指示处理

2.6、软件调用层次

应用层(sle_client_app.c)
    ├── sle_client_app()              ← APP_FEATURE_INIT 注册入口
    ├── sle_uart_rx_client()          ← 核心任务:UART 数据 → SLE 发送
    ├── sle_uart_tx_client()          ← 接收任务:SLE Notify → 串口打印
    └── sle_uart_read_int_handler()   ← UART 中断回调 → 消息队列
            │
封装层(sle_client_lib.c)
    ├── SleClientInit()               ← 初始化:注册回调、启用 SLE
    ├── SleStartScan()                ← 配置并启动扫描
    ├── sle_client_send_report_by_handle()  ← SSAP Write 发送
    └── ssapc_notification_callbacks()      ← SSAP Notify 接收
            │
协议栈 / 驱动层
    ├── SleAnnounceSeek*()            ← 扫描与设备发现
    ├── SleConnection*()              ← 连接与配对管理
    ├── Ssapc*()                      ← SSAP Client 发现/读写
    ├── uapi_uart_register_rx_callback() ← UART 接收回调注册
    └── osal_msg_queue_*()            ← 消息队列操作

2.7、核心 API 介绍

2.7.1、头文件
#include "sle_client_lib.h"
#include "cmsis_os2.h"
#include "ohos_init.h"
#include "uart.h"
2.7.2、应用层 API
API 名称 功能说明
SleClientInit() 初始化 SLE 客户端:注册回调、启用 SLE 协议栈
sle_client_send_report_by_handle(data, len) 通过 SSAP 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_sle_rx_data[256] SLE 接收数据缓冲区
g_sle_new_data_ready SLE 新数据就绪标志,由 Notify 回调置位
mouse_msg_queue UART 数据消息队列句柄
msg_data_t 消息队列数据结构(指针 + 长度)

3、程序设计

3.1、程序架构

本案例目录结构

e4_sle_uart_client/
├── sle_client_app.c      # 应用层:UART 收发任务、消息队列与入口
├── sle_client_lib.c      # 协议栈封装:扫描、连接、SSAP 发现、回调
├── sle_client_lib.h      # 头文件:宏定义与 API 声明
├── BUILD.gn              # GN 编译配置
├── README_zh.md          # 案例简要说明
└── 实验手册.md            # 本实验手册

程序执行流程:

系统启动
    │
    ▼
sle_client_app()                    ← APP_FEATURE_INIT 注册,自动执行
    │
    ├── osal_msg_queue_create()     ← 创建 UART 数据消息队列
    │
    ├── osThreadNew(sle_uart_rx_client)  ← 创建 UART→SLE 核心任务
    └── osThreadNew(sle_uart_tx_client)  ← 创建 SLE→串口 接收任务
            │
            ▼
sle_uart_rx_client()
    ├── SleClientInit()             ← 注册回调 → 启用 SLE → 开始扫描
    │       └── sle_enable_cbk → SleStartScan() → 连接 → 服务发现
    │
    └── uapi_uart_register_rx_callback()  ← 注册 UART 接收回调
            │
            ▼
    ┌───────────────────────────────────────────┐
    │  UART 中断 → 消息队列 → SSAP Write 发送    │
    └───────────────────────────────────────────┘
    ┌───────────────────────────────────────────┐
    │  sle_uart_tx_client:轮询 g_sle_new_data  │
    │  _ready → 打印 SLE 接收数据到串口           │
    └───────────────────────────────────────────┘

3.2、源文件说明

文件 说明
sle_client_app.c 应用层主程序,定义消息队列、UART 中断回调,创建 UART→SLE 与 SLE→串口两个 RTOS 任务
sle_client_lib.c SLE 协议栈封装,实现扫描、连接、SSAP 服务发现与各类回调处理
sle_client_lib.h 扫描参数、服务端名称、全局变量声明与 API 接口
BUILD.gn 编译配置,生成 sle_uart 静态库

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 接收中断回调 — sle_uart_read_int_handler
static void sle_uart_read_int_handler(const void *buffer, uint16_t length, bool error)
{
    printf("sle_uart_read_int_handler server.\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);
}

中断回调中只做 内存拷贝 + 入队,不做 SLE 发送,保证中断快速返回。

(3)核心任务 — sle_uart_rx_client
void sle_uart_rx_client(void *arg)
{
    unused(arg);
    SleClientInit();

    errcode_t ret = uapi_uart_register_rx_callback(CONFIG_SLE_UART_BUS,
        UART_RX_CONDITION_FULL_OR_IDLE, 1, sle_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)
        {
            sle_client_send_report_by_handle(msg_data.value, msg_data.value_len);
            osal_vfree(msg_data.value);
        }
    }
}

任务阻塞读取消息队列,取到 UART 数据后通过 SSAP Write 发送至 SLE 服务端,发送完毕释放动态内存。

(4)SLE 接收任务 — sle_uart_tx_client
void sle_uart_tx_client(void *arg)
{
    unused(arg);
    while (1)
    {
        if (g_sle_new_data_ready == true)
        {
            printf("sle rev date:%s", g_sle_rx_data);
            g_sle_new_data_ready = false;
        }
        osDelay(1);
    }
}

接收任务通过轮询 g_sle_new_data_ready 标志位检测服务端 Notify 的新数据,并在串口打印。

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

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

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

APP_FEATURE_INIT(sle_client_app);

系统启动后自动调用该函数,先创建消息队列,再创建 UART 转发与 SLE 接收两个独立任务。

(6)客户端初始化 — SleClientInit
void SleClientInit()
{
    uint8_t local_addr[SLE_ADDR_LEN] = {0x13, 0x67, 0x5c, 0x07, 0x00, 0x51};
    SleAddr local_address;
    local_address.type = 0;
    memcpy_s(local_address.addr, SLE_ADDR_LEN, local_addr, SLE_ADDR_LEN);

    sle_uuid_client_register();          /* 注册 SSAP 客户端 */
    SleClientSampleSeekCbkRegister();    /* 注册扫描回调 */
    SleClientSampleConnectCbkRegister(); /* 注册连接回调 */
    sle_client_sample_ssapc_cbk_register(ssapc_notification_callbacks,
                                         ssapc_indication_callbacks);
    EnableSle();                         /* 启用 SLE */
    SleSetLocalAddr(&local_address);     /* 设置本地 MAC 地址 */
}

EnableSle() 成功后触发 sle_client_sample_sle_enable_cbk 回调,延时 1 秒后开始扫描。

(7)扫描匹配与连接 — sle_client_sample_seek_result_info_cbk
static void sle_client_sample_seek_result_info_cbk(SleSeekResultInfo *seek_result_data)
{
    printf("%s sle uart scan data :%s\r\n", SLE_CLIENT_LOG, seek_result_data->data);
    if (strstr((const char *)seek_result_data->data, SLE_SERVER_NAME) != NULL)
    {
        memcpy_s(&g_sle_remote_addr, sizeof(sle_addr_t),
                 &seek_result_data->addr, sizeof(sle_addr_t));
        SleStopSeek();
    }
}

扫描到广播数据中包含 sle_uart_server 时,保存服务端地址并停止扫描;扫描停止回调中自动发起连接。

(8)SSAP Write 发送 — sle_client_send_report_by_handle
errcode_t sle_client_send_report_by_handle(const uint8_t *data, uint8_t len)
{
    ssapc_write_param_t param = {0};
    param.handle = g_sle_find_service_result.start_hdl;
    param.type = 0;
    param.data = receive_buf;
    param.data_len = len + 1;
    memcpy_s(param.data, param.data_len, data, len);
    return SsapWriteReq(g_client_id, g_sle_conn_id, &param);
}

Write 发送依赖服务发现后保存的服务句柄和当前连接 ID。属性句柄在 sle_client_sample_find_property_cbk 中保存至 g_sle_send_param.handle

(9)Notify 接收 — ssapc_notification_callbacks
void ssapc_notification_callbacks(uint8_t client_id, uint16_t conn_id,
                                    ssapc_handle_value_t *data, errcode_t status)
{
    data->data[data->data_len - 1] = '\0';
    memcpy(g_sle_rx_data, data->data, data->data_len);
    g_sle_new_data_ready = true;
}

服务端通过 Notify 发送的数据在此回调中接收,并置位 g_sle_new_data_ready 供接收任务打印。

(10)BUILD.gn — 编译配置
static_library("sle_uart") {
  sources = [
    "sle_client_app.c",
    "sle_client_lib.c",
  ]
  // ...
}

3.4、程序执行流程

PC 串口助手 A e3 服务端 SSAP Client sle_client_lib sle_client_app PC 串口助手 B PC 串口助手 A e3 服务端 SSAP Client sle_client_lib sle_client_app PC 串口助手 B osal_msg_queue_create() 创建 sle_uart_rx_client / sle_uart_tx_client SleClientInit() EnableSle() → SleStartScan() 广播 sle_uart_server 连接 → MTU 交换 → 服务发现 UART 发送字符串 sle_uart_read_int_handler → 消息队列 sle_client_send_report_by_handle() SsapWriteReq() Write 数据 串口打印 sle rev date:... UART 发送字符串 Notify 数据 notification_cb → g_sle_rx_data sle_uart_tx_client 打印 sle rev date:...

4、编译步骤

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

4.1、确认案例目录

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

applications/sample/wifi-iot/app/e4_sle_uart_client/
├── sle_client_app.c
├── sle_client_lib.c
├── sle_client_lib.h
├── BUILD.gn
└── 实验手册.md

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

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

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

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

4.3、修改 SDK 配置文件

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

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

"sle_uart"

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

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

"sle_uart"

4.4、编译固件

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

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

编译成功后,将固件烧录到 开发板 B(作为 SLE UART 透传客户端)。

4.5、编译服务端固件(开发板 A)

按照 e3_sle_uart_server 案例的 README,完成服务端的 BUILD.gn 与 SDK 配置注册(组件名同样为 sle_uart,但需将 features 指向 e3_sle_uart_server:sle_uart),编译后将固件烧录到 开发板 A

注意:服务端与客户端不能同时编译到同一块开发板固件中。编译服务端时,需将 features 中的 e4_sle_uart_client:sle_uart 替换为 e3_sle_uart_server:sle_uart

4.6、烧录固件

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


5、运行结果

5.1、硬件准备

  1. 开发板 A(服务端固件 e3_sle_uart_server)上电或复位,等待 SLE 广播启动;
  2. 开发板 B(客户端固件,本案例)上电或复位,开始扫描并连接服务端;
  3. 两块开发板分别通过 USB 连接 PC,各打开一个串口助手(波特率 115200,8N1)。

建议:先启动服务端开发板,确认广播启动日志输出后,再启动客户端开发板,以提高首次连接成功率。

5.2、串口配置

参数
波特率 115200
数据位 8
停止位 1
校验位
流控

5.3、客户端预期输出(开发板 B)

烧录客户端固件并复位后,串口助手可观察到 SLE 初始化、扫描、连接与服务发现过程:

[sle uart client] sle uart scan data :sle_uart_server
[sle uart client] SLE_ACB_STATE_CONNECTED
[sle uart client] exchange mtu, mtu size: 520, version: 1.
[sle uart client] find structure cbk client: 0 conn_id:1 status: 0
[sle uart client] sle_client_sample_find_property_cbk, client id: 0, conn id: 1, ...

表示 SLE 协议栈启用成功,已扫描到目标服务端并完成连接与 SSAP 服务发现。

5.4、UART 透传验证

连接建立且服务发现完成后,进行双向透传测试:

测试 1:PC → 客户端 → SLE → 服务端

  1. 开发板 B 对应的 PC 串口助手中输入字符串(如 SLE UART client test)并发送;
  2. 客户端串口输出:
sle_uart_read_int_handler server.
[sle uart client] sle_client_sample_write_cfm_cb, conn_id:1 client id:0 status:0 handle:xx type:xx
  1. 开发板 A(服务端)串口输出:
sle rev date:SLE UART client test

测试 2:PC → 服务端 → SLE → 客户端

  1. 开发板 A 对应的 PC 串口助手中输入字符串(如 data from SLE server)并发送;
  2. 服务端串口输出:
sle_uart_read_int_handler server.
sle rev date:data from SLE server
  1. 开发板 B(客户端)串口输出:
sle rev date:data from SLE server

完整日志示例:

[sle uart client] sle uart scan data :sle_uart_server
[sle uart client] SLE_ACB_STATE_CONNECTED
[sle uart client] exchange mtu, mtu size: 520, version: 1.
sle_uart_read_int_handler server.
sle rev date:data from SLE server
sle_uart_read_int_handler server.

5.5、服务端预期输出(开发板 A)

烧录 e3_sle_uart_server 固件并复位后,串口助手可观察到 SLE 初始化与服务注册过程:

[sle server] init ok
[sle server] sle enable callback status:0
[sle server] sle add service in
[sle server] add service cbk callback server_id:0, handle:xx, status:0
[sle server] uuid: 22 22.
[sle server] add property cbk callback server_id:0, service_handle:xx, handle:xx, status:0
[sle server] uuid: 23 23.
[sle server] set announce data success.
[sle server] sle announce enable callback id:01, state:0

客户端连接成功后,服务端还会输出:

[sle server] connect state changed callback conn_id:0x01, conn_state:0x2, ...
[sle server] pair complete conn_id:01, status:0
sle rev date:SLE UART client test

5.6、双向透传验证汇总

方向 操作 预期现象
PC B → 服务端 A 在客户端串口助手发送字符串 服务端串口打印 sle rev date:<字符串>
PC A → 客户端 B 在服务端串口助手发送字符串 客户端串口打印 sle rev date:<字符串>

5.7、结果分析

现象 说明
客户端输出 sle uart scan data :sle_uart_server 扫描成功,发现目标服务端
客户端输出 SLE_ACB_STATE_CONNECTED 已成功连接服务端
客户端输出 exchange mtu / find structure SSAP 服务发现正常
发送 UART 数据后出现 sle_uart_read_int_handler server. UART 中断回调正常触发
客户端输出 write_cfm_cb ... status:0 SSAP Write 发送成功
客户端输出 sle rev date:... Notify 接收正常,双向透传成功
服务端输出 sle rev date:... 客户端 UART 数据透传至服务端正常
客户端长时间无连接日志 服务端未启动或广播未就绪
UART 发送后无 sle_uart_read_int_handler UART 回调未注册或串口参数不匹配
有 UART 接收但 Write 失败 服务发现未完成,检查连接与发现日志

5.8、常见问题排查

问题 可能原因 解决方法
客户端扫描不到服务端 服务端未启动或广播失败 确认服务端输出 announce enable callback;先启动服务端
连接后立即断开 信号干扰或协议栈未就绪 两块板靠近放置;确认 sle_enable_cbk 延时 1s 已执行
UART 发送无响应 波特率不一致或 USB 串口未连接 确认串口助手 115200 8N1;检查 USB 线连接
有 UART 回调但服务端无数据 连接未建立或服务发现未完成 等待 SSAP 服务发现完成;检查 find property 日志
服务端发送客户端无接收 Notify 未启用或服务发现未完成 检查服务端描述符配置;确认连接已建立
编译报错找不到 sle_uart BUILD.gn 或 config.py 未正确修改 逐步核对 4.2、4.3 节的配置项
两块板烧录了相同固件 未区分服务端/客户端固件 分别编译烧录 e3(服务端)和 e4(客户端)

6、实验扩展

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

  1. 修改透传方向:在 ssapc_notification_callbacks 中增加 uapi_uart_write(),将 SLE 接收数据直接写回 UART,实现真正的串口回环透传;
  2. 添加连接状态判断:在 UART 中断回调中检查 g_sle_conn_id 是否为 0,未连接时不入队,避免无效发送;
  3. 修改目标服务端名称:更改 SLE_SERVER_NAME 宏,同时修改服务端 g_sleLocalName,观察扫描连接结果的变化;
  4. 调整消息队列深度:修改 osal_msg_queue_create 参数,测试高速 UART 发送时的队列缓冲能力;
  5. 断开重连测试:断开 SLE 连接后观察客户端是否自动重新扫描并成功重连,验证 UART 透传是否恢复;
  6. 结合 e2 案例对比:对比本案例(UART 触发发送)与 e2_sle_connect_client(定时发送固定字符串)的设计差异,理解透传模式的工程价值;
  7. 结合 e3 案例联调:深入阅读 e3_sle_uart_server 服务端代码,理解 SSAP Server 端 Notify 发送与 Write 接收的完整链路。
Logo

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

更多推荐