2. BLE 5.0 扫描与接收广播
任务说明
本案例实现一个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);
}
代码说明
代码核心逻辑如下:
- 初始化:
setup()中初始化BLE协议栈并创建扫描器对象。 - 扫描配置:启用主动扫描(
setActiveScan(true)),可获取外设的扫描响应数据;设置扫描窗口与间隔参数。 - 回调处理:每发现一个广播设备,回调函数
onResult()被触发,提取设备MAC地址、RSSI值、广播名称及厂商自定义数据。 - 循环扫描:
loop()中每次执行5秒扫描,打印设备总数后清空结果,间隔5秒再次扫描。
广播数据解析说明
BLE广播包结构遵循蓝牙核心规范。本代码解析了以下关键字段:
- 设备地址:通过
getAddress().toString()获取外设的MAC地址(6字节)。 - RSSI:通过
getRSSI()获取接收信号强度,单位为dBm,数值越大表示信号越强(如-40dBm优于-80dBm)。 - 设备名称:若广播包包含完整名称则直接显示,否则显示"Unknown"。
- 厂商数据:广播包中可携带厂商自定义数据,前2字节为Company ID(小端序)。示例代码提取并打印该ID,可用于识别iBeacon等特定广播格式。
验证方法
按以下步骤验证案例功能:
- 将代码烧录至ESP32开发板,打开Arduino IDE串口监视器,波特率设为115200。
- 确保周围有正在广播的BLE设备(如手机开启蓝牙广播、BLE信标、其他ESP32等)。
- 观察串口输出,应看到类似以下格式的信息:
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。
- 每次扫描结束后正确显示设备总数,且与打印的设备条目数一致。
关键参数说明
| 参数 | 值 | 说明 |
|---|---|---|
| scanTime | 5秒 | 每次扫描持续时长 |
| setActiveScan(true) | 主动扫描 | 可获取扫描响应数据(额外广播信息) |
| setInterval(100) | 100ms | 扫描间隔 |
| setWindow(99) | 99ms | 单次扫描窗口时长(须≤间隔) |
注意事项:若周围无BLE广播设备,扫描结果将显示0个设备,属正常现象。可通过手机安装"nRF Connect"应用并开启广播功能来验证扫描效果。RSSI值为负整数,范围通常在-30dBm(极近)至-100dBm(极远)之间。