コンピュータを楽しもう!!

今、自分が面白くていろいろやってみたことを書き綴りたいと思います。連絡先はtarosa.yでgmail.comです。

mrubyでのバイナリの扱い

GR-SAKURAにmrubyを移植しているのですが、Rubyに関してそれほど詳しくないので、バイナリをどう扱えばいいのか分かりませんでした。
例えば、Cなどでよくある write関数(write(int handle, void *buf, unsigned n))などを実現したいとき、mrubyではどのように扱えばいいのか?
mrubyに詳しい方に聞いてみたところ分かりました。

mrubyは文字列でバイナリも扱える

そう、mrubyは文字列でバイナリも扱えました。結局、Luaと同じでした。
例えば、0x10 0x20 0x30 0xD0 0xE0 0xF0というバイナリデータを作りたいときは以下のように書けます。

bin = 0x10.chr + 0x20.chr + 0x30.chr + 0xd0.chr + 0xe0.chr + 0xf0.chr

たとえ、文字列に0が含まれていても大丈夫です。下記のようなバイナリ列もできます。

bin = 0.chr + 0.chr + 0.chr + 0.chr + 0.chr + 0.chr

chrはmrbgem扱い

ここで注意が必要です。chrはmrbgemになっており、ビルドするときにmruby-numeric-extをインクルードする必要が有ります。
mruby-numeric-extをインクルードするには、build_config.rbに下記を追加するだけです。

# Cross build for GR-SAKURA
MRuby::CrossBuild.new('grsakura') do |conf|
   toolchain :grsakura

   conf.gem "#{root}/mrbgems/mruby-numeric-ext"
end

バイナリ列を扱うmruby関数を作る

では、バイナリ列を扱うmruby関数を作るにはどうするか、それは単純に文字列を扱うメソッドを作成すればいいだけです。
例えば、GR-SAKURAの関数にSerial.write(const unsigned char *buf,int len)というものが有ります。これをmrubyでラップするとすると以下のようになります。
ライブラリを定義します。int read() と write(const unsigned char *buf,int len)を作ってみます。

//**************************************************
// ライブラリを定義します
//**************************************************
void serial_Init(mrb_state *mrb)
{
	struct RClass *systemModule = mrb_define_module(mrb, "Serial");
	mrb_define_module_function(mrb, systemModule, "read", mrb_system_read, ARGS_NONE());
	mrb_define_module_function(mrb, systemModule, "write", mrb_system_write, ARGS_REQ(2));
}

read()は下記です。1文字読み込んでint型なので、mrb_fixnum_value()を使って戻しています。

//**************************************************
// シリアルから1バイト取得します: Serial.read
//  Serial.read()
// 戻り値
//	0x00〜0xFFの値、データが無いときは-1が返ります
//**************************************************
mrb_value mrb_system_read(mrb_state *mrb, mrb_value self)
{
int ret = -1;	
	if(Serial.available()){
		ret = Serial.read(); //1文字取得
	}
	return mrb_fixnum_value( ret );
}

write()は下記です。mrb_get_args()を"S"で受けて、RSTRING_PTR()で文字列のポインタを取得し、あとは通常のchar配列として扱います。

//**************************************************
// シリアルにデータを出力します: Serial.write
//  Serial.write( buf, len )
//	buf: 出力データ
//	len: 出力データサイズ
// 戻り値
//	出力したバイト数
//**************************************************
mrb_value mrb_system_write(mrb_state *mrb, mrb_value self)
{
int	len;
mrb_value value;
char	*str;
	mrb_get_args(mrb, "Si", &value, &len);
	str = RSTRING_PTR(value);
	int ret = Serial.write( (const unsigned char *)str, len );
	return mrb_fixnum_value( ret );
}

以上、防備録としてmrubyのバイナリの使い方を書いておきます。