第3章 WEBブラウザアプリ作成 - masato-ka/bel-key-security-app GitHub Wiki

 3章では2章で作成したBLEデバイスを制御するWEBブラウザアプリケーションを作成します。WEBブラウザアプリケーションはWEB Bluetooth APIを利用して作成します。作成にあたっては以下のリポジトリからサンプルアプリケーションをダウンロードしてください。 Gitを利用している場合はcloneをお勧めします。

3.1 サンプルアプリケーションについて

今回のハンズオンでは以下のサンプルアプリケーションを利用します。

https://github.com/masato-ka/bel-key-security-app.git

このサンプルアプリケーションのディレクトリ構成は以下のようになっています。

+css
 -ui-set.css
+img
 -lock.png
 -unlock.png
+js
 -app.js
+script
 -
+README.md
+index.html

jsフォルダ内のapp.jsを編集することでアプリケーションの実装を行って行きます。app.jsはメソッド単位で穴埋め式になっていますので、本章の各セクションに従って、各メソッドの処理を埋めていってください。  サンプルアプリケーションは以下のようなUI担っています。基本的には各ボタンを押下した際のイベント処理を実装することになります。

  • 図3.1 サンプルアプリケーションUI

サンプルアプリケーションUI

3.2 BLEデバイスとの接続

3.2.1 接続処理の記載

まずはWEB Bluetooth APIを使い、デバイスとの接続処理を記載します。デバイスとの接続処理はindex.htmlで定義されているconnectボタンを押下した場合に発生します。connectボタンを押下するとapp.jsのconnectファンクションが呼び出されます。その中に記載されているrequestDeviceファンクションを実装して行きます。ここではBLEデバイスのスキャンと接続、サービスとキャラクタリスティックの探索まで行っています。

function requestDevice(){
        navigator.bluetooth.requestDevice( //(1)
            { acceptAllDevices:true,optionalServices:[LockServiceUUID] } //(2)
         ) 
        .then(device => {
            lockDevice = device; 
            lockDevice.addEventListener('gattserverdisconnected', onDisconnected); //(3)
            return device.gatt.connect(); //(4)
        })                     
        .then(server => server.getPrimaryService(LockServiceUUID)) //(5)
        .then(service => { 
            return Promise.all([ //(6)
                service.getCharacteristic(LockCharacteristicUUID) //(7)
                  .then(characteristic => lockStatusCharacteristic = characteristic),
                service.getCharacteristic(BeepCharacteristicUUID) 
                  .then( characteristic => beepCharacteristic = characteristic),
              ]);
        })
    }

3.2.2 接続処理の説明

(1) WEB Bluetooth APIの接続処理の開始ファンクションです。 この行が呼ばれるとブラウザ上でWEB Bluetooth APIのダイアログが表示されデバイスのスキャンが開始されます。

(2) requestDeviceの引数 引数にはスキャン時のフィルタや各種設定を指定することができます。また、セキュリティのため、デフォルトで任意のサービスに接続できないようになっています。 そこで、今回はすべてのデバイスを検索対象とし、作成したデバイスのサービスUUIDを指定します。

