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のインスタンスを取得しています。













