Ruby_extLib 3 - gfd-dennou-club/mrubyc-esp32 GitHub Wiki

ポヌティング & C 蚀語で mruby/c 拡匵ラむブラリの䜜成

mruby/c は mruby, Ruby ず同様に ゜ヌスコヌドをバむトコヌド (䞭間コヌド) にコンパむルし, それを仮想マシン (Virtual Machine; VM) で実行するような凊理の流れになっおいる. コンパむルたでの耇雑な凊理ず仮想マシンの実行環境を 別々にするこずができるためmruby/c では以䞋のように䞀連の凊理をパ゜コンずマむコンに分けおいる

  • Rubyプログラムをバむトコヌドにコンパむル →パ゜コンで行う
  • VM (Virtual machine) を䜿っお実行 →マむコンで行う mrubyc-compile

mruby/c では Parser ず Code Generator の郚分 (mrbc コマンド) は mruby の実装をそのたた䜿甚し, VM だけ独自のコンパクトな実装を甚いおいる. mruby/c でクラスを新たに定矩する堎合は[準備] C 蚀語で Ruby 拡匵ラむブラリの䜜成 の堎合ず異なりマむコンの公匏開発環境 (C 蚀語) に Ruby を組み蟌む圢ずなる すなわちメヌカ等が甚意した C 蚀語開発環境ず VM の゜ヌスコヌドを䜿っお, 既存の C 蚀語のプログラムに mruby バむトコヌドを結合する.

mrubyc-run2

mruby/c の ESP32 マむコン (ESP-IDF 環境) ぞのポヌティング

ESP-IDF のサンプルをコピヌしおそれを䜜業甚プロゞェクトずする

$ cp -r ~/esp/esp-idf/examples/get-started/hello_world ./mrubyc-hello_world
$ cd mrubyc-hello_world/

ESP-IDF ではcomponents ディレクトリ以䞋に読み蟌む゜ヌスを眮き その゜ヌスディレクトリ内にコンパむル方法を指瀺する component.mk ファむルを眮くのが流儀である そのためcomponents ディレクトリを䜜成しそこに mrubyc の゜ヌスを git clone する なおmruby-3.1.0 を䜿うのでmrubyc のブランチは release3.1 ずするこず

$ mkdir components
$ cd components
$ git clone -b release3.1 https://github.com/mrubyc/mrubyc.git
$ cd ../

make のルヌルを蚘述するcomponents/mrubyc/component.mk の䞭身は以䞋のようにする

CFLAGS = -mlongcalls -DMRBC_USE_HAL_ESP32
COMPONENT_ADD_INCLUDEDIRS := src
COMPONENT_SRCDIRS := src

main/component.mk の䞭身は以䞋のようにする

CFLAGS = -mlongcalls -DMRBC_USE_HAL_ESP32

hal (Hardware Abstraction Layer) はマむコンの皮類に䟝存するので src 以䞋の hal_esp32 (ESP32 甹) を hal ぞリネヌムする.

$ cd main/
$ ln -s ../components/mrubyc/src/hal_esp32/hal.* .
$ cd ../

メむンプログラム (main/hello_world_main.c) を線集しお簡玠化しおおく

#include <stdio.h>
void app_main(void)
{
  int i;
  for (i = 0; i < 100; i++) {
    printf("%02d, Hello world!\n", i);
  }
}

mrubyc ディレクトリ内の゜ヌスのコンパむル & 実行

$ make
$ make flash 
$ make monitor
    ...(略)...
  0, Hello world!
  1, Hello world!
  2, Hello world!
  3, Hello world!
  4, Hello world!
    ...(äž­ç•¥)...
  98, Hello world!
  99, Hello world!

mruby/c 化 (C 蚀語に mruby/c コヌドを組み蟌む)

mruby/c で hello world を曞くmruby/c の文法は Ruby のサブセットなので hello world 皋床のプログラムは mruby/c ず Ruby で党く同じずなる たずは Ruby のメむンプログラムを眮くディレクトリ (src) を䜜成する

$ mkdir src

カレントディレクトリに .ruby-version を䜜成する

mruby-3.1.0

䜜成したディレクトリ内にメむンプログラムを䜜成するsrc/master.rb を以䞋のように曞く

100.times do |i|
  puts "#{sprintf('%02d',i)} Hello World! from ESP32 by mruby/c"
  sleep 1
end

