BLE通信を活用して、パンチするとスマホから効果音が鳴るARボクシングガジェットを作成します!
自分の動きに連動して音が鳴る爽快感を味わえます。そして、カスタマイズも無限大です!
※ボリュームONでご視聴ください
誰かが作ったフィットネスサービスではなく、自分で作るフィットネスサービスだからこそ、改良を重ねることに没頭でき、日頃の運動不足も解消できます!
今回の成果物は「加速度センサ」を活用して「パンチ」や「パンチの強弱」を検知し、離れたところにあるスマホやPCへ送信するシステムですが、micro:bitには他にも様々なセンサーやジェスチャー検出も用意されています。また通信方向としてスマホやPCからmicro:bit側へも送信可能です。様々なモノに応用可能ですのでぜひチャレンジしてみてください。
micro:bitのMakeCodeには実行できるシミュレーターがありますが、BLE通信はシミュレーションでは動きませんので、micro:bitの実物が必要です。
手に持ってパンチして使うには、micro:bit本体の他に電池ボックスなど給電する機器が必要になります。
micro:bitは2020年11月に新バージョンであるV2が発売されています。見た目はかなり似ているので、購入する際はV1かV2か確かめてください。V2の大きな変更点として「マイク」「スピーカー」が追加になっています。値段は同じなのでV2を購入しましょう。
様々なショップで販売されておりますので、購入しやすいところでお求めください。また、電池やUSBケーブルなど既にお持ちであれば購入不要です。ここでは「秋月電子通商」で購入する場合の例を挙げます。
補足
ポケット付きのリストバンドです。この中にmicro:bitと電池ケースを入れてパンチすることが可能になります。
この部品は無くても、手でmicro:bitと電池ケースを持つことでも代用は可能です。ただし、パンチしたときにmicro:bitや電池ケースを飛ばさないように気を付けて下さい。
これは一例です。micro:bitと電池ケースが程良く入る大きさで、電気的にショートなどしなければ、他のもので代用可能です。ちなみに、Amazonでより安価な「GOGO リストバンド」というものを試してみましたが、チャックがすぐに壊れましたのでお勧めはしません。
ARボクシングではBLE通信を扱います。
BLEを使うことでmicro:bitとPC(スマホ)の無線通信が可能となります。BLEは省電力であるためmicro:bit側はコイン電池でも動作可能というメリットがあります。
ここではBLE開発を始める前に知っておきたい導入知識を簡単に学びます。
色々な用語が出てきますが、暗記する必要はなく、軽く聞き流す程度の理解で問題ありません。
通常BLE通信を扱う場合はスマホアプリ開発が必要ですが、WebBluetoothを使うことでWeb上でBLE通信が可能です。
スマホアプリ開発を完全に置き換えるものではありませんが、利用するシーンによっては非常に便利です。
メリット:学習コストが低い
デメリット:制限事項がある
セントラルとペリフェラルで通信します。
今回のケースだと、セントラルがPCでペリフェラルがmicro:bitです。
通信手法は主に3つ
データ構造が階層になっています。
例えばBLEデバイスの加速度センサのデータを取得したい場合は、「加速度センサService」の「加速度センサデータCharacteristic」にアクセスする必要があります。
UUIDとは16進数の数値で決められているユニークなIDです。
ServiceやCharacteristicの名前は「加速度センサ」のような文字列ではなくUUIDで表現します。
BlueJelly
というラッパーライブラリを使えば文字列で定義されているため分かりやすいです。
その他にも様々なメソッドがラップされていて扱いやすいため、今回は BlueJelly
を活用してWebBluetoothのプログラミングを行います。
詳細を知りたい人はこちらをどうぞ。
まずはペリフェラル側でBLE設定とコード作成を行います。
ペリフェラルとは、今回はmicro:bitのことです。
MakeCodeにアクセスして新しいプロジェクト
ボタンを押してを新しいプロジェクトを作成します。
プロジェクト名は任意でOKです。
歯車アイコンをクリックし、プロジェクトの設定
を選択します。
デフォルトでは2番目が有効になっていますが、1番目のNo Pairing Required: Anyone can connect via Bluetooth.
を有効にします。
歯車アイコンをクリックし、拡張機能
を選択します。
検索ボックスにbluetoot
と入力すると出てくる bluetooth
を選択します。
一部の拡張機能を削除してbluetoothを追加する
を選択します。
そうすると、これまで無線
と書かれていたブロックがBluetooth
に変更され、BLEに関するブロックが扱えるようになります。
Bluetoothのブロックが見えるようになればOK。
以下のコードを作成して下さい。先ほど設定して出現させたBluetooth
ブロックを使います。
追加ブロックは以下の通りです。
基本 > アイコンを表示 ハートマーク Bluetooth > その他 > Bluetooth UARTサービス Bluetooth > Bluetooth 接続されたとき 基本 > アイコンを表示 > ▼ うれしい顔 Bluetooth > Bluetooth 接続が切断されたとき 基本 > アイコンを表示 > ▼ かなしい顔
micro:bit本体にこのコードを書き込んで下さい この時点では「ハート」が表示されるだけです
次はセントラル側です。 プログラミングの前に、まずは動作確認を行います。
セントラルとは、今回の場合はPCやスマホ、タブレットを指します。
対応OSは以下の通りで、ブラウザは必ずChromeを使用してください。
詳細はこちらを参照して下さい
先程のコードを書き込んだmicro:bitの電源をONにします。
以下のページにアクセスして、Scan
ボタンを押してください。
Scanの動作サンプル
このようにBBC micro:bit[xxxxx]
と表示されるので、選択して「ペア設定」ボタンを押します。xxxxx
はIDで、micro:bit毎に異なります
以下のような画面になれば成功です。
上手くいかない場合は、以下を1つずつ試してみて下さい。
先程はこちらで予めWebに用意したセントラル側のコードを実行しましたが、今回は皆さんのPCローカル上にセントラル側のコードを用意し実行します。
以下からBlueJellyをダウンロードしてください。
BlueJellyのGitHubリポジトリ
Code
からDownload ZIP
を選択します。
zipファイルを展開後、src
フォルダに入っているscan.html
を実行して、前回と同じ結果になることを確かめて下さい。
※スマホやタブレットの場合で、ローカルファイルでは上手くいかない方はこちらを試してみて下さい。
先程のzipにはたくさんのファイルが入っていますが、ほとんどが個別のサンプル実行用途です。
先程のScan動作に必要なファイルは以下の3つのみです。
BlueJellyは xxx.html
ファイルのみを変更するだけで、様々なWebBluetoothを実現できます。bluejelly.js
と style.css
についてはそのままでOKです。
では、敢えてHTMLを新規作成して確認してみましょう。
以下の中身をコピーしてhtmlファイルを作成し実行してください。ファイル名は何でも良いです。
ダウンロードしたフォルダの中に入っていたbluejelly.js
とstyle.css
が同じディレクトリにある必要があります。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="BlueJelly">
<meta name="viewport" content="width=640, maximum-scale=1.0, user-scalable=yes">
<title>BlueJelly</title>
<link href="https://fonts.googleapis.com/css?family=Lato:100,300,400,700,900" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="style.css">
<script type="text/javascript" src="bluejelly.js"></script>
</head>
<body>
<h1></h1>
<div class="container">
<div class="title margin">
<p id="title">BlueJelly Sample</p>
<p id="subtitle">Hello, BLE</p>
</div>
<div class="contents margin">
<button id="scan" class="button">Scan</button>
<hr>
<div id="device_name"> </div>
</div>
<div class="footer margin">
For more information, see <a href="https://jellyware.jp/kurage" target="_blank">jellyware.jp</a> and <a href="https://github.com/electricbaka/bluejelly" target="_blank">GitHub</a> !
</div>
</div>
<script>
//--------------------------------------------------
//Global変数
//--------------------------------------------------
//BlueJellyのインスタンス生成
const ble = new BlueJelly();
//--------------------------------------------------
//ロード時の処理
//--------------------------------------------------
window.onload = function () {
//UUIDの設定
ble.setUUID("UUID1", "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000");
}
//--------------------------------------------------
//Scan後の処理
//--------------------------------------------------
ble.onScan = function (deviceName) {
document.getElementById('device_name').innerHTML = deviceName;
}
//-------------------------------------------------
//ボタンが押された時のイベント登録
//--------------------------------------------------
document.getElementById('scan').addEventListener('click', function() {
ble.scan('UUID1');
});
</script>
</body>
</html>
これまでと同じ結果になることを確かめて下さい。
ざっくり解説します。
new BlueJelly()
でインスタンスを生成します。setUUID()
でUUIDを設定します。今回はScanするだけなのでダミーのUUIDです。onSCan()
ではScanされた後の処理を書いておきます。今回はDevice名の表示を行います。scan()
処理を割り当てます。ユーザーがボタンを押したときに実行されます。詳細はこちらを参照して下さい。
DOM(Document Object Model)やイベント処理をはじめて学ぶ人や、jQueryなどのライブラリを使い慣れている人向けの補足です。
document.getElementById(id).innerHTML = 値
で、HTMLで指定したidに対する要素の値を変更することができます。
以下のコードを実行すると元々HTMLに書かれている100
が200
に変更された状態で表示されます。実際にhtmlファイルを新規作成して確かめてみて下さい。
<!doctype html>
<html>
<body>
<div id="data_text">100</div>
<script>
document.getElementById('data_text').innerHTML = 200;
</script>
</body>
</html>
document.getElementById(id).addEventListener('click', function() { 処理 })
で、HTMLで指定したidに対するボタンがクリックされたときに「処理」を実行します。
以下のコードを実行してScanボタンをクリックするとアラートでHello
と表示されます。実際にhtmlファイルを新規作成して確かめてみて下さい。
<!doctype html>
<html>
<body>
<button id="scan" class="button">Scan</button>
<script>
document.getElementById('scan').addEventListener('click', function() {
alert('Hello');
});
</script>
</body>
</html>
Chrome上で 右クリック
して 検証
を選択すると「chromeデベロッパーツール」が起動します。以下のショートカットキーでもOKです。
F12
command
+ option
+ I
Console
タブを選択することで、bluejellyのログや console.log
で記述したログなどが表示されて便利ですので、必要に応じてご活用ください。
Scanはペリフェラルを探して見つけるまでの流れでしたが、ここではその先の接続まで行います。
以下の中身をコピーしてhtmlファイルを作成し実行してください。ファイル名は何でも良いです。bluejelly.js
とstyle.css
が同じディレクトリにある必要があります。
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="BlueJelly">
<meta name="viewport" content="width=640, maximum-scale=1.0, user-scalable=yes">
<title>BlueJelly</title>
<link href="https://fonts.googleapis.com/css?family=Lato:100,300,400,700,900" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="style.css">
<script type="text/javascript" src="bluejelly.js"></script>
</head>
<body>
<div class="container">
<div class="title margin">
<p id="title">BlueJelly Sample</p>
<p id="subtitle">ConnectとUUID</p>
</div>
<div class="contents margin">
<button id="scan" class="button">Scan</button>
<button id="connect" class="button">Connect</button>
<hr>
<div id="device_name"> </div>
<div id="uuid_name"> </div>
<div id="status"> </div>
</div>
<div class="footer margin">
For more information, see <a href="http://jellyware.jp/kurage" target="_blank">jellyware.jp</a> and <a href="https://github.com/electricbaka/bluejelly" target="_blank">GitHub</a> !
</div>
</div>
<script>
//--------------------------------------------------
//Global変数
//--------------------------------------------------
//BlueJellyのインスタンス生成
const ble = new BlueJelly();
//--------------------------------------------------
//ロード時の処理
//--------------------------------------------------
window.onload = function () {
//UUIDの設定
ble.setUUID("UUID1", BlueJelly.MICROBIT_UART_SERVICE, BlueJelly.MICROBIT_TX_CHARACTERISTIC);
}
//--------------------------------------------------
//Scan後の処理
//--------------------------------------------------
ble.onScan = function (deviceName) {
document.getElementById('device_name').innerHTML = deviceName;
document.getElementById('status').innerHTML = "found device!";
}
//--------------------------------------------------
//ConnectGATT後の処理
//--------------------------------------------------
ble.onConnectGATT = function (uuid) {
console.log('> connected GATT!');
document.getElementById('uuid_name').innerHTML = uuid;
document.getElementById('status').innerHTML = "connected GATT!";
}
//-------------------------------------------------
//ボタンが押された時のイベント登録
//--------------------------------------------------
document.getElementById('scan').addEventListener('click', function() {
ble.scan('UUID1');
});
document.getElementById('connect').addEventListener('click', function(){
ble.connectGATT('UUID1');
});
</script>
</body>
</html>
scan
ボタンを押すと前回と同じ状態になります。さらにconnect
ボタンを押すことで、接続されてmicro:bitのLEDが「ハートマーク」から「うれしい顔」に変化すれば成功です!
さらにブラウザを更新してしばらくすると、「かなしい顔」に変換します。これはBluetoothが切断されたことを意味します。
ざっくり解説します。
scanのフローは省略しています。
new BlueJelly()
でインスタンスを生成します。setUUID()
でUUIDを設定します。今回はUART SERVICE
の中のTX CHARACTERISTIC
を指定しています。onConnectGATT()
ではConnectされた後の処理を書いています。今回はUUIDとSTAUS表示を行いました。connectGATT()
を割り当てておくことで、ユーザーがボタンを押したときに実行されます。詳細はこちらを参照して下さい。
ここまではBLE接続するだけでしたが、ペリフェラルからデータを送信します。
micro:bitには様々なサービスがありますが、汎用的に使えて扱いやすい「UARTサービス」を活用します。
micro:bitのMakeCodeに戻ります。
前回のコードに以下を追加してください。
入力 > ボタンAが押されたとき Bluetooth > その他 > Bluetooth UART 文字列を書き出す > "hello" 入力 > ボタンAが押されたとき > ▼ B Bluetooth > その他 > Bluetooth UART 数値を文字で書き出す > 1234
コード全体としてはこのような形になります。
これをmicro:bitの実機に書き込んでください。
以下の中身をコピーしてhtmlファイルを作成し実行してください。ファイル名は何でも良いです。bluejelly.js
とstyle.css
が同じディレクトリにある必要があります。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="BlueJelly">
<meta name="viewport" content="width=640, maximum-scale=1.0, user-scalable=yes">
<title>BlueJelly</title>
<link href="https://fonts.googleapis.com/css?family=Lato:100,300,400,700,900" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="style.css">
<script type="text/javascript" src="bluejelly.js"></script>
</head>
<body>
<div class="container">
<div class="title margin">
<p id="title">BlueJelly Sample</p>
<p id="subtitle">Notifyでデータ読み込み</p>
</div>
<div class="contents margin">
<button id="startNotifications" class="button">Start Notify</button>
<button id="stopNotifications" class="button">Stop Notify</button>
<hr>
<div id="device_name"> </div>
<div id="uuid_name"> </div>
<div id="data_text"> </div>
<div id="status"> </div>
</div>
<div class="footer margin">
For more information, see <a href="http://jellyware.jp/kurage" target="_blank">jellyware.jp</a> and <a href="https://github.com/electricbaka/bluejelly" target="_blank">GitHub</a> !
</div>
</div>
<script>
//--------------------------------------------------
//Global変数
//--------------------------------------------------
//BlueJellyのインスタンス生成
const ble = new BlueJelly();
//--------------------------------------------------
//ロード時の処理
//--------------------------------------------------
window.onload = function () {
//UUIDの設定
ble.setUUID("UUID1", BlueJelly.MICROBIT_UART_SERVICE, BlueJelly.MICROBIT_TX_CHARACTERISTIC);
}
//--------------------------------------------------
//Scan後の処理
//--------------------------------------------------
ble.onScan = function (deviceName) {
document.getElementById('device_name').innerHTML = deviceName;
document.getElementById('status').innerHTML = "found device!";
}
//--------------------------------------------------
//ConnectGATT後の処理
//--------------------------------------------------
ble.onConnectGATT = function (uuid) {
console.log('> connected GATT!');
document.getElementById('uuid_name').innerHTML = uuid;
document.getElementById('status').innerHTML = "connected GATT!";
}
//--------------------------------------------------
//Read後の処理:得られたデータの表示など行う
//--------------------------------------------------
ble.onRead = function (data, uuid){
//フォーマットに従って値を取得
let value = "";
for(let i = 0; i < data.byteLength; i++){
let string_temp = String.fromCharCode(data.getInt8(i));
value = value + string_temp;
}
//コンソールに値を表示
console.log(value);
//HTMLに値を表示
document.getElementById('data_text').innerHTML = value;
document.getElementById('uuid_name').innerHTML = uuid;
document.getElementById('status').innerHTML = "read data"
}
//--------------------------------------------------
//Start Notify後の処理
//--------------------------------------------------
ble.onStartNotify = function(uuid){
console.log('> Start Notify!');
document.getElementById('uuid_name').innerHTML = uuid;
document.getElementById('status').innerHTML = "started Notify";
}
//--------------------------------------------------
//Stop Notify後の処理
//--------------------------------------------------
ble.onStopNotify = function(uuid){
console.log('> Stop Notify!');
document.getElementById('uuid_name').innerHTML = uuid;
document.getElementById('status').innerHTML = "stopped Notify";
}
//-------------------------------------------------
//ボタンが押された時のイベント登録
//--------------------------------------------------
document.getElementById('startNotifications').addEventListener('click', function() {
ble.startNotify('UUID1');
});
document.getElementById('stopNotifications').addEventListener('click', function() {
ble.stopNotify('UUID1');
});
</script>
</body>
</html>
Start Notify
ボタンを押して接続後に、micro:bitのAボタンでhello
、Bボタンで1234
が表示されれば成功です! Stop Notify
ボタンを押しても接続は保持したままですが、micro:bitのAボタンやBボタン押しで反応がなくなります。
まずはフローをざっくり解説します。
scanとconnectのフローは省略しています。
new BlueJelly()
でインスタンスを生成します。setUUID()
でUUIDを設定します。今回はUART SERVICE
の中のTX CHARACTERISTIC
を指定しています。onRead()
ではデータを読み込んだときの処理を書いています。今回はデータ値、UUID、STATUS表示です。onStartNotify()
ではNotify開始された後の処理を書いています。今回はUUIDとSTAUS表示です。onStartNotify()
ではNotify停止された後の処理を書いています。今回はUUIDとSTAUS表示です。BLEにてペリフェラルからセントラルへデータを送信する場合、セントラルで受け取るにはRead
もしくはNotify
のどちらかになります。今回使用しているmicro:bitの「UARTサービス」はRead
では取得できないため、Notify
を使う必要があります。
BlueJellyのNotify
は、scanやconnectが実行されていない場合は自動的にscanとconnectを行います。なので、今回はscan()
とconnectGATT()
を省略しています。
その他、コードの流れについて詳しい内容はこちらを参照して下さい。
加速度センサ値を利用して、以下のようにmicro:bitの傾きに応じてHTML上の数値がリアルタイムに変化するペリフェラル側のコードを作って下さい。
また、省電力のためにmicro:bit側のLEDの明るさを暗くしてください。
セントラル側
ペリフェラル側
解答を見る前に自分で作って悩むことがプログラミング向上に繋がります。
ここでは、一旦BLEを離れて、セントラル側で音声ファイルを再生するプログラミングを行います。
micro:bit内蔵のスピーカーで音を出すこともできますが、いわゆる「ビープ音」というもので、効果音としてはちょっとリアルさに欠けます。今回はPCやスマホ側からwav/mp3などのリアルな音声を出力します。
今回も一旦BLEを離れて、ペリフェラル側だけで完結するプログラミングです。
micro:bitの加速度センサを使ったジェスチャーについて解説します
これまで学んだ内容を組合せて、動画のようにパンチすると音が鳴るアプリを完成させてください。
「ものものテックQA掲示板」と「コンテンツの変更履歴」です。