(3) デバイスのへのイベントリスな設定 ユーザが接続するデバイスを指定するとthen(device=>が実行されます。このブロックの中では最初にデバイスに対するイベントリスナを登録します。ここではデバイスが切断された場合に実行されるイベントリスナを登録します。

(4) '''device.gatt.commect()''' を実行してデバイスとの接続を実行します。

(5) サービスのUUIDを指定して、ザービスのオブジェクトを取得します。

(6) (5)で検索されたさーびすからキャラクタリスティックを取得します。今回はセンサの情報を取得するキャラクタリスティックとブザーを鳴らすためのキャラクタリスティックの2つを取得します。通常1つのキャラクタリスティックのみを取得するには以下のように記載します。

service.getCharacteristic(LockCharacteristicUUID)
    .then(characteristic => lockStatusCharacteristic = characteristic),

取得したキャラクタリスティックでは変数に格納します。以降の処理では格納したキャラクタリスティックに対して操作を行なって行きます。

3.3 BLEデバイスとの切断

3.3.1 切断処理の記述

続いてBLEデバイスを切断するコードを書いて行きましょう。画面上の「Disconnect」を押下した場合に切断を行うようにします。app.js上では $scope.disconnectに以下のコードを記載します。

$scope.disconnect = function(){
    if(lockDevice.gatt.connected === true){//(1)
        lockDevice.gatt.disconnect();//(2)
        lockDevice=null;
    }
}

3.3.2 切断処理の解説

(1) デバイスへの接続状態を確認します。trueの場合は接続中です。

(2) デバイスの切断処理を実行します。

3.4 値を読み取る(Readの実装)

3.4.1 Read処理の実装

センサの値の読み取り処理を実行します。UI上から「Read」ボタンを押下した場合に$scope.readが呼ばれます。ここにキャラクタリスティックから値を読み取る処理を記述します。

    $scope.read = function(){

        lockStatusCharacteristic.readValue().then(value=>{ //(1)
            var lockValue = value.getUint8(0);//(2)
            if(lockValue == 2){//(3)
                $scope.statusImgPath = "./img/lock.png";
                $scope.message = "Lock";
            }else{
                $scope.statusImgPath = "./img/unlock.png";
                $scope.message = "Unlock";
            }
        })

    }

3.4.2 Read処理の説明

(1) キャラクタリスティックのreadValueファンクションを呼び出し現在の値を取得します。結果は後続のthenにvalueとして渡されます。

(2) BLEから取得されるデータはバイナリデータとなります。 JavaScriptではDataViewオブジェクトとして表示されるため、1バイトの整数値として値を取り出しています。

(3) 取り出した値を使いUIを変更する処理を記載します。今回はホールセンサがPIO2に付いています。そのため、入力があると4(0x04)が帰り、それ以外は0となります。

3.5 デバイスからのNotification(Subscribeの実装)

3.5.1 Notify処理の実装

キャラクタリスティックからNotificationをうけとる処理を記載します。UIの「Subscribe」を押下した場合にNotificationを開始するように$scope.subscribeに以下を記述します。

    $scope.subscribe = function(){
        if(isSubscribe === false){
            lockStatusCharacteristic.addEventListener("characteristicvaluechanged", onSubscribeLockInfo);//(1)
            $scope.subscribeUILabel = "Unsubscribe";
            isSubscribe = true;
            return lockStatusCharacteristic.startNotifications();//(2)
        }else{
            $scope.subscribeUILabel = "Subscribe";
            return lockStatusCharacteristic.stopNotifications();//(3)
        }

    }

3.5.2 Notify処理の説明

(1) データを受信した場合に実行されるイベントリスナを登録  はじめにNotifyを受信した場合の処理をイベントリスなとして登録します。イベントリスナの処理はonSubscribeLockInfoに実装します。 (2) デバイスにNotifyの開始を指示  デバイスに対してNotifyの開始を支持します。これ以降、デバイスからは一定の間隔でデータが送信されます。今回の場合は1秒間隔でデータが飛んできます。 (3) Notifyの実施を停止  デバイスへNotifyの停止を支持します。

3.5.3 イベントリスナの実装

デバイスからのデータ受信時に実行されるonSubscribeInfoファンクションは以下のように実装します。こちらも実装を行ってください。実装内容は以下のようになっています。

function onSubscribeLockInfo(event){ //(1)
    let lockValue = event.target.value.getUint8(0); //(2)
    if(lockValue == 2){
        $scope.statusImgPath = "./img/lock.png"
        $scope.message = "Lock"
        doBeep(false);
            
    }else{
        $scope.statusImgPath = "./img/unlock.png"
        $scope.message = "Unlock"
        doBeep(isBeep);
    }
    $scope.$apply();
}

3.5.4 イベントリスナの説明

(1) イベントリスナの定義  イベントリスナは引数をひとつとります。今回はeventとしました。 (2) データの取り出し  (1)で取得したeventからデータを取得します。Readの場合と同じようにDataViewオブジェクトとして取り扱います。

3.6 デバイスに値を書き込む(Writeの実装)

3.6.1 Write処理の実装

 最後に、デバイスへのデータの送信を行います。onSubscribeLockInfoファンクション中でdoBeepというファンクションが呼ばれています。このファンクションにブザーを鳴らす処理を追加して行きます。

function doBeep(event){
    if(event===true){
        value = new Uint8Array([0x04]);//(1)
        beepCharacteristic.writeValue(value);(2)            
    }else{
        value = new Uint8Array([0x00]);
        beepCharacteristic.writeValue(value);            
    }
}

3.6.2 Write処理の説明

(1) 送信データの作成  送信データはUint8Arrayオブジェクトとして表現します。バイト配列を引数に与えてください。今回は1byteなので要素数は1です。

(2) データの書き込み  データの書き込みはwriteValueファンクションを利用します。先ほどのUint8Arrayを引数に与えます。ここで、キャラクタリスティックがこれまでと違いbeepCharacteristicになっていることに注意してください。