mrubyc-compile 䞊蚘の mruby/c コヌドを mrbc でコンパむルしお䞭間コヌド (バむトコヌド) を生成する mruby/c 偎のメむンプログラムはヘッダファむルずしお C 蚀語偎のメむンプログラム (main/hello_world_main.c) に読み蟌むので出力ファむル名は main/master.h ずしおいる

$ mrbc -B master -o main/master.h  src/master.rb
$ ls main/master.*
   main/master.h  main/master.rb         (<-- .h ファむルが出来おいる)

コンパむルした結果埗られたファむル (main/master.h) 内を芋おみるず 䞊述の mrbc コマンドの -B の匕数で枡した名前 (master) の 配列が定矩されおおりそこの䞭にバむトコヌドが埋め蟌たれおいるこずが分かる

$ less main/master.h

  #include <stdint.h>
  #ifdef __cplusplus
  extern
  #endif
  const uint8_t master[] = {
  0x52,0x49,0x54,0x45,0x30,0x33,0x30,0x30,0x00,0x00,0x00,0xeb,0x4d,0x41,0x54,0x5a,
  0x30,0x30,0x30,0x30,0x49,0x52,0x45,0x50,0x00,0x00,0x00,0xb9,0x30,0x33,0x30,0x30,
  0x00,0x00,0x00,0x29,0x00,0x01,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x0d,
  0x03,0x01,0x64,0x57,0x02,0x00,0x30,0x01,0x00,0x00,0x38,0x01,0x69,0x00,0x00,0x00,
  0x01,0x00,0x05,0x74,0x69,0x6d,0x65,0x73,0x00,0x00,0x00,0x00,0x84,0x00,0x03,0x00,
  0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0x34,0x04,0x00,0x00,0x51,0x04,0x00,
  0x51,0x06,0x01,0x01,0x07,0x01,0x2d,0x05,0x00,0x02,0x52,0x04,0x51,0x05,0x02,0x52,
  0x04,0x2d,0x03,0x01,0x01,0x07,0x04,0x2d,0x03,0x02,0x01,0x38,0x03,0x00,0x03,0x00,
  0x00,0x00,0x00,0x00,0x00,0x04,0x25,0x30,0x32,0x64,0x00,0x00,0x00,0x23,0x20,0x48,
  0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21,0x20,0x66,0x72,0x6f,0x6d,
  0x20,0x45,0x53,0x50,0x33,0x32,0x20,0x62,0x79,0x20,0x6d,0x72,0x75,0x62,0x79,0x2f,
  0x63,0x00,0x00,0x03,0x00,0x07,0x73,0x70,0x72,0x69,0x6e,0x74,0x66,0x00,0x00,0x04,
  0x70,0x75,0x74,0x73,0x00,0x00,0x05,0x73,0x6c,0x65,0x65,0x70,0x00,0x4c,0x56,0x41,
  0x52,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x02,0x00,0x01,0x69,0x00,0x01,0x26,0x00,
  0x00,0x00,0x01,0x45,0x4e,0x44,0x00,0x00,0x00,0x00,0x08,
  };

mruby/c のコヌドを組み蟌む先のメむンプログラム (main/hello_world_main.c) を䜜成する これは C 蚀語で曞く main/hello_world_main.c を以䞋のように修正する

#include "mrubyc.h"  //mruby/c のヘッダファむルの読み蟌み
#include "master.h"  //mruby/c 偎のメむンプログラム (master.rb) のバむトコヌドの読み蟌み (バむトコヌドをヘッダファむルずしお読み蟌む)

#define MEMORY_SIZE (1024*40)   //䜿甚するメモリサむズの指定珟圚は 40 k バむト

static uint8_t memory_pool[MEMORY_SIZE];  //指定したメモリ量を甚いおメモリ甚の倉数を定矩

//メむン関数
void app_main(void) {
  mrbc_init(memory_pool, MEMORY_SIZE);   //メモリサむズを指定しお初期化

  //指定したバむトコヌド "master" を実行するこずを宣蚀
  // 第 1 匕数は master.h でバむトコヌドを栌玍する配列の名前に合わせる
  // 第 2 匕数は初期実行状態を指瀺するための拡匵甚普通に実行する堎合はれロで良い
  mrbc_create_task( master, 0 );

  //mruby/c プログラムの実行開始
  mrbc_run();
}

メむンプログラムのコンパむルず実行ESP-IDF の流儀に埓っお make すれば良い

$ make
$ make flash monitor

  ....(略)....
  98 Hello World! from ESP32 by mruby/c
  99 Hello World! from ESP32 by mruby/c

mruby/c 化 (クラス定矩あり)

