当前位置: 首页 > 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.xdnf.cn/news/1400707.html

相关文章:

  • jxWebUI--下拉选择框
  • AtCoder Beginner Contest 421
  • 海盗王64位dx9客户端修改篇之三
  • React前端开发_Day10
  • 骑行商城怎么开发
  • 【PCIE系列】1---PCIE系统拓扑结构分析
  • Ethan独立开发新品速递 | 2025-08-30
  • Libvio 访问异常排查关键要点
  • 基于Ultralytics YOLO通用目标检测训练体系与PyTorch EfficientNet的图像分类体系实现
  • oha:一款轻量级HTTP负载测试工具
  • 流式HTTP MCP服务器开发
  • ceph集群部署
  • 接雨水,leetCode热题100,C++实现
  • 嵌入式linux相机(2)
  • PostgreSQL数据类型一览(数值类型)
  • opencv实现轮廓绘制和选择
  • 生成式 AI 重构内容生产:效率提升背后的创作版权边界争议
  • day43-Ansible-PlayBook
  • 如何使用快照将 AWS OpenSearch 服务中的数据从开发环境复制到生产环境
  • 知料觅得-新一代AI搜索引擎
  • Linux网络服务发现在VPS云服务器自动化配置的关键技术与实践
  • 给某个conda环境安装CUDA 12.4版本 全局CUDA不变
  • C++的迭代器和指针的区别
  • 【小白笔记】基本的Linux命令来查看服务器的CPU、内存、磁盘和系统信息
  • Java SpringAI应用开发面试全流程解析:RAG、流式推理与企业落地
  • 物联网(IoT)中常用的通信协议
  • GD32VW553-IOT 基于 vscode 的 bootloader 移植(基于Cmake)
  • 微论-突触的作用赋能思考(可能是下一代人工智能架构的启发式理论)
  • 响应式编程框架Reactor【5】
  • Spring代理的特点