ブロックを積みながら(10) micro:bit、複数BLE serviceの共存の限界

Joseph Halfmoon

前回は BBC micro:bitのbluetooth led serviceというものを走らせ、スマートフォンからDroidScriptで点灯制御してみました。今回は、複数のservicesをmicro:bit上で共存させ、その機能をスマホから使ってみたいと思います。しかし今回、micro:bit v2を購入できていないのが、ボディーブローのように効いてきました。

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

ぶっちゃけ、結論を最初に申し上げると、以下のようです。

  • BBC micro:bit 上で複数の Bluetooth serviceを同時に利用することはできる
  • ただし、メモリの制限でエラーになることがある。(micro:bit v1.5)

まず、MakeCodeブロックエディタから呼び出して使える Bluetooth service(UART除く)は以下のようなものがあります。このうち前回は bluetooth led serviceだけを”on start”ブロック内に置いて走らせました。この際、

全部の service を “on start” ブロックに置いてしまえばいいんじゃね

と発想いたしました。ALL BLE services

そのようにいたしたくなるのは、MakeCodeエディタで micro:bit のフラッシュにプログラムを書きこむ度に、例の面倒な「ペアリング」をしなければならないためであります。全部のサービスを起動しておけば、micro:bitのフラッシュを書き換える必要はないのでペアリングは不要。スマホ側の「アプリ」の修正だけで全てのサービスを利用することができる筈、と考えました。

しかし、ズボラな悪だくみは成功しません。全ての service を”on start”に入れ込むとHEXファイルを書きこみ終了するなり

020

なるエラーが出ます(micro:bitのledマトリックス上では、02という数字が流れた後に「泣き顔」みたいなフェイスマークが現れるのでエラーと分かります。)以下に micro:bit のエラー・コードの一覧ページへのリンクを置きます。

micro:bit Error codes

上記のページからエラーコード 020 の説明文を引用させていただきます。

020 There is no free memory on the micro:bit. Your program might be too complex or contain a lot of large variables. Try reducing your code by making functions out of large sections.

メモリ(RAM)が足らないみたいです。手元の micro:bit v1.5は16KBしかなく、以前も MicroPythonの動作の制約になっていたのを思い出しました。在庫0で買えないでいる micro:bit v2が手元にあればな!v2なら確かRAMは128KBある筈。残念。

仕方がないので、テキトーにサービスの数を削って行くことにいたしました。全てのサービスが同じだけメモリを食う分けではないでしょうから、ちゃんと調べるには6個からn個とる組み合わせをシラミツブシに調べるべきでしょうが、手間が膨大。とりあえずスマホアプリが一応できている led service は残し、他にいくつかサービスを加えてみます。

4個のサービスを同時に入れてみました。HEXファイルの書き込み終了後も 020 エラーは出ません。そこで面倒な「ペアリング」をやってから、スマホアプリを起動、コネクト!

コネクトした直後に 020 エラー再発

であります。4個も駄目。3個にしてみました。また駄目でした。ledの隣にね、buttonが2つある。他のサービスよりbuttonの状態を読むのは簡単そうだから led + button でどう?

動きました、万歳

簡単なサービスの追加ですが、一応、2個のサービスが並存しています。念のため、今回使用した「スマホアプリ」のJavaScriptコード(DroidScript)全文を末尾に掲げますが、前回のものの「チョイ直し」です。動作させたスマホ画面の上半分はこんな感じ。

Button入力対応になった「スマホアプリ」の画面

LEDandBUTTONmicro:bitのボタンを押したことがちゃんとスマホに伝わっています。

さてねえ、残りのBLEサービスを何個かづつ「消化」していくか。しかし面倒。それに別件のマクドナルドのBLEテーブルロケータで、ちょっとそっち方面に興味が湧いてきました。micro:bit v1.5の小さいメモリでもBLEビーコン的なものは出来そうなので、まずそっちをやりますか。v2買えたらまたペアリングして使うサービスに戻って来たいですが。いつもながらの行き当たりばったり。

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

ブロックを積みながら(11) micro:bit、EddystoneでBLE Beaconを へ進む

前回のDroidscriptによるスマホアプリをButton対応に拡張したもの
app.LoadPlugin("MicroBit");

var btnArray = [];

function OnStart()
{
    lay = app.CreateLayout( "linear", "Vertical" );
    txtButtonA = app.CreateText("Button A = ...", 1.0, 0.07, "Bottom" );
    lay.AddChild(txtButtonA);
    txtButtonB = app.CreateText("Button B = ...", 1.0, 0.07, "Bottom" );
    lay.AddChild(txtButtonB);
    txt0 = app.CreateText("Send String to LED:", 1.0, 0.1, "Bottom" );
    lay.AddChild(txt0);
    lay0 = app.CreateLayout( "linear", "Horizontal, FillXY" );
    edt = app.CreateTextEdit("", 0.5, 0.07 )
    edt.SetTextSize( 16 );
    lay0.AddChild( edt );
    btnSend = app.CreateButton( "Send", 0.25, 0.07 );
    btnSend.SetOnTouch( btnSend_OnTouch );
    lay0.AddChild( btnSend );
    lay.AddChild(lay0);
    lay.AddChild(ledButtonLayout());
    btnMatrix = app.CreateButton( "Matrix", 1.0, 0.07 );
    btnMatrix.SetOnTouch( btnMatrix_OnTouch );
    lay.AddChild(btnMatrix);
    app.AddLayout( lay );

    microbit = app.CreateMicroBit();
    microbit.SetOnConnect( OnConnect );
    microbit.SetOnButton( OnButton );
    microbit.Scan();
}


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

function ledButtonLayout()
{
    layLB = app.CreateLayout( "linear", "Vertical" );
    for (let yidx = 0; yidx < 5; yidx++) {
        layLB0 = app.CreateLayout( "linear", "Horizontal");
        for (let xidx =0; xidx < 5; xidx++) {
            btnArray[yidx*5 + xidx] = app.CreateButton( "0", 0.15, 0.07 );
            btnArray[yidx*5 + xidx].SetStyle("Black");
            btnArray[yidx*5 + xidx].SetOnTouch( toggleColor );
            layLB0.AddChild(btnArray[yidx*5 + xidx]);
        }
        layLB.AddChild(layLB0);
    }


function toggleColor()
{
    if (this.GetText() == "0") {
        this.SetStyle("#22aa22");
        this.SetText("1");
    } else {
        this.SetStyle("Black");
        this.SetText("0");    
    }
}
    
function btnMatrix_OnTouch()
{
    var workArray = [];
    for (let idx = 0; idx < 24; idx++) {
        if (btnArray[idx].GetText() == "0") {
            workArray[idx] = 0
        } else {
            workArray[idx] = 1
        }
    }
    microbit.SetLEDs(workArray);
}

function OnConnect()
{
    app.ShowPopup("Connected!");
}

function OnButton( name, stat )
{
    var status;
    if (stat == "1") {
        status = "ON";
    } else {
        status = "OFF";
    }
    if (name == "A") {
        txtButtonA.SetText( "Button A = " + status );
    } else {
        txtButtonB.SetText( "Button B = " + status );
    }
}