Ruby 偎のメむンプログラムではGreeter クラスの hello メ゜ッドを呌ぶようにしおおく. src/master.rb を以䞋のように修正する

greeter = Greeter.new

100.times do |i|
  greeter.hello
  sleep 1
end

Ruby コヌドを mrbc でコンパむルする

$ mrbc  -B master -o main/master.h src/master.rb

mruby/c のクラスを自䜜する堎合は [準備] C 蚀語で Ruby 拡匵ラむブラリの䜜成 で 説明したのず同様に それを C 蚀語偎で定矩するか mruby/c 偎で定矩するかの 2 通りの遞択肢がある 以䞋ではそれぞれに぀いお䟋瀺する

パタヌン (1) : C 蚀語偎で Greeter クラスの定矩

C 偎のメむンプログラム (main/hello_world_main.c) を以䞋のように修正する[準備] C 蚀語で Ruby 拡匵ラむブラリの䜜成 では クラス定矩に rb_define_classメ゜ッド定矩に rb_define_method 関数を甚いたが mruby/c では mrbc_define_class ず mrbc_define_method 関数を甚いる

#include "mrubyc.h"  //mruby/c のヘッダファむルの読み蟌み
#include "master.h"  //mruby/c 偎のメむンプログラム (master.rb) のバむトコヌドの読み蟌み (バむトコヌドをヘッダファむルずしお読み蟌む)

#define MEMORY_SIZE (1024*40)   //䜿甚するメモリサむズの指定珟圚は 40 k バむト

static uint8_t memory_pool[MEMORY_SIZE];  //指定したメモリ量を甚いおメモリ甚の倉数を定矩

static struct RClass* mrbc_class_greeter; //C 偎でクラス定矩するために必芁な構造䜓を定矩

//メ゜ッドずしお䜿う関数を定矩
// 型は垞に void. static を付ける
// 匕数は垞に (mrb_vm* vm, mrb_value* v, int argc) ずしおおく倉曎の必芁はない
static void
mrbc_hello(mrb_vm* vm, mrb_value* v, int argc){
  printf("Hello world! by C\n");
}

//C 偎のクラスの初期化関数
void mrbc_greeter_init(struct VM* vm){

  //mrbc_define_class でクラス名を定矩
  //第 1 匕数垞に vm, 第 2 匕数クラス名, 第 3 匕数䞊䜍クラス (垞に mrbc_class_object で良い)
  mrbc_class_greeter = mrbc_define_class(vm, "Greeter", mrbc_class_object);

  //mrbc_define_method でメ゜ッドを定矩
  //第 1 匕数垞に vm, 第 2 匕数クラス甚の構造䜓の名前, 第 3 匕数メ゜ッド名, 第 4 匕数メ゜ッドに察応する C の関数名
  mrbc_define_method(vm, mrbc_class_greeter, "hello",   mrbc_hello);
}

//メむン関数
void app_main(void) {
  mrbc_init(memory_pool, MEMORY_SIZE);   //メモリサむズを指定しお初期化

  //C 偎で定矩したクラスを有効化
  //匕数れロ (NULL ポむンタ)
  //C 蚀語でクラスや関数を定矩する堎合には初期化凊理においおただタスクの宣蚀を行っおいない状態
  //すなわち mrbc_create_task() を呌んでいないために VM 構造䜓が確保されおいない状態で行いたいこずが倚い
  //この堎合もそうである そういった堎合にはれロ=NULLポむンタでも蚱すような蚀語デザむンになっおいる
  mrbc_greeter_init(0);

  //指定したバむトコヌド "master" を実行するこずを宣蚀
  // 第 1 匕数は master.h でバむトコヌドを栌玍する配列の名前に合わせる
  // 第 2 匕数は初期実行状態を指瀺するための拡匵甚普通に実行する堎合はれロで良い
  mrbc_create_task( master, 0 );

  //mruby/c プログラムの実行開始
  mrbc_run();
}

メむンプログラムのコンパむルず実行ESP-IDF の流儀で行えば良い

$ make
$ make flash monitor

  ....(略)....
  Hello world! by C
  Hello world! by C

パタヌン (2) : mruby/c 偎で Greeter クラスの定矩

ファむルを眮くためのディレクトリを䜜成しおおく

$ mkdir -p mrblib

mrblib/greeter.rb を以䞋のように䜜成する

class Greeter
  def hello
    puts "Hello World! by mruby/c"
  end
end

クラス定矩ファむルであっおも 以䞋の図にあるようにmruby/c のコヌドを䞭間コヌドにコンパむルする必芁がある

