ブロックを積みながら(8) micro:bitのBluetooth serviceの使用。

Joseph Halfmoon

昨日は Bluetooth ではない Radio の方で終わっていたので、本日こそ Bluetooth を使ってみたいと思います。MakeCodeでは、いくつかのBluetoothサービスのBlockが用意されています。それを利用してみたいと思います。今回は led serviceというものを使用、micro:bit と スマホをペアリングし、スマホからの指示で micro:bit に Hello World させようという趣向です。

※「ブロックを積みながら」投稿順 index はこちら

(末尾に「文字形態」のソース全文を掲げてあります。)

さて MakeCodeのBluetooth関係のAPIというかブロックというかを調べると、以下の4カテゴリに分類できるように思われます。

  • 「サービス」を起動するためのAPI
  • 「サービス」の中でも UART サービスのためのAPI
  • 「アドバタイズ」関連
  • その他の通信制御

「サービス」とはGATT(汎用アトリビュート・プロファイル)の中でデータをカプセル化するものであります(私はBLE素人なので、素人の言う事に騙されないように。)実際に MakeCodeが用意してくれている API でとりあつかえる具体的な service を列挙する方が分かり易いです。

  1. accelerometer
  2. button
  3. io pin
  4. led
  5. Magnetometer
  6. Temperature
  7. UART

どれも micro:bit が搭載しているハードウエアに対応しており、それらからデータを読み出したり、データを書きこんだりするためのものです。その中で1番目から6番目までは、サービスを起動するだけのAPI(ブロック)であり、どんな動作が起こるのかは通信相手からの通信に全面的に依存します。唯一 UART サービスは汎用的なシリアル通信とみなせるので、送受信のためのメソッドなどが用意されておりMakeCodeでの「プログラム」対象となります。そして、これらの「サービス」は、通信相手とペアリングしていることが必須となります。

ペアリング不要というか、その前段階でもある「アドバタイズ」については、後でまた動かして行きたいと思います。

さて今回使用してみる led Service というのは、ペアリングしている相手からの指示によって micro:bit 上の ledアレイを制御できるサービスです。ぶっちゃけ micro:bit 側は

on startブロックの中で bluetooth led service を起動してやるだけ

で動作可能です。当然ながら通信相手が micro:bit の bluetooth led service を「知っていて」必要な指示を出さねば意味を成しません。そこで今回、ほぼ2年ぶりくらいで取り出したのが以下の スマホ上でアプリをセルフ開発できる環境です。

DroidScript

私のお気に入りの開発環境(と言う割には使っていない。しかしスマホの片隅で生きてはいた)です。JavaScriptでスマホアプリをセルフ開発できるIDEです。素晴らしいのが スマホ上のDroidScriptがWebサーバになり、PC上のブラウザにIDEの画面を表示してくれるので、「セルフ」のくせに「PC上でのクロス」的な操作性の良さで開発できること。私のように、本気でスマホのアプリと向き合いたくない輩にはありがたい環境です。ちょこっと書けば長時間のビルドなどなく、一撃でスマホアプリが作れるっと。さらに micro:bit に関しては、

DroidScriptにはmicro:bitプラグインあり

ということで、micro:bitが持っているサービスを「知っている」のです。英文のAPIドキュメントを眺めながらスマホアプリを書くことなく、JavaScriptのレベルで出来合いのMethodを呼び出してやればちゃんとサービス宛てに通信ができ、micro:bitが動きます。お気楽極楽。ただ1点、とても面倒くさいことがあるのをまったく失念しておりました。なにせ DroidScriptでmicro:bitのBluetoothを触ったのは2年ほど前であります。

micro:bit はflashに書きこむ度にペアリング忘れてしまう!

micro:bit とスマホとのペアリングそのものは、公式のmicro:bitアプリでできます。ただ、この手順がちょっとややこしくて失敗しやすいです。それでも一度ペアリングしておけば、スマホ側はそのペアリング情報をちゃんと記憶しています。micro:bitの方は、電源落としてもペアリング情報は消えませんが、フラッシュへ書き込むとペアリング情報が消えてしまうのでした。つまり MakeCodeであらたなプログラムを書く度に、面倒なペアリングを繰り返さないとならないです。

