C言語でEV3開発(19)
コマンド通信処理でEchoBackしてみた

2021年2月2日

どもです。
前回は、Bluetoothの接続状態の変化に伴う、状態遷移について書きました。
今回は、BluetoothでPCとEv3を接続してコマンドの送受信をしてみます。

1.コマンド

送受信するコマンドは、一番基本(と、考えている)の「EchoBack」コマンドです。
このコマンドでは、コマンドデータに乗せたデータがそのまま返されます。
コマンドフォーマットは、特に何かの規格に準拠する、というものではなく、完全に独自仕様としています。
具体的には、以下のように設計しています。
コマンドフォーマット

2.通信設計

通信をする場合には、送信側と受信側が存在します。
(当たり前ですね。)
そのため、通信を設計する際には、どちらを送信側にして、どちらを受信側にするかを決める必要があります。
「適宜切り替える」というのが、「もっともよい、あるべき姿」です。
しかし、この方式を実装するとなると、通信の制御がとても難しくなる。
「送信の衝突」を「0」から設計、実装することはできません。
スキルが足りません…。
なので、今回は以下のように固定します。

デバイス 役割
PC 送信
Ev3 受信

PC側からEv3にコマンドを送信し、対応する処理を実施した後に結果/レスポンスを返すようにします。
コミュニケーション図は、下図の通りです。
コマンドシーケンス
送信側は、送信間隔を10msecに設計します。
従って、送信側は、コマンドを送信、レスポンスを受信後、10msec待ってから次のコマンドを送信します。

受信側は、受信したデータを、そのままレスポンスデータに乗せて返します。
コマンドデータを受信してからレスポンスを受信するまでは、特に間隔を設けません。
受信した先から、返信していきます。
ただし、次のコマンドを受信するまでは、少~し間隔を設けています。
また、Ev3では受信したデータをデバッグできません。
そのため、その代わりにコマンドを受信した回数をモニターするようにしました。

3.コマンド処理

実際にコマンドを受信、処理して返すまでの実装は以下の通りです。

void BtTask(intptr_t unused) {
    while (bt_task_running) {
        bt_task_count++;

        port_check_connectoin();
        port_read_data();
        if (0 < bt_rcv_msg_len) {
            cmd_hdl_count++;
            
            cmd_sequence();
            port_write_data();
            port_reset();
        }
        dly_tsk(BT_TASK_INTERVAL);
    }
}

処理の概要を説明すると、port_check_connectoin()でBluetoothの接続を確認し、port_read_data()でデータの読出しを行います。
データを受信していた場合には、cmd_sequence()で内容の確認とレスポンスデータの作成を行います。
cmd_sequence()の実装は、下記の通りです。

void cmd_sequence(void) {
    cmd_latch_buff();
    cmd_reset_snd_buff();
    cmd_handle_main();
}

cmd_latch_buff()、cmd_reset_snd_buff()は、それぞれ関数名通りの処理を実施します。
で、cmd_handle_main()の中で実際にデータの処理を行います。
そのコードが、以下です。

void cmd_handle_main() {
    char cmd_code = 0;

    cmd_code = rcv_msg_buf[0];
    switch (cmd_code) {
    case 0x00: cmd_code00(); break;
    default: cmd_invalid_code(); break;
    }
}
void cmd_code00(void) {
    uint8_t res = 0x00;
    uint8_t cmd_data_len = 0x00;
    uint8_t res_data_len = 0x00;
    uint8_t sub_cmd_code = 0x00;
    uint8_t sub_res_code = 0x00;
    uint8_t *rcv_buf;
    uint8_t *snd_buf;
    int idx = 0;

    rcv_buf = (uint8_t *)(&rcv_msg_buf[3]);
    snd_buf = (uint8_t *)(&snd_msg_buf[4]);
    
    sub_cmd_code = rcv_msg_buf[1];
    cmd_data_len = rcv_msg_buf[2];

    sub_res_code = sub_cmd_code;
    if ((!(0 < cmd_data_len)) || (!(cmd_data_len < 30))) {
        res = 0xFE;
    }
    if (0x00 != sub_cmd_code) {
        res = 0xFD;
        sub_res_code = 0xFF;
    }
    if (0x00 == res) {
        for (idx = 0; idx < (int)cmd_data_len; idx++) {
            *snd_buf = *rcv_buf;
            snd_buf++;
            rcv_buf++;
        }
        res_data_len = cmd_data_len;
    } else {
        res_data_len = 0;
    }
    
    snd_msg_buf[0] = 0x01;
    snd_msg_buf[1] = sub_res_code;
    snd_msg_buf[2] = res;
    snd_msg_buf[3] = res_data_len;
    snd_msg_len = 4 + res_data_len;
}

4.実際に動かしてみた

実際に動かしている様子の動画は、以下の通りです。

映像の後ろの方に映っているのは、PCツールで送信/受信しているデータです。
Ev3では、BLT_TSK_CNT、CMD_HDL_CNTの値が増加しています。
この値は、コマンドの受信/送信タスクと、実際に受信/送信(返信)したコマンドの数です。
PCツールからコマンドを送信、レスポンスを受信するタイミングと、Ev3の画面上のこれらの値が更新されるタイミングが一致しています。
このように、PCツールからEv3に対してコマンドデータを送信することが可能になりました。

5.まとめ

今回のエントリーでは、実際にコマンドを送受信する機能を紹介しました。
今後は、今回紹介した通信機能を用いて、Ev3で組み立てた車を制御していく仕組みを紹介します。

2017/07/15 追記

今エントリーのソースコード一式を公開しました。
ココからダウンロードできます。