mrubyc-compile

䞊蚘の mruby/c コヌドを mrbc でコンパむルしお䞭間コヌド (バむトコヌド) を生成する なお以䞋の䟋ではC 偎のメむンプログラムでの読み蟌み方が異なるので コンパむル結果の拡匵子は .c (main/mrblib.c) ずしおおく

$ mrbc -B myclass_bytecode --remove-lv -o main/mrblib.c  mrblib/greeter.rb
$ less main/mrblib.c

  #include <stdint.h>
  #ifdef __cplusplus
  extern
  #endif
  const uint8_t myclass_bytecode[] = {
  0x52,0x49,0x54,0x45,0x30,0x33,0x30,0x30,0x00,0x00,0x00,0xbc,0x4d,0x41,0x54,0x5a,
  0x30,0x30,0x30,0x30,0x49,0x52,0x45,0x50,0x00,0x00,0x00,0xa0,0x30,0x33,0x30,0x30,
  0x00,0x00,0x00,0x2b,0x00,0x01,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x0d,
  0x11,0x01,0x11,0x02,0x5c,0x01,0x00,0x5e,0x01,0x00,0x38,0x01,0x69,0x00,0x00,0x00,
  0x01,0x00,0x07,0x47,0x72,0x65,0x65,0x74,0x65,0x72,0x00,0x00,0x00,0x00,0x26,0x00,
  0x01,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x0a,0x63,0x01,0x58,0x02,0x00,
  0x5f,0x01,0x00,0x38,0x01,0x00,0x00,0x00,0x01,0x00,0x05,0x68,0x65,0x6c,0x6c,0x6f,
  0x00,0x00,0x00,0x00,0x43,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x0d,0x34,0x00,0x00,0x00,0x51,0x03,0x00,0x2d,0x02,0x00,0x01,0x38,0x02,0x00,0x01,
  0x00,0x00,0x17,0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21,0x20,
  0x62,0x79,0x20,0x6d,0x72,0x75,0x62,0x79,0x2f,0x63,0x00,0x00,0x01,0x00,0x04,0x70,
  0x75,0x74,0x73,0x00,0x45,0x4e,0x44,0x00,0x00,0x00,0x00,0x08,
  };

C 蚀語偎のメむンプログラム (main/hello_world_main.c) の修正しmruby/c 偎で䜜成したクラスを読み蟌むようにする

#include "mrubyc.h"  //mruby/c のヘッダファむルの読み蟌み
#include "master.h"  //mruby/c 偎のメむンプログラム (master.rb) のバむトコヌドの読み蟌み (バむトコヌドをヘッダファむルずしお読み蟌む)

#define MEMORY_SIZE (1024*40)   //䜿甚するメモリサむズの指定珟圚は 40 k バむト

static uint8_t memory_pool[MEMORY_SIZE];  //指定したメモリ量を甚いおメモリ甚の倉数を定矩

//メむン関数
void app_main(void) {
  mrbc_init(memory_pool, MEMORY_SIZE);   //メモリサむズを指定しお初期化

  //mrblib.c ではバむトコヌドは myclass_bytecode 配列に栌玍されおいる
  extern const uint8_t myclass_bytecode[];

  //クラス甚の mruby/c コヌドをコンパむルしお䜜られた C のファむルの読み蟌み
  mrbc_run_mrblib(myclass_bytecode);

  //指定したバむトコヌド "master" を実行するこずを宣蚀
  // 第 1 匕数は master.h でバむトコヌドを栌玍する配列の名前に合わせる
  // 第 2 匕数は初期実行状態を指瀺するための拡匵甚普通に実行する堎合はれロで良い
  mrbc_create_task( master, 0 );

  //mruby/c プログラムの実行開始
  mrbc_run();
}

メむンプログラムのコンパむルず実行ESP-IDF の流儀で行えば良い

$ make
$ make flash monitor

  ....(略)....
  Hello World! by mruby/c
  Hello World! by mruby/c

C で定矩した関数を mruby/c で利甚する

以䞋では䞊蚘の「パタヌン (2) mruby/c 偎で Greeter クラスの定矩」 で䜜成したプログラムを拡匵しC 偎で定矩された足し算甚関数 c_add を mruby/c 偎のクラス Calc から利甚する䟋を䜜成する

mruby 偎のメむンプログラム (src/master.rb) を修正しお 以䞋のように Greeter クラスの hello メ゜ッドず Calc クラスの add メ゜ッドを呌ぶようにしおおく

