プログラムを無線通信版とシリアル有線通信版に使い分ける

2023.1.5 2022.12.23 Coskx Lab

通信機能が働いていないことがあります
 → 
原因と対処方法の説明はこちら

この文書内では,対処されたプログラム例を載せています。

1 はじめに

PCのシリアルターミナルとmicro:bit間で通信する方法は2つあります。
(PCの代わりにスマートフォンが使われる事もありますが,読み替えてください。)

(1) PC(あるいはスマートフォン)のシリアルターミナルとメインmicro:bit間を有線でつないでシリアル有線通信
         詳細はこちら 



(2) PC(あるいはスマートフォン)のシリアルターミナルと中継micro:bit間をシリアル有線通信し,中継micro:bitとメインmicro:bit間を無線通信
         詳細はこちら 



プログラム開発・調整の場面で,この2つの通信方法のうち,片方だけの通信を行うプログラムを作成すると思いますが,即座にもう片方の通信方法を使うように書き換えることが求められることがあります。
「無線で...を送信」と「シリアル有線通信 ...を書き出す」
「無線で受信したとき」と「シリアル有線通信 ...受信したとき」
を差し替えれば良いのですが,「送信」ブロックがプログラム全体にちりばめられていると,修正が困難になります。
プログラムを一工夫し,特定個所の書き換えで確実に書き換える例を示したいと思います。

2 使用環境

3 考え方

無線通信版とシリアル有線通信版のプログラムは,通信部分のみを書き出すとそれぞれ次のようになります。 一番の問題は「送信」ブロックがプログラム全体にちりばめられていることです。



ここで,送信関数を導入し,プログラム全体にちりばめられている「送信」ブロックを送信関数呼び出しにします。
無線送信あるいはシリアル送信をここで振り分けるようにします。
同様に受信時の作業も関数化します。
あとは,不要な通信方法を削除すれば,修正忘れ無しに通信方法を変更できます。
元に戻す時も,簡単にできるでしょう。



4 プログラム例

シリアルターミナルアプリから,予め決めておいたコマンド(文字列)をmicro:bitに送って,それに対応した動作を行わせます。
次のような動作をすることを目指しますが,無線でも有線でも同じ作業です。

ターミナルからメインmicro:bitに加速度センサ値と明るさセンサ値を要求し,測定値を送信してもらいます。
具体的には。ターミナルから,「ACC 0 10 4」を与えると,加速度センサのx軸計測値を10回平均で求め送信するを4回行います。(「ACC 1 20 5」を与えると,加速度センサのy軸計測値を20回平均で求め送信するを5回行います。「ACC 2 40 3」を与えると,加速度センサのy軸計測値を40回平均で求め送信するを3回行います。)
ターミナルから「LLevel 10 3」を与えると明るさセンサの計測値を10回平均で求め送信するを3回行います。

メインmicro:bitのプログラムにおいて無線通信(中継micro:bit経由)とシリアル有線通信を簡単に切り替えるからくりは,関数の利用です。

プログラム全体に散在する送信ブロックでは,引数に送信したい文字列を伴わせて関数sendStringを呼び出すようにします。 関数sendString中で,送信すべき文字列を無線通信またはシリアル有線通信に振り分けます。

逆に無線または有線で文字列を受信したら,関数parseCommandに受信文字列を送って,必要な作業を行ってもらいます。

4.1 無線通信



次のプログラムは無線通信を行っています。
シリアル有線通信の部分はブロックから抜き取って無効になっています。

関数parseCommandは,無線で受信した文字列を引数として受け取って,文字列を解析して必要な作業を行います。
関数measureLightLevelは,測定のための平均化回数と測定回数を引数として受け取って,明るさレベルを測定し,測定結果を文字列として送信します。
関数measureACCは,軸番号(x,y,zは0,1,2と解釈する),測定のための平均化回数と測定回数を引数として受け取って,加速度を測定し,測定結果を文字列として送信します。

    無線通信テスト用プログラム(メインmicro:bit)




通信に必要となる中継micro:bitのプログラムの説明は次を参照してください。     無線通信の動作テスト 

4.2 シリアル有線通信


