今から始める TOPPERS/HRP3

[TOPPERSプロジェクト](https://www.toppers.jp)は LEGO MINDSTORMS EV3 向けにリアルタイムOS環境 [EV3RT](https://dev.toppers.jp/trac_user/ev3pf/wiki/WhatsEV3RT) を提供しています。EV3RT は [TOPPERS/HRP2 カーネル](http://toppers.jp/hrp2-kernel.html) をベースとしていますが、[TOPPERS/HRP3 カーネル](http://toppers.jp/hrp3-kernel.html) をベースにしたソースが既に [GitHub に公開されています](https://github.com/ev3rt-git/ev3rt-hrp3)。

本日のネタは、この HRP3 版をインストールして使ってみることです。現行の(HRP2ベースの) EV3RT 開発環境は構築済みで、git コマンドも使えるようになっていることを前提に話を進めます。

Windows を使っている場合は Cygwin 環境に ruby もインストールしておいてください。configure や cfg が HRP3 では ruby スクリプトになったせいで、ruby コマンドが必須になっています。

# ソースの取得とダイナミックローダのビルド

まずは、ソース取得のために git clone します。

```sh
$ git clone https://github.com/ev3rt-git/ev3rt-hrp3
```

ソースを取得できたら、以下の手順で HRP3 のダイナミックローダをビルドします。

```sh
$ cd ev3rt-hrp3
$ git submodule init
$ git submodule update
$ cd sdk/firmware
$ make img=loader
```

ビルドが完了すると uImage という名前でダイナミックローダができています。これを HRP2 版 EV3RT で作成した SD カード内にある uImage ファイルと入れ替えます。

できた SD カードを EV3 にセットして起動して、以下の画面が現れたら EV3 側のセットアップは完了です。

![IMG_5247.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106817/362aaf04-8edb-902e-75ed-e3367b9fe3e2.jpeg)

# helloev3 アプリのビルド

取得したソースには SDK も含まれているので、HRP2 の場合と同様にアプリをビルドできます。

```sh
$ cd ev3rt-hrp3/sdk/workspace
$ make app=helloev3
```

macOS の場合、実害は無いようですが、ビルド中に以下のメッセージが何度も出てきます。

```
find: illegal option -- t
usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]
       find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]
```

これは、HRP3 のビルド環境が GNU の find コマンドを前提としているため、macOS の find コマンドに無いオプション(ファイル/ディレクトリの種別を指定するオプション)でエラーになっています。ちょっとメッセージがうるさいので、私は [Homebrew](https://brew.sh/index_ja) で findutils を入れて GNU の find を使うようにしました。

ビルドしてできた app ファイルを HRP2 と同様に EV3 に転送して起動すると以下の画面になります。このアプリでセンサーやモータの動作を確認できます。

![IMG_5248.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106817/a90b4169-b8cf-7c00-e020-538be39f08f7.jpeg)

# C++ で書かれたアプリのビルド

GitHub から取得したソースには C++ API が含まれていないので、HRP2版からコピーします。

```sh
$ cp -pr hrp2/sdk/common/library/libcpp-ev3 ev3rt-hrp3/sdk/common/library/
```

これだけで C++ API が使えるようになります。HackEV を使って５秒おきに前進/更新を繰り返す簡単なアプリケーションを書いてみます。

```sh
$ cd ev3rt-hrp3/sdk/workspace
$ mkdir walker
```

以下４つのファイルを作成します。

* walker/app.h:

```cpp
#ifdef __cplusplus
extern "C" {
#endif

#include "ev3api.h"

#define MAIN_PRIORITY    TMIN_APP_TPRI + 1
#define WALKER_PRIORITY    TMIN_APP_TPRI + 2

#ifndef STACK_SIZE
#define STACK_SIZE      4096
#endif /* STACK_SIZE */

#ifndef TOPPERS_MACRO_ONLY

extern void main_task(intptr_t exinf);
extern void walker_task(intptr_t exinf);

#endif /* TOPPERS_MACRO_ONLY */

#ifdef __cplusplus
}
#endif
```

* walker/app.cpp:

```cpp
#include "app.h"
#include "Motor.h"
#include "TouchSensor.h"

using namespace ev3api;

bool isForwarding = true;

Motor* leftWheel;
Motor* rightWheel;
TouchSensor* touchSensor;

void walker_task(intptr_t exinf) {
    if (touchSensor->isPressed()) {  // 左ボタン押下でメインを起こす
        wup_tsk(MAIN_TASK);
    } else {  // 走行
        if(isForwarding) {
            leftWheel->setPWM(25);
            rightWheel->setPWM(25);
            isForwarding = false;
        } else {
            leftWheel->setPWM(-25);
            rightWheel->setPWM(-25);
            isForwarding = true;
        }
    }
    ext_tsk();
}

void main_task(intptr_t unused)
{
    leftWheel = new Motor(PORT_C);
    rightWheel = new Motor(PORT_B);
    touchSensor = new TouchSensor(PORT_1);

    sta_cyc(WALKER_CYC);
    slp_tsk();  // 起きたら、走行をやめる
    stp_cyc(WALKER_CYC);
    leftWheel->stop();
    rightWheel->stop();
    ext_tsk();
}
```

* walker/app.cfg:

```cpp
INCLUDE("app_common.cfg");

#include "app.h"

DOMAIN(TDOM_APP) {
CRE_TSK( MAIN_TASK, { TA_ACT,  0, main_task,   MAIN_PRIORITY,   STACK_SIZE, NULL } );

CRE_CYC( WALKER_CYC, { TA_NULL, { TNFY_ACTTSK, WALKER_TASK }, 2000U * 1000U, 1U * 1000U });
CRE_TSK( WALKER_TASK, { TA_NULL,  0, walker_task, WALKER_PRIORITY, STACK_SIZE, NULL });
}

ATT_MOD("app.o");
```

* walker/Makefile.inc:

```Makefile
mkfile_path := $(dir $(lastword $(MAKEFILE_LIST)))

APPL_COBJS +=

SRCLANG := c++

ifdef CONFIG_EV3RT_APPLICATION

# Include libraries
include $(EV3RT_SDK_LIB_DIR)/libcpp-ev3/Makefile

endif
```

以下でビルドします。

```sh
$ make app=walker
```

ビルドしてできた app ファイルを EV3 に転送して起動すると、以下のように前進と更新を繰り返します。止めるにはタッチセンサを押下します。

![walker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106817/2e6a8f71-b927-d244-211c-6c2c417d472a.gif)

# おわりに

今のところ、HRP2 ベースの EV3 アプリを HRP3 ベースに移植する上で修正が必要だったのは以下２点です。

* タイマーの時間指定が msec から usec になったので、従来の時間指定を 1000 倍する必要あり
* EV3_CRE_CYC は無くなった。ユーザドメインの周期処理がサポートされたので、代わりにこれを使う
* Makefile.inc で APPL_DIR へのフォルダ追加は、HRP3 ではエラーになるので、代わりに  APPL_DIRS を使う