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開発を始める前に知っておきたい導入知識を簡単に学びます。
色々な用語が出てきますが、暗記する必要はなく、軽く聞き流す程度の理解で問題ありません。

WebBluetoothとは

通常BLE通信を扱う場合はスマホアプリ開発が必要ですが、WebBluetoothを使うことでWeb上でBLE通信が可能です。
スマホアプリ開発を完全に置き換えるものではありませんが、利用するシーンによっては非常に便利です。

メリット:学習コストが低い

デメリット:制限事項がある

セントラルとペリフェラル

セントラルとペリフェラルで通信します。
今回のケースだと、セントラルがPCでペリフェラルがmicro:bitです。

通信手法は主に3つ

ServiceとCharacteristic

データ構造が階層になっています。

例えばBLEデバイスの加速度センサのデータを取得したい場合は、「加速度センサService」の「加速度センサデータCharacteristic」にアクセスする必要があります。

UUID

UUIDとは16進数の数値で決められているユニークなIDです。
ServiceやCharacteristicの名前は「加速度センサ」のような文字列ではなくUUIDで表現します。

BlueJelly というラッパーライブラリを使えば文字列で定義されているため分かりやすいです。
その他にも様々なメソッドがラップされていて扱いやすいため、今回は BlueJelly を活用してWebBluetoothのプログラミングを行います。

参考

詳細を知りたい人はこちらをどうぞ。

まずはペリフェラル側でBLE設定とコード作成を行います。
ペリフェラルとは、今回はmicro:bitのことです。

ペアリング無しに設定

MakeCodeにアクセスして新しいプロジェクトボタンを押してを新しいプロジェクトを作成します。
プロジェクト名は任意でOKです。

歯車アイコンをクリックし、プロジェクトの設定を選択します。

デフォルトでは2番目が有効になっていますが、1番目のNo Pairing Required: Anyone can connect via Bluetooth.を有効にします。

無線ブロックをBLEブロックに変更

歯車アイコンをクリックし、拡張機能を選択します。

検索ボックスにbluetootと入力すると出てくる bluetooth を選択します。

一部の拡張機能を削除してbluetoothを追加するを選択します。

そうすると、これまで無線と書かれていたブロックがBluetoothに変更され、BLEに関するブロックが扱えるようになります。

Bluetoothのブロックが見えるようになればOK。

プログラミング

以下のコードを作成して下さい。先ほど設定して出現させたBluetoothブロックを使います。

追加ブロックは以下の通りです。

基本 > アイコンを表示 ハートマーク
Bluetooth > その他 > Bluetooth UARTサービス

Bluetooth > Bluetooth 接続されたとき
基本 > アイコンを表示 > ▼ うれしい顔

Bluetooth > Bluetooth 接続が切断されたとき
基本 > アイコンを表示 > ▼ かなしい顔

micro:bit本体にこのコードを書き込んで下さい この時点では「ハート」が表示されるだけです

次はセントラル側です。 プログラミングの前に、まずは動作確認を行います。

セントラル側の環境確認

セントラルとは、今回の場合はPCやスマホ、タブレットを指します。
対応OSは以下の通りで、ブラウザは必ずChromeを使用してください。

詳細はこちらを参照して下さい

Web上でScan動作確認

先程のコードを書き込んだmicro:bitの電源をONにします。

以下のページにアクセスして、Scanボタンを押してください。
Scanの動作サンプル

このようにBBC micro:bit[xxxxx]と表示されるので、選択して「ペア設定」ボタンを押します。
xxxxxはIDで、micro:bit毎に異なります

以下のような画面になれば成功です。

上手くいかない場合は、以下を1つずつ試してみて下さい。

先程はこちらで予めWebに用意したセントラル側のコードを実行しましたが、今回は皆さんのPCローカル上にセントラル側のコードを用意し実行します。

ローカル上でScan動作確認

以下からBlueJellyをダウンロードしてください。
BlueJellyのGitHubリポジトリ

CodeからDownload ZIPを選択します。
zipファイルを展開後、srcフォルダに入っているscan.htmlを実行して、前回と同じ結果になることを確かめて下さい。

※スマホやタブレットの場合で、ローカルファイルでは上手くいかない方はこちらを試してみて下さい。

ファイルを新規作成してScan動作確認

先程のzipにはたくさんのファイルが入っていますが、ほとんどが個別のサンプル実行用途です。
先程のScan動作に必要なファイルは以下の3つのみです。

BlueJellyは xxx.html ファイルのみを変更するだけで、様々なWebBluetoothを実現できます。
bluejelly.jsstyle.css についてはそのままでOKです。

では、敢えてHTMLを新規作成して確認してみましょう。
以下の中身をコピーしてhtmlファイルを作成し実行してください。ファイル名は何でも良いです。
ダウンロードしたフォルダの中に入っていたbluejelly.jsstyle.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>

これまでと同じ結果になることを確かめて下さい。

解説

ざっくり解説します。

詳細はこちらを参照して下さい。

JavaScriptの補足

DOM(Document Object Model)やイベント処理をはじめて学ぶ人や、jQueryなどのライブラリを使い慣れている人向けの補足です。

document.getElementById(id).innerHTML = 値で、HTMLで指定したidに対する要素の値を変更することができます。
以下のコードを実行すると元々HTMLに書かれている100200に変更された状態で表示されます。実際に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です。

Consoleタブを選択することで、bluejellyのログや console.log で記述したログなどが表示されて便利ですので、必要に応じてご活用ください。

Scanはペリフェラルを探して見つけるまでの流れでしたが、ここではその先の接続まで行います。

Connect動作確認

以下の中身をコピーしてhtmlファイルを作成し実行してください。ファイル名は何でも良いです。
bluejelly.jsstyle.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のフローは省略しています。

詳細はこちらを参照して下さい。

ここまではBLE接続するだけでしたが、ペリフェラルからデータを送信します。
micro:bitには様々なサービスがありますが、汎用的に使えて扱いやすい「UARTサービス」を活用します。

ペリフェラル側コード

micro:bitのMakeCodeに戻ります。
前回のコードに以下を追加してください。

入力 > ボタンAが押されたとき
Bluetooth > その他 > Bluetooth UART 文字列を書き出す > "hello"

入力 > ボタンAが押されたとき > ▼ B
Bluetooth > その他 > Bluetooth UART 数値を文字で書き出す > 1234

コード全体としてはこのような形になります。

これをmicro:bitの実機に書き込んでください。

セントラル側コード

以下の中身をコピーしてhtmlファイルを作成し実行してください。ファイル名は何でも良いです。
bluejelly.jsstyle.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のフローは省略しています。

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の加速度センサを使ったジェスチャーについて解説します

完全版

ARボクシング

これまで学んだ内容を組合せて、動画のようにパンチすると音が鳴るアプリを完成させてください。

完全版

完全版

「ものものテックQA掲示板」と「コンテンツの変更履歴」です。

完全版