次のプログラムは上記のプログラムでシリアル有線通信を行なうように変更しています。
無線通信の部分はブロックから抜き取って無効になっています。

「無線で受信したとき」ブロックが無効になっていません。
この状態であっても,無線動作の仕様によって,無線送信作業が一度も行われない場合は,無線モジュールが起動されていないため,「無線で受信したとき」ブロックも起動しません。
無線動作の仕様変更に備えて,「無線で受信したとき」ブロックそのものを消去しておく方が良いかもしれません。
無線動作の仕様はこちら

関数parseCommand,関数measureLightLevel,関数measureACCは,変更されていません。

    シリアル有線通信テスト用プログラム(メインmicro:bit)




4.3 テキスト型式プログラム

上記のプログラムをコピペする場合は,次のソースコードをJavaScriptソースとして貼り付ければ完了です。
しかし,このコードは無線,シリアル有線の両方が有効になっています。片側を除去して無効にして,テストしてください。

function parseCommand (commandstring: string) {
    sendString(commandstring)
    parameters = commandstring.split(" ")
    command = parameters[0]
    paramN1 = parseFloat(parameters[1])
    paramN2 = parseFloat(parameters[2])
    paramN3 = parseFloat(parameters[3])
    if (command.includes("ACC")) {
        measureACC(paramN1, paramN2, paramN3)
    } else if (command.includes("LLevel")) {
        measureLightLevel(paramN1, paramN2)
    }
}
function sendString (stringtosend: string) {
    serial.writeLine(stringtosend)
    radio.sendString(stringtosend)
}
serial.onDataReceived(serial.delimiters(Delimiters.NewLine), function () {
    parseCommand(serial.readUntil(serial.delimiters(Delimiters.NewLine)))
})
function measureLightLevel (Nmeans: number, Nrepeats: number) {
    for (let index = 0; index < Nrepeats; index++) {
        value = 0
        for (let index = 0; index < Nmeans; index++) {
            value = value + input.lightLevel()
            basic.pause(20)
        }
        value = value / Nmeans
        sendString("LLevel = " + ("" + value))
    }
}
radio.onReceivedString(function (receivedString) {
    parseCommand(receivedString)
})
function measureACC (axis: number, Nmeans2: number, Nrepeats2: number) {
    for (let index = 0; index < Nrepeats2; index++) {
        value = 0
        for (let index = 0; index < Nmeans2; index++) {
            if (axis == 0) {
                value = value + input.acceleration(Dimension.X)
            } else if (axis == 1) {
                value = value + input.acceleration(Dimension.Y)
            } else {
                value = value + input.acceleration(Dimension.Z)
            }
            basic.pause(20)
        }
        value = value / Nmeans2
        sendString("ACC (" + ("" + axis) + ") = " + ("" + value))
    }
}
let value = 0
let paramN3 = 0
let paramN2 = 0
let paramN1 = 0
let command = ""
let parameters: string[] = []
radio.setGroup(1)
radio.sendString("")

4.4 実行の様子

実行時のシリアルターミナル画面です。
$$で始まる行はシリアルターミナルから送信した文字列のセルフエコーです。

$$ LLevel 10 3
LLevel 10 3
LLevel = 233.5
LLevel = 233.6
LLevel = 233.2
$$ LLevel 10 4
LLevel 10 4
LLevel = 233.4
LLevel = 233.7
LLevel = 233.4
LLevel = 233.5
$$ ACC 0 10 3
ACC 0 10 3
ACC(0) = -51.6
ACC(0) = -48.8
ACC(0) = -51.2
$$ ACC 1 10 4
ACC 1 10 4
ACC(1) = 70.8 
ACC(1) = 72.4 
ACC(1) = 70.8 
ACC(1) = 69.2 
$$ ACC 2 10 2
ACC 2 10 2
ACC(2) = -1017.2
ACC(2) = -1018

5 まとめ

プログラムを無線通信版にしたり,シリアル有線通信版にしたりする作業は,工夫しないとプログラム全体の書換が必要になってしまいます。
ここでは,関数を利用して,限られた部分の書換で,通信手段の変更ができることを示しました。
なお,ここではプログラムの書換を行っていますが,切替用の変数を使って,その変数の値によって作業を切り替えるようにする事もできるでしょう。