1. 前言

在BLE开发中,原厂给的DEMO中肯定少不了对心率带的适配,相当于我们学习各种编程语言的“Hello World”。

现在我们将通过 Bleak 来获取心率带的数据。

如果阅读本文存在障碍的话,可以进入作者主页查看更多相关内容

2. 步骤

  1. 扫描周边设备
  2. 判断周边是否存在心率带
  3. 对心率带发起连接
  4. 发现服务
  5. 获取心率数据推送的特征句柄
  6. 使能心率推送的通知功能

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,为了方便,我们待会使能通知时直接使用。

android 获取某个蓝牙设备的连接状态 如何获取蓝牙设备数据_推送

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())

结果:

android 获取某个蓝牙设备的连接状态 如何获取蓝牙设备数据_Access_02


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. 写在最后

以上的功能在之前的章节都有提到过,现在是将他们结合在一起。