连接参数调优
使用技术:SLE 连接参数更新、功耗/延迟/吞吐权衡
前置阅读:Hello SLE
学习目标
- 理解连接参数的三个核心维度:连接间隔、从设备延迟、监管超时
- 掌握连接参数对功耗、延迟、吞吐量的影响矩阵
- 掌握
sle_update_connect_param()在线更新连接参数的方法 - 理解连接参数更新请求的协商机制(发起方 vs 响应方)
- 能根据实际应用场景(低功耗/低延迟/高吞吐)选择最优参数组合
规格与功能
本案例演示如何根据场景需求选择和调整连接参数,覆盖三种典型场景的参数配置。
| 规格项 | 低功耗模式 | 平衡模式 | 低延迟模式 |
|---|---|---|---|
| 连接间隔 | 100ms | 12.5ms | 7.5ms |
| 从设备延迟 | 49 | 0 | 0 |
| 监管超时 | 10s | 5s | 2s |
| 典型功耗 | ~1-2mA | ~5mA | ~8-10mA |
| 数据延迟 | ~100ms | ~12.5ms | ~7.5ms |
| 吞吐量 | ~10 KB/s | ~80 KB/s | ~130 KB/s |
| 适用场景 | 传感器定时上报 | 通用交互 | 实时控制、HID |
程序运行流程:
- Server 和 Client 建立连接(使用默认参数 12.5ms 间隔)
- 连接建立后,Server 根据场景调用
sle_update_connect_param()发起参数更新请求 - Client 在
connect_param_update_req_cb中收到请求,同意更新 - 双方
connect_param_update_cb收到更新完成通知 - 新参数立即生效
连接参数决定了 SLE 通信的节奏。选错了参数,要么功耗超标、要么延迟太大、要么频繁断连。理解这个权衡是做产品的基础。
基本概念
三个核心参数
flowchart LR
subgraph Interval[连接间隔 conn_interval]
I1[时间轴]
I2[连接事件1]
I3[连接事件2]
I4[连接事件3]
I2 --> I3 --> I4
end
| 参数 | 含义 | 单位 | 范围 | 默认值 |
|---|---|---|---|---|
连接间隔 (conn_interval) |
两个连接事件之间的时间 | 1.25ms/单位 | 6 ~ 3200 (7.5ms ~ 4s) | 0x64 (12.5ms) |
从设备延迟 (conn_max_latency) |
从设备可以跳过的最大连接事件数 | 次 | 0 ~ 499 | 0 |
监管超时 (conn_supervision_timeout) |
多长时间没收到数据判定为断连 | 10ms/单位 | 10 ~ 3200 (100ms ~ 32s) | 0x1F4 (5s) |
连接间隔的权衡
连接间隔是最关键的参数——它直接决定了通信的频率:
短间隔 (7.5ms) 长间隔 (100ms)
├────────┤ ├──────────────────────────────┤
每次间隔都通信 很久才通信一次
延迟低 (~7.5ms) 延迟高 (~100ms)
功耗高 (射频一直开着) 功耗低 (射频大部分时间休眠)
吞吐高 (每秒 133 次) 吞吐低 (每秒 10 次)
适合实时控制和音频 适合定时传感器上报
从设备延迟与省电
从设备延迟允许从设备(通常是 T 角色)在没有数据要发时跳过连接事件:
latency = 0(不允许跳过)
时间轴 |E|E|E|E|E|E|E|E|E|E|
每次都参与,功耗最高
latency = 4(允许跳过 4 次)
时间轴 |E| | | | |E| | | | |
每 5 次参与 1 次,功耗降为原来的 20%
E = 从设备参与连接事件
// 低功耗传感器场景:100ms 间隔 + 跳过 49 次 = 每 5 秒才参与一次
param.conn_interval_min = 80; // 100ms
param.conn_interval_max = 80;
param.conn_max_latency = 49; // 跳过 49 次
param.conn_supervision_timeout = 600; // 6 秒
// 实际通信周期: 100ms × (49+1) = 5 秒
// 从设备每 5 秒醒来一次,其余时间深度休眠
从设备延迟 ≠ 数据延迟。如果从设备有数据要发,它不用等待——随时可以在下一个连接事件中发送,不受 latency 限制。latency 只限制"无数据时跳过"的上限。
监管超时的安全性
监管超时必须满足协议栈的约束公式:
如果不满足,协议栈会拒绝参数更新。设置太短可能频繁误断(短暂干扰就断连),设置太长可能断连很久才发现:
| supervision_timeout | 效果 |
|---|---|
| 1~2 秒 | 断开感知快,但可能因短暂干扰频繁误断 |
| 5 秒 | 平衡点——大多数场景的推荐值 |
| 10~20 秒 | 断开感知慢,但连接非常稳定 |
| 32 秒 | 最大允许值,仅适合对断连完全不敏感的场景 |
通信流程:参数更新协商
连接参数更新是一个协商过程——发起方提出新参数,响应方可以接受或拒绝:
sequenceDiagram
participant S as Server (发起方)
participant C as Client (响应方)
S->>S: 决定需要调整参数
S->>C: sle_update_connect_param(new_params)
C->>C: connect_param_update_req_cb
alt 接受新参数
C->>C: 调用 sle_connect_param_update_rsp(accept)
C-->>S: 更新确认
Note over S: connect_param_update_cb(SUCCESS)
Note over C: connect_param_update_cb(SUCCESS)
Note over S,C: 新参数在新连接间隔开始生效
else 拒绝
C->>C: 调用 sle_connect_param_update_rsp(reject)
C-->>S: 更新被拒
Note over S: connect_param_update_cb(REJECTED)
Note over S,C: 保持原参数不变
end
涉及 API
| API | 谁调用 | 用途 |
|---|---|---|
sle_update_connect_param() |
Server 或 Client | 发起连接参数更新请求 |
sle_connect_param_update_rsp() |
响应方 | 接受或拒绝参数更新请求 |
sle_default_connection_param_set() |
Server | 设置默认连接参数(广播前设置) |
sle_set_acb_evt_param() |
Server | 设置连接事件参数(重试间隔和次数) |
sle_connection_register_callbacks() |
双方 | 注册连接回调(含 connect_param_update_req_cb、connect_param_update_cb) |
案例说明
做什么
演示三种场景的参数配置和动态切换:设备启动时用默认参数连接,连接成功后根据用户配置切换到低功耗或低延迟模式。
场景参数速查
| 场景 | 间隔 | 延迟 | 超时 | 功耗 | 延迟 | 吞吐 |
|---|---|---|---|---|---|---|
| 低功耗传感器(温湿度/1分钟一报) | 100ms | 49 | 10s | 极低 | 5秒 | ~10 KB/s |
| 通用交互(设备配置/状态查询) | 12.5ms | 0 | 5s | 中 | 12.5ms | ~80 KB/s |
| 低延迟实时(HID/音频/控制) | 7.5ms | 0 | 2s | 高 | 7.5ms | ~130 KB/s |
| 高吞吐(OTA/文件传输) | 2.5ms | 0 | 5s | 极高 | 2.5ms | ~280 KB/s |
关键配置
三种场景的参数代码
// 场景 1:低功耗传感器
sle_connection_param_update_t lp_param = {0};
lp_param.conn_id = g_conn_id;
lp_param.interval_min = 80; // 100ms
lp_param.interval_max = 80;
lp_param.max_latency = 49; // 跳过 49 次, 5 秒才参与一次
lp_param.supervision_timeout = 600; // 10 秒
sle_update_connect_param(&lp_param);
// 场景 2:通用交互(默认)
sle_connection_param_update_t normal_param = {0};
normal_param.conn_id = g_conn_id;
normal_param.interval_min = 0x64; // 12.5ms
normal_param.interval_max = 0x64;
normal_param.max_latency = 0; // 不跳过
normal_param.supervision_timeout = 0x1F4; // 5 秒
sle_update_connect_param(&normal_param);
// 场景 3:低延迟实时
sle_connection_param_update_t ll_param = {0};
ll_param.conn_id = g_conn_id;
ll_param.interval_min = 6; // 7.5ms
ll_param.interval_max = 6;
ll_param.max_latency = 0; // 不跳过
ll_param.supervision_timeout = 200; // 2 秒
sle_update_connect_param(&ll_param);
参数合法性校验
static errcode_t validate_conn_params(sle_connection_param_update_t *param)
{
// 约束 1:interval_min ≤ interval_max
if (param->interval_min > param->interval_max) {
return ERRCODE_INVALID_PARAM;
}
// 约束 2:supervision_timeout > (1+max_latency) × interval_max × 2
uint32_t min_timeout = (1 + param->max_latency) * param->interval_max * 2;
if (param->supervision_timeout <= min_timeout) {
return ERRCODE_INVALID_PARAM;
}
// 约束 3:interval 范围 (6 ~ 3200)
if (param->interval_min < 6 || param->interval_max > 3200) {
return ERRCODE_INVALID_PARAM;
}
return ERRCODE_SUCC;
}
代码详解
连接建立后立即更新参数
static void conn_state_changed_cb(uint16_t conn_id,
const sle_addr_t *addr,
sle_acb_state_t conn_state,
sle_pair_state_t pair_state,
sle_disc_reason_t disc_reason)
{
if (conn_state == SLE_ACB_STATE_CONNECTED) {
g_conn_id = conn_id;
// 连接建立后,根据场景需求更新参数
sleep(50); // 等待连接稳定
sle_connection_param_update_t param = {0};
param.conn_id = conn_id;
param.interval_min = 80; // 100ms 低功耗
param.interval_max = 80;
param.max_latency = 49;
param.supervision_timeout = 600;
sle_update_connect_param(¶m);
}
}
Client 端处理参数更新请求
static void conn_param_update_req_cb(uint16_t conn_id,
const sle_connection_param_update_t *param)
{
printf("[client] param update requested: interval=%d, latency=%d\n",
param->interval_min, param->max_latency);
// 校验参数是否在本端可接受范围内
if (param->interval_min < 6 || param->interval_max > 3200) {
// 参数不可接受,拒绝
sle_connect_param_update_rsp(conn_id, false);
return;
}
// 接受参数更新
sle_connect_param_update_rsp(conn_id, true);
}
参数更新完成确认
static void conn_param_update_cb(uint16_t conn_id, errcode_t status,
const sle_connection_param_update_t *param)
{
if (status == ERRCODE_SUCC) {
printf("[server] conn param updated: interval=%d ms, latency=%d\n",
param->interval_min * 125 / 100,
param->max_latency);
// 新参数已生效
g_current_conn_interval = param->interval_min;
} else {
printf("[server] conn param update failed: %d\n", status);
// 保持原参数,考虑重试或降级
}
}
动态切换场景模式
// 根据业务需求切换连接参数
void switch_to_low_power_mode(void)
{
sle_connection_param_update_t param = build_low_power_params();
sle_update_connect_param(¶m);
}
void switch_to_low_latency_mode(void)
{
sle_connection_param_update_t param = build_low_latency_params();
sle_update_connect_param(¶m);
}
void switch_to_high_throughput_mode(void)
{
sle_connection_param_update_t param = build_high_throughput_params();
sle_update_connect_param(¶m);
}