当前位置: 首页 > news >正文

【物联网】bleak (scan)扫描在干什么? BLE 广播(Advertising)

好的,这里把 **bleak 的扫描(scan)**讲清楚,并结合你正在用的桥接脚本给出最实用的写法。


1) bleak 扫描在干什么?

  • BLE 广播(Advertising):外设(你的 Arduino)周期性广播一个小数据包,里面可能包含:

    • local_name(本地名称,BLE 广播名)
    • service_uuids(该设备声明的服务 UUID)
    • manufacturer_data / service_data
    • tx_powerrssi
  • Central(你的 RPi) 不需要连上就能“听”到这些广播。

  • bleak 的 Scanner 就是把广播 “抓包+解析”,返回一个或一批 BLEDevice(设备基本信息)以及对应的 AdvertisementData(广播详细字段)。

你在代码里写的 device.name 本质上就是广播里的 local name(所以我们强调 BLE.setLocalName(...))。


2) bleak 扫描的几种方式

A. 一步到位:发现所有设备再自己筛选

from bleak import BleakScannerdevices = await BleakScanner.discover(timeout=5.0)  # 扫5秒
for d in devices:print(d.address, d.name)  # d.name 通常是 local_name
  • 简单直观,后续用 if d.name == "your_name": 取目标。

B. 边扫边筛:find_device_by_filter

from bleak import BleakScanner# 兼容新老 bleak:有的版本 filter 只收 device,有的收 (device, adv)
def match(dev, adv=None):name_ok = (dev.name and "ardunio_ble" in dev.name)if adv:  # 新版可以读 adv.local_name / adv.service_uuids / adv.rssiname_ok = name_ok or (adv.local_name == "ardunio_ble")return name_okdevice = await BleakScanner.find_device_by_filter(match, timeout=10.0)
  • 只要匹配到就返回目标设备,效率高、代码简洁。

C. 订阅回调:做更细的过滤/调试

from bleak import BleakScannerdef callback(device, adv_data):print(device.address, adv_data.local_name, adv_data.rssi, adv_data.service_uuids)scanner = BleakScanner(callback, service_uuids=["<SERVICE_UUID 可选>"])
await scanner.start()
await asyncio.sleep(5.0)
await scanner.stop()
devices = await scanner.get_discovered_devices()
  • 可以实时看到 RSSI、manufacturer data 等,便于调试或做更严格过滤

平台差异:

  • Linux(树莓派):用 BlueZ;确保 bluetoothd 在跑、蓝牙没被 rfkill。
  • macOS:用 CoreBluetooth;service_uuids 过滤更有效。
  • Windows:用 WinRT;名字/UUID 过滤也可用。
    bleak 整体是 异步 的,要在 asyncio 事件循环里跑。

3) 扫描结果里都有什么?

  • BLEDeviceaddress(MAC 或随机地址)、name(通常是 local_name)、rssi(部分平台)
  • AdvertisementData(新版可用):local_nameservice_uuidsmanufacturer_dataservice_datatx_powerrssi

常见困惑:名字有时“变或看不到”。这是因为:

  • 外设没把名字放进广播(或被截断:广播总长 31 字节)。
  • 中央设备缓存了 GATT 的 Device Name。解决:保持 setLocalNamesetDeviceName 一致,必要时清除配对、重启蓝牙。

4) 在你的桥接脚本里,扫描为什么这样写?

你脚本里用的是:

device = await BleakScanner.find_device_by_filter(lambda d: d.name and DEVICE_NAME in d.name, timeout=SCAN_TIMEOUT
)
  • 优点:只要看到名字匹配就返回;避免误连其它设备。
  • 为什么按“名字”而不是“MAC 地址”过滤?
    Arduino 的地址是稳定的,但课堂/演示更容易靠名字区分设备(也省去记录 MAC 的步骤)。
  • 改进(兼容新版 bleak 的 (device, adv) 过滤,同时加服务 UUID/信号强度兜底):
DEVICE_NAME = "ardunio_ble"
SERVICE_UUID = "f75cfb20-4bce-4d2a-a9a7-3d9a93e0e2f5"def match(dev, adv=None):name_ok = (dev.name and DEVICE_NAME in dev.name)if adv:name_ok = name_ok or (adv.local_name == DEVICE_NAME)# 进一步确认:服务 UUID 命中 和/或 信号强度不太弱uuid_ok = (SERVICE_UUID in (adv.service_uuids or []))rssi_ok = (adv.rssi is None) or (adv.rssi > -90)return (name_ok or uuid_ok) and rssi_okreturn name_okdevice = await BleakScanner.find_device_by_filter(match, timeout=10.0)

