欢迎光临
我们一直在努力

【星闪 SLE 学习笔记 04】深入理解设备发现、连接建立与配对机制

【星闪 SLE 学习笔记 04】深入理解设备发现、连接建立与配对机制

在这里插入图片描述

前三篇分别完成了 SLE 技术体系梳理、双板串口透传和 SSAP 服务模型分析。继续沿着通信链向前追溯,这一篇重点研究:两块设备在交换 SSAP 数据之前,究竟怎样从 announce、seek 走到连接与配对。

文章摘要: 本文结合国际星闪联盟公开标准范围与 BearPi-Pico H3863 官方公开 SDK,分析 SLE UART 示例中的 announce 参数设置、公开数据组织、Client seek、目标筛选、连接状态回调和配对流程,并通过两块开发板的受控断开实验观察 Server 重新 announce、Client 重新 seek 以及链路恢复过程。全文区分标准定义、SDK 可观察实现与实板结果,不把 Demo 的名称筛选和删除配对记录策略写成所有 SLE 产品的固定机制。

关键词: 星闪;SLE;设备发现;Announce;Seek;连接;配对;BearPi-Pico H3863


一、为什么连接之前还要研究设备发现

第二篇跑通双板通信时,我已经见过以下日志:

set announce data success
sle announce enable callback
sle uart scan data
[Connected]
pair complete
exchange mtu

当时关注的是“最终能否通信”,所以这些日志主要被当作成功检查点。第三篇继续分析 SSAP 后,我逐渐意识到:这些提示其实对应一条完整的状态链。

Server 配置并启动 announce
→ Client 启动 seek
→ Client 从公开数据中识别目标
→ Client 停止 seek 并发起连接
→ 连接状态变为 CONNECTED
→ Client 判断当前配对状态
→ 未配对时发起 pairing
→ 配对完成
→ 交换 SSAP 信息
→ 发现服务并开始数据交互

因此,只看到 [Connected] 还不能说明整条应用链路已经准备完成。后面至少还要观察配对、SSAP 信息交换和服务发现。

本文源码分析继续基于固定提交:

08d2880956249642cc90d7eae9b80ed721646967

固定提交的目的,是避免 SDK 后续修改后,文章中的函数和实际代码无法对应。


二、先明确本文的信息边界

1. 标准公开范围

国际星闪联盟 T/XS 20001《基础服务层 设备发现与服务管理》的公开页面说明,其范围包括设备发现角色、设备公开信息、设备发现过程、广播数据格式,以及服务及其元素和服务交互机制。T/XS 20001-2022

联盟标准目录还列出了 T/XS 20001-2025 V1.1.0。本文对具体函数和字段的分析来自固定版本 H3863 SDK,不能把厂商 API 名称直接写成标准接口。国际星闪联盟标准目录

2. SDK 可观察实现

以下接口名称来自 H3863 SDK:

sle_set_announce_param()
sle_set_announce_data()
sle_start_announce()
sle_set_seek_param()
sle_start_seek()
sle_connect_remote_device()
sle_pair_remote_device()

本文可以根据公开源码解释它们在当前 Demo 中的调用关系,但其他芯片或其他 SDK 不一定使用相同名称。

3. 实板结果

状态回调顺序、断开原因和恢复过程,需要以本次两块 H3863 的串口日志为依据。串口工具显示的时间是主机接收日志的时间,不能当作协议保证的空口时延。


三、Server 如何进入 announce

Server 的设备公开逻辑主要位于:

application/samples/products/sle_uart/
└─ sle_uart_server/sle_uart_server_adv.c

初始化调用链可以概括为:

sle_uart_server_adv_init()
├─ sle_set_default_announce_param()
│ └─ sle_set_announce_param()
├─ sle_set_default_announce_data()
│ └─ sle_set_announce_data()
└─ sle_start_announce()

1. announce 参数

当前源码可以观察到以下配置:

param.announce_mode = SLE_ANNOUNCE_MODE_CONNECTABLE_SCANABLE;
param.announce_handle = 1;
param.announce_level = SLE_ANNOUNCE_LEVEL_NORMAL;
param.announce_channel_map = 0x07;
param.announce_interval_min = 0xC8;
param.announce_interval_max = 0xC8;
param.announce_tx_power = 18;

