編集履歴一覧に戻る
RYUJIのアイコン画像

RYUJI が 2021年06月15日18時39分46秒 に編集

初版

タイトルの変更

+

【Me守る君】スマートバンドによる自動バイタル計測

タグの変更

+

obnizBoard1Y

+

Bluetooth

+

スマートバンド

+

Nodejs

メイン画像の変更

メイン画像が設定されました

本文の変更

+

# まえがき(が長くてすみません) 先日開催された[「obniz IoTコンテスト 2021」](https://elchika.com/promotion/obniz2021/)に参加させていただき、せっかく無償でobnizをいただけましたので更に何か試してみようと思案しました。 コンテスト参加向けのシステムを作成するにあたり、初めてobnizを触ったので色々わからないことが多かったのですが、逆に面白そうな機能も知ることができました。その中でも特にobnizの特徴として挙げられる機能の一つにBLE通信があると思います。他の機器でこの機能を実装しようとすると何かしら追加機材が必要になりますがobnizは最初からボード内にBLE通信機能が実装されています。 以前、androidスマホアプリなどでBluetooth接続機器の操作を試したことがあったので、これはひょっとしてobnizでも何か役に立ちそうな仕組みに使えないか?と考えました。 かなり前のことですが、近所のドラッグストアのスマホグッズコーナーに何やら怪しい?スマートバンドが売っていてこれが超お買い得価格設定でしたのでGetしてみました。確か、実売価格で¥1,200くらいだったと思います。(ちょっと正確に覚えてないけど、あまりの安さにて慌てて購入した記憶あり) ![近所のドラッグストアでGetしたスマートバンド](https://camo.elchika.com/8b3ff9554e42d29d4874acc9b52e037582674cc2/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65663338303863652d313163612d343231322d623133622d3563336436616637346266322f31323165383233662d646239642d343638322d383166312d303734373033613039303864/) ! ネット通販サイトを見ると、この手の(かつ、この金額の)ものは色々出回ってますね。。。。ちょっと怪しげですが。 実際使ってみると普通に使えましたし、なんといってもSpO2も計れちゃうのがすごい。当然ながら医療機器ではありませんので、数値は絶対値として信頼できるものではありません。が、継続して使えばなんとなくの変化は見えるかも?と思ってしばらく使ってました。 ところがこれは計測データを蓄積しても、これまた怪しい専用アプリでデータを見るしか無いというのが何しろネックです。 ということで、まえがきがやたら長くて大変申し訳無いです。 結論として、このデバイスにobnizから自前でBLE通信して計測データを取得する仕組みを作ってみよう、というのが今回のシステムになります。 ※当然ながら、本システムで見守り及びバイタル計測の責を追うシステムにそのまま使うことは推奨しません。計測データは参考値範囲として利用するか、あくまでシステム試作事例としてご覧になっていただきますようお願いします。 # システム概要 obnizを使ってスマートバンドの自動計測ができれば、例えば遠く離れて暮らす両親のちょっとした見守りシステム、もしくは健康管理システムになります。お年を召した方々に、スマホアプリでスマートバンドの計測データ管理をしてもらうのはハードル高いですでが、このシステムであればこのバンドを付けてもらうだけであとは何もしなくてOK。たまに充電だけはしないといけないので充電のレクチャだけですね。(厳密に言えば両親宅にWifiが必要なのはハードルありか。。。。) どちらにせよ、最初に設定して置いてくればあとは何もしなくても毎日バンドをつけていてもらえばOKです。 クラウド経由で定期的に計測したバイタルデータが見守り側のLINEに通知され、蓄積されたデータをグラフで見ることもできます。 ![遠隔でも繋がる!](https://camo.elchika.com/016c1a4acd38236e423ed08caaef75a2a3054cd3/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65663338303863652d313163612d343231322d623133622d3563336436616637346266322f36303665613631372d643634392d343063332d383962622d346430636463313336623665/) 実際同様な仕組みで、介護施設等に向けたobnizを利用したシステムが商用化されているようですね。 今回のシステムは仕組みは同様ですが「わずか千円ちょっとのスマートバンドでもこれが実現できちゃう」というところがミソでしょうか。 ## LINE通知画面 ![キャプションを入力できます](https://camo.elchika.com/de9021f9d2cea362a55fe034139c533a17c33807/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65663338303863652d313163612d343231322d623133622d3563336436616637346266322f39393236343136312d303038342d343139662d613133632d373034386363626165393037/) ## グラフ表示(Machinist) ![キャプションを入力できます](https://camo.elchika.com/be389864e92fdd9af2c0b8be6cfc468aa8d87c21/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65663338303863652d313163612d343231322d623133622d3563336436616637346266322f38636137363635342d346339622d346532372d613538632d303738323639633066373666/) # 機材説明 |機器名|メーカー|備考| |:---:|:---:|:---:| |obniz Board 1Y|obniz|言わずと知れたobniz| |LM716 (SmartBand)|不明|近所のドラッグストアで購入。メーカ等は不明です。| ※スマートバンドは同じ箱のものを2つ購入し、どちらも外観は一緒なのですが、実際にBluetoothでスキャンすると違う機器名でした。。。機械は一緒でも中身のファームウェアとかはいろいろあるのかも。 # システム構成 今回のシステム構成は単純です。 有線はありません。(電源は取るとして) obnizから直接BLE通信を行ってスマートバンドへのコマンド送信、及び測定データ取得を行います。 ![システム構成](https://camo.elchika.com/a0bef09a92dbc1303052758aa00b54e1febcb64b/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65663338303863652d313163612d343231322d623133622d3563336436616637346266322f61316136373838662d646438612d343662362d383137612d333235623438613161303264/) 前回投稿させていただいた[「Wekiの気持ち」](https://elchika.com/article/7879e42d-8897-467b-8934-58c2e739b834/)では、できるだけobniz-Cloud上で仕組みを作ろうとしたのですが、幾つか問題があってGoogleAppsScript側に一部ロジックを寄せています。(その問題というのが、Authorizationヘッダ付きのHTTPリクエストがいまいちうまくいかないという問題で、ちょっとまだ私の中でも解決していません) 今回はできるだけシステム構成を単純にしたかったので、上記の問題が発生しないnode.js上でのプログラムとしています。 また、これには他にも理由があります。というのは、obniz-Cloudの実行時間制限によるものです。 obniz-Cloudは最大で30秒までの実行時間制限があります。 今回、最初はobniz-Cloud上でのアプリ実行として試作を始めたのですが、スマートバンドへBLE接続しバイタル計測して結果を取得するだけでも30秒以上かかってしまい、クラウドアプリケーションが毎回タイムアウトエラーになってしまいました。(あと10秒くらいあるとギリギリ入りそうなんですが・・・残念) node.jsの場合はとりあえずはPCで動かすので時間制限は関係ないです。また、node.jsを利用できるクラウドサービスではデフォルトでも60秒くらいは実行時間がありますし、大抵の場合延長する事もできますのでこの辺の問題は回避できました。 # 実行動画 @[youtube](https://youtu.be/bHEDU2X7Wq8) # プログラム解説 順番にポイントとなる箇所の説明を記載しておきます。 ## BLE初期化 特殊なことはありません。最初に必要な手続きとして記述。 ```JS:BLE初期化 await obniz.ble.initWait(); obniz.display.clear(); obniz.display.print("BLE initialized\n"); ``` ## デバイススキャン 今回はデバイス名称を指定してスキャンします。 ```JS:デバイススキャン var target = { localName: TARGET_DEVICE_NAME }; var peripheral = await obniz.ble.scan.startOneWait(target); ``` ## デバイスへ接続 スキャンした戻り値(peripheral)を確認して、これを使ってデバイスに接続します。 ```JS:デバイスへ接続 if(peripheral) { obniz.display.print("found band\n"); try { await peripheral.connectWait(); obniz.display.print("connected band\n"); ・ ・ ・ ``` ## 変更通知の受け取り申し込み 機器からの変更通知として、今回はバイタル測定結果の通知が発生した際にNotifyを受け取るための申込みを行います。 この申込に必要なのは、ターゲットとなる「Service」と「Characteristics」です。これもデバイスの仕様書がないので、事前に実行したログ等から読みとって探しておく必要があります。 ```JS:Subscribe await peripheral.getService(TARGET_SERVICE) .getCharacteristic(TARGET_CHARACTERISTIC_SUBSCRIBE) .registerNotifyWait((data) => { ・ ・ ・ ``` ## 計測実行コマンドの書き込み Notifyを受け取る準備ができたら、計測の実行コマンドを送信(書き込み)します。このコマンドも同様に事前に把握必要。 なお、前段のNotify申込時の「Characteristics」とコマンド書き込みの「Characteristics」はそれぞれ別に定義されているはずです。 また、今回利用した機材については仕様の公開情報がありません。このため、下記サンプルコード中のコマンドバイト配列についてはこちらで調査した機器のものを勝手に公開はできませんので、仮にすべて 0xff で表記しています。 ```JS:Write var bytes = new Uint8Array(9); // 以下は実際には調査したコマンドバイト配列を設定しますが、説明用に今回はすべて 0xff で埋めてます bytes[0] = 0xff; bytes[1] = 0xff; bytes[2] = 0xff; bytes[3] = 0xff; bytes[4] = 0xff; bytes[5] = 0xff; bytes[6] = 0xff; bytes[7] = 0xff; bytes[8] = 0xff; obniz.display.print("starting measurement\n"); await peripheral.getService(TARGET_SERVICE).getCharacteristic(TARGET_CHARACTERISTIC_WRITE).writeWait(bytes); ``` ## 通知データについて これについても当然ながら公式のデータシートがあるわけでもなく、ご自身で実際にその機器とやりとりしているBluetooth通信を調べるしかないです。Bluetooth通信のログなどはAndroidスマホ等で出力できますのでこれをみて頑張って解析するしか無いです。 この手段についてはここでは割愛します。 ## LINEとMachinistへのデータ送信 LINEへのメッセージ送信については、[Line DevelopersサイトのMessaging API](https://developers.line.biz/ja/services/messaging-api/) をご参考にREST-API仕様をご確認ください。 Machinistへのデータ送信のREST仕様も、[Machinistサイト](https://machinist.iij.jp/getting-started/) に詳細説明あります。 これらのほかクラウドサービスとobnizの連携については今後スポットをあてて説明するような紹介投稿できればと思っています。 (ボリューム的に厳しいのでこの章は今度) # プログラムソース 今回はobniz-Cloud上ではなく、node.js向けのソースです。 ```JS:for_node.js // this is obniz program on node.js const Obniz = require("obniz"); const request = require("request"); const TARGET_DEVICE_NAME = "LM716"; const TARGET_SERVICE = "ターゲットサービスのID"; const TARGET_CHARACTERISTIC_WRITE = "コマンド書き込みのCharacteristicID"; const TARGET_CHARACTERISTIC_SUBSCRIBE = "Subscribe用のCharacteristicID"; const url_push = "https://api.line.me/v2/bot/message/push"; const channel_id = "LINEのチャネルID"; const access_token = "LINE接続のアクセストークン"; const url_machinist = "https://gw.machinist.iij.jp/endpoint"; const access_token_machinist = "Machinist接続のアクセストークン"; var obniz = new Obniz("ご自身のobniz-ID"); // called on online obniz.onconnect = async function() { // Smartband接続 await obniz.ble.initWait(); obniz.display.clear(); obniz.display.print("BLE initialized\n"); var target = { localName: TARGET_DEVICE_NAME }; var peripheral = await obniz.ble.scan.startOneWait(target); if(peripheral) { obniz.display.print("found band\n"); try { await peripheral.connectWait(); obniz.display.print("connected band\n"); // subscribe await peripheral.getService(TARGET_SERVICE).getCharacteristic(TARGET_CHARACTERISTIC_SUBSCRIBE).registerNotifyWait((data) => { if(data.length >= 19) { var data = { "SpO2": data[16], "bp_low": data[17], "bp_high": data[18], "pulse": data[19] }; var message = []; message.push("SpO2: " + data.SpO2 + " %"); message.push("bp: " + data.bp_high + "/" + data.bp_low + " mmHg"); message.push("pulse: " + data.pulse + " bpm"); // print data obniz.display.clear(); obniz.display.print(message[0]+"\n"); obniz.display.print(message[1]+"\n"); obniz.display.print(message[2]+"\n"); // Push to LINE var postdata = { "data": data, "message": message.join("\n") } postMessage(postdata); } }); // write var bytes = new Uint8Array(9); // 以下は実際には調査したコマンドバイト配列を設定しますが、説明用に今回はすべて 0xff で埋めてます bytes[0] = 0xff; bytes[1] = 0xff; bytes[2] = 0xff; bytes[3] = 0xff; bytes[4] = 0xff; bytes[5] = 0xff; bytes[6] = 0xff; bytes[7] = 0xff; bytes[8] = 0xff; obniz.display.print("starting measurement\n"); await peripheral.getService(TARGET_SERVICE).getCharacteristic(TARGET_CHARACTERISTIC_WRITE).writeWait(bytes); } catch(e) { console.error(e); } } }; // LINEにメッセージ and Machinistにデータを送る async function postMessage(postdata) { // LINEに送信 var line_options = { url: url_push, method: "post", headers: { "Authorization": "Bearer " + access_token }, json: { "to": channel_id, "messages": [{ "type": "text", "text": postdata.message }] } } await request(line_options, async(error, response, body) => { console.log(body); if(error) { console.log(error); } }); // Machinistに登録 var mdata = { "agent": "Smart-band Agent", "metrics": [ { "name": "SpO2", "namespace": "Smart-band Sensor", "data_point": { "value": parseInt(postdata.data.SpO2) } }, { "name": "blood pressure high", "namespace": "Smart-band Sensor", "data_point": { "value": parseInt(postdata.data.bp_high) } }, { "name": "blood pressure low", "namespace": "Smart-band Sensor", "data_point": { "value": parseInt(postdata.data.bp_low) } }, { "name": "pulse", "namespace": "Smart-band Sensor", "data_point": { "value": parseInt(postdata.data.pulse) } } ] } var machinist_options = { url: url_machinist, method: "post", headers: { "Authorization": "Bearer " + access_token_machinist }, json: mdata } await request(machinist_options, async(error, response, body) => { console.log(body); if(error) { console.log(error); } }); } // called on offline obniz.onclose = async function () {} ``` # 今後の課題 今回は、なんちゃってスマートバンドを用いた計測でしたので計測した測定値そのものの信頼性についてはコメントしませんが、BLE経由で計測機器に自動計測実行を行い、計測データを収集してLINE通知、クラウドサービス上にデータ保存してグラフ表示、ということまでシステムを組む、という例を作成できました。 当然ながら、これ自身で見守りシステムとしてそのまま使えるものではありませんが、信頼性のある機材が揃えば実際の運用にも繋げられる可能性を見いだせたかと思います。 ※当然ながら、本システムで見守り及びバイタル計測の責を追うシステムにそのまま使うことは推奨しません。計測データは参考値範囲として利用するか、あくまでシステム試作事例としてご覧になっていただきますようお願いします。