You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NRF5340蓝牙外设无法支持多设备连接问题排查求助

NRF5340 Zephyr外设多连接故障排查

问题背景

基于Zephyr v2.5.1开发NRF Connect外设项目,硬件为NRF5340开发板。代码设计支持多连接功能,各项单连接功能正常,但仅允许一台手机连接,第二台手机需等待前一台断开后才能成功连接。

项目配置(proj.conf)

CONFIG_BT=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_SMP=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DIS=y
CONFIG_BT_DIS_PNP=n
CONFIG_BT_BAS=y
CONFIG_BT_HRS=y
CONFIG_BT_DEVICE_NAME="Zephyr Heartrate Sensor"
CONFIG_BT_DEVICE_APPEARANCE=833
CONFIG_BT_MAX_CONN=2

主程序代码(main.c)

/* main.c - Application main entry point */

/*
 * Copyright (c) 2015-2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <sys/printk.h>
#include <sys/byteorder.h>
#include <zephyr.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
#include <bluetooth/services/bas.h>
#include <bluetooth/services/hrs.h>

#define NUM_CONN_MAX    CONFIG_BT_MAX_CONN

static struct bt_conn *curr_conn[NUM_CONN_MAX];

static const struct bt_data ad[] = {
    BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    BT_DATA_BYTES(BT_DATA_UUID16_ALL,
              BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
              BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
              BT_UUID_16_ENCODE(BT_UUID_DIS_VAL))
};

static struct bt_conn **find_curr_conn(struct bt_conn *conn)
{
    int i;
    struct bt_conn **conn_found = NULL;

    for (i = 0; i < NUM_CONN_MAX; i++) {
        if (curr_conn[i] == conn) {
            conn_found = curr_conn + i;
            break;
        }
    }

    return conn_found;
}

static void connected(struct bt_conn *conn, uint8_t err)
{
    struct bt_conn **conn_room;

    if (err) {
        printk("Connection failed (err 0x%02x)\n", err);
    } else {
        conn_room = find_curr_conn(NULL);
        if (conn_room) {
            *conn_room = bt_conn_ref(conn);
            printk("Connected\n");
        } else {
            printk("No room to trace connection!\n");
        }
    }
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
    struct bt_conn **conn_found;

    printk("Disconnected (reason 0x%02x)\n", reason);

    conn_found = find_curr_conn(conn);
    if (conn_found) {
        bt_conn_unref(*conn_found);
        *conn_found = NULL;
    }
}

static struct bt_conn_cb conn_callbacks = {
    .connected = connected,
    .disconnected = disconnected,
};

static void bt_ready(void)
{
    int err;

    printk("Bluetooth initialized\n");

    err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
    if (err) {
        printk("Advertising failed to start (err %d)\n", err);
        return;
    }

    printk("Advertising successfully started\n");
}

static void auth_cancel(struct bt_conn *conn)
{
    char addr[BT_ADDR_LE_STR_LEN];

    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

    printk("Pairing cancelled: %s\n", addr);
}

static struct bt_conn_auth_cb auth_cb_display = {
    .cancel = auth_cancel,
};

static void bas_notify(void)
{
    uint8_t battery_level = bt_bas_get_battery_level();

    battery_level--;

    if (!battery_level) {
        battery_level = 100U;
    }

    bt_bas_set_battery_level(battery_level);
}

static void hrs_notify(void)
{
    static uint8_t heartrate = 90U;

    /* Heartrate measurements simulation */
    heartrate++;
    if (heartrate == 160U) {
        heartrate = 90U;
    }

    bt_hrs_notify(heartrate);
}

void main(void)
{
    int err;

    err = bt_enable(NULL);
    if (err) {
        printk("Bluetooth init failed (err %d)\n", err);
        return;
    }

    bt_ready();

    bt_conn_cb_register(&conn_callbacks);
    bt_conn_auth_cb_register(&auth_cb_display);

    /* Implement notification. At the moment there is no suitable way
     * of starting delayed work so we do it here
     */
    while (1) {
        k_sleep(K_SECONDS(1));

        /* Heartrate measurements simulation */
        hrs_notify();

        /* Battery level simulation */
        bas_notify();
    }
}

排查与解决方案

1. 修改广播模式(核心问题)

当前代码使用BT_LE_ADV_CONN_NAME启动广播,该模式在建立第一个连接后会自动停止广播,导致后续设备无法发现外设。需要改为持续可连接广播模式:

将bt_ready()函数中的广播启动代码:

err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);

替换为:

err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), NULL, 0);

BT_LE_ADV_CONN模式会在建立连接后继续广播,允许其他设备发起连接请求。

2. 补充外设连接数配置

在proj.conf中添加外设模式的最大连接数配置,确保与全局连接数一致:

CONFIG_BT_PERIPHERAL_MAX_CONN=2

虽然Zephyr默认CONFIG_BT_PERIPHERAL_MAX_CONN等于CONFIG_BT_MAX_CONN,但显式配置可避免版本差异导致的限制。

3. 优化连接跟踪日志

在connected函数中添加已连接数统计打印,方便调试:

static void connected(struct bt_conn *conn, uint8_t err)
{
    struct bt_conn **conn_room;
    int connected_count = 0;

    // 统计当前已连接数
    for (int i = 0; i < NUM_CONN_MAX; i++) {
        if (curr_conn[i] != NULL) {
            connected_count++;
        }
    }

    if (err) {
        printk("Connection failed (err 0x%02x), current connected: %d\n", err, connected_count);
    } else {
        conn_room = find_curr_conn(NULL);
        if (conn_room) {
            *conn_room = bt_conn_ref(conn);
            connected_count++;
            printk("Connected, current connected: %d\n", connected_count);
        } else {
            printk("No room to trace connection! Current connected: %d\n", connected_count);
        }
    }
}

4. 验证硬件与资源支持

NRF5340硬件支持最多8个BLE连接,CONFIG_BT_MAX_CONN=2的配置完全符合硬件能力,无需担心资源不足问题。

5. 调试日志分析

利用已开启的CONFIG_BT_DEBUG_LOG=y,查看连接过程中的HCI日志:

  • 当第二台手机发起连接时,检查是否收到连接请求
  • 若连接被拒绝,查看错误码(如0x0D表示资源不足,需检查配置;0x08表示连接超时,需检查广播是否正常)

内容的提问来源于stack exchange,提问作者Muhammad Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 19:35:03