MakeCodeによりmicro:bit ver2.21にBLEサービスを実装し、Windows PCのChromeブラウザで動作するWebBluetoothを使って、micro:bitの加速度センサー情報を表示し、ブラウザに表示されたボタンの操作により、micro:bitのLEDを点灯します。
micro:bitの仕様
micro:bitのサービスのUUIDは「micro:bit BlueToothプロファイル」に示します。
加速度センサーは下記図に示す通り、XYZ軸の加速度を-2G~+2Gの範囲で知ることができます。次に「micro:bit BlueToothプロファイル」から引用を示します。
Contains accelerometer measurements for X, Y and Z axes as 3 signed 16 bit values in that order and in little endian format. X, Y and Z values should be divided by 1000.
- X軸:-2032~2048 右に傾けるとプラス、左に傾けるとマイナス
- Y軸:-2032~2048 手前に傾けるとプラス、奥に傾けるとマイナス
- Z軸:-2048~2032 上(LED表面)がプラス、下(裏面)がマイナス
LEDマトリックスについては、
Revised 5 byte representation of the LED Matrix:
Octet 0, LED Row 1: bit4 bit3 bit2 bit1 bit0
Octet 1, LED Row 2: bit4 bit3 bit2 bit1 bit0
Octet 2, LED Row 3: bit4 bit3 bit2 bit1 bit0
Octet 3, LED Row 4: bit4 bit3 bit2 bit1 bit0
Octet 4, LED Row 5: bit4 bit3 bit2 bit1 bit0
MakeCodeによるmicro:bitのBLEサービスの実装
micro:bitのMakeCodeには、Bluetoothのブロックが標準(の拡張機能)で用意され、これらのBLEサービス(ペリフェラル)を使ってプログラミングできます。
MakeCodeでは、独自の拡張機能を開発することが可能です。もちろん、標準(の拡張機能)にはないBLEサービスの開発と追加も可能です。詳細については「MakeCodeでmicro:bitのBLEサービスを実装したい」を参照します
- 「Microsoft MakeCode for micro:bit 」のエディタをブラウザから開きます。
- 「読み込み」ボタンとクリックして、ダイアログを表示します。
- 「ファイルを読み込む」選択します。
- 「microbit_test.hex」からダウンロードしたZipファイルを解凍し、解凍したファイルをドラッグアンドドロップすると、サンプルコードが展開されます。サンプルコードを整形し、各イベントごとにLED表示を次のように変更します。
- 歯車のアイコンから、「プロジェクトの設定」を選択します
- “No Pairing Required:Anyone can connect via Bluetooth”にチェックし、「保存」を選択します
- 「ダウンロード」ボタンをクリックし、作成したコードをmicro:bitに書き込みます
WebBluetoothを使ったmicrobitアプリの作成
作成したmicrobitアプリを次に示します。
index.html
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css"> <title>micro:bit BLE Interface</title> </head> <body> <div class="container"> <div class="row justify-content-center"> <div class="col-10 pt-2 mb-2"> <h3>micro:bit BLE Interface</h3> <div id="error">Error : </div> </div> </div> <div class="row justify-content-center bg-light"> <div class="col-6 pt-2"> <div id="device_name">Device Name : </div> <div id="connect_status">Status : Disconnect</div> <div class="mt-2"> <button type="button" class="btn btn-secondary scanBtn">Connect</button> <button type="button" class="btn btn-secondary resetBtn">Disconnect</button> </div> </div> <div class="col-6 pt-2"> <div id="read_data">加速度 :</div> <div class="col-12 pt-2"> <button type="button" class="btn btn-secondary notifyBtn">Notify Start</button> </div> <div class="col-12 pt-2 mb-2"> <div> <button type="button" class="btn btn-secondary commandBtn1">LED0</button> <button type="button" class="btn btn-secondary commandBtn2">LED1</button> <button type="button" class="btn btn-secondary commandBtn3">LED2</button> </div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script type="module" src="./microbit.js"></script> </body> </html>
microbit.js
import microbit_ble from './microbit_ble.js'; let Device_ble = new microbit_ble(); /*///////////////////////////////// console.log("float test05"); function toFloat(v) { var a = new ArrayBuffer(4), b = new Uint32Array(a), f = new Float32Array(a); b[0] = v; return f[0]; } console.log(toFloat(0x419A0000)); // -> 19.25 console.log(toFloat(0x41FC51EC)); // -> 31.54 console.log(toFloat(0x429DEB85)); // -> 78.96 ////////////////////////////////*/ //Connectボタンの処理 const scanBtn = document.querySelector('.scanBtn'); scanBtn.addEventListener('click', function (clickEvent) { //this.EGRequest = 0; //暫定的に記載 Device_ble.Blecan(); }) //コマンド書き込み const onBtn1 = document.querySelector('.commandBtn1'); onBtn1.addEventListener('click', function (clickEvent) { //セットしたコマンドを送信 Device_ble.Write(Device_ble.CustomLed_Characteristic_UUID, Device_ble.Command1); }) const onBtn2 = document.querySelector('.commandBtn2'); onBtn2.addEventListener('click', function (clickEvent) { //セットしたコマンドを送信 Device_ble.Write(Device_ble.CustomLed_Characteristic_UUID, Device_ble.Command2); }) const onBtn3 = document.querySelector('.commandBtn3'); onBtn3.addEventListener('click', function (clickEvent) { //セットしたコマンドを送信 Device_ble.Write(Device_ble.CustomLed_Characteristic_UUID, Device_ble.Command3); }) //データ通知 const notifyBtn = document.querySelector('.notifyBtn'); notifyBtn.addEventListener('click', function (clickEvent) { Device_ble.StartNotify(Device_ble.CustomAcc_Characteristic_UUID); }) //Disconnectボタンの処理 const resetBtn = document.querySelector('.resetBtn'); resetBtn.addEventListener('click', function (clickEvent) { Device_ble.Reset(); }) //HTMLに値を表示 Device_ble.onScan = function (deviceName) { document.getElementById('device_name').innerHTML = 'Device Name : ' + deviceName; document.getElementById('error').innerHTML = "Error : "; } Device_ble.onConnectGATT = function () { document.getElementById('connect_status').innerHTML = 'Status : Connected'; document.getElementById('error').innerHTML = "Error : "; }; Device_ble.onWrite = function () { document.getElementById('connect_status').innerHTML = 'Status : Sended command' document.getElementById('error').innerHTML = "Error : "; } Device_ble.onData = function (data) { document.getElementById('read_data').innerHTML = '加速度 : ' + data; document.getElementById('error').innerHTML = "Error : "; } Device_ble.onError = function (error) { document.getElementById('error').innerHTML = 'Error : ' + error; }
- 79-81行目でmicro:bitからの加速度データを変換しています。
microbit_ble.js
export default class { //constructor constructor() { this.device = null; this.server = null; this.serviceacc = null; this.serviceled = null; this.Commanduuid = null; this.Readuuid = null; this.Notifyuuid = null; this.ServiceAcc_UUID = "e95d0753-251d-470a-a062-fa1922dfa9a8"; this.CustomAcc_Characteristic_UUID = "e95dca4b-251d-470a-a062-fa1922dfa9a8"; this.ServiceLed_UUID = "e95dd91d-251d-470a-a062-fa1922dfa9a8"; this.CustomLed_Characteristic_UUID = "e95d7b77-251d-470a-a062-fa1922dfa9a8"; // micro:bit LED Data this.Command1 = [[0, 0, 0, 0, 0]]; this.Command2 = [[0x8, 0, 0, 0, 0]]; this.Command3 = [[0, 0, 0, 0, 0x8]]; this.alpsoptions = { acceptAllDevices: true, optionalServices: [this.ServiceAcc_UUID, this.ServiceLed_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 Service03...'); this.serviceacc = await this.server.getPrimaryService(this.ServiceAcc_UUID); this.serviceled = await this.server.getPrimaryService(this.ServiceLed_UUID); console.log('Got Service'); } catch (error) { this.onError(error); console.log('Argh! ' + error); } } async Write(uuid, moveCommand) { try { console.log('Execute01 : Write ' + uuid); this.Commanduuid = await this.serviceled.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); } } DataChanged(event) { console.log('Execute06 : DataChanged'); try { let AcceleratorX = 0; let AcceleratorY = 0; let AcceleratorZ = 0; AcceleratorX = event.target.value.getInt16(0, true) / 1000.0; AcceleratorY = event.target.value.getInt16(2, true) / 1000.0; AcceleratorZ = event.target.value.getInt16(4, true) / 1000.0; console.log(AcceleratorX + ' ' + AcceleratorY + ' ' + AcceleratorZ); this.onData("x=" + AcceleratorX + " y=" + AcceleratorY + " z=" + AcceleratorZ ); } catch (error) { this.onError(error); console.log('Argh! ' + error); } } async StartNotify(uuid) { try { console.log('Execute03 : StartNotify ' + uuid); this.Notifyuuid = await this.serviceacc.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(); } }
WebBluetoothを使ったmicrobitアプリの実行
- ブラウザから「http://localhost/microbit/」でアクセスすると、作成したmicrobitアプリが実行され、次のように表示されます。
- 「Connect」ボタンをクリックすると次のようなダイアログが表示されます。
- micro:bitのLEDマトリックスが次のように表示されます。
- 「Notify Start」ボタンを押すと、「加速度:」の項目に次のように表示されます。
- 「LED1」ボタンを押すと、micro:bitのLEDマトリックスが次のように表示されます。
- Chromeの「デベロッパーツール」の「コンソール」には次のようにメッセージが表示されます。
「BBC micro:bit」をクリックして、「ペア設定」ボタンを押すと、次のように表示されます
エラーメッセージ:Add the service UUID to ‘optionalServices’ in requestDevice() options
micro:bitの加速度センサー情報取得とLEDマトリックスの点灯のために、今回作成したmicrobitアプリでは、2種類のサービスUUIDを使ってBLEを制御する必要があり、この時に上記のエラーメッセージが発生しました。
正常に動作させるためには、サービスUUIDを、上記「microbit_ble.js」ファイルの23-26行目のように定義し、32行目の「navigator.bluetooth.requestDevice」関数を呼び出す必要があります(詳細については「Web Bluetooth」を参照します)。
39行目で「server.getPrimaryService」関数により、加速度センサの他に、LEDのサービスUUIDのインスタンスを取得しています。