EV3RTを使ってみる(3.5)

Toppers/EV3RTを使ってプログラムを作成したときの記事です。
この記事を書いた時のEV3RTのバージョンはβ3です。
目次はこちら

ここ最近、時間を見つけてはToppers/EV3RTの動作確認をしていたりします。ただ、私はToppersの中の人ではない 1ので、動作仕様については公式ドキュメントに書かれている以上のことは知りません。ですから、あまり難しいことは考えず、「あー、こんな動作するんだー」という感じのいい加減な動作確認をしています。

さて、現在 2のEV3RT最新版はβ3-1となっていますが、まだまだ開発中の段階のようで、センサーポート1番がタッチセンサしか認識しないといった不具合が報告されています 3。また、ダイナミックローダを使った場合、ミューテックス機能はサポートされておらず、セマフォ機能は動作しない 4ので、まともに排他制御をするのは難しいと思われます。というわけで、マルチタスクのプログラムを作る場合は、ダイナミックローダを使う方式ではなく、カーネルと一緒にコンパイルしたプログラムをSDカード経由でロードする方式をとった方がよさそうです。SDカードをEV3本体から引き抜くときに苦労してしまいますが。。。

Toppers/EV3RTの方も開発が継続されているようですので、今後はダイナミックローダでも排他制御機能を使えるようになるかもしれません。とはいっても、現在のバージョンであっても、ダイナミックローダを使わななければ排他制御機能を問題なく利用できますし、シングルタスクのプログラムであれば排他制御を必要としませんのでダイナミックローダを使っても問題ありません。

Notes:

  1. Toppers会員でもありません。というか、私は組み込み分野の人ではないのでToppersの中の人とは接点がないかも。
  2. 2015年3月12日
  3. 最初にサンプルプログラムを実行したときは、ポート1を指定したときだけフリーズしたので、ハードウェアの初期不良かと勘違いしてしまいました。
  4. さらに、アプリケーションのロードができなくなります。カーネルかダイナミックローダにバグがあるのかも?

EV3RTを使ってみる(3)

Toppers/EV3RTを使ってプログラムを作成したときの記事です。
この記事を書いた時のEV3RTのバージョンはβ3です。
目次はこちら

簡単なアプリケーションを作成できたのだけれども、複雑なアプリケーションを開発していくとなると、プログラムを複数のファイルに分割したくなってきます。むしろ、ファイルを分割しないと途中で投げ出したくなります。というか、app.cという適当すぎる名前のファイルにすべてのプログラムを書いていくなど美しくありません。

もちろん、EV3RTは複数のソースファイルを結合してアプリケーションを構築する方法もサポートしているようですので、今回はそれを試してみました。アプリケーションの動作は前回作成したものとまったく同じです。

まずは、アプリケーションのプログラムを作成していきます。

タスク処理の起動部分はhello.cという名前のファイルに書き、表示処理の部分はdisplay.cという名前のファイルに書きました。また、これらのソースファイルにあわせてhello.hとdisplay.hという名前のヘッダファイルも作成しています。このあたりは普通のC言語プログラムでの開発と同じです 1

void main_task(intptr_t exinf);   /* externを書く必要はなかった */
#include "ev3api.h"
#include "hello.h"
#include "display.h"

void main_task(intptr_t exinf) {
  int battery;
  ulong_t time;

  while(1){
    battery = ev3_battery_voltage_mV();
    get_tim(&time);

    display(time, battery);
    tslp_tsk(100);
  }
}
void display(ulong_t time, int battery);
#include "ev3api.h"
#include "display.h"

void display(ulong_t time, int battery)
{
  char battery_str[32];
  char time_str[32];

  sprintf(battery_str, "Battery: %d", battery);
  sprintf(time_str, "Time: %ld", time);
  
  ev3_lcd_set_font(EV3_FONT_MEDIUM);
  ev3_lcd_draw_string("HELLO EV3", 20, 20);
  ev3_lcd_draw_string(battery_str, 20, 40);
  ev3_lcd_draw_string(time_str, 20, 60);
}

次に、app.cという名前のファイルを必ず要求されるので、このファイルも作っておきます。中身はコンパイルが通るのであれば何でもいいですので、空のファイルでも作っておくのが無難でしょう。

