Web Bluetooth APIを使って、アルプスのセンサネットワークモジュールから加速度データを取得します。「Web BluetoothによるSensorTagのScan」でWeb Bluetooth APIを使って、SensorTagをScanするスクリプトを作成しました。また「C# .NET Frameworkを使ってアルプスのセンサネットワークモジュールからモーションデータの取得」では、C# .NET Frameworkのコンソールアプリを作成しました。

センサネットワークモジュール加速度データ取得プログラムの作成

3つのファイル「alps.html」「alps.js」「alps_ble.js」で構成し、Web Bluetooth APIで作成したセンサネットワークモジュール加速度データ取得プログラムを次に示します。プログラムは、クラス設計により、PromiseでなくAsync/Awaitを使用して非同期で処理します。

alps.html

<html>

<head>
    <meta charset="utf-8">
    <title>Sensor Network Module</title>
    <script type="module" src="./alps.js"></script>
</head>

<body>
    <h2>Sensor Network Module</h2>

    <div>
        <button class="scanBtn">
            Connect
        </button>
    </div><br>

    <div>
        <button class="commandBtn">
            Command
        </button>
    </div><br>

    <div>
        <button class="notifyBtn">
            Notify Start
        </button>
    </div><br>

    <div>
        <button class="readdataBtn">
            Read Data
        </button>
    </div><br>

    <div>
        <button class="resetBtn">
            Disconnect
        </button>
    </div><br>
    <hr>

    <div>
        <div id="device_name">Device Name : </div>
        <div id="connect_status">Status : Disconnect</div>
        <div id="read_data">Data :</div><br>
        <div id="error">Error : </div>
    </div>
</body>

</html>
  • 1行目で、alps_ble.jsに作成したクラスをimport します。
  • 3行目でimportしたクラスをインスタンス化します。

alps.js

import Alps_ble from './alps_ble.js';

let alps_ble = new Alps_ble();

//Connectボタンの処理
const scanBtn = document.querySelector('.scanBtn');
scanBtn.addEventListener('click', function (clickEvent) {
    //this.EGRequest = 0; //暫定的に記載
    alps_ble.Blecan();
})

//コマンド書き込み
const onBtn = document.querySelector('.commandBtn');
onBtn.addEventListener('click', function (clickEvent) {
    //セットしたコマンドを送信
    alps_ble.Write(alps_ble.Custom3_Characteristic_UUID, alps_ble.Command);
})

//データ読み込み
const battBtn = document.querySelector('.readdataBtn');
battBtn.addEventListener('click', function (clickEvent) {
    alps_ble.Read(alps_ble.Custom1_Characteristic_UUID);
})

//データ通知
const notifyBtn = document.querySelector('.notifyBtn');
notifyBtn.addEventListener('click', function (clickEvent) {
    alps_ble.StartNotify(alps_ble.Custom1_Characteristic_UUID);
})

//Disconnectボタンの処理
const resetBtn = document.querySelector('.resetBtn');
resetBtn.addEventListener('click', function (clickEvent) {
    alps_ble.Reset();
})

//HTMLに値を表示

alps_ble.onScan = function (deviceName) {
    document.getElementById('device_name').innerHTML = 'Device Name : ' + deviceName;
    document.getElementById('error').innerHTML = "Error : ";
}
alps_ble.onConnectGATT = function () {
    document.getElementById('connect_status').innerHTML = 'Status : Connected';
    document.getElementById('error').innerHTML = "Error : ";
};
alps_ble.onWrite = function () {
    document.getElementById('connect_status').innerHTML = 'Status : Sended command'
    document.getElementById('error').innerHTML = "Error : ";
}