示例还显式设置本地地址:

01:02:03:04:05:06

announce_mode 的枚举名表明当前模式具备 connectable 和 scannable 属性。源码注释把 0xC8 按 125 μs 单位解释为 25 ms。这里记录的是当前示例配置,不能外推为 SLE 的固定 announce 间隔。

2. announce handle

当前示例使用:

#define SLE_ADV_HANDLE_DEFAULT 1

所以日志中会看到 announce ID 或 handle 为 1。这个 1 是 Demo 中定义的值,不应与第三篇的 Service Handle 1 混为一谈。虽然数值相同,但两者属于不同对象和不同接口上下文。


四、逐字节读懂公开数据

Server 在设置公开数据时会主动打印字节序列。第二篇曾记录:

announce_data_len = 6
announce_data:
0x01 0x02 0x01 0x02 0x02 0x00

结合 sle_uart_server_adv.h 中的数据类型枚举和当前组包代码,可以按以下方式理解:

数据类型长度字段值
第一段 0x01:discovery level 0x02 0x01:normal
第二段 0x02:access mode 0x02 0x00

seek response 日志为:

0x0c 0x02 0x0a
0x10 0x0b
0x73 0x6c 0x65 0x5f 0x75 0x61 0x72 0x74
0x5f 0x73 0x65 0x72 0x76 0x65 0x72

其中:

0x0C → TX power level
0x02 → 当前字段长度
0x0A → 当前示例填写的值

0x10 → 当前完整名称字段长度
0x0B → complete local name
后续字节 → "sle_uart_server"

这里的字段解释来自 H3863 示例自身的枚举和组包过程。不能因为它看起来与其他无线协议的数据结构相似,就直接套用其他协议的字段顺序。

本次保存的 Server 完整日志与源码配置一致:

[21:30:10:646] sle set announce param, handle:1, mode:3,
min_interval:c8, max_interval:c8, tx_power:18
[21:30:10:666] data.announce_data_len = 6
[21:30:10:666] data.announce_data:
0x01 0x02 0x01 0x02 0x02 0x00
[21:30:10:687] data.seek_rsp_data_len = 20
[21:30:10:687] data.seek_rsp_data:
0x0c 0x02 0x0a 0x10 0x0b …
[21:30:10:708] set announce data success
[21:30:10:708] sle start announce in, adv_id:1
[21:30:10:739] sle announce enable callback id:01, state:0

state:0 与前面的 set announce data success 共同说明,本次 Server 已成功设置数据并进入 announce。原始带时间戳日志已作为本文实验材料保存,而不是只保留单行成功截图。


五、Client 怎样找到目标 Server

Client 逻辑主要位于:

application/samples/products/sle_uart/
└─ sle_uart_client/sle_uart_client.c

1. 注册 seek 回调

初始化阶段注册:

sle_enable_cb
seek_enable_cb
seek_result_cb
seek_disable_cb

这些回调共同组成了 Client 的发现状态机。

2. 设置 seek 参数

sle_uart_start_scan() 设置:

param.filter_duplicates = 0;
param.seek_filter_policy = 0;
param.seek_phys = 1;
param.seek_type[0] = 1;
param.seek_interval[0] = 100;
param.seek_window[0] = 100;

随后调用:

sle_set_seek_param()
→ sle_start_seek()

本文只记录固定 SDK 中的原始值。在没有进一步核实对应版本定义之前,不自行给 100 添加时间单位。

3. 按名称筛选目标

seek result 回调检查返回数据中是否包含:

sle_uart_server

匹配后执行:

保存对端地址
→ sle_stop_seek()

这里要注意:按本地名称筛选目标,是当前教学示例采用的应用策略,不代表 SLE 标准要求所有设备必须依赖名称才能连接。

4. 示例中的长度处理边界

sle_seek_result_info_t 同时提供:

uint8_t data_length;
uint8_t *data;