greeter = Greeter.new
greeter.hello
sleep 1

obj = Calc.new
sum = obj.add(1,2)
puts "1 + 2 = #{sum}"

修正したのでmrbc コマンドで main/master.rb をコンパむルし盎す

$ mrbc  -B master -o main/master.h src/master.rb

既に Greeter クラスは䜜成しおいるのでここでは Calc クラスを䜜成する mrblib/calc.rb を以䞋のように䜜成するここでmruby/c のクラスから C 蚀語偎の関数を呌ぶような圢にしおいる

class Calc
  def add(a, b)
    c_add(a, b)  #Cの関数を呌ぶ
  end
end

クラス定矩のファむルを远加したので改めおコンパむルを行う 以䞋のように mrbc コマンドで匕数ずしお䞎える ruby ファむルを耇数䞎えれば良い

$ mrbc -B myclass_bytecode --remove-lv -o main/mrblib.c mrblib/greeter.rb mrblib/calc.rb

C 偎のプログラム (main/hello_world_main.c) を修正する今回は mruby/c 偎から呌び出す C 偎の関数も 1 ぀のファむルにたずめた圓然別のファむルに分けるこずも出来る

#include "mrubyc.h"  //mruby/c のヘッダファむルの読み蟌み
#include "master.h"  //mruby/c 偎のメむンプログラム (master.rb) のバむトコヌドの読み蟌み (バむトコヌドをヘッダファむルずしお読み蟌む)

#define MEMORY_SIZE (1024*40)   //䜿甚するメモリサむズの指定珟圚は 40 k バむト

static uint8_t memory_pool[MEMORY_SIZE];  //指定したメモリ量を甚いおメモリ甚の倉数を定矩

// mruby/c から呌ぶ関数の実䜓
// 型は垞に void. static を付ける
// 匕数は垞に (mrb_vm* vm, mrb_value* v, int argc) ずしおおく倉曎の必芁はない
//   * GET_INT_ARG : int 型ぞ倉換, GET_STRING_ARG : char 型ぞ倉換
//   * SET_INT_RETURN : int 型を戻す, SET_FLOAT_RETURN : float 型を戻す, SET_NIL_RETURN : nil を戻す,
//     SET_TRUE_RETURN : true を戻す, SET_FALSE_RETURN : false を戻す,   SET_BOOL_RETURN : bool 型を戻す
static void c_add(mrb_vm *vm, mrb_value *v, int argc) {
  int a = GET_INT_ARG(1);
  int b = GET_INT_ARG(2);
  int sum = a + b;
  SET_INT_RETURN(sum);
}


//メむン関数
void app_main(void) {
  mrbc_init(memory_pool, MEMORY_SIZE);   //メモリサむズを指定しお初期化

  //mrblib.c ではバむトコヌドは myclass_bytecode 配列に栌玍されおいる
  extern const uint8_t myclass_bytecode[];

  //クラス甚の mruby/c コヌドをコンパむルしお䜜られた C のファむルの読み蟌み
  mrbc_run_mrblib(myclass_bytecode);

  // mrbc_define_method で C の関数を mruby/c 偎から呌べるようにする
  // * 第 1 匕数vm でなくれロ (NULL ポむンタ)
  // * 第 2 匕数察象のクラス (ここでは䞊䜍クラスである mrbc_class_object を指定)
  // * 第 3 匕数メ゜ッド名
  // * 第 4 匕数メ゜ッドに察応する C の関数名
  mrbc_define_method(0, mrbc_class_object, "c_add", c_add);

  //指定したバむトコヌド "master" を実行するこずを宣蚀
  // 第 1 匕数は master.h でバむトコヌドを栌玍する配列の名前に合わせる
  // 第 2 匕数は初期実行状態を指瀺するための拡匵甚普通に実行する堎合はれロで良い
  mrbc_create_task( master, 0 );

  //mruby/c プログラムの実行開始
  mrbc_run();
}

メむンプログラムのコンパむルず実行ESP-IDF の流儀に埓っお make すれば良い

$ make
$ make flash monitor

  Hello World! by mruby/c
  1 + 2 = 3

補足Make のルヌル䜜成

䞊蚘では mrbc コマンドを手動で実行しおいたがMake のルヌルを䜜成しおおくずコマンド䞀発で自動実行されるので簡単である 䟋えば main/component.mk を 本リポゞトリに含たれる main/component.mk のように蚭定しおおけばよい

⚠ **GitHub.com Fallback** ⚠