alps_ble.onData = function (data) {
    document.getElementById('read_data').innerHTML = 'Data : ' + data;
    document.getElementById('error').innerHTML = "Error : ";
}
alps_ble.onError = function (error) {
    document.getElementById('error').innerHTML = 'Error : ' + error;
}
  • 29行目の「navigator.bluetooth.requestDevice」メソッドで、センサネットワークモジュールを検索します。
  • 33行目の「device.gatt.connect」メソッドで、センサネットワークモジュールと接続します。
  • 35行目の「getPrimaryService」メソッドで、PrimaryUUIDからPrimaryServiceを取得します。
  • 46行目の「getCharacteristic」メソッドで、書き込みUUIDからCharacteristicを取得します。
  • 53行目で、作成したUint8Array(char型バッファ)にコマンドを設定します。
  • 56行目の「writeValue」メソッドで、コマンドを書き込みます。
  • 70行目の「getCharacteristic」メソッドで、読み込みUUIDからCharacteristicを取得します。
  • 71行目の「readValue」メソッドで、データを読み込みます。
  • 72行目で、読み込んだデータを表示します。
  • 96行目の「DataChanged」メソッドで、イベントにより通知されたデータを処理します。
  • 125行目の「ConvertAcceleration」メソッドで、読み込んだデータから加速度を計算します。
  • 151行目の「getCharacteristic」メソッドで、読み込みUUIDからCharacteristicを取得します。
  • 152行目の「addEventListener」メソッドで、EventListenerとして「DataChanged」メソッドを設定します。その時にクラス内のメソッドを読み出せるように「.bind(this)」を付与します。
  • 153行目の「startNotifications」メソッドで、Notificationを開始します。
  • 164行目の「stopNotifications」メソッドで、Notificationを終了します。
  • 184行目の「gatt.disconnect」メソッドで、接続を切断します。

alps_ble.js

export default class {
    //constructor
    constructor() {
        this.device = null;
        this.server = null;
        this.service = null;
        this.Commanduuid = null;
        this.Readuuid = null;
        this.Notifyuuid = null;

        this.Service1_UUID = "47fe55d8-447f-43ef-9ad9-fe6325e17c47";
        this.Custom1_Characteristic_UUID = "686a9a3b-4c2c-4231-b871-9cfe92cc6b1e";
        this.Custom2_Characteristic_UUID = "078ff5d6-3c93-47f5-a30c-05563b8d831e";
        this.Custom3_Characteristic_UUID = "b962bdd1-5a77-4797-93a1-ede8d0ff74bd";


        // モーション(動き)検知 100ms間隔(モーション系センサのみ)
        this.Command = [[0x2F, 0x03, 0x03], [0x01, 0x03, 0x03], [0x04, 0x03, 0x01], [0x06, 0x04, 0x64, 0x00], [0x2F, 0x03, 0x01], [0x20, 0x03, 0x01]];

        this.alpsoptions = {
            acceptAllDevices: true,
            optionalServices: [this.Service1_UUID]
        };

    }
    async Blecan() {
        try {
            console.log('Requesting Bluetooth Device...');
            this.device = await navigator.bluetooth.requestDevice(this.alpsoptions);
            this.onScan(this.device.name);
            this.onConnectGATT();

            this.server = await this.device.gatt.connect();
            console.log('Getting GAP Service...');
            this.service = await this.server.getPrimaryService(this.Service1_UUID);
            console.log('Got Service');
        } catch (error) {
            this.onError(error);
            console.log('Argh! ' + error);
        }
    }

    async Write(uuid, moveCommand) {
        try {
            console.log('Execute : Write');
            this.Commanduuid = await this.service.getCharacteristic(uuid);
            //this.Readuuid = await this.service.getCharacteristic(this.Custom1_Characteristic_UUID);

            for (let i = 0; i < moveCommand.length; i++) {
                //console.log(moveCommand[i]);
                let uint8array = new Uint8Array(moveCommand[i].length);
                for (let Offset = 0; Offset < moveCommand[i].length; Offset++) {
                    uint8array[Offset] = moveCommand[i][Offset];
                }
                //console.log(uint8array);
                await this.Commanduuid.writeValue(uint8array, true);
            }
            this.onWrite();
        }
        catch (error) {
            this.onError(error);
            console.log('Argh! ' + error);
        }
    }