但示例直接把 data 交给 %s 和 strstr()。这与第三篇遇到的字符串边界问题相似:严格实现应按 data_length 解析各个公开数据字段,而不是默认整个回调缓冲区一定以 \\0 结尾。

本篇先记录这个实现边界,不把它扩展成完整的公开数据解析器,以免偏离设备发现与连接主线。


六、从停止 seek 到发起连接

匹配到目标后,Client 并没有立即在 seek result 回调中调用连接接口,而是先执行 sle_stop_seek()。

等 seek disable 回调返回成功后,再执行:

sle_remove_paired_remote_device(remote_addr)
→ sle_connect_remote_device(remote_addr)

这说明当前示例的顺序是:

找到目标
→ 停止 seek
→ 等待停止回调
→ 删除旧配对记录
→ 发起连接

“连接前删除旧配对记录”是当前 Demo 的选择。它会使后续连接重新经历配对流程,不能写成所有正式产品都应该采用的策略。


七、连接状态不等于配对状态

H3863 公开头文件中,连接状态定义为:

枚举数值当前头文件含义
SLE_ACB_STATE_NONE 0x00 未连接
SLE_ACB_STATE_CONNECTED 0x01 已连接
SLE_ACB_STATE_DISCONNECTED 0x02 已断接

配对状态则是另一组枚举:

枚举数值当前头文件含义
SLE_PAIR_NONE 0x01 未配对
SLE_PAIR_PAIRING 0x02 正在配对
SLE_PAIR_PAIRED 0x03 已完成配对

Client 的连接状态回调在进入 CONNECTED 后继续判断:

if (pair_state == SLE_PAIR_NONE) {
sle_pair_remote_device(&g_sle_uart_remote_addr);
}

所以完整逻辑是:

先建立连接
→ 再检查是否已经配对
→ 未配对时主动发起 pairing

这就是为什么日志中 [Connected] 和 pair complete 会分别出现。

Client 日志完整呈现了发现、连接和配对的先后顺序:

[adv_report] event_type:0x03, data length:6,
data:0x01 0x02 0x01 0x02 0x02 0x00
[adv_report] event_type:0x0b, data length:20,
data:0x0c 0x02 0x0a 0x10 0x0b 0x73 …
[sle uart client] sle uart scan data: … sle_uart_server
[Connected]
addr:01:**:**:**:05:06, conn_id:00
[sle uart client] conn state changed disc_reason:0x0
[sle uart client] SLE_ACB_STATE_CONNECTED
[sle uart client] pair complete conn_id:0, addr:01***0506

这里可以观察到两类报告:长度 6 的 announce data,以及长度 20 的 seek response data。名称 sle_uart_server 位于第二类数据中。Client 找到名称后才进入 [Connected],之后又单独出现 pair complete,实测顺序与源码调用链一致。


八、配对完成后怎样进入 SSAP

Client 的 pair complete 回调在 status == 0 时构造:

ssap_exchange_info_t info = {0};
info.mtu_size = 520;
info.version = 1;
ssapc_exchange_info_req(0, g_sle_uart_conn_id, &info);

信息交换成功后,Client 才开始:

ssapc_find_structure()
→ 查找 Property
→ 保存 Property Handle
→ 准备应用数据交互

因此,连接、配对、SSAP 信息交换和服务发现是连续但不同的阶段:

CONNECTED
≠ pair complete
≠ exchange info complete
≠ property discovery complete

第三篇已经详细分析 SSAP 服务模型,本篇只说明它在设备连接流程中的位置。


九、断开后 Demo 怎样恢复

1. Client 的恢复路径

Client 在连接状态变成 DISCONNECTED 时执行:

删除对端配对记录
→ sle_uart_start_scan()
→ 再次 seek

2. Server 的恢复路径

Server 的连接状态回调在断开后:

清理连接/配对状态
→ 向消息队列写入 sle_dis_connect

Server 任务读到该消息后执行:

sle_start_announce(SLE_ADV_HANDLE_DEFAULT);

因此,当前 Demo 的恢复状态机可以概括为:

连接断开
├─ Server:再次启动 announce
└─ Client:再次启动 seek
↓
重新发现 → 重新连接 → 重新配对 → 重新交换 SSAP 信息

这里的“自动恢复”来自示例应用代码的两端配合,不能笼统写成“协议栈会自动重连”。


十、实板实验:观察断开与恢复状态机

本次实验继续使用:

开发板角色串口
连接外置 2.4 GHz 天线的开发板 Server COM4
未连接外置天线的开发板 Client COM3

两端保留第三篇验证通过的固件,不需要再次修改源码。

实验过程

  • 打开 COM4 和 COM3,串口参数保持 115200、8N1、无流控;
  • 为两端开启串口时间显示;
  • 先复位 Server,等其进入 announce;
  • 再复位 Client,等待连接、配对、SSAP 信息交换和 Property 发现;
  • 两端稳定后,只按 Client 板的 RST;
  • 保存 Server 的断开状态、disc_reason 和重新 announce 日志;
  • 保存 Client 重启后的 seek、连接、配对和服务发现日志;
  • 重连完成后发送:
  • RECONNECT-OK-04

    实测结果一:Server 检测到断开并重新 announce

    本次 Client 复位后,Server 保存到以下连续日志:

    [21:32:52:173] [Disconnected]
    [21:32:52:173] connect state changed callback
    conn_id:0x00
    conn_state:0x2
    pair_state:0x3
    disc_reason:0x7
    [21:32:52:173] sle start announce in, adv_id:1
    [21:32:52:195] sle announce enable callback id:01, state:0

    根据当前 SDK 头文件:

    conn_state:0x2 → SLE_ACB_STATE_DISCONNECTED
    pair_state:0x3 → SLE_PAIR_PAIRED

    这表示回调发生时,连接状态已经变为断开,而记录的配对状态为已配对。日志中的 disc_reason:0x7 是本次实测值。继续检查同一版本 sle_connection_manager.h,公开的 sle_disc_reason_t 只列出 0x10(remote)和 0x11(local),没有给出 0x07 的映射。因此本文只记录原始值,不把它强行解释成某个公开枚举原因。

    Server 在同一个时间点调用 sle_start_announce(),随后约 22 ms 后记录 announce enable 成功。这 22 ms 只是串口工具时间戳之间的观察差值,包含日志输出与调度影响,不是协议保证的恢复时延。

    实测结果二:重新连接、配对与交换 SSAP 信息

    Server 随后记录:

    [21:32:58:116] [Connected]
    [21:32:58:116] conn_state:0x1, pair_state:0x1, disc_reason:0x0
    [21:32:58:133] sle announce terminal callback id:01
    [21:32:58:367] pair complete conn_id:00, status:0
    [21:32:58:383] ssaps_mtu_changed_cbk … mtu_size:208, status:0

    这里的 conn_state:0x1 对应已连接,pair_state:0x1 对应尚未配对;大约 251 ms 后,Server 记录配对成功。随后出现 SSAP MTU 更新。

    Server 源码使用 %x 打印 mtu_size,所以日志中的 208 是十六进制 0x208,即十进制 520,与 Client 日志的 mtu size:520 一致。

    Client 端也完整记录了恢复后的上层准备过程:

    pair complete conn_id:0
    ssapc exchange info, conn_id:0, err_code:0
    exchange_info_cbk … status:0
    exchange mtu, mtu size:520, version:1
    discovery character cbk complete in
    operate ind:13, descriptors count:1, status:0, property->handle 2

    到 property->handle 2 为止,可以确认恢复后的连接已经重新完成配对、SSAP 信息交换和 Property 发现,而不是只停留在链路层 [Connected]。

    实测结果三:恢复后的应用数据成功到达 Server

    为了确认恢复的不只是连接和服务发现,我又进行了一次独立的 Client 复位与重连,并在 Property 发现完成后从 Client 发送:

    RECONNECT-OK-04

    第二次实验中,Client 的关键时间线为:

    [21:53:08.113] 收到 announce report
    [21:53:08.132] 收到 seek response report
    [21:53:08.205] Connected
    [21:53:08.455] pair complete
    [21:53:08.485] SSAP exchange info 成功,MTU 520
    [21:53:08.501] 发现 Property Handle 2
    [21:53:23.785] write response,err_code:0

    同一次实验的 Server 日志为:

    [21:53:02.238] Disconnected,conn_state:0x2,disc_reason:0x7
    [21:53:02.258] 重新调用 sle_start_announce
    [21:53:02.284] announce enable,state:0
    [21:53:08.198] Connected,conn_state:0x1,pair_state:0x1
    [21:53:08.440] pair complete,status:0
    [21:53:08.455] MTU 更新,mtu_size:0x208
    [21:53:23.770] write request,handle:2,status:0
    [21:53:23.770] received data, len:15, data:RECONNECT-OK-04

    RECONNECT-OK-04 共 15 个 ASCII 字节,Server 日志报告的 len:15 与内容完全对应。Client 随后收到 Handle 2 的写响应,err_code:0。至此,本次实验已经从断开、重新发现、重新连接和配对,一直验证到 SSAP Property 写入和应用数据接收。

    Server 记录写请求的时间为 21:53:23.770,Client 记录写确认的时间为 21:53:23.785。两者相差 15 ms,但这是两个串口窗口的主机接收时间差,不能据此宣称协议单次写入时延为 15 ms。


    十一、源码与实板日志共同支持的结论

    结合固定版本源码与本次两端日志,可以确认:

  • Server 显式设置 announce 参数、公开数据与 seek response 数据;
  • Client 主动启动 seek,并在回调中按 sle_uart_server 名称筛选目标;
  • Client 停止 seek 后,才在 disable 回调中发起连接;
  • Client 在连接成功且未配对时调用配对接口;
  • 配对成功后才请求交换 SSAP 信息;
  • Demo 在断开分支中分别安排 Server 重新 announce、Client 重新 seek;
  • 本次 Client 复位后,Server 实测进入 conn_state:0x2,并成功重新 announce;
  • 随后两端重新完成连接、配对、SSAP 信息交换和 Property 发现;
  • 第二次独立重连实验中,Client 通过 Property Handle 2 写入 15 字节数据,Server 完整收到 RECONNECT-OK-04,Client 收到 err_code:0 的写确认。
  • 仍不能确认或外推:

    • disc_reason:0x7 对应的规范化原因名称;
    • 串口时间差能够代表精确空口时延;
    • 每次断开都一定恢复成功;
    • 串口日志能够证明全部空口协议时序;
    • Demo 的名称筛选和删除配对记录策略适合正式产品;
    • 两端时间戳之差能够直接作为协议性能指标。

    十二、我的阶段性理解

    这次把两端完整日志按时间顺序排开后,我对连接流程有了三个更具体的认识。

    第一,设备“被看到”、建立连接、完成配对和具备应用数据能力是不同阶段。Client 能看到 sle_uart_server,只表示发现结果中出现了目标;看到 [Connected],也还不能说明已经完成 pairing 和 SSAP 服务发现。

    第二,回调驱动代码不能只看单个函数。只有把 seek result、seek disable、connect state、pair complete 和 exchange info 等回调连起来,才能理解为什么程序会从公开数据走到 Property Handle。

    第三,当前 Demo 的自动恢复不是一个孤立接口。Server 在断开后重新 announce,Client 重启后重新 seek,两端状态机重新相遇,才完成后续连接和配对。本次两次重连日志不仅验证到 Property 发现,还用 RECONNECT-OK-04 完成了应用数据闭环。


    参考资料

  • 国际星闪联盟:T/XS 20001-2022《基础服务层 设备发现与服务管理》
  • 国际星闪联盟:标准目录(含 T/XS 20001-2025 V1.1.0)
  • BearPi-Pico H3863 官方公开源码仓库
  • SLE UART Client 源码
  • SLE UART Server announce 源码
  • SLE 设备发现公开头文件
  • SLE 连接管理公开头文件
  • 赞(0)
    未经允许不得转载:171主机测评 » 【星闪 SLE 学习笔记 04】深入理解设备发现、连接建立与配对机制
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址