LoginSignup
13
14

More than 3 years have passed since last update.

Web Bluetooth API を使う

Last updated at Posted at 2021-02-24

目次

1.はじめに
2.BLE の概要
3.Web Bluetooth API
4.scan~ 接続/切断
5.Notificaton
6.Write
7.Read
8.複数の characteristic を使う
9.デバッグ
10.あとがき

1.はじめに

…主に自分が見直すための記録です…

OS の機能(カメラ、Bluetooth、…)を使おうと思ったら JAVA とかでネイティブアプリを作るものだと思っていました。
まさか JS で Web から OS の機能を操作できるとは…!
仕様に制限がありますが、Web アプリでできることが格段に増えて夢が広がります。
近年では PWA も着実に流行ってきているので JS を学ぶ身として楽しみです!

今回は仕事で使う機会があったWeb Bluetooth API(Chrome)について、学んだことを簡単にまとめておきます。

2.BLE の概要

BLEについて調べ始めるといきなり知らない単語が出てきました。
 GATT通信とか Characteristicとか…
知らなくてもプログラムは組めますが、やっぱり知っていたほうがいいです。
これらの理解には以下のサイトが分かりやすかったです。いきなり他力本願ですがw
サルでもわかるBLE入門
※リンク切れてますね…すみません

単語まとめ

単語 簡単な解説
ペリフェラル 子機。接続するまで周期的にアドバタイズしている。
セントラル 親機。複数のペリフェラルと接続可能。
アドバタイズ 接続待ちの間ペリフェラルが発するブロードキャスト通信
GATT 通信 ペアリング後の一対一の通信
Service Characteristic を機能単位で一括りにしたラベルのようなもの
Characteristic 機能 Notification/Read/Write がある
UUID Service/Characteristic にアクセスするための固有 ID

3.Web Bluetooth API

  • Web Bluetooth API 対応状況 … Can I use can i use bluetooth.png Chrome で使うのが無難かと。
  • 公式リファレンス … Web Bluetooth
  • 公式サンプル集 … Web Bluetooth Samples
  • ライブラリ
    • Web Bluetooth にはプログラミングを簡単にするためのライブラリがあります。 このライブラリを使わなくても解説が非常に参考になるので一読、お勧めします。(今回は使いません) BlueJelly
  • その他制限
    • セキュリティのため、スキャン開始時にユーザーアクションが必須となります。 そのため、「アクセス時自動接続」等はできません。
    • デバイスの機能にアクセスするため、httpsもしくはlocalhostでしか動きません。

Bluetooth 使用の流れは
スキャン ⇒ GATT サーバ接続 ⇒ Service 取得 ⇒ Characteristic 取得 ⇒ それぞれの動作 となります。

4.scan~ 接続/切断

スキャンにはユーザーアクションが必須となります。
今回は簡単にボタンを用意しました。

index.html
<body>
    <button id="scanBtn">接続</button>
    <button id="disConnect">切断</button>
    <button id="writeBtn">送信</button>
</body>

接続ボタンでスキャンを開始、切断ボタンで接続を切ります。
続いて JS ファイルの内容です。
Web Bluetooth は PromiseAsync Await 方式で記述します。

bluetooth.js
//ボタン登録 ---------------------------------------------------------
const scan = document.getElementById('scanBtn');
const disconnect = document.getElementById('disConnect');

scan.addEventListener('click', () => {
    startScan();
});

disconnect.addEventListener('click', () => {
    onDisconnected();
})
//----------------------------------------------------------------------

//UUID登録 使うサービスとcharacteristicのUUIDを登録する
const serviceUUID = '00000000-0000-0000-0000-000000000000';
//デバイスからのデータCharacteristic
const CharacteristicUUID_Notification = '000000000-0000-0000-0000-000000000000';
const CharacteristicUUID_ReadRead = '00000000-0000-0000-0000-000000000000';
const CharacteristicUUID_Write = '00000000-0000-0000-0000-000000000000';

// BLE接続用
let keyDevice;
let keyServer;
let keyService;
let keyNotificationCharacteristic;
let keyReadCharacteristic;
let keyWriteCharacteristic;


/**
 * web bluetooth api
 * bluetooth接続機器をスキャンする
 */
