後輩たちのためのEV3rt講座⑩(Bluetooth通信実践)

かれこれこのシリーズも10回目ですね(正確には12本記事がありますが…)

#前回のおさらい#
前回の記事、Bluetooth環境開発編で、PCとEV3を接続することまでは完了しました。
今回は、実際にプログラムを書いてPCとEV3との間でデータをやりとりしてみたいと思います。

#通信には大きく3つある#
PCとEV3の間でデータをやりとりする方法は以下の方法があります。

①HRP2カーネルのシリアルインターフェスドライバを利用する
②ログタスクsyslogを使用する
③ファイルとして扱う

①に関しては私は全く無知です。ただ[EV3rtのホームページ](https://dev.toppers.jp/trac_user/ev3pf/wiki/UserManual#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%9E%E3%83%8B%E3%83%A5%E3%82%A2%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E9%96%8B%E7%99%BA%E8%80%85%E5%90%91%E3%81%91)に載っているのでそれを確認していただけたら良いかと思います。

②はEV3rtのログとして出す方法です。デフォルトではEV3rtのログは本体のコンソールに表示されますが、その設定を変更することでBluetoothで接続されたPCに出すことが出来ます。
詳しくは @yamanekko さんの記事『[EV3RTでログをリアルタイムに見る方法](https://qiita.com/yamanekko/items/ae50d56ebb4cf28f3082)』を御覧ください。
詳しく紹介してくださっています。

ということで、今回私が紹介するのは③の「ファイルとして扱う」方法です。

#通信の基本概念#
C言語にはファイルを扱う関数が標準搭載されていますよね。
`stdio.h`をincludeすることで使うことが出来ます。
これは通常の場合、そのハードのボリュームにあるファイルに対して、操作を行うことが出来ますが、この関数をBluetooth通信に用いる事ができるのです。

この方法、何がいいかと言うと、新たに特殊な関数を用いる必要がないのです。
今まで通り、ファイルに書き出すかのように通信ができるのです。
僕は非常に便利な機能であると思っています。
ということで、実際にどのようにプログラミングするのかを以下記します。

#プログラミング方法#

##設定##
```app.c
#include "ev3api.h"
#include "app.h"
#include <stdio.h>          /* ファイル関数を使うため*/ 

#if defined(BUILD_MODULE)
#include "module_cfg.h"
#else
#include "kernel_cfg.h"
#endif

#define DEBUG

#ifdef DEBUG
#define _debug(x) (x)
#else
#define _debug(x)
#endif

static FILE *bt = NULL;     /* Bluetoothファイルポインタの宣言*/

void main_task(intptr_t unused)
{
```
まずはここまでの説明を。

先程言ったように、`#include <stdio.h> `でファイル操作関数が使えるようにします。

続いて、`static FILE *bt = NULL;`では、ファイルポインタを宣言しています。
今はNULLになっていますが、このあとBluetooth経由仮想シリアルポートを開通させて指定します。

では続きを・・・

```app.c
void main_task(intptr_t unused)
{
    bt = ev3_serial_open_file(EV3_SERIAL_BT);
    assert(bt != NULL);
```

さて、`bt = ev3_serial_open_file(EV3_SERIAL_BT);`の文ですが、これだけはEV3rt専用関数ですね。
ここで先程定義したファイルポインタbtに対して、Bluetooth経由仮想シリアルポートを指定します。
実際にファイルが作成されるわけではありません。あくまでデータのやりとりです。

そしてその下の`assert(bt != NULL);`ですが、これはbtがちゃんと設定されているかをチェックするものです。
失敗しているとここでプログラムが停止します。


ここまで出来たら、もう設定は完了です!!
あとは基本的に好きなように通信ができます。

##通信##
では、いくつか通信の例を出してみます。

###fgetc関数(PC→EV3)###

```app.c
while(1){
    uint8_t c = fgetc(bt);
    if(c=='1'){
        bt_cmd = 1;
    }
    if (bt_cmd == 1){
        break;
    }
}
```
こちらはfgetc関数を使った例です。
fgetcは1文字単位で読み込む関数です。
例では、PC側から`1`を送信したらbreakするというようになっています。

###fgets関数(PC→EV3)###

```app.c
char str[5];
while(1){
    fgets(str,5,bt);
    ev3_lcd_draw_string(str,30,0);
}
```
fgets関数はfgetcと似ていますが、読み込む文字数を指定できます。
例の場合、「strというchar配列に、5-1=4文字分、btから取得する」という感じです。

この5-1=4文字分というのがミソで、配列の最後にナル文字(\0)が入るため、読み込みたい文字数nに対して**n+1**を指定しないと正しく通信できません。

ここで注意点ですが、指定文字数の違う関数は混ぜて使わないほうが良いです。
通信するライン(専門的にはstream)は1本しかないので、「A用のデータがBにいっちゃった」なんてこともありえます。
特にコマンド等に使用する場合は、文字数を一つに決めておきましょう。

###fprintf関数(EV3→PC)###

```app.c
fprintf(bt,"%d\r\n",ev3_color_sensor_get_reflect(0));
```
これは単純明快ですね！
ファイルポインタbtを用いてカラーセンサーの値を送信します。


今回3つの関数を紹介しましたが、他にもファイル操作関数はたくさんあります。
自分の用途にあったものを選択すると良いでしょう。

#PC側の操作#
ここまでずっとEV3側の話をしてきましたが、PC側はどうすればよいのか？
その話を最後にしておきます。

##WindowsのTeraTermは特に問題ない##
Windowsユーザーの方々は基本的にapp送信にTeraTermを使用していることと思います。
TeraTermは特に設定しなくても、あの真っ黒の画面を用いてデータの送信、受信ができます。
送信するときは当然ながらキーボードで入力してください。

##問題はMacユーザー…##
Macは非常に複雑です。
状況を整理すると、Macユーザーは基本的にapp送信にminicomを使用しているでしょう。
しかし、minicomはデータの送信が出来ません。(受信は出来たり出来なかったり、僕もよくわからん…)
ということで対処法をお伝えします。

###screenコマンドを使う###
Linuxデバイス同士で使うことができるコマンドです。
これを使うにあたってEV3の端末名が必要です。
デフォルトでは`/dev/tty.MindstormsEV3-SerialPor`になっています。
(見覚えのある人はなかなか鋭い)
確認する方法は

```
$ ls /dev/tty.*
```

です。Macに保存されているデバイスの一覧が出てきます。

それでは実行していきますが、minicomは起動した状態でおいておきましょう。
ターミナルの違うウィンドウ(もしくはタブ)を開いて、以下のようにします。

```
$ screen /dev/tty.MindstormsEV3-SerialPor
```

実行しても何も出てきませんが、ちゃんと接続されていれば、この状態でデータの送受信が出来ます。

#まとめ#
ということで、今回はBluetoothを介したデータのやりとりについて紹介しました。
あ、ちなみに、
私が年末発表した[EV3rt汎用Bluetoothロガー](https://qiita.com/koushiro/items/dffdd4375d3ce110cb41)は、この技術をもとに開発しました。
なかなかいい感じに出来ているので良かったら見てください〜

次回:[第11回](https://qiita.com/koushiro/items/b7a66f0b3c0d7695b53a)
前回:[第9回](https://qiita.com/koushiro/items/578a1d65721b982c55c6)