5) 扫描 → 连接 → 订阅 Notify 的完整最小流程

from bleak import BleakScanner, BleakClientDEVICE_NAME = "ardunio_ble"
SENSOR_CHAR_UUID = "f75cfb21-4bce-4d2a-a9a7-3d9a93e0e2f5"def on_notify(_handle, data: bytes):print("notify:", data.decode("utf-8", errors="ignore"))async def run():dev = await BleakScanner.find_device_by_filter(lambda d, a=None: (a and a.local_name == DEVICE_NAME) or (d.name == DEVICE_NAME),timeout=10.0)if not dev:print("device not found")returnasync with BleakClient(dev) as client:await client.start_notify(SENSOR_CHAR_UUID, on_notify)print("connected & subscribed. Press Ctrl+C to exit.")while True:await asyncio.sleep(1)asyncio.run(run())

6) 实操建议(避免“扫描不到/连不上”)

  • 保证广播名稳定BLE.setLocalNameBLE.setDeviceName 用同一个短名字(例如 cc_nano33_01)。
  • 广播不停:Arduino 程序里不要早停广播;Nano 33 IoT 默认 OK。
  • 延长扫描时间:广告间隔较长时把 timeout 设为 8–10 秒。
  • 靠近一点:RSSI 太低(<-90dBm)容易掉包/连不上。
  • Linux 蓝牙服务sudo systemctl status bluetooth 要在跑;rfkill list 确保没被软/硬阻断。
  • 版本匹配:bleak ≥ 0.22 的 find_device_by_filter(device, advertisement_data);如果老版本只传 device,用我上面的兼容写法。

7) 回到你的项目:扫描在整条链路中的角色

  1. 扫描:在 RPi 上用 bleak 找到目标 Arduino(按 LocalName 过滤)。
  2. 连接 & 订阅:连上后 start_notify(SENSOR_CHAR_UUID, handler),接收 Arduino 推的 JSON。
  3. 上行:把 JSON 发到 MQTT iot/sensors/data
  4. 下行:订阅 iot/commands/arduino,收命令后 write_gatt_char(COMMAND_CHAR_UUID, ...) 写回 Arduino。

所以“扫描”是整条 BLE→MQTT 桥的入口。稳定、准确地找对设备,才能保证后续 Notify/Write 都能正常工作。

http://www.dtcms.com/a/358220.html

相关文章:

  • 【Zephyr炸裂知识系列】11_手撸内存泄露监测算法
  • HoloLens2是如何扫描周边环境生成三角面片的,跟周边光线强弱关系
  • 基于单片机甲醛浓度检测报警系统Proteus仿真(含全部资料)
  • 深入理解C++中的返回值优化与流插入操作符
  • Java试题-选择题(22)
  • U盘作为系统启动盘之后格式化恢复
  • 一文了解大模型微调
  • 【开题答辩全过程】以 靖西市旅游网站为例,包含答辩的问题和答案
  • 基于EcuBus-Pro实现LIN UDS升级
  • 《C++——makefile》
  • 日志ELK、ELFK、EFK
  • 使用Python和GitHub构建京东数据自动化采集项目
  • 线程相关问题(AI回答)
  • 营业执照经营范围行业提取工具库项目方案解读(php封装库)
  • 【学Python自动化】 4. Python 控制流与函数学习笔记
  • FlowUs AI-FlowUs息流推出的AI创作助手
  • DAY 18 推断聚类后簇的类型 - 2025.8.30
  • ADB常用命令大全
  • Linux驱动开发重要操作汇总
  • 1.8 Memory
  • vue表格底部添加合计栏,且能跟主表同时滑动
  • 【Linux基础】深入理解计算机启动原理:MBR主引导记录详解
  • U-Boot移植过程中的关键目录文件解析
  • 循迹小车控制实验:实验介绍
  • 基于FPGA的简易医疗呼叫器实现,包含testbench
  • Linux 830 shell:expect,ss -ant ,while IFS=read -r line,
  • 在 VS2017 中使用 Visual Leak Detector 检测内存泄漏(记录一下 以前开发中使用过)
  • 数据结构(C语言篇):(七)双向链表
  • 学习游戏制作记录(视觉上的优化)
  • GRPO(组相对策略优化):大模型强化学习的高效进化