2. BLE扫描参数配置

任务说明

本案例要求编写BLE扫描参数配置代码,通过设置不同的扫描窗口(scan window)和扫描间隔(scan interval),实现主动扫描与被动扫描两种模式下的设备发现效果对比。代码基于Zephyr RTOS的BLE控制器API实现,目标是在nRF52840 DK开发板上运行,通过串口日志观察不同参数组合下的扫描结果差异。

扫描参数原理

BLE扫描过程中,扫描窗口决定每次射频开启监听的时间长度,扫描间隔决定两次扫描启动之间的时间跨度。当扫描窗口等于扫描间隔时,射频持续开启,为连续扫描模式;当扫描窗口小于扫描间隔时,射频周期性开关,为占空比扫描模式。主动扫描会额外发送扫描请求获取设备名称等附加信息,被动扫描仅监听广播报文。本案例通过配置三组参数对比发现效果:连续主动扫描、低占空比主动扫描、连续被动扫描。

代码实现


/* main.c - BLE扫描参数配置示例 */
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/sys/printk.h>

#define SCAN_INTERVAL_CONT  0x0010  /* 10ms 连续扫描 */
#define SCAN_WINDOW_CONT    0x0010  /* 10ms */
#define SCAN_INTERVAL_LOW   0x0100  /* 160ms 低占空比 */
#define SCAN_WINDOW_LOW     0x0020  /* 20ms */
#define SCAN_TIMEOUT        30000   /* 30秒 */

static uint8_t scan_mode = 0;
static struct bt_le_scan_param scan_params;

/* 扫描回调函数 */
static void scan_cb(const bt_addr_le_t *addr, int8_t rssi,
                    uint8_t adv_type, struct net_buf_simple *adv_data)
{
    char addr_str[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));

    printk("[%s] RSSI:%d Type:%d DataLen:%u\n",
           addr_str, rssi, adv_type, adv_data->len);

    if (adv_type == BT_LE_ADV_IND) {
        /* 主动扫描时,设备会响应扫描请求并发送附加数据 */
        printk("  -> 可连接广播,主动扫描可获取完整数据\n");
    }
}

/* 配置并启动扫描 */
static void start_scan(uint16_t interval, uint16_t window,
                       uint8_t type, uint8_t filter_dup)
{
    scan_params.type = type;
    scan_params.options = BT_LE_SCAN_OPT_NONE;
    scan_params.interval = interval;
    scan_params.window = window;
    scan_params.filter_dup = filter_dup;

    int err = bt_le_scan_start(&scan_params, scan_cb);
    if (err) {
        printk("扫描启动失败 (err %d)\n", err);
    } else {
        printk("扫描已启动: interval=%u.%ums window=%u.%ums type=%s\n",
               interval * 5 / 8, (interval * 5 * 100) % 800,
               window * 5 / 8, (window * 5 * 100) % 800,
               type == BT_LE_SCAN_TYPE_ACTIVE ? "主动" : "被动");
    }
}

/* 切换扫描模式 */
static void switch_scan_mode(void)
{
    int err = bt_le_scan_stop();
    if (err) {
        printk("停止扫描失败 (err %d)\n", err);
        return;
    }

    scan_mode = (scan_mode + 1) % 3;

    switch (scan_mode) {
    case 0:
        printk("\n=== 模式1: 连续主动扫描 (interval=10ms, window=10ms) ===\n");
        start_scan(SCAN_INTERVAL_CONT, SCAN_WINDOW_CONT,
                   BT_LE_SCAN_TYPE_ACTIVE, BT_LE_SCAN_FILTER_DUP_ENABLE);
        break;
    case 1:
        printk("\n=== 模式2: 低占空比主动扫描 (interval=160ms, window=20ms) ===\n");
        start_scan(SCAN_INTERVAL_LOW, SCAN_WINDOW_LOW,
                   BT_LE_SCAN_TYPE_ACTIVE, BT_LE_SCAN_FILTER_DUP_ENABLE);
        break;
    case 2:
        printk("\n=== 模式3: 连续被动扫描 (interval=10ms, window=10ms) ===\n");
        start_scan(SCAN_INTERVAL_CONT, SCAN_WINDOW_CONT,
                   BT_LE_SCAN_TYPE_PASSIVE, BT_LE_SCAN_FILTER_DUP_ENABLE);
        break;
    }
}

/* 按键中断回调 - 使用GPIO按键切换模式 */
void button_pressed(void)
{
    switch_scan_mode();
}

int main(void)
{
    int err;

    printk("BLE扫描参数配置示例启动\n");

    /* 初始化蓝牙 */
    err = bt_enable(NULL);
    if (err) {
        printk("蓝牙初始化失败 (err %d)\n", err);
        return err;
    }
    printk("蓝牙已初始化\n");

    /* 启动默认模式:连续主动扫描 */
    switch_scan_mode();

    /* 使用定时器周期切换模式(演示用,实际可用按键触发) */
    while (1) {
        k_sleep(K_SECONDS(30));
        switch_scan_mode();
    }
    return 0;
}

工程配置


# prj.conf
CONFIG_BT=y
CONFIG_BT_OBSERVER=y
CONFIG_BT_SCAN_FILTER_ENABLE=y
CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_PRINTK=y

编译与运行


# 在Zephyr工程目录下执行
west build -b nrf52840dk_nrf52840 -d build/scan_demo
west flash -d build/scan_demo

# 查看串口输出(默认115200波特率)
screen /dev/ttyACM0 115200

验证方法与判定标准

准备一个持续广播的BLE信标设备(如手机开启BLE广播或使用另一块开发板运行广播示例),放置于扫描设备1~3米范围内。观察串口输出:

  • 模式1(连续主动扫描):应能持续、密集地收到广播报文,且对可连接广播设备会显示“可连接广播,主动扫描可获取完整数据”提示,RSSI值波动小,发现速度快。
  • 模式2(低占空比主动扫描):收到广播报文的频率明显降低,间隔约160ms一次,RSSI值波动较大,发现速度变慢,但仍能获取主动扫描附加数据。
  • 模式3(连续被动扫描):接收频率与模式1相当,但不会出现“可连接广播,主动扫描可获取完整数据”提示,因为被动扫描不发送扫描请求。

判定标准:三种模式均能发现广播设备;模式1与模式3的报文接收频率明显高于模式2;模式1与模式2能获取主动扫描附加信息而模式3不能。若上述现象均符合,则案例验证通过。

扫描参数与预期效果对照

扫描参数配置与预期效果对照 时间轴 (ms) 模式1: 连续主动 interval=10ms window=10ms 射频持续开启 模式2: 低占空比主动 interval=160ms window=20ms 20ms 160ms间隔 模式3: 连续被动 interval=10ms window=10ms 射频持续开启(不发送扫描请求) 主动扫描(发送扫描请求) 被动扫描(仅监听) 低占空比扫描

关键参数说明

参数模式1模式2模式3
扫描间隔10ms160ms10ms
扫描窗口10ms20ms10ms
扫描类型主动主动被动
占空比100%12.5%100%
发现速度最快较慢快
附加数据获取支持支持不支持

代码中扫描间隔和窗口的单位为0.625ms,因此0x0010表示10ms,0x0100表示160ms,0x0020表示20ms。实际运行时可观察串口日志中打印的换算后时间值确认参数生效。每30秒自动切换一次模式,便于对比观察。