2. BLE扫描参数配置与广播设备发现

BLE扫描参数配置与广播设备发现 学习卡片

任务说明

本案例实现一个BLE中心设备(主机),完成以下目标:扫描周围BLE外设的广播包,解析广播数据中的设备地址与RSSI值,并通过串口打印显示。案例基于ESP32开发板与Arduino框架,使用BLE 5.0支持的库进行扫描。

硬件与依赖

  • 硬件:ESP32开发板(支持BLE 5.0)、USB转串口线
  • 软件:Arduino IDE(安装esp32开发板支持包,版本≥2.0.0)
  • 依赖库:BLEDevice.h、BLEScan.h(ESP32 Arduino核心自带)

实现代码

#include <BLEDevice.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

// 扫描时长(秒)
const int scanTime = 5;

// 回调类:处理每个扫描到的广播设备
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    // 获取设备地址(MAC)
    std::string addr = advertisedDevice.getAddress().toString();
    // 获取RSSI(信号强度,单位dBm)
    int rssi = advertisedDevice.getRSSI();
    // 获取设备名称(若无广播名称则显示"Unknown")
    std::string name = advertisedDevice.haveName() ? 
                       advertisedDevice.getName() : "Unknown";

    // 串口打印:地址、RSSI、名称
    Serial.printf("Addr: %s | RSSI: %d dBm | Name: %s\n",
                  addr.c_str(), rssi, name.c_str());

    // 解析广播数据中的厂商自定义字段(示例:Apple iBeacon类型0x004C)
    if (advertisedDevice.haveManufacturerData()) {
      std::string manufData = advertisedDevice.getManufacturerData();
      if (manufData.length() >= 2) {
        uint8_t companyId[2];
        companyId[0] = (uint8_t)manufData[0];
        companyId[1] = (uint8_t)manufData[1];
        Serial.printf("  Manufacturer ID: 0x%02X%02X\n",
                      companyId[1], companyId[0]);
      }
    }
    Serial.println("---");
  }
};

void setup() {
  Serial.begin(115200);
  delay(1000); // 等待串口稳定

  // 初始化BLE
  BLEDevice::init("");

  // 创建扫描器
  BLEScan* pBLEScan = BLEDevice::getScan();
  // 设置回调
  pBLEScan->setAdvertisedDeviceCallbacks(
    new MyAdvertisedDeviceCallbacks());
  // 主动扫描(可获取广播响应数据)
  pBLEScan->setActiveScan(true);
  // 设置扫描窗口与间隔(单位:毫秒)
  pBLEScan->setInterval(100);
  pBLEScan->setWindow(99);

  Serial.println("开始BLE扫描...");
}

void loop() {
  // 执行扫描,参数为扫描时长(秒)
  BLEScan* pBLEScan = BLEDevice::getScan();
  BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
  
  // 打印扫描到的设备总数
  Serial.printf("扫描完成,共发现 %d 个设备\n", 
                foundDevices.getCount());
  Serial.println("等待下一次扫描...\n");
  
  // 释放扫描结果内存
  pBLEScan->clearResults();
  
  // 间隔5秒后再次扫描
  delay(5000);
}

代码说明

代码核心逻辑如下:

  1. 初始化:setup()中初始化BLE协议栈并创建扫描器对象。
  2. 扫描配置:启用主动扫描(setActiveScan(true)),可获取外设的扫描响应数据;设置扫描窗口与间隔参数。
  3. 回调处理:每发现一个广播设备,回调函数onResult()被触发,提取设备MAC地址、RSSI值、广播名称及厂商自定义数据。
  4. 循环扫描:loop()中每次执行5秒扫描,打印设备总数后清空结果,间隔5秒再次扫描。

广播数据解析说明

BLE广播包结构遵循蓝牙核心规范。本代码解析了以下关键字段:

  • 设备地址:通过getAddress().toString()获取外设的MAC地址(6字节)。
  • RSSI:通过getRSSI()获取接收信号强度,单位为dBm,数值越大表示信号越强(如-40dBm优于-80dBm)。
  • 设备名称:若广播包包含完整名称则直接显示,否则显示"Unknown"。
  • 厂商数据:广播包中可携带厂商自定义数据,前2字节为Company ID(小端序)。示例代码提取并打印该ID,可用于识别iBeacon等特定广播格式。

验证方法

按以下步骤验证案例功能:

  1. 将代码烧录至ESP32开发板,打开Arduino IDE串口监视器,波特率设为115200。
  2. 确保周围有正在广播的BLE设备(如手机开启蓝牙广播、BLE信标、其他ESP32等)。
  3. 观察串口输出,应看到类似以下格式的信息:
Addr: 24:0a:c4:12:34:56 | RSSI: -55 dBm | Name: MyDevice
---
Addr: 5c:f3:70:ab:cd:ef | RSSI: -72 dBm | Name: Unknown
  Manufacturer ID: 0x004C
---
扫描完成,共发现 2 个设备
等待下一次扫描...

判定标准:

  • 串口能持续打印设备地址与RSSI,且RSSI值随设备距离变化而改变(靠近时数值增大,远离时减小)。
  • 若外设广播了名称,能正确显示;若广播了厂商数据,能打印对应的Company ID。
  • 每次扫描结束后正确显示设备总数,且与打印的设备条目数一致。
BLE 5.0 扫描与接收广播 - 工作流程 ESP32 中心设备 BLE Scanner 外设 A 广播者 外设 B 广播者 外设 C iBeacon 广播包: 地址+RSSI+名称 数据解析 • MAC地址 • RSSI值 • 设备名称 • 厂商数据 Addr: 24:0a:c4:12:34:56 | RSSI: -55 dBm | Name: MyDevice Addr: 5c:f3:70:ab:cd:ef | RSSI: -72 dBm | Name: Unknown Manufacturer ID: 0x004C 扫描完成,共发现 2 个设备 扫描周期: 5秒扫描 + 5秒间隔 | 主动扫描模式

关键参数说明

参数值说明
scanTime5秒每次扫描持续时长
setActiveScan(true)主动扫描可获取扫描响应数据(额外广播信息)
setInterval(100)100ms扫描间隔
setWindow(99)99ms单次扫描窗口时长(须≤间隔)

注意事项:若周围无BLE广播设备,扫描结果将显示0个设备,属正常现象。可通过手机安装"nRF Connect"应用并开启广播功能来验证扫描效果。RSSI值为负整数,范围通常在-30dBm(极近)至-100dBm(极远)之间。