【血圧予測#3】Polar OH1の心拍データをリアルタイムで取得【MacOS】

Python

心拍は血流速度です。従って、その時間変化は本質的に血圧の情報を含んでいます。そこで、2点間の心拍から血圧を予測することに挑戦してみます。

前回までには、心拍計の選定と心拍数を取得するために必要なラズベリーパイの準備の手順を紹介しました。

  1. 心拍計の選定の記事はこちら
  2. ラズベリーパイの準びはこちら

本記事では、ラズベリーパイからPolar OH1の心拍数を取得するプログラムを作成する前段階として、MacOSからPolar OH1の心拍数を取得するプログラムを作成してみます。

1. Python用のBLEライブラリの選定

こちらで紹介されているように、PythonからBluetoothのシグナルを取り扱うためのライブラリにはpybleuzやpyGATTなど複数あります。

2025/01/25段階ではMacOSから扱う場合、bleakというライブラリが良さそうです。

2. Polar OH1の検出

近くにあるBluetoothのデバイスからシグナルを受信し、デバイスの情報を以下のようにして表示させることができます。

import asyncio
from bleak import BleakScanner

async def run():
    devices = await BleakScanner.discover(return_adv=True)
    for device, adv_data in devices.values():
        print(f"address: {device.address}, name: {device.name}, uuid: {adv_data.service_uuids}")

loop = asyncio.get_event_loop()
loop.run_until_complete(run())

上記の出力結果は、例えば以下のようになります。

address: 23A0251C-8590-B388-0D30-96CA94743EBE, name: None, uuid: []
address: 1C4BD0DD-96AC-9125-A347-EC6A5278F0E8, name: None, uuid: []
address: A1EC6040-E143-9DC2-0F69-61BA09B78380, name: LE-Bose QuietComfort 35 Serie, uuid: ['0000febe-0000-1000-8000-00805f9b34fb', '0000fe26-0000-1000-8000-00805f9b34fb', '0000fe03-0000-1000-8000-00805f9b34fb']
address: 80A32DBE-B8F5-34E5-B941-B56281A68524, name: Polar OH1 XXXXXXXX, uuid: ['xxxxxxxx-0000-1000-8000-00805f9b34fb', 'xxxxxxxx-0000-1000-8000-00805f9b34fb']
address: B4BF7906-1270-323E-0CC0-9D96C5DE9938, name: None, uuid: ['0000fe9f-0000-1000-8000-00805f9b34fb']
address: 849BE66D-7DDD-CCBB-4BEE-D5205E463919, name: None, uuid: []
address: C7317B31-46B6-0C97-3764-48C2CA2347FA, name: None, uuid: []
address: E4702025-2C65-4317-8F67-9BB31171E59C, name: None, uuid: []
address: E125DF3B-223D-DEE3-17E5-D5FB32CC3605, name: XXXXさんのApple Watch, uuid: []
address: BE041992-7286-25C4-4ECE-F274983ABD10, name: None, uuid: []

XXXXXXXXやXXXXの部分は個人情報なので伏せてあります。

こちらを参考にしました。

3. デバイスに接続する際のアドレスを調べる

前節のaddressで接続できる場合もあります。その場合は、前節のaddressをメモしてこの節は飛ばして次節に進んでください。

本節では上記のaddressで次節に従っても接続できなかった場合のaddressの取得方法について説明します。

async def run():
    devices = await BleakScanner.discover(return_adv=True)
    for device, adv_data in devices.values():
      if device.name is not None:
         if 'Polar OH1' in device.name:
            print(f'cnt:{adv_data.count}, idx:{adv_data.index}, local_name:{adv_data.local_name}, mdata:{adv_data.manufacturer_data}, pdata:{adv_data.platform_data}, rssi:{adv_data.rssi}, service_data:{adv_data.service_data}, service_uuids:{adv_data.service_uuids}, tx_power:{adv_data.tx_power}')

loop = asyncio.get_event_loop()
loop.run_until_complete(run())

上記を実施した結果が以下となります。

cnt:<built-in method count of AdvertisementData object at 0x10cdcd270>, idx:<built-in method index of AdvertisementData object at 0x10cdcd270>, local_name:Polar OH1 XXXXXXXX, mdata:{107: b"r\x08\xfdV\x14\x03\x00\x00\x00\x00z\x01\x01'\x00Q"}, pdata:(<CBPeripheral: 0x600000ca05b0, identifier = XXXXXXXX-B8F5-34E5-B941-B56281A68524, name = Polar OH1 5F9C7424, mtu = 0, state = disconnected>, {
    kCBAdvDataIsConnectable = 1;
    kCBAdvDataLocalName = "Polar OH1 XXXXXXXX";
    kCBAdvDataManufacturerData = {length = 18, bytes = 0x6b007208fd561403000000007a0101270051};
    kCBAdvDataRxPrimaryPHY = 1;
    kCBAdvDataRxSecondaryPHY = 0;
    kCBAdvDataServiceUUIDs =     (
        "Heart Rate",
        FEEE
    );
    kCBAdvDataTimestamp = "759475474.275034";
}, -54), rssi:-54, service_data:{}, service_uuids:['xxxxxxxx-0000-1000-8000-00805f9b34fb', 'xxxxxxxx-0000-1000-8000-00805f9b34fb'], tx_power:None

上記の identifier である’XXXXXXXX-B8F5-34E5-B941-B56281A68524′ をaddressとして次節に進んでください。

4. 心拍数をリアルタイムに取得して表示

以下で心拍数をリアルタイムでMacOSのPythonのコンソールに表示できます。

model_uidbattery_uidheartbeat_uidこちらで定義されている値であり、Polar OH1の各商品ごとに変わるものではないので、そのまま以下のコードの値を使用して下さい。

なお、こちらのbleakライブラリのページを探しても心拍数受信用のUUIDは見つかりませんので注意して下さい。

import asyncio
from bleak import BleakClient
 
model_uid     = "00002a24-0000-1000-8000-00805f9b34fb"
battery_uid   = "00002a19-0000-1000-8000-00805f9b34fb"
heartbeat_uid = "00002a37-0000-1000-8000-00805f9b34fb"
 
running = True
async def run(address):
    async with BleakClient(address) as client:
        model = await client.read_gatt_char(model_uid)
        model = model.decode("utf-8")
        battery = await client.read_gatt_char(battery_uid)
        battery = battery[0]
        print("device: {:s}, battery: {:d}%, press enter to stop".format(model, battery))
        await client.start_notify(heartbeat_uid, lambda sender, data: print("\r{:d} bpm ".format(data[1]), end=""))
        while await client.is_connected() and running:
            await asyncio.sleep(1)
 
threading.Thread(target=asyncio.run, args=(run(address),)).start() # mix of paradigms: event loop in background
input()

上記を実施すると以下のようになります。

device: OH1, battery: 80%, press enter to stop
81 bpm

2行目の bpm の数値の部分が毎秒変化して表示されます。

こちらを参考にしました。

5. まとめ

本記事ではMacOSからPolar OH1の心拍数を取得する手順について説明しました。

次回ではラズベリーパイからの取得や、機械学習や深層学習を用いた血圧予測について記します。

以上になります。

コメント