VB .NETでPULSENSE(PS-100)から心拍数データを取得する

月曜日 , 10, 10月 2016 Leave a comment

自分の死活確認を外部から行えるようにしたいと思い、ウェアラブルデバイスをいじり始めました。
いじりはじめたデバイスのひとつが、エプソンの『PULSENSE』。
ただ、PULSENSEのwebサイトの開発者向け情報を見ると「商品化を前提とした法人格を有する組織が守秘義務契約を結んだら情報提供をする」的なことが書いてあり、購買欲は一気にしぼんでしまいました。
が、「Heart Rate Profileに準拠」とも書いてあり、デバイス自体は制御できそうです。
インターネットで検索をしても「つながった」という情報があまり見つからない(Heart Rate Profile準拠のアプリでの接続事例が大半)のですが、イトーヨーカドーのネットショップで普及モデルのPS-100が非常に安く売っていたので、ダメもとで購入してみました。

結論としては、PULSENSEから心拍数データが取得できました。

無題

今回は、Windows上でペアリングを行った状態で、データの取得のみを自前のプログラムで行っています。
プログラムから自動的にペアリングが可能かどうかは今後の検証していく予定です。

まず、必要なものから。

  • Windows 8.1以降のWindows
  • Visual Studio

Visual Studioを起動し、適当なプロジェクト(Windowsフォームアプリケーションかコンソールアプリケーションあたりが良いと思います)を作成します。

作成したプロジェクトを閉じて、プロジェクトが格納されている場所にある、拡張子が.vbprojのファイルをテキストエディタで開きます。

開いたらTargetFrameworkVersionタグを検索して、その上あたりに以下の一行を追加します。

<TargetPlatformVersion>8.1</TargetPlatformVersion>

無題2

追加したら、Visual Studioでプロジェクトを開きます。

プロジェクトが開いたら、ソリューションエクスプローラで「参照」を右クリックし、メニューの「参照の追加」を選びます。

参照マネージャが開いたら、左側のツリーリストから「Windows 8.1」-「コア」を選択します。
一覧表示された「Windows」をチェックします。

無題3

続けて、参照ボタンを押し、アセンブリの参照を追加します。追加するのは以下の2つです。

  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\Facades\System.Runtime.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5.1\System.Runtime.WindowsRuntime.dll

無題4

(アセンブリの場所は環境に合わせて適宜調整してください。厳密には検証していませんが、バージョン番号は4.5.1以降ならば大丈夫そうな感じがします。)
アセンブリの参照を追加をしたら、それぞれチェックを行い、OKボタンを押します。

この時、「”Windows”への参照を追加できませんでした。コンポーネント’Windows’への参照は、プロジェクトに既に存在します。」といったエラーメッセージが表示されることがありますが、無視してしまっても大丈夫なようです。

image

また、アセンブリ参照が正しく追加されたはずのなのにソリューションエクスプローラを見るとSystem.Runtimeに警告マークが表示されていることがあります。
その時は、再度アセンブリ参照の追加を行うと消えるようです。

ここまででWinRT(Windows Runtime)のAPIを使用できる状態になりました。
あとは下記のソースをモジュールとして追加するなどすれば心拍数データが取得できるようになります。

※注意
PULSENSEからデータ取得ができるか否かを探るのが目的なため、
heart rate measurementで定められたデータの解釈を端折っています。この点、ご注意ください。

' This software is distributed under the license of NYSL.
' ( http://www.kmonos.net/nysl/ )
Imports System
Imports System.Threading.Tasks
Imports Windows.Foundation
Imports Windows.Devices.Enumeration
Imports Windows.Devices.Bluetooth

''' 
''' PULSENSE(PS-100)から心拍データを取得する
''' 
Module Module1

    Sub Main()
        HeartRateMonitor()
    End Sub

    ''' 
    ''' PULSENSE(PS-100)の検索から心拍データを取得する準備まで
    ''' 
    Private Sub HeartRateMonitor()

        ' Heart Rateのデバイスを検索
        Dim devices As DeviceInformationCollection = System.WindowsRuntimeSystemExtensions.AsTask(
            DeviceInformation.FindAllAsync(
                GenericAttributeProfile.GattDeviceService.GetDeviceSelectorFromUuid(
                    GenericAttributeProfile.GattServiceUuids.HeartRate))).GetAwaiter().GetResult()

        ' デバイスが見つからない場合は終了
        If devices.Count = 0 Then
            Console.WriteLine("デバイスなし")
            Exit Sub
        End If

        ' 見つかった最初のデバイスを使用
        Dim device = devices(0)

        ' サービスのインスタンス取得
        Dim service As GenericAttributeProfile.GattDeviceService = System.WindowsRuntimeSystemExtensions.AsTask(
            GenericAttributeProfile.GattDeviceService.FromIdAsync(device.Id)).GetAwaiter().GetResult()

        ' キャラクタリスティックでHeart Rate Measurementのインスタンスを取得
        Dim characteristic As GenericAttributeProfile.GattCharacteristic =
            service.GetCharacteristics(GenericAttributeProfile.GattCharacteristicUuids.HeartRateMeasurement)(0)
        ' キャラクタリスティックに対しイベントハンドラ設定
        AddHandler characteristic.ValueChanged, AddressOf GattValueChanged

        ' デバイスに対して通知設定を書き込む
        System.WindowsRuntimeSystemExtensions.AsTask(
            characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GenericAttributeProfile.GattClientCharacteristicConfigurationDescriptorValue.Notify))

        ' 終了するまでループ
        While True
            System.Threading.Thread.Sleep(500)
        End While

    End Sub

    ''' 
    ''' PULSENSE(PS-100)から取得した心拍データ表示
    ''' 
    ''' 
    ''' 
    Private Sub GattValueChanged(sender As GenericAttributeProfile.GattCharacteristic, e As GenericAttributeProfile.GattValueChangedEventArgs)

        ' デバイスから取得したデータをバイトの配列に展開
        Dim bytes() As Byte
        bytes = New Byte(e.CharacteristicValue.Length - 1) {}
        Windows.Storage.Streams.DataReader.FromBuffer(e.CharacteristicValue).ReadBytes(bytes)

        ' 2番目の要素を表示
        ' (PS-100では2番目の要素で値が取得できてしまうので正規の処理を端折る)
        Console.WriteLine(e.Timestamp.ToString & vbTab & bytes(1))
    End Sub

End Module


 

C#を使った場合、async/awaitまわりがスマートに記述できると思います。
MSDNなどで用いられているサンプルコードもほとんどC#での記述だったので、今回はVB .NETで書いてみました(^-^;

当初はJavaで実装できないか調査を行ってみたのですが、JavaでBluetoothを扱うための仕様(JSR-82)はBLEに対応していませんでした。
Intelが公開しているTinyBをJNI経由でアクセスできるようラップしたライブラリを使えば、JavaからBLEを扱うこともできそうなのですが、下位層で使用しているBlueZがLinux(とAndroid)にしか対応していないため、Javaでの実装を諦めました。