const startScan = () => {
    navigator.bluetooth.requestDevice({
        filters: [{
            //name: 'デバイス名',
            //namePrefix:'デバイスのプレフィックス',
            services: [serviceUUID],
        }]
    })
        .then(device => {
            startModal('接続中…');
            //接続
            console.log("device.id    : " + device.id);
            console.log("device.name  : " + device.name);
            console.log("device.uuids : " + device.uuids);
            keyDevice = device;
            keyDevice.addEventListener('gattserverdisconnected', onDisconnected);
            return device.gatt.connect();
        })
        //サービスを取得
        .then(server => {
            keyServer = server;
            console.log('Getting service...');
            return server.getPrimaryService(serviceUUID);
        })
        //Characteristicを取得
        .then(service => {
            keyService = service;
            console.log('Getting Notification Characteristic...');
            //複数のCharacteristicを使う場合はここから更に登録する
            return service.getCharacteristic(//使用するCharaceristicUUID);
        })
        //Characteristicの動作
        .then(characteristic => {
            // characteristicに対してRead/Write/Notificationsの処理を記述
        })
        .catch(error => {
            console.log(error);
        })
};

const onDisconnected = () => {
    console.log('> Bluetooth Device disconnected')
};

  • UUID について
    • Service と Characterisitc にそれぞれ設定されている、16 進数の文字列とハイフンからなる 128bit の文字列です。 また、UUID には 2 種類あります。
    • 定義済み UUID   Bluetooth SIG が定義して割り当てている UUID … 定義済み UUID 一覧
    • オリジナル UUID   開発者が決めた UUID
    • 注意 仕様書の UUID が大文字英字で書かれていてもソースには小文字、ハイフン込みで書きます。

5.Notificaton

bluetooth.js
//Characteristicを取得
        .then(service => {
            keyService = service;
            console.log('Getting Notification Characteristic...');
            return service.getCharacteristic(CharacteristicUUID_Notification);
        })
        //Characteristicの動作
        .then(characteristic => {
            keyNotificationCharacteristic = characteristic;
            return keyNotificationCharacteristic.startNotifications().then(_ => {
                console.log('> Notifications started');
                keyNotificationCharacteristic.addEventListener('characteristicvaluechanged',
                    handleNotifications);
            });
        })...

//notification
const handleNotifications = (event) => {
    let value = event.target.value;
    let notifyArray = [];

    for (let i = 0; i < value.byteLength; i++) {
        notifyArray.push('0x' + ('00' + value.getUint8(i).toString(16)).slice(-2));
    };
    console.log('> ' + a.join(' '));
};

.then(service  で
getCharacteristic() メソッドで Notification を引数にします。

その後、
.then(characteristic  で handleNotifications を呼んでいます。

BLE では、セントラルとペリフェラルとのやりとりは ArrayBuffer 形式で行われます。
handleNotifications では、ペリフェラルからの通知を 16 進数に変換しています。

6.Write

bluetooth.js にWriteを追加します。

bluetooth.js
let msg = 555;
document.getElementById('writeBtn').addEventListener('click', () => {
    writeStart();
});

const writeStart = (msg) => {
    keyService.getCharacteristic(CharacteristicUUID_Write)
        .then(characteristic => {
            keyWriteCharacteristic = characteristic;
            keyWriteCharacteristic.writeValue(new Uint8Array([msg]));
        })
};

ペリフェラルによるのかもしれないが、一度に Write できる量に限りがあるようで、20Byte ずつ Write することで解決しました。

7.Read

bluetooth.js
keyService.getCharacteristic(CharacteristicUUID_Read)
    .then(characteristic => {
        keyReadCharacteristic = characteristic;
        keyReadCharacteristic.readValue()
            .then(response => {
                const msg = new TextDecoder('utf-8').decode(response)
                console.log(msg)
            })
    })

8.複数の characteristic を使う

Promiseの逐次処理で書くことで、例えば
スキャン ⇒ Notification ⇒ Write が一連の流れで処理できます。

また、Promise.all で複数のCharacteristicを登録することもできます。

bluetooth.js
    .then(service => {
        chosenHeartRateService = service;
        return Promise.all([
            service.getCharacteristic(CharacteristicUUID_1)
                .then(//CharacteristicUUID_1の処理**/),
            service.getCharacteristic(CharacteristicUUID_2)
                .then(/**CharacteristicUUID_2の処理**/),
        ])
    })

9.デバッグ

@tomoya0x00 さんの BLEのデバッグツールまとめ を参考にしました。
nRF Connect for Mobile と、
BLE Peripheral Simulator を使いました。(Android)

10.あとがき

今回、仕事でWebからBluetooth通信を用いる機能の実装を行いました。
ただ、セキュリティの関係で暗号化しなければならない、数秒以内にWriteで返信しなければならない等
結構大変でした…。もっと初心者にやさしくして…w

また、同時にQRコードリーダーも実装(読み込んでtrueの時、BluetoothでWriteする)ということも行ったのですが、
Webでこんなにいろんなことができることに驚きました。
まだまだ頑張って勉強します!

13
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
14