前回はEddystone使ってmicro:bitをBLEビーコン化してみました。ビーコンのスキャンは「出来合い」のスマホアプリです。しかしビーコン使ってそれらしい「アプリもどき」を作るとなれば、自前のプログラムでEddystoneのパケットを解読できないとなりません。そこでDroidScriptで「アプリもどき」を作成。とりあえずmicro:bitからのパケットを「近くにいる」時だけ取り出したいです。
※「ブロックを積みながら」投稿順 index はこちら
(末尾に今回使用のJavaScriptソースの全文を掲げました。)
BBC micro:bit 側のコード改定
前回はデバッグも考えてPCのUSBポートにぶら下げるつもりのコードになっていましたが、今回は、後でですが、屋外の広いところでどのくらいの距離まで届くもんだか実験したいと思い、以下微修正であります。(JavaScript形態のソースは末尾にあります。)
- 電池動作とする
- USBシリアル出力廃止
- 「生きている」ことが分かるように「Lチカ」する
- namespaceとinstanceの値を分かり易いキリの良い数字にする
スマホ上の「アプリもどき」
例によってAndroidスマホ上でアプリのセルフ開発ができるJavaScript IDEである
を使用させていただきました。作成したスマホの「アプリもどき」はDriodScriptのサンプルプログラムのチョイ変程度のものです。(末尾にJavaScriptソースあり)
- Eddystoneのアドバタイズパケットのみに反応する
- あるRSSIのスレッショルド以上のパケットだけ処理する
- 処理する場合、パケットの内容を人間可読な形でダンプする
結構 BLEのパケットは、私の机付近でも飛び交っているので、1のように「フィルタリング」しないと大量に出力され過ぎます。そして元々の「ビーコン」の目的を考えると、ビーコンが近傍にあるときだけ何らかの処理をする、あるいは、ビーコンまでの距離や位置を推定する、ということだと思うので2のRSSIを加味するのは必須です。そして、アプリにする前段階の開発ツール的なものであるので、抽出可能なデータは全てダンプして表示するとしました。
ここまで書いておいて何ですが、ひとつだけ悲しいお知らせがあります。今回使用している以下のプラグイン、有料です。
DriodScript BluetoothLE Plugin
クラシックBluetooth(BR/EDR)についてはDroidScriptの無償ライブラリに含まれています。また、前回使用の micro:bit用プラグイン(無償)に MakeCodeで生成できる BLEサービスをサポートするためのBLE機能(スキャンとコネクトなど)が含まれています。しかし、Eddystoneなどアドバタイズパケットをハンドルする機能は無償のプラグインには含まれていないようです。私は上記買いました。上記プラグインはEddystoneに対応しているので非常に簡単に使うことができます。
さてその有償プラグイン使って作った「アプリもどき」のコードは末尾にあります。画面は以下のようです。
- 最上部にThresholdとしてRSSIの設定値を表示。これ以下のパケットは「遠く」にあるものと見なして捨てる。
- RSSIの設定値はその下のスライダーで-40dBmから-100dBmまで設定できる。
- 最下部の SCAN トグルボタンを一回押すとスキャン開始、Eddystoneのアドバタイズパケットで上記のRSSI以上のものを中央部にダンプする。もう一回押すとスキャン停止。
BBC micro:bitが送信しているのは、EddystoneのUIDフレームで、
- namespaceID = 0x1000 (4096)
- instanceID = 0xFF (255)
であるので、ちゃんと捕まえられていることが分かります。プログラム的には必要な情報が抽出できているので、これを切っ掛けにアプリ的には「ファンシー」な表示画面などを「その先の世界」を見せられればよろしいか、と。(私的には16進数が得られれば満足ですが。)
ブロックを積みながら(11) micro:bit、EddystoneでBLE Beaconを へ戻る
ブロックを積みながら(13) micro:bit V2、BLEサービス「積んで」みたけれど へ進む
Eddystoneパケットダンプ、スマホアプリもどきJavaScript(DroidScript)
app.LoadPlugin( "BluetoothLE" ); rssiTH = -70; function OnStart() { lay = app.CreateLayout( "Linear", "VCenter,FillXY" ); lbl = app.CreateText( "", 0.8, 0.1 ); lay.AddChild( lbl ); skb = app.CreateSeekBar( 0.8 ); skb.SetRange( 1.0 ); skb.SetValue( 0.5 ); skb.SetOnTouch( skb_OnTouch ); lay.AddChild( skb ); txt = app.CreateText( "", 0.8, 0.6, "Log" ); lay.AddChild( txt ); btn = app.CreateToggle( "Scan", 0.4, 0.1 ); btn.SetOnTouch( btn_OnTouch ); lay.AddChild( btn ); app.AddLayout( lay ); ble = app.CreateBluetoothLE(); ble.SetOnDevice( OnDevice ); setTH() } function setTH( ) { lbl.SetText("Threshold : " + rssiTH.toFixed(0) + " dBm"); } function skb_OnTouch( value ) { rssiTH = -40 - (60*value); setTH() } function btn_OnTouch( isChecked ) { if( isChecked ) ble.StartScan(); else ble.StopScan(); } function OnDevice( name, address, bonded, rssi, data ) { if (data.type.startsWith("Eddy") && (rssi > rssiTH)) { txt.Log( "--------------------------------------" ); txt.Log( data.type + ": " + rssi +" dBm" ); txt.Log( "Bonded: " + bonded ); Object.keys(data).forEach(function (key) { txt.Log( key + "=" + data[key] ); }); } }
Eddystoneパケット送信用JavaScript(BBC micro:bit MakeCode)
bluetooth.advertiseUid( 4096, 255, 7, false ) basic.forever(function () { led.toggle(0, 0) basic.pause(1000) })