3. BLE连接建立流程与连接参数协商
任务定义
本案例基于 Nordic nRF52840 DK 开发板与 Zephyr RTOS,实现 BLE 5.0 中心设备(Central)主动扫描并连接外设(Peripheral),连接建立后触发连接参数更新请求,并正确监听与处理断开连接事件。最终通过串口日志观察连接状态迁移与断开原因。
硬件与软件准备
- 硬件:两块 nRF52840 DK 开发板(一块作为 Central,一块作为 Peripheral)
- 软件:Zephyr RTOS v3.4.0 及以上,nRF Connect SDK v2.5.0
- 外设端固件:使用 Zephyr 自带
peripheral_hr示例(或任意可广播的外设示例)
实现方案
中心设备端代码完成以下核心逻辑:
- 启动扫描,过滤可连接广播设备;
- 发现目标外设后停止扫描并发起连接;
- 连接建立后(
CONNECTED回调),发送连接参数更新请求(间隔 30ms,从延迟 0,超时 400ms); - 注册断开回调,在断开事件中打印断开原因;
- 使用按键 1 主动断开当前连接,验证主动断开路径。
代码实现
创建 central_conn.c 文件,内容如下:
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/printk.h>
#define DEVICE_NAME "Central_Conn"
#define TARGET_NAME "Peripheral_HR"
#define CONN_INTERVAL_MIN 0x18 /* 30ms */
#define CONN_INTERVAL_MAX 0x18
#define CONN_LATENCY 0
#define CONN_TIMEOUT 400 /* 400ms */
static struct bt_conn *default_conn;
static bool conn_active;
static struct gpio_dt_spec button = GPIO_DT_SPEC_GET(DT_NODELABEL(button0), gpios);
/* 扫描发现回调 */
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
struct net_buf_simple *ad)
{
char name[BT_MAX_NAME_LEN] = {0};
int ret = bt_data_parse(ad, &parse_name, name, sizeof(name));
if (ret && strcmp(name, TARGET_NAME) == 0) {
printk("Found target: %s (RSSI %d)\n", name, rssi);
bt_scan_stop();
struct bt_conn *conn;
int err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
BT_LE_CONN_PARAM_DEFAULT, &conn);
if (err) {
printk("Create conn failed (err %d)\n", err);
}
}
}
/* 连接参数更新完成回调 */
static void param_updated(struct bt_conn *conn, uint16_t interval,
uint16_t latency, uint16_t timeout)
{
printk("Conn params updated: interval %d.%02d ms, latency %d, timeout %d ms\n",
interval * 1.25, (interval * 125) % 100, latency, timeout * 10);
}
/* 断开回调 */
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
printk("Disconnected (reason 0x%02x)\n", reason);
conn_active = false;
default_conn = NULL;
/* 重新开始扫描 */
bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
}
/* 连接回调注册 */
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
.le_param_updated = param_updated,
};
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
printk("Connection failed (err 0x%02x)\n", err);
return;
}
default_conn = bt_conn_ref(conn);
conn_active = true;
printk("Connected\n");
/* 发起连接参数更新请求 */
struct bt_le_conn_param param = {
.interval_min = CONN_INTERVAL_MIN,
.interval_max = CONN_INTERVAL_MAX,
.latency = CONN_LATENCY,
.timeout = CONN_TIMEOUT,
};
int ret = bt_conn_le_param_update(default_conn, ¶m);
printk("Param update request: %s\n", ret == 0 ? "sent" : "failed");
}
/* 按键回调:主动断开 */
void button_pressed(void)
{
if (conn_active && default_conn) {
printk("Button pressed, disconnecting...\n");
bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
}
}
/* 扫描参数与回调 */
static struct bt_scan_cb scan_cb = {
.cb = device_found,
};
void main(void)
{
int err;
/* 初始化 GPIO 按键 */
gpio_pin_configure_dt(&button, GPIO_INPUT);
gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
gpio_init_callback(&button_cb, button_pressed, BIT(button.pin));
gpio_add_callback(button.port, &button_cb);
err = bt_enable(NULL);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
bt_scan_cb_register(&scan_cb);
err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
if (err) {
printk("Scan start failed (err %d)\n", err);
}
printk("Scanning for %s...\n", TARGET_NAME);
}
配置与构建
在 prj.conf 中添加以下配置:
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_SCAN=y
CONFIG_BT_SCAN_FILTER_ENABLE=y
CONFIG_BT_SCAN_UUID_CNT=0
CONFIG_BT_SCAN_NAME_CNT=1
CONFIG_BT_SCAN_NAME="Peripheral_HR"
CONFIG_BT_CONN=y
CONFIG_BT_CONN_PARAM_UPDATE=y
CONFIG_GPIO=y
构建并烧录:
west build -b nrf52840dk_nrf52840 -d build/central
west flash -d build/central
验证方法
- 给外设板烧录
peripheral_hr示例(广播名默认为Peripheral_HR); - 上电两块板卡,观察中心设备串口输出;
- 预期日志顺序:
Bluetooth initialized→Scanning...→Found target→Connected→Param update request: sent→Conn params updated: interval 30.00 ms...; - 按下中心设备按键 1,观察输出
Button pressed, disconnecting...后出现Disconnected (reason 0x13)(0x13 表示远端用户主动断开); - 断开后设备自动重新扫描,可再次连接。
判定标准
- 串口日志完整呈现“扫描→发现→连接→参数更新→断开”全流程;
- 连接参数更新后实际间隔为 30ms(日志显示 30.00 ms);
- 主动断开后 reason 为 0x13,且设备能重新进入扫描状态。