1. 前言
在BLE开发中,原厂给的DEMO中肯定少不了对心率带的适配,相当于我们学习各种编程语言的“Hello World”。
现在我们将通过 Bleak 来获取心率带的数据。
如果阅读本文存在障碍的话,可以进入作者主页查看更多相关内容
2. 步骤
- 扫描周边设备
- 判断周边是否存在心率带
- 对心率带发起连接
- 发现服务
- 获取心率数据推送的特征句柄
- 使能心率推送的通知功能
2.1 扫描设备
因为本人是做BLE产品的,周边的蓝牙设备比较多,为了让扫描结果更清晰,在显示结果的时候可以通过信号强度来控制,让信号大于某一值时才显示。如代码及结果:
import asyncio
from bleak import discover
async def main():
devices = await discover()
for d in devices:
if d.rssi > -70:
print(d, d.rssi)
if __name__ == "__main__":
asyncio.run(main())
结果:
C6:49:E0:3B:58:85: COROS PACE 2 071237 -64
E4:72:54:52:A1:6D: COROS APEX PRO 0A56B2 -43
EF:FF:2D:2A:18:96: COROS VERTIX 2 F3DF6E -61
心率带设备是 COROS APEX PRO 0A56B2,因为就在旁边,所以信号强度最大。
2.2 判断周边是否存在心率带
import asyncio
from bleak import discover
target_addr = ""
heart_uuid = "0000180d-0000-1000-8000-00805f9b34fb"
async def main():
global target_addr
devices = await discover()
for d in devices:
if d.rssi > -60:
print(d, d.rssi)
if heart_uuid in d.metadata['uuids']:
target_addr = d.address
print("heart rate device addr:", target_addr)
if __name__ == "__main__":
asyncio.run(main())
结果:
E4:72:54:52:A1:6D: COROS APEX PRO 0A56B2 -57
heart rate device addr: E4:72:54:52:A1:6D
2.3 对心率带发起连接
import asyncio
from bleak import discover
target_addr = ""
heart_uuid = "0000180d-0000-1000-8000-00805f9b34fb"
async def main():
global target_addr
devices = await discover()
for d in devices:
if d.rssi > -60:
print(d, d.rssi)
if heart_uuid in d.metadata['uuids']:
target_addr = d.address
print("heart rate device addr:", target_addr)
if target_addr != "":
async with BleakClient(target_addr) as client:
print(f"Connected: {client.is_connected}")
if __name__ == "__main__":
asyncio.run(main())
结果:
E4:72:54:52:A1:6D: COROS APEX PRO 0A56B2 -57
heart rate device addr: E4:72:54:52:A1:6D
Connected: True
2.4 发现服务
import asyncio
from bleak import discover
target_addr = ""
heart_uuid = "0000180d-0000-1000-8000-00805f9b34fb"
async def main():
global target_addr
devices = await discover()
for d in devices:
if d.rssi > -60:
print(d, d.rssi)
if heart_uuid in d.metadata['uuids']:
target_addr = d.address
print("heart rate device addr:", target_addr)
if target_addr != "":
async with BleakClient(target_addr) as client:
print(f"Connected: {client.is_connected}")
svcs = await client.get_services()
print("Services:")
for service in svcs:
print(service)
if __name__ == "__main__":
asyncio.run(main())
结果:
E4:72:54:52:A1:6D: COROS APEX PRO 0A56B2 -57
heart rate device addr: E4:72:54:52:A1:6D
Connected: True
Services:
00001800-0000-1000-8000-00805f9b34fb (Handle: 1): Generic Access Profile
00001801-0000-1000-8000-00805f9b34fb (Handle: 10): Generic Attribute Profile
0000180d-0000-1000-8000-00805f9b34fb (Handle: 14): Heart Rate
0000180f-0000-1000-8000-00805f9b34fb (Handle: 20): Battery Service
0000180a-0000-1000-8000-00805f9b34fb (Handle: 23): Device Information
6e400001-b5a3-f393-e0a9-77656c6f6f70 (Handle: 34): Unknown
6e400001-b5a3-f393-e0a9-77757c7f7f70 (Handle: 40): Unknown
6e400001-b5a3-f393-e0a9-e50e24dcca9e (Handle: 46): Nordic UART Service
0000fee7-0000-1000-8000-00805f9b34fb (Handle: 52): Tencent Holdings Limited
00001814-0000-1000-8000-00805f9b34fb (Handle: 61): Running Speed and Cadence
00001816-0000-1000-8000-00805f9b34fb (Handle: 67): Cycling Speed and Cadence
2.5 获取心率数据推送的特征句柄
import asyncio
from bleak import discover
target_addr = ""
heart_uuid = "0000180d-0000-1000-8000-00805f9b34fb"
async def main():
global target_addr
devices = await discover()
for d in devices:
if d.rssi > -60:
print(d, d.rssi)
if heart_uuid in d.metadata['uuids']:
target_addr = d.address
print("heart rate device addr:", target_addr)
if target_addr != "":
async with BleakClient(target_addr) as client:
print(f"Connected: {client.is_connected}")
svcs = await client.get_services()
print("Services:")
for service in svcs:
print(service)
for service in svcs:
print(service)
for char in service.characteristics:
print(char)
if __name__ == "__main__":
asyncio.run(main())
结果:
E4:72:54:52:A1:6D: COROS APEX PRO 0A56B2 -57
heart rate device addr: E4:72:54:52:A1:6D
Connected: True
Services:
00001800-0000-1000-8000-00805f9b34fb (Handle: 1): Generic Access Profile
00002a00-0000-1000-8000-00805f9b34fb (Handle: 2): ['read', 'write']
00002a01-0000-1000-8000-00805f9b34fb (Handle: 4): ['read']
00002a04-0000-1000-8000-00805f9b34fb (Handle: 6): ['read']
00002aa6-0000-1000-8000-00805f9b34fb (Handle: 8): ['read']
00001801-0000-1000-8000-00805f9b34fb (Handle: 10): Generic Attribute Profile
00002a05-0000-1000-8000-00805f9b34fb (Handle: 11): ['indicate']
0000180d-0000-1000-8000-00805f9b34fb (Handle: 14): Heart Rate
00002a37-0000-1000-8000-00805f9b34fb (Handle: 15): ['notify']
00002a38-0000-1000-8000-00805f9b34fb (Handle: 18): ['read']
0000180f-0000-1000-8000-00805f9b34fb (Handle: 20): Battery Service
00002a19-0000-1000-8000-00805f9b34fb (Handle: 21): ['read']
0000180a-0000-1000-8000-00805f9b34fb (Handle: 23): Device Information
00002a29-0000-1000-8000-00805f9b34fb (Handle: 24): ['read']
00002a24-0000-1000-8000-00805f9b34fb (Handle: 26): ['read']
00002a25-0000-1000-8000-00805f9b34fb (Handle: 28): ['read']
00002a27-0000-1000-8000-00805f9b34fb (Handle: 30): ['read']
00002a28-0000-1000-8000-00805f9b34fb (Handle: 32): ['read']
6e400001-b5a3-f393-e0a9-77656c6f6f70 (Handle: 34): Unknown
6e400003-b5a3-f393-e0a9-77656c6f6f70 (Handle: 35): ['notify']
6e400002-b5a3-f393-e0a9-77656c6f6f70 (Handle: 38): ['write-without-response', 'write']
6e400001-b5a3-f393-e0a9-77757c7f7f70 (Handle: 40): Unknown
6e400003-b5a3-f393-e0a9-77757c7f7f70 (Handle: 41): ['notify']
6e400002-b5a3-f393-e0a9-77757c7f7f70 (Handle: 44): ['write-without-response', 'write']
6e400001-b5a3-f393-e0a9-e50e24dcca9e (Handle: 46): Nordic UART Service
6e400003-b5a3-f393-e0a9-e50e24dcca9e (Handle: 47): ['notify']
6e400002-b5a3-f393-e0a9-e50e24dcca9e (Handle: 50): ['write-without-response', 'write']
0000fee7-0000-1000-8000-00805f9b34fb (Handle: 52): Tencent Holdings Limited
0000fea1-0000-1000-8000-00805f9b34fb (Handle: 53): ['read', 'notify', 'indicate']
0000fea2-0000-1000-8000-00805f9b34fb (Handle: 56): ['read', 'write', 'indicate']
0000fec9-0000-1000-8000-00805f9b34fb (Handle: 59): ['read']
00001814-0000-1000-8000-00805f9b34fb (Handle: 61): Running Speed and Cadence
00002a53-0000-1000-8000-00805f9b34fb (Handle: 62): ['notify']
00002a54-0000-1000-8000-00805f9b34fb (Handle: 65): ['read']
00001816-0000-1000-8000-00805f9b34fb (Handle: 67): Cycling Speed and Cadence
00002a5b-0000-1000-8000-00805f9b34fb (Handle: 68): ['notify']
00002a5c-0000-1000-8000-00805f9b34fb (Handle: 71): ['read']
00002a5d-0000-1000-8000-00805f9b34fb (Handle: 73): ['read']
00002a55-0000-1000-8000-00805f9b34fb (Handle: 75): ['write', 'indicate']
可以看到下图中红色框框就是我们需要的特征,注意 ‘notify’ 这个通知属性,是我们需要拿下的,它的handle是15,为了方便,我们待会使能通知时直接使用。
2.6 使能心率推送的通知功能
import asyncio
from bleak import discover
target_addr = ""
heart_uuid = "0000180d-0000-1000-8000-00805f9b34fb"
async def main():
global target_addr
devices = await discover()
for d in devices:
if d.rssi > -60:
print(d, d.rssi)
if heart_uuid in d.metadata['uuids']:
target_addr = d.address
print("heart rate device addr:", target_addr)
if target_addr != "":
async with BleakClient(target_addr) as client:
print(f"Connected: {client.is_connected}")
svcs = await client.get_services()
print("Services:")
for service in svcs:
print(service)
for service in svcs:
print(service)
for char in service.characteristics:
print(char)
await client.start_notify(15, notification_handler)
await asyncio.sleep(20.0)
await client.stop_notify(15)
if __name__ == "__main__":
asyncio.run(main())
结果:
bytearray的格式可能不太直观,我们可以对它进行转换:
def notification_handler(sender, data):
"""Simple notification handler which prints the data received."""
print("{0}: {1}".format(sender, data))
print("当前心率:", bytes(data)[1])
结果:
Connected: True
Services:
00001800-0000-1000-8000-00805f9b34fb (Handle: 1): Generic Access Profile
00002a00-0000-1000-8000-00805f9b34fb (Handle: 2): ['read', 'write']
00002a01-0000-1000-8000-00805f9b34fb (Handle: 4): ['read']
00002a04-0000-1000-8000-00805f9b34fb (Handle: 6): ['read']
00002aa6-0000-1000-8000-00805f9b34fb (Handle: 8): ['read']
00001801-0000-1000-8000-00805f9b34fb (Handle: 10): Generic Attribute Profile
00002a05-0000-1000-8000-00805f9b34fb (Handle: 11): ['indicate']
0000180d-0000-1000-8000-00805f9b34fb (Handle: 14): Heart Rate
00002a37-0000-1000-8000-00805f9b34fb (Handle: 15): ['notify']
00002a38-0000-1000-8000-00805f9b34fb (Handle: 18): ['read']
0000180f-0000-1000-8000-00805f9b34fb (Handle: 20): Battery Service
00002a19-0000-1000-8000-00805f9b34fb (Handle: 21): ['read']
0000180a-0000-1000-8000-00805f9b34fb (Handle: 23): Device Information
00002a29-0000-1000-8000-00805f9b34fb (Handle: 24): ['read']
00002a24-0000-1000-8000-00805f9b34fb (Handle: 26): ['read']
00002a25-0000-1000-8000-00805f9b34fb (Handle: 28): ['read']
00002a27-0000-1000-8000-00805f9b34fb (Handle: 30): ['read']
00002a28-0000-1000-8000-00805f9b34fb (Handle: 32): ['read']
6e400001-b5a3-f393-e0a9-77656c6f6f70 (Handle: 34): Unknown
6e400003-b5a3-f393-e0a9-77656c6f6f70 (Handle: 35): ['notify']
6e400002-b5a3-f393-e0a9-77656c6f6f70 (Handle: 38): ['write-without-response', 'write']
6e400001-b5a3-f393-e0a9-77757c7f7f70 (Handle: 40): Unknown
6e400003-b5a3-f393-e0a9-77757c7f7f70 (Handle: 41): ['notify']
6e400002-b5a3-f393-e0a9-77757c7f7f70 (Handle: 44): ['write-without-response', 'write']
6e400001-b5a3-f393-e0a9-e50e24dcca9e (Handle: 46): Nordic UART Service
6e400003-b5a3-f393-e0a9-e50e24dcca9e (Handle: 47): ['notify']
6e400002-b5a3-f393-e0a9-e50e24dcca9e (Handle: 50): ['write-without-response', 'write']
0000fee7-0000-1000-8000-00805f9b34fb (Handle: 52): Tencent Holdings Limited
0000fea1-0000-1000-8000-00805f9b34fb (Handle: 53): ['read', 'notify', 'indicate']
0000fea2-0000-1000-8000-00805f9b34fb (Handle: 56): ['read', 'write', 'indicate']
0000fec9-0000-1000-8000-00805f9b34fb (Handle: 59): ['read']
00001814-0000-1000-8000-00805f9b34fb (Handle: 61): Running Speed and Cadence
00002a53-0000-1000-8000-00805f9b34fb (Handle: 62): ['notify']
00002a54-0000-1000-8000-00805f9b34fb (Handle: 65): ['read']
00001816-0000-1000-8000-00805f9b34fb (Handle: 67): Cycling Speed and Cadence
00002a5b-0000-1000-8000-00805f9b34fb (Handle: 68): ['notify']
00002a5c-0000-1000-8000-00805f9b34fb (Handle: 71): ['read']
00002a5d-0000-1000-8000-00805f9b34fb (Handle: 73): ['read']
00002a55-0000-1000-8000-00805f9b34fb (Handle: 75): ['write', 'indicate']
15: bytearray(b'\x06?')
当前心率: 63
15: bytearray(b'\x06?')
当前心率: 63
15: bytearray(b'\x06?')
当前心率: 63
15: bytearray(b'\x06?')
当前心率: 63
15: bytearray(b'\x06?')
当前心率: 63
15: bytearray(b'\x06?')
当前心率: 63
15: bytearray(b'\x06?')
当前心率: 63
15: bytearray(b'\x06?')
当前心率: 63
15: bytearray(b'\x06?')
当前心率: 63
15: bytearray(b'\x06?')
当前心率: 63
15: bytearray(b'\x06@')
当前心率: 64
15: bytearray(b'\x06@')
当前心率: 64
15: bytearray(b'\x06@')
当前心率: 64
15: bytearray(b'\x06@')
当前心率: 64
15: bytearray(b'\x06@')
当前心率: 64
15: bytearray(b'\x06@')
当前心率: 64
15: bytearray(b'\x06@')
当前心率: 64
15: bytearray(b'\x06?')
当前心率: 63
15: bytearray(b'\x06>')
当前心率: 62
15: bytearray(b'\x06?')
当前心率: 63
要重点注意的是,心率通知传输过来的格式,总共2个字节,心率值在第二个字节!
3. 写在最后
以上的功能在之前的章节都有提到过,现在是将他们结合在一起。