3. BLE连接建立与参数协商
BLE连接建立是主机(Central)与从机(Peripheral)之间建立双向数据通道的过程。连接建立后,双方通过连接事件(Connection Events)进行周期性通信,通信的节奏由连接间隔、连接延迟和监控超时三个参数共同决定。此外,MTU交换用于提升单次数据传输的有效载荷,而连接回调则是应用层感知连接状态变化的入口。
3.1 连接参数详解
连接参数在BLE链路层(Link Layer)建立连接时确定,直接影响通信的实时性、功耗和稳定性。应用层可以在连接建立后发起参数更新请求,但最终由链路层根据双方能力协商决定。
| 参数 | 含义 | 范围 | 步长 | 典型值 |
|---|---|---|---|---|
| 连接间隔 (Connection Interval) | 两个相邻连接事件之间的时间间隔 | 7.5 ms ~ 4 s | 1.25 ms | 30~50 ms (低延迟) / 100~400 ms (低功耗) |
| 连接延迟 (Slave Latency) | 从机可以跳过而不丢失连接的最大连接事件数 | 0 ~ 499 | 1 | 0~9 (低延迟) / 10~100 (低功耗) |
| 监控超时 (Supervision Timeout) | 从机未收到任何数据包后判定连接丢失的超时时间 | 100 ms ~ 32 s | 10 ms | 2~6 s (通常取 4 s) |
| MTU (Maximum Transmission Unit) | 单个ATT数据包的最大有效载荷 | 23 ~ 512 字节 | 1 字节 | 247 (BLE 4.0/4.1) / 512 (BLE 4.2+) |
3.1.1 连接间隔
连接间隔以 1.25 ms 为单位递增。主机在 CONNECT_REQ 中发送初始间隔,从机可以在连接建立后通过 LL_CONNECTION_PARAM_REQ 发起参数更新。BLE 协议规定连接间隔的最小值为 6(对应 7.5 ms),最大值为 3200(对应 4 s)。实际产品中,交互式应用(如遥控器、游戏手柄)常用 30~50 ms,数据采集类(如传感器)常用 100~400 ms。
3.1.2 连接延迟
连接延迟允许从机在指定数量的连接事件中不发送数据包,以节省功耗。延迟值 N 表示从机最多可以连续跳过 N 个连接事件。但协议规定:从机不得连续跳过超过 supervision_timeout / (interval * 2) 个事件,以确保链路不被判定超时。连接延迟为 0 表示每个连接事件从机都必须响应。
3.1.3 监控超时
监控超时是链路层判断连接丢失的最长时间。如果从机在该时间内未收到任何有效数据包(包括空包),链路层将强制断开连接,并通过回调通知应用层。超时值必须大于 connection_interval * (1 + slave_latency) * 2,否则参数更新将被拒绝。
3.2 MTU 交换
MTU(最大传输单元)决定了单个 ATT 数据包可以携带的用户数据大小。默认 MTU 为 23 字节(其中 3 字节为 ATT 头,实际有效载荷仅 20 字节)。通过 MTU 交换,双方可以协商一个更大的值,减少大数据传输时的分片开销。
- MTU 交换流程: 客户端发送
MTU Exchange Request携带本端最大接收 MTU,服务端回复MTU Exchange Response携带本端最大接收 MTU,最终取两者中的较小值作为连接 MTU。 - BLE 版本差异: BLE 4.0/4.1 最大支持 247 字节,BLE 4.2+ 最大支持 512 字节(基于 LE Data Length Extension)。
- 影响: MTU 从 23 提升到 247 时,单次传输效率提升约 10 倍,适合固件升级、批量数据同步等场景。
3.3 连接回调代码实现
连接回调是应用层监听连接状态、获取连接参数、处理 MTU 更新的核心接口。下面展示一个典型的连接回调实现,包括连接建立、参数获取、MTU 更新和断开通知。
/* ble_conn_cb.h - 连接回调接口定义 */
typedef struct {
void (*on_connected)(void *conn_handle, uint8_t role,
uint16_t interval, uint16_t latency,
uint16_t timeout);
void (*on_disconnected)(void *conn_handle, uint8_t reason);
void (*on_mtu_updated)(void *conn_handle, uint16_t mtu);
void (*on_param_updated)(void *conn_handle,
uint16_t interval, uint16_t latency,
uint16_t timeout);
} ble_conn_cb_t;
/* 注册回调 */
void ble_conn_cb_register(ble_conn_cb_t *cb);
/* ble_conn_cb_example.c - 连接回调实现示例 */
#include "ble_conn_cb.h"
/* 连接建立回调 */
static void on_connected(void *conn_handle, uint8_t role,
uint16_t interval, uint16_t latency,
uint16_t timeout) {
printf("[BLE] 连接建立, 角色: %s\n",
role == 0 ? "主机" : "从机");
printf(" 连接间隔: %.2f ms\n", interval * 1.25);
printf(" 连接延迟: %u 个事件\n", latency);
printf(" 监控超时: %.2f s\n", timeout * 0.01);
/* 发起 MTU 交换 (通常在连接后立即发起) */
ble_mtu_exchange(conn_handle, 512);
}
/* 断开连接回调 */
static void on_disconnected(void *conn_handle, uint8_t reason) {
printf("[BLE] 连接断开, 原因: 0x%02X\n", reason);
/* 此处可添加重连逻辑 */
}
/* MTU 更新回调 */
static void on_mtu_updated(void *conn_handle, uint16_t mtu) {
printf("[BLE] MTU 已更新: %u 字节\n", mtu);
/* MTU 更新后可以安全地发送小于等于 mtu-3 字节的数据包 */
}
/* 连接参数更新回调 (从机成功更新参数后触发) */
static void on_param_updated(void *conn_handle,
uint16_t interval, uint16_t latency,
uint16_t timeout) {
printf("[BLE] 连接参数已更新\n");
printf(" 新连接间隔: %.2f ms\n", interval * 1.25);
printf(" 新连接延迟: %u\n", latency);
printf(" 新监控超时: %.2f s\n", timeout * 0.01);
}
/* 注册回调结构体 */
static ble_conn_cb_t g_conn_cb = {
.on_connected = on_connected,
.on_disconnected = on_disconnected,
.on_mtu_updated = on_mtu_updated,
.on_param_updated = on_param_updated,
};
/* 应用初始化时调用 */
void app_ble_init(void) {
ble_conn_cb_register(&g_conn_cb);
printf("BLE 连接回调已注册\n");
}
on_connected中立即获取连接参数并打印,方便调试时确认实际协商结果。- MTU 交换应在连接后尽快发起,通常在
on_connected回调中调用ble_mtu_exchange。 on_param_updated仅在从机侧参数更新成功时触发,主机侧一般不会收到该回调(主机是参数更新的发起方)。- 断开回调的
reason参数可以区分“正常断开”、“超时断开”、“对方主动断开”等场景,用于日志记录和重连策略。
3.4 参数更新请求与协商
从机可以在连接建立后通过 ble_conn_param_update_req 发起参数更新。主机收到请求后,根据自身能力决定接受或拒绝。参数更新请求需要满足协议约束:
- 间隔约束:
min_interval ≤ max_interval,且两者均必须落在 7.5 ms ~ 4 s 范围内。 - 延迟约束:
latency ≤ (supervision_timeout / (max_interval × 2)) - 1。 - 超时约束:
supervision_timeout > max_interval × (1 + latency) × 2。
/* 从机发起连接参数更新请求示例 */
ble_conn_param_t new_params = {
.min_interval = 40, /* 40 × 1.25 ms = 50 ms */
.max_interval = 48, /* 48 × 1.25 ms = 60 ms */
.latency = 4, /* 最多跳过 4 个连接事件 */
.timeout = 400, /* 400 × 10 ms = 4 s */
};
int err = ble_conn_param_update_req(conn_handle, &new_params);
if (err == 0) {
printf("参数更新请求已发送\n");
} else {
printf("参数更新请求失败, err: %d\n", err);
}
3.5 常见错误与调试
| 错误场景 | 典型原因 | 排查方向 |
|---|---|---|
| 连接建立失败 | 设备不在广播状态、MAC 地址错误、蓝牙未初始化 | 检查广播日志;确认扫描过滤条件;确认协议栈状态 |
| 参数更新被拒绝 | 间隔/延迟/超时不满足协议约束、主机不支持该参数 | 打印主机返回的错误码;检查参数范围是否越界;降低延迟值重试 |
| MTU 交换超时或失败 | 对方不支持 MTU 交换、链路质量差导致丢包 | 确认对端 BLE 版本;重试交换;增大监控超时 |
| 连接频繁断开 | 监控超时过短、信号干扰、连接间隔过大导致从机饥饿 | 增大监控超时;减小连接间隔;增加射频重传次数 |
| 数据传输速率低于预期 | MTU 未协商、连接间隔过大、未启用 LE Data Length Extension | 确认 MTU 交换成功;调小连接间隔;检查是否支持 2-Mbit PHY |
- 使用 BLE 嗅探器(如 nRF Sniffer、Ellisys)捕获空中包,直接查看 CONNECT_REQ 中的连接参数和 MTU 交换过程。
- 在 on_connected 和 on_param_updated 回调中打印完整参数,与预期值对比。
- 如果参数更新被拒绝,可以先尝试用更保守的参数(如更大间隔、更小延迟)发起更新,确认链路正常后再逐步收紧。
3.6 性能与稳定性优化
连接参数的选择本质是实时性、功耗和链路稳定性的三方权衡。以下优化策略可供参考:
- 低延迟场景(遥控器、游戏手柄): 连接间隔 30~50 ms,延迟 0,超时 2~4 s。确保每 30~50 ms 有一次双向通信,触控响应在 100 ms 以内。
- 低功耗场景(传感器、信标): 连接间隔 200~400 ms,延迟 10~50,超时 4~6 s。从机在大部分连接事件中休眠,大幅降低平均电流。
- 大数据传输场景(OTA 升级、日志导出): 先发起 MTU 交换到 512 字节,再尝试缩小连接间隔至 15~30 ms,同时启用 2-Mbit PHY 和 LE Data Length Extension 以提升吞吐量。
- 链路稳定性: 监控超时建议设置为连接间隔 × (1 + 延迟) × 4 以上,为射频重传保留足够余量。若部署在强干扰环境(如 Wi-Fi 密集区),可适当增大连接间隔以避免碰撞。