4. BLE连接建立流程
任务说明
本案例实现BLE主机从扫描到发起连接、并在连接建立后完成连接参数协商的完整流程。使用Zephyr RTOS的BLE主机API,通过一个最小可运行的中央设备示例,演示扫描发现从设备、发起连接、以及连接参数更新请求的处理过程。
实现方案
基于Zephyr的struct bt_le_conn_param和连接回调机制,实现以下核心功能:
- 扫描并过滤指定MAC地址的广播设备
- 调用
bt_conn_le_create发起连接 - 连接建立后,通过
bt_conn_le_param_update发起连接参数协商(间隔、延迟、超时) - 通过回调函数输出连接状态变化和参数协商结果
代码实现
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gap.h>
/* 目标从设备MAC地址(需按实际设备修改) */
#define PEER_ADDR {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}
static struct bt_conn *default_conn;
static bool scan_started;
/* 连接参数协商结果回调 */
static void conn_param_updated(struct bt_conn *conn, uint16_t interval,
uint16_t latency, uint16_t timeout)
{
printk("连接参数协商成功: 间隔=%.2fms 延迟=%d 超时=%dms\n",
interval * 1.25, latency, timeout * 10);
}
/* 连接断开回调 */
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
printk("连接断开, reason=0x%02x\n", reason);
if (conn == default_conn) {
bt_conn_unref(default_conn);
default_conn = NULL;
}
}
/* 连接建立回调 */
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
printk("连接失败: 0x%02x\n", err);
return;
}
default_conn = bt_conn_ref(conn);
printk("连接已建立\n");
/* 发起连接参数协商: 间隔30ms, 延迟0, 超时2s */
struct bt_le_conn_param param = {
.interval_min = 24, /* 30ms / 1.25ms = 24 */
.interval_max = 24,
.latency = 0,
.timeout = 200 /* 2000ms / 10ms = 200 */
};
int ret = bt_conn_le_param_update(conn, ¶m);
printk("参数更新请求: %s\n", ret == 0 ? "已发送" : "失败");
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
.le_param_updated = conn_param_updated,
};
/* 扫描回调 */
static void scan_cb(const bt_addr_le_t *addr, int8_t rssi,
uint8_t adv_type, struct net_buf_simple *ad)
{
/* 过滤目标MAC地址 */
bt_addr_le_t target = {.type = BT_ADDR_LE_RANDOM, .a.val = PEER_ADDR};
if (bt_addr_le_cmp(addr, &target) == 0) {
printk("发现目标设备, 停止扫描并连接\n");
bt_le_scan_stop();
scan_started = false;
/* 发起连接 */
struct bt_le_conn_param conn_param = {
.interval_min = 24,
.interval_max = 24,
.latency = 0,
.timeout = 200
};
int ret = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
&conn_param, &default_conn);
if (ret) {
printk("连接创建失败: %d\n", ret);
}
}
}
void main(void)
{
int ret = bt_enable(NULL);
if (ret) {
printk("蓝牙初始化失败: %d\n", ret);
return;
}
printk("蓝牙主机已初始化\n");
bt_conn_cb_register(&conn_callbacks);
/* 启动扫描 */
struct bt_le_scan_param scan_param = {
.type = BT_LE_SCAN_TYPE_ACTIVE,
.options = BT_LE_SCAN_OPT_NONE,
.interval = 0x0010,
.window = 0x0010,
};
ret = bt_le_scan_start(&scan_param, scan_cb);
if (ret) {
printk("扫描启动失败: %d\n", ret);
return;
}
scan_started = true;
printk("开始扫描...\n");
/* 主循环保持运行 */
while (1) {
k_sleep(K_SECONDS(1));
}
}
关键逻辑说明
代码中bt_conn_le_create使用BT_CONN_LE_CREATE_CONN参数直接发起连接,连接参数在创建时即指定。连接建立后,connected回调中再次调用bt_conn_le_param_update演示连接参数协商过程——这是BLE协议中主机可随时发起的参数更新流程。从设备可能接受或拒绝该请求,结果通过le_param_updated回调反馈。
工程配置
在prj.conf中启用必要配置:
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_SCAN=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_CONN=y
CONFIG_BT_SMP=n
验证方法
使用nRF52840DK或同类开发板,配合一个可广播的BLE外设(如手机nRF Connect应用模拟)。按以下步骤验证:
- 将代码中
PEER_ADDR修改为目标外设的MAC地址 - 编译烧录后,观察串口日志:应依次输出“开始扫描”、“发现目标设备”、“连接已建立”、“参数更新请求: 已发送”
- 若外设接受参数更新,会输出“连接参数协商成功”及具体参数值
- 断开外设连接,应看到“连接断开”日志
判定标准:日志顺序与上述一致,且参数协商成功回调中显示的间隔值约为30ms(即interval=24),说明连接参数协商流程完整执行。