fsmeraldi / bleakheart

An asynchronous BLE Heart Monitor library with support for additional data from Polar monitors (ECG, accelerometers, etc)
Mozilla Public License 2.0
18 stars 11 forks source link

ECG Recorder Immediately Disconnects #5

Closed WillPowellUk closed 1 month ago

WillPowellUk commented 4 months ago

Thank you very much for this repo it is very useful @fsmeraldi !

I have got HR rate to record on my Polar H10 reliably (I am on Windows so had to modify this a little - see https://github.com/WillPowellUk/bleakheart/blob/main/examples/heartrate_queue.py and https://github.com/WillPowellUk/bleakheart/blob/main/examples/ecg_queue_windows.py) however I cannot get ECG to work reliably - it sometimes records data as expected but often it records and immediately disconnects:

Scanning for BLE devices
Connecting to E2:F5:07:72:6D:27: Polar H10 AE60D42F...
After connecting, will print ECG data in the form
('ECG', tstamp, [s1,S2,...,sn])
where samples s1,...sn are in microVolt, tstamp is in ns
and it refers to the last sample sn.
Connected: True
>>> Hit Enter to exit <<<
Request for available ECG settings returned the following:
error_code:     0
error_msg:      SUCCESS
SAMPLE_RATE:    [130]
RESOLUTION:     [14]
Sensor disconnected
Bye.

Do you have any ideas? I have tried taking out polar H10 battery for a full device restart so i believe the problem is on the BLE side. Unsure how to debug something temperamental like this.

Thanks, Will

WillPowellUk commented 4 months ago

I just tried on Ubuntu and it has a similar issue - it works immediately the first time but then takes around 20 seconds to start ECG again. Unsure why this is but I think it is related. Any ideas?

fsmeraldi commented 4 months ago

Hello @WillPowellUk ,

glad you find it useful, and thanks for the Windows suggestions! Unfortunately I have no idea, I use mint myself (which is Ubuntu-based) but have not had that issue. If you read previous posts, there was an issue with late start of ECG notifications that was solved (in my case) by a Bleak update, but I had not heard of this yet. The only thing I can think of is this: from the PMD documentation, section Prerequisites, https://github.com/polarofficial/polar-ble-sdk/blob/52ef4c4b77e5f83b0839f0f4f72623a9b9d79372/technical_documentation/Polar_Measurement_Data_Specification.pdf I understand that the sensor tries to switch the connection over to the two megabit mode and a certain MTU size. It's a wild guess, but maybe that's where the disconnection happens? I wonder if specifying for instance the MTU when connecting with Bleak would help. Do let me know if you find a solution!

Best wishes Fabrizio

WillPowellUk commented 4 months ago

Thank you for your response @fsmeraldi . Sadly Bleak is already up to date but I will try to investigate the MTU idea. Can I ask - do you know if it is possible to stream both the HR and ECG at the same time as the RR intervals are sampled at a higher rate than the 130Hz ECG?

fsmeraldi commented 4 months ago

Hi @WillPowellUk , yes, it is perfectly possible to stream both at the same time. In fact, with the driver you have the choice to use callbacks (same or different), push them both to the same async queue, or push each of them to a different queue- you can then await each queue in a different coroutine, which personally I find convenient (though of course it will depend on your application). Ballpark, at rest you will get two ECG packets for each HR packet.

fsmeraldi commented 4 months ago

Hello @WillPowellUk, in the end, did you manage to sort the issue with ECG measurements? I incorporated your idea of using threads in the new release, thanks. Just a heads up that asyncio events are not thread safe, you need to wrap the call to set in call_soon_threadsafe - that is unlikely to cause deadlocks in the example, but it may be an issue in other code. See here and here

zHElEARN commented 2 months ago

I have the same issue on Windows. When running ecg_queue.py, the heart rate monitor immediately disconnects.

(base) PS D:\Repos\bleakheart\examples> python .\ecg_queue.py
Scanning for BLE devices
Connecting to A0:9E:1A:BA:2B:0B: Polar H10 BA2B0B2D...
After connecting, will print ECG data in the form
('ECG', tstamp, [s1,S2,...,sn])
where samples s1,...sn are in microVolt, tstamp is in ns
and it refers to the last sample sn.
Connected: True
Request for available ECG settings returned the following:
error_code:     0
error_msg:      SUCCESS
SAMPLE_RATE:    [130]
RESOLUTION:     [14]
>>> Hit Enter to exit <<<
Sensor disconnected
Bye.

When running heartrate_queue.py, it successfully reads the heart rate data.

(base) PS D:\Repos\bleakheart\examples> python .\heartrate_queue.py
Scanning for BLE devices
Connecting to A0:9E:1A:BA:2B:0B: Polar H10 BA2B0B2D...
After connecting, will print heart rate data in the form
   ('HR', tstamp, (bpm, rr_interval), energy)
where tstamp is in ns, rr intervals are in ms, and
energy expenditure (if present) is in kJoule.
Connected: True
>>> Hit Enter to exit <<<
('HR', 1720893228620014000, (75, 801), None)
('HR', 1720893229625157700, (74, 815), None)
('HR', 1720893229781382100, (71, 840), None)
('HR', 1720893230615382100, (72, 834), None)
('HR', 1720893231649344200, (72, 832), None)
('HR', 1720893232640073200, (71, 848), None)
('HR', 1720893233629887100, (68, 883), None)
('HR', 1720893234665009900, (67, 897), None)
('HR', 1720893235654421200, (66, 910), None)
('HR', 1720893236689847900, (65, 929), None)
('HR', 1720893237635267100, (65, 925), None)

Quitting on user command
Sensor disconnected
Bye.

I wrote a short program myself and found that when calling the write_gatt_char() function, the Bluetooth connection disconnects. I think it might be that bleak is unable to send data to the PMD.

import asyncio
from bleak import BleakClient, BleakScanner

import profiles

async def handler_hr(sender, data):
    print(list(data))

async def handler_ecg(sender, data):
    print(list(data))

async def main():
    devices = await BleakScanner.discover()

    polar_device = None
    for device in devices:
        if "Polar H10" in device.name:
            polar_device = device
            break

    if polar_device is None:
        print("Polar H10 not found")
        return 1

    print(polar_device)

    def disconnected_callback(client):
        print("Disconnected")

    async with BleakClient(polar_device, disconnected_callback=disconnected_callback) as client:
        print(await client.read_gatt_char(profiles.MANUFACTURER_NAME_UUID))

        await client.start_notify(profiles.HEARTRATE_MEASUREMENT_UUID, handler_hr)

        # Bluetooth connection disconnects after executing the following line
        #await client.write_gatt_char(profiles.PMD_CONTROL_UUID, profiles.START_ECG_STREAM_BYTES)
        await client.start_notify(profiles.PMD_DATA_UUID, handler_ecg)

        await asyncio.sleep(10)

        print(client.is_connected)

        await asyncio.sleep(20)

        await client.stop_notify(profiles.HEARTRATE_MEASUREMENT_UUID)
        await client.stop_notify(profiles.PMD_DATA_UUID)

asyncio.run(main())
zHElEARN commented 2 months ago

I found an application called Bluetooth LE Explorer in the Microsoft Store on Windows 11: https://www.microsoft.com/store/productId/9N0ZTKF1QD98?ocid=pdpshare. With this application, I was able to successfully write GATT and read notifications. Therefore, I suspect that the issue lies with the write_gatt_char function in the bleak library. 1 2

zHElEARN commented 2 months ago

I have identified a characteristic of the immediate Bluetooth disconnection. When Windows is not paired with the Polar H10 heart rate monitor, the connection will be immediately disconnected after executing write_gatt_char(). After pairing the Polar H10 heart rate monitor with Windows, the ECG signal can be received normally.

However, it is not possible to pair the Polar H10 heart rate monitor directly through Windows Bluetooth settings, but you can use the Bluetooth LE Explorer application to pair the heart rate monitor.

It's quite strange because, theoretically, BLE devices should not require pairing.

@WillPowellUk , you can try pairing the heart rate monitor and see if you can then read the ECG signal.

not work work
fsmeraldi commented 2 months ago

Hello @zHElEARN, glad to hear that you got it to work in the end, and thanks for sharing this workaround Fabrizio

WillPowellUk commented 1 month ago

I am so sorry for the slow response - I did not get notified by these new comments. Thank you for your suggestion to use Bluetooth LE explorer @zHElEARN . Unfortunately, in my case this did not work - however I have found that not wearing the device for a minute and then putting it back on acts as a reset and the device can connect through the script as before. But you are right in the fact that it is a connection issue - if the script stops without disconnecting then next time it is ran this happens:

DEBUG:bleak.backends.winrt.scanner:45 devices found. Watcher status: <BluetoothLEAdvertisementWatcherStatus.STOPPED: 3>.
INFO:bleakheart.examples.ecg_rr_recorder:Connecting to E2:F5:07:72:6D:27: Polar H10 AE60D42F...
>>> Hit Enter to exit <<<DEBUG:bleak.backends.winrt.client:Connecting to BLE device @ E2:F5:07:72:6D:27
DEBUG:bleak.backends.winrt.client:getting services (service_cache_mode=None, cache_mode=None)...

It is stuck on getting the client services.

If I do disconnect is properly i.e. by hitting enter and waiting for disconnection, it connects reliably the next time I use it:

INFO:bleakheart.examples.ecg_rr_recorder:Quitting on user command INFO:bleakheart.examples.ecg_rr_recorder:('ECG', 1723022133143026014, [-47, -16, -68, -96, -84, -82, -47, 7, -2, -56, -108, -127, -77, -28, -32, -35, -30, -42, -40, 37, 273, 579, 735, 501, -207, -805, -702, -278, -56, 21, 82, 122, 136, 103, 87, 141, 162, 113, 113, 164, 188, 174, 193, 226, 212, 209, 261, 278, 261, 332, 405, 379, 372, 414, 426, 412, 409, 381, 310, 270, 237, 139, 32, -18, -49, -89, -103, -94, -94, -96, -89, -91, -98]) DEBUG:bleak.backends.winrt.client:Disconnecting from BLE device... DEBUG:bleak.backends.winrt.client:max_pdu_size_changed_handler: 23 DEBUG:bleak.backends.winrt.client:session_status_changed_event_handler: id: BluetoothLE#BluetoothLE84:c5:a6:14:48:a2-e2:f5:07:72:6d:27, error: <BluetoothError.SUCCESS: 0>, status: <GattSessionStatus.CLOSED: 0> INFO:bleakheart.examples.ecg_rr_recorder:Sensor disconnected DEBUG:bleak.backends.winrt.client:closing requester DEBUG:bleak.backends.winrt.client:closing session INFO:bleakheart.examples.ecg_rr_recorder:Bye.

WillPowellUk commented 1 month ago

As others, I was not so familiar with asynchronous programming - but I have now created a working example to stream ecg, heart rate and acc at once that works well on Windows.

Thank you for your library @fsmeraldi !

zHElEARN commented 1 month ago

I am so sorry for the slow response - I did not get notified by these new comments. Thank you for your suggestion to use Bluetooth LE explorer @zHElEARN . Unfortunately, in my case this did not work - however I have found that not wearing the device for a minute and then putting it back on acts as a reset and the device can connect through the script as before. But you are right in the fact that it is a connection issue - if the script stops without disconnecting then next time it is ran this happens:

DEBUG:bleak.backends.winrt.scanner:45 devices found. Watcher status: <BluetoothLEAdvertisementWatcherStatus.STOPPED: 3>.
INFO:bleakheart.examples.ecg_rr_recorder:Connecting to E2:F5:07:72:6D:27: Polar H10 AE60D42F...
>>> Hit Enter to exit <<<DEBUG:bleak.backends.winrt.client:Connecting to BLE device @ E2:F5:07:72:6D:27
DEBUG:bleak.backends.winrt.client:getting services (service_cache_mode=None, cache_mode=None)...

It is stuck on getting the client services.

If I do disconnect is properly i.e. by hitting enter and waiting for disconnection, it connects reliably the next time I use it:

INFO:bleakheart.examples.ecg_rr_recorder:Quitting on user command INFO:bleakheart.examples.ecg_rr_recorder:('ECG', 1723022133143026014, [-47, -16, -68, -96, -84, -82, -47, 7, -2, -56, -108, -127, -77, -28, -32, -35, -30, -42, -40, 37, 273, 579, 735, 501, -207, -805, -702, -278, -56, 21, 82, 122, 136, 103, 87, 141, 162, 113, 113, 164, 188, 174, 193, 226, 212, 209, 261, 278, 261, 332, 405, 379, 372, 414, 426, 412, 409, 381, 310, 270, 237, 139, 32, -18, -49, -89, -103, -94, -94, -96, -89, -91, -98]) DEBUG:bleak.backends.winrt.client:Disconnecting from BLE device... DEBUG:bleak.backends.winrt.client:max_pdu_size_changed_handler: 23 DEBUG:bleak.backends.winrt.client:session_status_changed_event_handler: id: BluetoothLE#BluetoothLE84:c5:a6:14:48:a2-e2:f5:07:72:6d:27, error: <BluetoothError.SUCCESS: 0>, status: <GattSessionStatus.CLOSED: 0> INFO:bleakheart.examples.ecg_rr_recorder:Sensor disconnected DEBUG:bleak.backends.winrt.client:closing requester DEBUG:bleak.backends.winrt.client:closing session INFO:bleakheart.examples.ecg_rr_recorder:Bye.

@WillPowellUk The general conclusion is: The H10 heart rate sensor cannot be connected to two devices simultaneously.

For example, if the H10 heart rate sensor is connected to a mobile phone, then it cannot be connected to a computer, and you will encounter the issue where it gets stuck on "getting the client services."

If the script is stopped without disconnecting properly, it’s possible that the computer remains connected to the heart rate sensor via Bluetooth. When you rerun the script, it might get stuck on acquiring client services because the previous connection handle was not closed properly.

If you do not wear the device for one minute, the H10 heart rate sensor will turn off its Bluetooth connection. Then, when you run the script again, you should be able to connect and retrieve data normally.

WillPowellUk commented 1 month ago

@zHElEARN Yes this makes a lot of sense! I think we can conclude that. Thanks for your efforts