树莓派Debian系统下Python连接PolarH10 BLE设备获取数据问题
解决树莓派Python连接Polar H10 BLE设备的问题
我之前也踩过gattlib的不少坑,结合BlueZ的工作逻辑和Polar H10的BLE规范,给你一步步拆解解决方案:
一、先搞定RuntimeError: Channel or attrib not ready连接报错
这个错误大概率是连接参数不对,或者设备被其他设备占用了。试试这几个调整:
- Polar H10默认用公共BLE地址,连接时要指定非随机地址模式:
import gattlib req = gattlib.GATTRequester(mymac, False) # 第二个参数False对应公共地址 req.connect() # 别用connect(True),True是随机地址模式,不适合Polar H10 - 如果还是报错,先确保设备在蓝牙范围内,并且没被手机等其他设备占用。可以先用
bluetoothctl disconnect <你的Polar H10 MAC>强制断开其他连接。
二、别再硬编码0x15了!正确识别服务和特征
你代码里的0x15是特征的handle值,这个值不是固定的——不同设备、甚至同一设备重启后都可能变化。正确做法是通过标准UUID来查找:
先用BlueZ命令行确认Polar H10的关键UUID:
bluetoothctl scan on # 找到Polar H10的MAC connect <你的Polar H10 MAC> menu gatt list-attributes # 会列出所有服务、特征的UUID和对应handle你要重点找这几个标准UUID:
- 心率服务UUID:
0000180d-0000-1000-8000-00805f9b34fb(BLE标准心率服务) - 心率测量特征UUID:
00002a37-0000-1000-8000-00805f9b34fb(用来获取心率数据) - 客户端特征配置(CCC)描述符UUID:
00002902-0000-1000-8000-00805f9b34fb(用来开启通知)
- 心率服务UUID:
在Python里用gattlib动态遍历服务和特征:
services = req.discover_primary() for service in services: print(f"服务UUID: {service['uuid']}, 起始handle: {service['start']}, 结束handle: {service['end']}") # 遍历该服务下的所有特征 characteristics = req.discover_characteristics(service['start'], service['end']) for char in characteristics: print(f" 特征UUID: {char['uuid']}, handle: {char['handle']}") # 查找特征对应的描述符 descriptors = req.discover_descriptors(char['handle']) for desc in descriptors: print(f" 描述符UUID: {desc['uuid']}, handle: {desc['handle']}")运行这段代码,就能找到心率特征和它的CCC描述符的实际handle,后续用这些动态获取的值,别再硬编码了。
三、用gattlib正确订阅心率通知
要接收BLE通知,必须先给CCC描述符写入0x0001(开启通知),再用异步回调接收数据。完整代码示例:
import time import gattlib # 替换成你的Polar H10 MAC地址 POLAR_H10_MAC = "AA:BB:CC:DD:EE:FF" # 从上面的遍历代码中获取的CCC描述符handle(示例值,换成你自己的) CCC_HANDLE = 0x16 # 从遍历代码中获取的心率特征handle(示例值,换成你自己的) HEART_RATE_CHAR_HANDLE = 0x15 class HeartRateResponse(gattlib.GATTResponse): def on_response(self, data): # 解析Polar H10的心率数据:第一个字节是标志位,判断是8位还是16位格式 if data[0] & 0x01 == 0: heart_rate = data[1] else: heart_rate = (data[1] << 8) | data[2] print(f"当前心率: {heart_rate} BPM") def main(): try: # 初始化连接(公共地址模式) req = gattlib.GATTRequester(POLAR_H10_MAC, False) req.connect() print("连接成功") # 开启心率通知:给CCC描述符写入0x0001(小端字节序,所以是b"\x01\x00") req.write_by_handle(CCC_HANDLE, b"\x01\x00") print("已开启心率通知") # 创建回调对象并订阅特征 response = HeartRateResponse() req.read_by_handle_async(HEART_RATE_CHAR_HANDLE, response) # 保持程序运行接收通知 while True: time.sleep(1) except Exception as e: print(f"出错了: {e}") if __name__ == "__main__": main()
四、如果gattlib还是坑,试试Bleak(更推荐!)
gattlib的Python绑定比较老旧,兼容性问题多。Bleak是现代的Python BLE库,基于asyncio,适配BlueZ更稳定,文档也更清晰。用Bleak实现Polar H10心率通知的代码:
import asyncio from bleak import BleakClient, BleakScanner # BLE标准心率服务和特征UUID,不用找handle直接用 HEART_RATE_SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb" HEART_RATE_CHAR_UUID = "00002a37-0000-1000-8000-00805f9b34fb" def heart_rate_callback(sender, data): # 解析心率数据 if data[0] & 0x01 == 0: heart_rate = data[1] else: heart_rate = (data[1] << 8) | data[2] print(f"当前心率: {heart_rate} BPM") async def main(): # 扫描并找到Polar H10 devices = await BleakScanner.discover() polar_device = next((d for d in devices if "Polar H10" in d.name), None) if not polar_device: print("没找到Polar H10设备") return # 连接设备并订阅通知 async with BleakClient(polar_device.address) as client: print(f"已连接到 {polar_device.name}") # 直接用UUID订阅心率通知 await client.start_notify(HEART_RATE_CHAR_UUID, heart_rate_callback) # 保持连接接收数据(这里运行30秒,也可以换成while True) await asyncio.sleep(30) await client.stop_notify(HEART_RATE_CHAR_UUID) if __name__ == "__main__": asyncio.run(main())
这个代码不用手动找handle,直接用标准UUID就能订阅,省心很多。
关键注意事项
- 确保树莓派的BlueZ版本足够新(推荐5.50以上),可以用
bluetoothctl --version查看,旧版本可能不支持某些BLE特性。 - 运行Python代码时需要root权限,所以用
sudo python3 your_script.py启动。 - Polar H10需要处于未连接状态(断开手机APP的连接),否则树莓派无法连接。
内容的提问来源于stack exchange,提问作者jaaq
相关产品推荐
相关产品推荐