    async Read(uuid) {
        try {
            let x = 0, y = 0, z = 0;
            console.log('Execute : read');
            this.Readuuid = await this.service.getCharacteristic(uuid);
            let value = await this.Readuuid.readValue();
            console.log("read :" +
                value.getUint32(0, false).toString(16) +
                value.getUint32(4, false).toString(16) +
                value.getUint32(8, false).toString(16) +
                value.getUint32(12, false).toString(16) +
                value.getUint32(16, false).toString(16)
            );

            if (value.getUint8(0, false) == 0xf2) {
                [x, y, z] = this.ConvertAcceleration(
                    value.getUint8(8, false), value.getUint8(9, false),
                    value.getUint8(10, false), value.getUint8(11, false),
                    value.getUint8(12, false), value.getUint8(13, false)
                );
                console.log("Acceleration :" + " " + x.toFixed(3) + " " + y.toFixed(3) +
                    " " + z.toFixed(3));
                this.onData(x.toFixed(3) + " " + y.toFixed(3) + " " + z.toFixed(3));
            }
        }
        catch (error) {
            this.onError(error);
            console.log('Argh! ' + error);
        }
    }
    DataChanged(event) {
        try {
            let x = 0, y = 0, z = 0;
            console.log('Execute : DataChanged');
            let value = event.target.value;
            if (value.getUint8(0, false) == 0xf2) {
                [x, y, z] = this.ConvertAcceleration(
                    value.getUint8(8, false), value.getUint8(9, false),
                    value.getUint8(10, false), value.getUint8(11, false),
                    value.getUint8(12, false), value.getUint8(13, false)
                );
            }
            console.log("DataChanged :" +
                value.getUint32(0, false).toString(16) +
                value.getUint32(4, false).toString(16) +
                value.getUint32(8, false).toString(16) +
                value.getUint32(12, false).toString(16) +
                value.getUint32(16, false).toString(16)
            );
            console.log("Acceleration :" + " " + x.toFixed(3) + " " + y.toFixed(3) +
                " " + z.toFixed(3));
            this.onData(x.toFixed(3) + " " + y.toFixed(3) + " " + z.toFixed(3));
        }
        catch (error) {
            this.onError(error);
            console.log('Argh! ' + error);
        }
    }

    ConvertAcceleration(x_l, x_m, y_l, y_m, z_l, z_m) {
        let x = 0, y = 0, z = 0;
        x = (x_l + x_m * 256);
        if (x >= 0x8000) {
            x = -1 * (0x10000 - x);
        }
        x = x / 4096;

        y = y_l + y_m * 256;
        if (y >= 0x8000) {
            y = -1 * (0x10000 - y);
        }
        y = y / 4096;

        z = z_l + z_m * 256;
        if (z >= 0x8000) {
            z = -1 * (0x10000 - z);
        }
        z = z / 4096;
        return [x, y, z];

    }

    async StartNotify(uuid) {
        try {
            console.log('Execute : StartNotify');
            this.Notifyuuid = await this.service.getCharacteristic(uuid);
            this.Notifyuuid.addEventListener('characteristicvaluechanged', this.DataChanged.bind(this));
            await this.Notifyuuid.startNotifications();
        }
        catch (error) {
            this.onError(error);
            console.log('Argh! ' + error);
        }
    }

    async StopNotify() {
        try {
            console.log('Execute : StopNotify');
            await this.Notifyuuid.stopNotifications();
        }
        catch (error) {
            this.onError(error);
            console.log('Argh! ' + error);
        }
    }

    async Disconnect() {
        if (!this.device) {
            var error = "No Bluetooth Device";
            console.log('Error : ' + error);
            this.onError(error);
            return;
        }

        if (this.device.gatt.connected) {
            console.log('Execute : disconnect');

            this.isNotify = false;
            this.device.gatt.disconnect();
        } else {
            var error = "Bluetooth Device is already disconnected";
            console.log('Error : ' + error);
            this.onError(error);
            return;
        }
    }

    Clear() {
        console.log('Excute : Clear Device and Characteristic');
        this.device = null;
    }

    //reset
    Reset() {
        console.log('Excute : reset');
        this.Disconnect(); //GNDisconnect() is not Promise Object
        this.Clear();
    }
}

センサネットワークモジュール加速度データ取得プログラムの実行

センサネットワークモジュールモーションデータ取得プログラムを実行し、アルプスのセンサネットワークモジュールとペアリングできると、次のように受信されたモーションデータを16進表示します。データの最初のバイト「Event Code」が「E0」が ステータス通知、「F2」がデータパケット 1となります。データパケット 1から加速度データを取得して表示します。