/* 空のファイル。日記でも書いとけばいいんじゃないかな。 */

作成が必要なプログラムはここまで。次に、ビルドするための設定を作成していきます。

まずは、makeのコンパイル対象となるファイルにhello.cとdisplay.cを追加します。前回の環境構築時に作成した空のファイルMakefile.incにファイル追加の設定を書き込みます。hello.cとdisplay.cをコンパイルの対象にするには、APPL_COBJS変数にhello.oとdisplay.oを追加します。

APPL_COBJS += hello.o display.o

拡張子が .o となっていることに注意してください。「 XXX.o の基となるファイルは XXX.c である」というルールがMakefileに書かれていて、自動的に拡張子を置換してhello.cとdisplay.cをコンパイルしてくれるようになります 2

最後に、アプリケーションのビルド設定ファイルapp.cfgを作成します。前回と異なるところは、main_taskの定義をしているhello.hを読み込んでいるところと、オブジェクトファイルであるhello.oとdisplay.oを組み込むよに設定してあるところです。

INCLUDE("app_common.cfg");

#include "hello.h"

DOMAIN(TDOM_APP) {
  CRE_TSK(MAIN_TASK, { TA_ACT, 0, main_task, TMIN_APP_TPRI + 1, STACK_SIZE, NULL });
}

ATT_MOD("hello.o");
ATT_MOD("display.o");

あとは、makeを使ってアプリケーションをビルドして、ビルドしたものをEV3本体に転送してあげれば動作します。前回と同じ簡単なアプリケーションですので、動作したときの感動はあまりありませんが。
2015-03-06-1

Notes:

  1. 標準のC言語の機能をサポートしているようです。ですので、関数の宣言にexternをつける必要はありません(すべてのブロックの外側で何もつけていない宣言はexternをつけた宣言と同じ意味になります)。
  2. makeの置換機能を使った有名なビルドルールですね。makeの置換機能は大変強力で、上手く使えばソースファイルを自動的に探してコンパイルするなんてこともできます。

EV3RTを使ってみる(2)

Toppers/EV3RTを使ってプログラムを作成したときの記事です。
この記事を書いた時のEV3RTのバージョンはβ3です。
目次はこちら

前回サンプルプログラムを動作させることができましたので、今回は自分で作ったプログラムを動かしていこうと思います。最初に作るプログラムといえばHelloですね。公式Wikiにも開発方法の説明がありますが、Wikiに書かれている方法とは少し違って、スクラッチから書いていく方法を試してみます 1

まずは、workspace(サンプルプログラムが入っている場所)に新しいディレクトリを作成します。適当にhelloというディレクトリを作成しました。

$ mkdir hello
$ ls
Makefile  gyroboy  helloev3      linetrace  test-cpp  trike
common    hello    hwbrickbench  loader     test-cyc

作成したディレクトリの中にプログラムを作っていくのですが、作成したプログラムをビルドするときに必要となるファイルが上のcommonディレクトリに入っていますので、最初にcommonディレクトリの中のファイルをhelloディレクトリにコピーします。あと、Makefile.incという名前のファイルも必要なので空ファイルを作っておきます。

