mruby on EV3RT+TECSにBTシリアル通信APIを追加した話＆マルチタスクについて

本記事はETロボコン＆EV3 Advent Calendar 2018 18日目の記事です．

- mruby on EV3RT+TECSに，mrubyから呼び出すことができるBTシリアル通信のAPIを追加する方法
- ユーザタスクを複数動かす方法

以上の2点について説明をしていきます．
# 背景
私達のチームはETロボコン2018関西地区大会のプライマリークラスに出場しました．使用したプラットフォームはmruby on EV3RT+TECSです．mruby on EV3RT+TECSでは各種センサ，モータおよび倒立走行のためのバランサAPIなど基本的なものは提供されており，mrubyアプリで利用することができました．しかし，Bluetoothでシリアル通信を行うAPIは提供されていないようでした．BTによる情報の送受信，特に受信はリモートスタートを行う上で欠かせない要素です．そこで，バランサAPIの呼び出し方を参考に，EV3RT上でC向けに提供されているBTシリアルのAPIをmrubyアプリから呼び出せるようにしました．
# BTSerialAPI-for-mruby-on-EV3RT-TECS
mruby on EV3RT+TECSは，mruby TECS bridge を利用することでC言語で書かれたコードをmrubyから呼び出すことができます．EV3RT上ではC言語で書かれたBTシリアルAPIが提供されているため，ブリッジに必要な記述を追加することでmruby側から利用することが可能となります．
ブリッジに必要な記述の追加パッチおよびファイルをまとめてGitHubに置いてあります．対象とするプラットフォームのバージョンはmruby-on-ev3rt+tecs_package-beta2.1.0です．
## 導入方法

1. mruby-on-ev3rt+tecs_package-beta2.1.0をTOPPERSの配布場所からダウンロードし．任意の場所に解凍します．
2. こちら([GitHub](https://github.com/tlk-emb/BTserialAPI-for-mruby-on-ev3rt-tecs))からファイルをダウンロードし，`../mruby-on-ev3rt+tecs_package-beta2.1.0/` に配置します．
3. 同じディレクトリで以下のpatchコマンドを実行します．  
` $ patch -p1 < diff.patch`
4. 以上でパッチは完了です．以後，通常のmruby on ev3rt+tecsの導入手順に従ってください．  

## 使用方法
BTserialクラスのメソッド`send_byte`及び`read_byte`が使用可能になります．

- `BTserial.send_byte(str)`
- `integer = BTserial.read_byte()`

それぞれ1バイト（1文字）の送信及び受信を行います． `send_byte(str)`の引数は文字列です．先頭の1文字のみが送信されます． `read_byte`は受信した1バイトをint型の値として返します．この値はASCIIコードです．
追加のモジュールの読み込み等は不要です．ワークスペース内のファイルに追記が必要ですが，最初から用意されている`Bluetooth`，`sd`，`sample1`についてはパッチで処理済みです．新しいワークスペースを作りたいときはこれら3つからコピーして作るのが良いかと思います．


## 注意
正直に言いますと，TECSコンポーネントやmruby TECS bridge，またシリアル通信の作法などについては勉強不足であまり良くわかっておりません．バランサAPIの呼び出し方を参考に，動いたら勝ちという気持ちで書いて動いたものがこちらとなります．そのため，もしかするとあまりよろしくないことをしている可能性もあるので，お気づきの方はぜひご教授ください．

通信確立後1回目のアプリケーション実行では，最初の数バイトは意図していない文字列を受信してしまいます．おそらく何かしらのゴミがたまっていて，はじめにクリアしてやる必要があるのだろうかと思うのですが，現状対応できていません．

`read_byte`は何かしらを受信するまで後の処理をブロックします．そのため，実際のロボットアプリで使用する場合は通信を別タスクにする必要があります．次のセクションでmruby on EV3RT+TECSで複数のユーザタスクを動かす方法について簡単にメモを残しておこうと思います．

# マルチタスクについて
mruby on EV3RT+TECS にはマルチタスクのサンプルワークスペース`sample1`が用意されています．しかし，ドキュメントでの説明がほとんどなく，はじめは理解するのに時間を要すると思います．また，競技に取り組む上では通信の別タスク化やサイクリックタスクの利用が必須となってくると思います．そこで，利用可能なタスクの種類と記述が必要な箇所を簡単にまとめておこうと思います．

まず，mrubyのアプリはバイトコードに変換されてVMの上で実行されます．複数のコードを実行するためには，あらかじめVMを複数用意する必要があります．使用するVMについては`tEV3Sample.cdl`および`tEV3Sample_bigtire.cdl`に記述されています．ここでは，以下の5つのVMが記述されています．

- Main VM: メインタスクを実行するVM
- Sub VM 1: ラウンドロビンで実行されるサブタスク１を実行するVM
- Sub VM 2: ラウンドロビンで実行されるサブタスク２を実行するVM
- Wakeup VM: 周期起床タスクを実行するVM
- Cyclic VM: 周期起動タスクを実行するVM

次に，各VMがどのコードを実行するかはMakefile中の101行目以降に記述されています．

```
APP_RB = main.rb

APP_PARAM_RB = $(MRUBY_DIR)/$(PARAM_NAME)
APP_PARAM_SET_RB = $(MRUBY_DIR)/$(PARAM_SET_NAME)
APP_RB_Sub1 = $(MRUBY_DIR)/button_sample.rb
APP_RB_Sub2 = $(MRUBY_DIR)/led_sample.rb
APP_RB_Wakeup = battery_sample_wup.rb
APP_RB_Cyclic = count_disp.rb
```

最後に，タスク間のデータのやり取りは`sharedmemory_def.rb`に記述された変数を介して行われます．この変数への書き込みの仕方は，`count_disp.rb`や`battery_sample_wup.rb`を参照してください．また，各タスクの実行開始方法については`main.rb`を参照してください．

実際に複数タスクを動かしたいときは，必要なVMについての記述を残して残りはコメントアウトしておくのが良いと思います．
私達が競技で使用したプログラムでは，メインタスクとウェイクアップタスクを利用しました．メインタスクはウェイクアップタスクの開始およびBTシリアル通信を行い，ウェイクアップタスクは4ミリ秒周期でライントレースや各種ロボットの制御を行いました．

# 最後に
開発中，最も悩んだのがこのBTシリアル通信とマルチタスクだったりします．mruby on EV3RT+TECSを使用したチームはほとんどいなかったそうですが，今後，使用するチームがいた時に何かしら助けになればと思います．