SPRESENSE の開発環境に Java とか Python はあるけど、なんで Ruby が無いの?! ってことで、無いなら自分で用意してみるかと思い調べてみました。mruby を動かす前に、mruby/c なるものの存在を初めて知りました。まずは mruby/c の導入から SPRESENSE 上で "Hello world" や L チカを動かすところまで、やったことを作業メモとしてまとめておきます。
もし文中に誤り、もっと良い方法、参考になる情報等があればアドバイス頂けるとうれしいです。
今後、SPRESENSE ハードウェアと連携したライブラリを拡充させていければと思っています。
mruby/c とは?
本家はこちら
https://www.s-itoc.jp/activity/research/mrubyc/
センサーネットワークや、ウェアラブルなどの小型端末向けの開発言語「mruby/c」
mruby/cは、Rubyの特徴を引き継ぎつつ、プログラム実行時に必要なメモリ消費量が従来のmruby(福岡で開発された組込み向けの軽量Ruby)より少ないmrubyの実装です。
メモリ消費50KB未満(RAM)で動くというから驚きです。
Spresense Arduino環境ライブラリ準備
本家のリポジトリです。ライセンスは修正BSDライセンスです。
https://github.com/mrubyc/mrubyc
本家にStarを付けてForkさせて頂きました。
https://github.com/baggio63446333/mrubyc
Arduino の libraries フォルダ以下へ clone します。
$ cd ~/Arduino/libraries
$ git clone https://github.com/baggio63446333/mrubyc
以下はライブラリ環境構築の作業メモなので、手っ取り早く試してみるには、Hello World サンプルコード まで読み飛ばしてください。
HAL(Hardware Abstraction Layer)の準備
HAL層が元々用意されています(良いですね)
Spresense環境で動作しているリアルタイムOSのnuttx
が POSIX 準拠なのでhal_posix
をベースにhal
を用意しました。また、HALはArduino用のAPIをそのまま使いたいので拡張子を hal.cpp に変更しました。他のhal_xxx
ディレクトリはArduino IDEでビルドエラーになるのを防ぐために削除しておきます。
$ cd mrubyc
$ git rm -r src/hal_esp32 src/hal_pic24 src/hal_posix src/hal_psoc5lp
library.properties 追加
Arduinoライブラリとしてそのまま利用するため、library.properties
ファイルを追加します。
$ cd mrubyc
$ git add library.properties
その他
-
src/class.c
コンパイルエラー修正(ERROR
というシンボルが既に定義されているので_ERROR
に置換)
Hello World サンプルコード
examples/hello 追加
簡単なrubyプログラムを準備し、
puts "Hello World!"
hello.rb
を事前にバイトコードにコンパイルして hello.c を作成します。
$ cd examples/hello
$ mrbc -E -Bcode hello.rb
ハマったメモ
https://github.com/mruby/mruby (master) から mrbc ツールを生成して、それを使ってバイトコードに変換して動かしてみたところ、実行時に Illegal bytecode
エラーが発生。
<raise> /home/user/Arduino/libraries/mrubyc/src/load.c:55
Error: Illegal bytecode.
エラーの箇所はここ
if( memcmp(p, "RITE0006", 8) != 0 ) {
mrbc_raise(vm, E_BYTECODE_ERROR, NULL);
return -1;
}
バイトコンパイルした hello.c コードをみてみるとヘッダが "RITE0007" となっており、バージョンが一致せずエラーになっているようです。どうも使用した mrbc ツールが新しすぎたようです。
mrbc ツールのバージョンの合わせ方がよく分からなかったので
ダウンロードページの mruby コンパイラをそのまま使用するようにしました。
https://www.s-itoc.jp/files/original/201904181318159501cbec95e.zip
バージョンを合わせたのに、相変わらず Illegal bytecode
エラーが出ていて、それは Endian ? の問題でした。mrbc に -e
ではなく -E
オプションを付ける必要がありました。-e
だと "RITE" 文字列のバイトオーダーがひっくり返るので memcmp に引っ掛かるのは必然な気もしますが気のせいでしょうか。。とにかく -E
オプションを付ければ OK です。
めでたく "Hello World!" が表示されました。
Hello World サンプルコード(mrbファイル読み込み)
examples/hellob 追加
ruby スクリプトをバイトコンパイルしてさらにプログラムに静的に組み込んで動かす、というのだとスクリプト言語の良さが薄れてしまうので、バイトコードはファイルから読み込むようにしました。SD カードに mrb ファイルを置いておきそれを読み込んで実行します。
uint8_t *buf = load_mrb("hello.mrb");
if(NULL == mrbc_create_task( buf, 0 )){
次のコマンドで hello.mrb を生成できます。
$ cd examples/hellob
$ mrbc -E hello.rb
hello.mrb ファイルを SD カードに置いて実行します。
TIPS
毎回、SD カードを抜き差しして mrb ファイルをコピーして、という作業も面倒です。
このサンプルでは実行の最後に SD カードを USB マスストレージでマウントしています。
SPRESENSE 拡張ボード側の USB 端子を PC につないでおけば、SD カードを抜き差しせずに、PC から直接 mrb ファイルを SD カードにコピーすることができます。
/* Start USB MSC */
if (SD.beginUsbMsc()) {
Serial.println("USB MSC Failure!");
} else {
Serial.println("*** USB MSC Prepared! ***");
Serial.println("Insert SD and Connect Extension Board USB to PC.");
}
rubyスクリプトを変更して実行という繰り返しの動作をかなり快適に行うことができる環境が整いました。
digital 制御サンプルコード(mrbファイル読み込み)
続いてLチカに挑戦してみたいと思います。
次のようなコードを書けばrubyとCソースをつなぐことができるようです。簡単ですね。
mrbc_define_method(0, mrbc_class_object, "digital_write", c_digital_write);
まずは、pinMode()
, digitalWrite()
, digitalRead()
を ruby から制御できるようにつないで、
SPRESENSE メインボードに4つあるLEDを順番に光らせます。
SPRESENSE LTE拡張ボードについているボタンを押せば点滅させるLEDの場所を変えていきます。
val = 0
portled = 64
portsw = 33
pin_mode(portsw, 0);
while true
if val == 1
val = 0
else
val = 1
end
if digital_read(portsw) == 0
portled += 1
if portled > 67
break
end
end
digital_write(portled, val)
sleep 1
end
次のコマンドで ledswitch.mrb を生成して、SD カードに置いておきます。
$ cd examples/ledswitch
$ mrbc -E ledswitch.rb
無事にLチカできました。サンプルコードはリポジトリに入れておきました。
今後は、GPS, オーディオ再生/録音, カメラ制御とか、SPRESENSE ならではの処理を追加してみます。
バイトコードも LTE ネットワーク越しに送ってリモートでスクリプトを差し替えてみたいと思います。
あと、RAM サイズ的には入りそうなので、mruby にも挑戦してみたいところです。