$ cp common/* hello/
$ touch hello/Makefile.inc
$ ls hello
Makefile.app  Makefile.appmod  Makefine.inc  app_common.cfg

app_common.cfgというファイルまでコピーされていますが、このファイルは無くても問題ありません 2

次に、プログラムを書いていきます。必ずapp.cという名前のソースファイルが必要となるので、app.hとapp.cを作成して、メインの処理を行う関数main_taskを書いていきます 3

まず、app.hは関数の宣言を書くだけです 4

extern void main_task(intptr_t exinf);

次に、ソースファイルを作成していくのですが、単純にHelloと表示するだけでは寂しいので、0.1秒おきにバッテリ電圧とシステム時間を表示するプログラムにしてみました。ev3_*という関数はEV3を制御するためのAPIです。また、tslp_tskという関数はtoppersのAPIで、C言語のselectやJavaのwaitと同じような使い方ができそうです。

#include "ev3api.h"
#include "app.h"

void main_task(intptr_t unused) {
  int battery;
  ulong_t time;
  char battery_str[32];
  char time_str[32];

  while(1){
    battery = ev3_battery_voltage_mV();
    get_tim(&time);

    sprintf(battery_str, "Battery: %d", battery);
    sprintf(time_str, "Time: %ld", time);

    ev3_lcd_set_font(EV3_FONT_MEDIUM);
    ev3_lcd_draw_string("HELLO", 20, 20);
    ev3_lcd_draw_string(battery_str, 20, 40);
    ev3_lcd_draw_string(time_str, 20, 60);

    tslp_tsk(100);
  }
}

最後に、これらのファイルをビルドするための設定ファイルを用意します。この設定ファイルの名前もapp.cfgにしなくてはなりません。このファイルで関数main_taskをタスクに登録するため、main_taskの宣言をしているapp.hを読み込む必要があります。また、コンパイル後にできたオブジェクトファイルapp.oを組み込むように登録しておきます。CRE_TSKというのはToppersの静的APIというものだそうで、この部分で関数main_taskを実行するタスクを作成しています 5

INCLUDE("app_common.cfg");

#include "app.h"

DOMAIN(TDOM_APP) {
CRE_TSK(MAIN_TASK, { TA_ACT, 0, main_task, TMIN_APP_TPRI + 1, STACK_SIZE, NULL });
}

ATT_MOD("app.o");

最終的に用意したファイルの一覧です。

$ ls hello
Makefile.app     Makefile.inc  app.cfg  app_common.cfg
Makefile.appmod  app.c         app.h

後はビルドしてEV3に転送すれば作成したプログラムが動作します。ビルドとEV3への転送方法は前回と同じ方法で行いました。

$ make mod=hello
rm -rf /opt/ev3rt/workspace/OBJ
configure: Generating Makefile.appmod from ../hello/Makefile.appmod.
...
  LD      app
$ lsz app > /dev/tty.MindstormsEV3 < /dev/tty.MindstormsEV3
Sending: app
Bytes Sent:  88383   BPS:3753                            

Transfer complete

実行すると、Helloという文字列とバッテリ電圧とシステム時間だけが表示されます。EV3のバッテリ電圧は安定しているみたいですね。
2015-03-04-1

Notes:

  1. 現在(2015年3月4日)のところは既存のサンプルプログラムを拡張する方法が書かれています。
  2. あっても問題ありません
  3. 後でこの関数名を登録するので、他の関数名にしても動かすことはできます。
  4. サンプルプログラムではTOPPERS_MACRO_ONLYという定義のチェックをしていますが、これはアセンブラからC言語のヘッダファイルを読み込むための定義のようですので、アプリケーションには不要、、、だと思います。
  5. このタスクを自動で実行するためにTA_ACTを指定しています。あとはタスクの優先度とスタックサイズを指定しています。

EV3RTを使ってみる(1)

Toppers/EV3RTを使ってプログラムを作成したときの記事です。
この記事を書いた時のEV3RTのバージョンはβ3です。
目次はこちら

ETロボコンに参加しようと思っているので、その準備を開始しました。先日、ETロボコン2015の実施説明会も開催されたようですし、去年のように本番直前になって慌てるなんてことにならないようにしなければ。

というわけで、2012年に買ったMacBookAir(OSX ver.10.9.5)に開発環境の1つであるEV3RTをインストールしてみました。公式のWikiにインストール方法が丁寧に説明されていますので、これに従ってインストールすればサンプルプログラムをコンパイルするところまで行けます。ただし、GNUツールチェーンのバージョン違いには注意。1つ古いバージョンのGNUツールチェーンではサンプルプログラムをコンパイルできませんでした 1

2015-03-03-1さて、サンプルプログラムをEV3本体で動かすためにmicroSDカードを買ってきて 2、そのmicroSDカードのルートディレクトリにアプリケーションローダであるsdcard/uImageをコピーして、このSDカードをEV3のスロットに入れて電源を入れるとアプリケーションローダの画面が表示されます。ちなみに、差し込んだSDカードは簡単には取り出せません。テープを巻いて取り出しやすくしている人もいるようですが、私は「毛抜き」で無理やり抜いています 3

次に、コンパイルしたサンプルプログラムをEV3に移すのですが、なんとかコマンドラインからBluetooth経由で転送したいので、ZMODEMのプロトコルを話すソフトウェア lrzsz (ver.0.12.20) をインストール。このソフトウェアに lsz という名前のプログラムが入っていて、このプログラムがZMODEM経由のファイル送信をしてくれます。

あとは、EV3本体側をBluetoothからアプリケーションをロードする状態にして、ノートPCとEV3本体のBluetooth通信をペアリングして、コンパイルしたサンプルプログラムをEV3本体に転送すればEV3本体でサンプルプログラムが動作します。

というわけで、サンプルプログラムのコンパイルからEV3本体に移すまで:

まずは、サンプルプログラムのコンパイル。今回はhelloev3を試してみました。作業するディレクトリや作成されるファイルの名前などは公式Wikiに書いてあります。

$ make mod=helloev3
rm -rf /opt/ev3rt/workspace/OBJ
configure: Generating Makefile.appmod from ...
...
...
  LD      app

次にプログラムを転送する前に、Bluetoothのペアリングをして、Bluetoothのシリアルポートデバイスを確認。環境によってデバイスの名前が違うかも。

$ ls -l /dev/tty.MindstormsEV3
crw-rw-rw- 1 root wheel 33, 12  3  3 17:42 /dev/tty.MindstormsEV3

このシリアルポートデバイスを経由してファイルデータを転送します。ZMODEMは応答メッセージを返すタイプのプロトコルですので、lszの入力側にもリダイレクトが必要です。また、データ量の割に時間がかかるので気長に待ちます 4

$ lsz app > /dev/tty.MindstormsEV3 < /dev/tty.MindstormsEV3
Sending: app
Bytes Sent: 127963   BPS:3787                            

Transfer complete

とりあえず、成功しました。
2015-03-03-2

Notes:

  1. ちなみに、同じバーションのGNUツールチェーンでEV3RTとNextOSEKの両方に対応できます。NXTの開発もできます。たぶん、私はしませんけど。
  2. 人生で初めてmicroSDカードを買いました。もちろん、既に手元にある人はそれを使っても構いません。
  3. お手軽ですがお勧めはしません(笑)
  4. 私の環境だと10秒〜20秒ぐらい。

svnのリポジトリを復旧する

私のsubversionリポジトリは、一定時間おきにsvnsyncでミラーリングかけることでバックアップしているのだけれども、このバックアップのスクリプトがエラーを出すようになってしまいました。バックアップ側にはこれといって問題はなさそうだったので、リポジトリの方を確認。

% svnadmin verify [path to repository]
* Verified version 1.
* Verified version 2.
....
* Verified version 2000.
svnadmin: Checksum mismatch:
expected: [xxxxx]
actual: [XXXXX] ← expectedと違う

どうも、データファイルが壊れているようなメッセージなので、リポジトリを復旧することにしました。壊れた部分のデータはバックアップ側にもなかったので諦めることに。。。

まずは、壊れていない部分をdumpします。

% svnadmin dump [path to repository] > svn.dump
* Dumped revision 1.
* Dumped revision 2.
...

壊れているバージョンがあれば、そこから先をdumpできない。例えば、ver.2001が壊れていたとすると、ver.2000までしかdumpされません。そこで、壊れているバージョンをskipして、その先の壊れていない部分のdumpデータを先に作っていたdumpファイルに追加します。例えば、ver.2001が壊れている場合、ver.2001をskipし、ver.2002からdumpしてファイルに追加します。

% svnadmin dump --incremental -r2002:HEAD [path to repository] >> svn.dump
* Dumped revision 2002.
* Dumped revision 2003.
...

あとは、作成したdumpデータを新しいリポジトリに登録すれば復旧完了です。

% svnadmin create [path to new repository]
% svnadmin load [path to new repository] < svn.dump

これで、壊れていない部分のデータについて復旧できます。ただし、壊れた部分のversion番号は詰められるので、壊れたデータの数だけversion番号が小さくなります。そのため、このリポジトリと同期しているファイルがある場合、version番号のずれによる不具合が起こる可能性があるので、新しくcheckoutした方が無難かも。

追記(2015/03/03):
もちろん、壊れた部分のデータも失われているので、何が失われたかを確認する必要がありますね。