DroidScriptで相手の micro:bit のサービスを使うためにはペアリング必須です。幸い、UARTサービス以外のサービスを利用するのであれば、micro:bit側でやることはサービスの起動だけなので、micro:bit側のコードの変更は最小化できそうです。そこでなるべく MakeCodeでmicro:bit側の処理を書きなおしたくないです。どちらかと言えばスマホ側のプログラミングがメインにせざるを得ないです。そういうこと考えるとペアリングで通信相手と「握る」のもよし悪しです。RadioやBLEでもアドバタイズであれば相手など確認せず垂れ流しなので、ナンボFlashメモリを書き換えてもメンドイことはないです。でも、ま、今度は当分、ペアリングありということで。

micro:bit側のコード

MakeCodeのブロックエディタの画面をキャプチャしたものを先頭のアイキャッチ画像に貼り付けました。末尾に JavaScript(TypeScript)表現のテキストも貼り付けたのでそちらの方が読みやすいと思います。やっていることは以下のようです。

  • led serviceを起動する
  • デバッグ用に Bluetooth の connect と disconnect の状況をシリアルポートに出力しつづける
  • 接続相手から文字列が送られてきたらLEDマトリックスに表示する
スマホ側のコード(Android 10上のDroidScript)

下にパソコン上のブラウザでDroidScriptを編集している画面をキャプチャしました。このコードも末尾にテキストで貼り付けてあります。こちらでやっていることは以下です。

  • ペアリング相手のmicro:bitを探す(相手が表示されたらタップすると接続される)
  • 文字列を受付、Sendキーがタッチされたら送信する

DScript_ledServiceちとピンボケですが、実際にスマホから micro:bitに送信しているところがこちら。ledServiceMicrobit_SmartPhone

デバッグ用にmicro:bitのUSBシリアルに接続してある仮想端末の様子は以下のようです。Connected状態になると毎秒 ピリオド1個 を表示。Bluetoothの通信範囲外に相手のスマホが去ったりすると Disconnected表示し、毎秒 アンダースコア1個を表示。スマホ側を近づけてDroidScriptのスクリプトを再実行すれば再びConnectedとなると。ledService uart log

ううむ、どちらかというと、BLE通信よりもスマホ上のGUIの構築の方がずっと難物だな。。。端から「サービス」をなめながら DroidScript のGUIを学ぶしかありますまい。

ブロックを積みながら(7) micro:bitの無線、RadioとBLE、整理してみました へ戻る

ブロックを積みながら(9) BLE LED service、スマホアプリ作成 へ進む

micro:bit側の bluetooth led service 利用コード(JavaScript)
bluetooth.onBluetoothConnected(function () {
    connectOK = 1
    serial.writeLine("Connected")
})
bluetooth.onBluetoothDisconnected(function () {
    connectOK = 0
    serial.writeLine("Disconnected")
})
let connectOK = 0
connectOK = 0
bluetooth.startLEDService()
basic.forever(function () {
    if (connectOK == 1) {
        serial.writeString(".")
    } else {
        serial.writeString("_")
    }
    basic.pause(1000)
})
SmartPhone側の DroidScript 利用コード(JavaScript)
app.LoadPlugin("MicroBit");

function OnStart()
{
    lay = app.CreateLayout( "linear", "VCenter,FillXY" );	
    edt = app.CreateTextEdit("", 1.0, 0.5 )
    edt.SetTextSize( 16 );
    lay.AddChild( edt );
    btnSend = app.CreateButton( "Send", 0.23, 0.1 );
    btnSend.SetOnTouch( btnSend_OnTouch );
    lay.AddChild( btnSend );
    app.AddLayout( lay );

    microbit = app.CreateMicroBit();
    microbit.Scan();
}

function btnSend_OnTouch()
{
    var txt = edt.GetText()
    microbit.ScrollText(txt);
}