Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't get notifications from two heart rate sensors at the same time #630

Closed
koenvervloesem opened this issue Aug 29, 2021 · 2 comments
Closed

Comments

@koenvervloesem
Copy link
Contributor

koenvervloesem commented Aug 29, 2021

  • bleak version: 0.12.1
  • Python version: 3.8.10
  • Operating System: Ubuntu 20.04.2 LTS
  • BlueZ version (bluetoothctl -v) in case of Linux: 5.53

Description

I'm trying to adapt my heart rate monitor script from #613 (comment) to read the Heart Rate Measurement characteristic from multiple BLE devices at the same time. I looked at the two_devices.py example for the approach with asyncio.gather.

I've tested this with two devices (the PineTime with InfiniTime firmware and a Xanes F15). The script is able to read the heart rate for each of the devices individually. But when I run my adapted script on both devices at the same time, only one of them sends its notification messages.

What I Did

The code:

import asyncio
import functools
import sys

import bleak

DEVICE_NAME_UUID = "0000{0:x}-0000-1000-8000-00805f9b34fb".format(0x2A00)
HEART_RATE_MEASUREMENT_UUID = "0000{0:x}-0000-1000-8000-00805f9b34fb".format(0x2A37)

def heart_rate_changed(device_name: str, handle: int, data: bytearray):
    print(f"{device_name}: {data[1]} bpm")

async def connect(address):
    try:
        async with bleak.BleakClient(address) as client:
            device_name = (await client.read_gatt_char(DEVICE_NAME_UUID)).decode()
            print(f"Connected to {device_name}")
            await client.start_notify(HEART_RATE_MEASUREMENT_UUID, functools.partial(heart_rate_changed, device_name))
            print(f"Start notifications...")
            while True:
                await asyncio.sleep(1)

    except asyncio.exceptions.TimeoutError:
        print(f"Can't connect to device {address}. Does it run a GATT server?")

async def main(addresses):
    await asyncio.gather(*(connect(address) for address in addresses))

if __name__ == "__main__":

    if len(sys.argv) >= 2:
        addresses = sys.argv[1:]
        asyncio.run(main(addresses))
    else:
        print("Please specify at least one BLE MAC address on the command line.")

This uses asyncio.run as suggested in #613 (comment) to disconnect correctly after an unhandled exception. The two_devices.py example uses loop.run_until_complete, but even if I use that approach the script only connects to one device.

Running the script for only the PineTime works:

$ python3 heart-rate.py F3:BE:3E:97:17:A4
Connected to InfiniTime                             
Start notifications...                              
InfiniTime: 53 bpm                                                                                       
InfiniTime: 30 bpm                                  
InfiniTime: 52 bpm                                                                                       
InfiniTime: 45 bpm

Running the script for only the Xanes F15 works:

$ python3 heart-rate.py EB:76:55:B9:56:18
Connected to F15
F15: 70 bpm
Start notifications...
F15: 70 bpm
F15: 70 bpm
F15: 70 bpm

Running the script for both devices only shows the notification messages from one (I made sure to wait long enough):

$ python3 heart-rate.py EB:76:55:B9:56:18 F3:BE:3E:97:17:A4
Connected to InfiniTime
Start notifications...
InfiniTime: 54 bpm
InfiniTime: 45 bpm
InfiniTime: 72 bpm
InfiniTime: 57 bpm

I've tried this a couple of times and I switched the arguments, but it always seems to pick the device listed as the second argument.

@dlech
Copy link
Collaborator

dlech commented Aug 29, 2021

There are a number of duplicate issues about this already. The recommendation is to use BleakScanner to find the devices first, then connect when the scanning is done.

@koenvervloesem
Copy link
Contributor Author

Ok, I solved this as you suggested:

import asyncio
import functools
import sys

from bleak import BleakClient, BleakScanner

DEVICE_NAME_UUID = "00002a00-0000-1000-8000-00805f9b34fb"
HEART_RATE_MEASUREMENT_UUID = "00002a37-0000-1000-8000-00805f9b34fb"

addresses = []
heart_rate_sensors = []


def device_found(device, _):
    if device.address in addresses:
        heart_rate_sensors.append(device)
        print(f"Found device {device.name}")


def heart_rate_changed(
    device_name: str, handle: int, data: bytearray
):
    print(f"{device_name}: {data[1]} bpm")


async def connect(device):
    try:
        async with BleakClient(device) as client:
            device_name = (
                await client.read_gatt_char(DEVICE_NAME_UUID)
            ).decode()
            print(f"Connected to {device_name}")
            await client.start_notify(
                HEART_RATE_MEASUREMENT_UUID,
                functools.partial(heart_rate_changed, device_name),
            )
            print(f"Start notifications for {device_name}...")
            while True:
                await asyncio.sleep(1)

    except asyncio.exceptions.TimeoutError:
        print(
            f"Can't connect to device {device.address}. Does it run a GATT server?"
        )


async def main():
    scanner = BleakScanner()
    scanner.register_detection_callback(device_found)
    await scanner.start()
    await asyncio.sleep(5.0)
    await scanner.stop()
    await asyncio.gather(
        *(connect(device) for device in heart_rate_sensors)
    )


if __name__ == "__main__":

    if len(sys.argv) >= 2:
        addresses = sys.argv[1:]
        asyncio.run(main())
    else:
        print(
            "Please specify at least one Bluetooth address on the command line."
        )

And this works:

$ python3 heart-rate.py EB:76:55:B9:56:18 F3:BE:3E:97:17:A4
Found device F15
Found device InfiniTime
Connected to F15
Start notifications for F15...
Connected to InfiniTime
Start notifications for InfiniTime...
F15: 71 bpm
F15: 71 bpm
InfiniTime: 46 bpm
F15: 71 bpm
F15: 70 bpm
F15: 70 bpm
F15: 70 bpm
F15: 70 bpm
F15: 69 bpm
F15: 69 bpm
F15: 68 bpm
InfiniTime: 49 bpm
F15: 68 bpm
F15: 67 bpm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants