跳转至

连接参数调优

使用技术: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

程序运行流程:

  1. Server 和 Client 建立连接(使用默认参数 12.5ms 间隔)
  2. 连接建立后,Server 根据场景调用 sle_update_connect_param() 发起参数更新请求
  3. Client 在 connect_param_update_req_cb 中收到请求,同意更新
  4. 双方 connect_param_update_cb 收到更新完成通知
  5. 新参数立即生效

连接参数决定了 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 + conn_max_latency) × conn_interval_max × 2

如果不满足,协议栈会拒绝参数更新。设置太短可能频繁误断(短暂干扰就断连),设置太长可能断连很久才发现:

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_cbconnect_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(&param);
    }
}

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(&param);
}

void switch_to_low_latency_mode(void)
{
    sle_connection_param_update_t param = build_low_latency_params();
    sle_update_connect_param(&param);
}

void switch_to_high_throughput_mode(void)
{
    sle_connection_param_update_t param = build_high_throughput_params();
    sle_update_connect_param(&param);
}