ESP32用のmruby/c


ESP32用のmruby/cとしては,FemtoRubyを使ったR2P2があるが,PWMを使おうとしたらエラーが出たので,自分でもESP32用にmruby/cの環境を作ってみることにした. 自分のブログを見直してみたら,私がmruby/cを最初に使ってみたのはESP32だった. そのときには,Arduinoを使ってmrbのコードをソースに埋め込んでコンパイルしていた. そのため,rubyのコードを変えると,mrbを作って,それを埋め込んでArduinoでコンパイルして,マイコンに書き込むという作業をする必要があり,時間もかかるし面倒だった.

これまで,何種類かのマイコン用にmruby/cの環境を作ってきた. それらにおいては,mrbファイルはflashに書き込んで,そのアドレスを使って直接mrbコードを読み出して使っている. しかし,同じことをESP32でやろうとしたら,そのままではうまく行かなかった. ESP32では,フラッシュに直接アドレスが割り当てられるわけでは無く,その内容が使われるときにcacheにコピーされるので,単にflashに書き込むだけでは駄目である. また,mrbコードをプログラムに埋め込んでおいて,その部分だけを書き換えようとしても,チェックデジットや誤り訂正などがあり,その内容を不用意に書き換えるとエラーが出てしまった. いろいろ試行錯誤した結果,使っていないflash領域にmrbを書き込んで,それをメモリに割り当てることで,ほぼ同じ感覚で使えるようになった.

まず,私のホームページに載っているようにArduino用のmruby/cをセットアップする. そして,Arduinoで以下のコードをコンパイルして,ESP32に書き込む.

#include "spi_flash_mmap.h"
#include <mrubyc.h>

#define MEMORY_SIZE (1024*30)
static uint8_t memory_pool[MEMORY_SIZE];
#define FLASH_CODE 0x10000 + 0x120000 //esp32

spi_flash_mmap_handle_t s_data_map_handle;
const uint8_t *flash_code = NULL;

int hal_write(int fd, const void *buf, int nbytes) {
    Serial.write((char*)buf,nbytes);
    return (nbytes);
}
int hal_flush(int fd) { return 0; }
void hal_abort(const char *s){}

unsigned char hal_read(int fd) {
  while (Serial.available() == 0);
  return Serial.read();
}

void setup() {
  uint32_t data_flash_offset = FLASH_CODE; // running->address + 1114112;
  uint32_t map_size = 0x10000; // 64KB
  esp_err_t err = spi_flash_mmap(
    data_flash_offset, map_size, SPI_FLASH_MMAP_DATA, // as rodata
    (const void **)&flash_code, &s_data_map_handle);
  Serial.begin(19200);
  mrbc_init(memory_pool, MEMORY_SIZE);
  mrbc_init_class_digital();
  mrbc_init_class_adc();
  mrbc_init_class_pwm();
  mrbc_init_class_i2c();
  mrbc_init_class_spi();
  mrbc_init_class_uart();
  mrbc_init_class_get();
  mrbc_init_class_wifi();
  mrbc_create_task( (uint8_t *) flash_code, 0 );
  mrbc_run();
}
void loop() {}

rubyスクリプトはmrbcでコンパイルして,mrbファイルを作り,それをrodata用のflashの空いた領域に書き込む.

mrbc temp.rb
python3 esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 write-flash 0x130000 temp.mrb

この際,Arduinoのコードと重ならないような64k区切りの場所を選んで,flashのパーティションの容量に収まるようにもしなければならない. すると上のプログラムが,この内容をメモリに割り当ててくれるので,他のマイコンと同様にmrbコードを実行することができる.

まあ,ESP32では他のmruby/c環境もあるので,それらを使う方が良いかも知れない. でも,自分で組んだプログラムだと,仕組みが分かっているので,トラブルが起きたときに,その原因を究明するのが比較的に容易である. まだ、様々な機能がきちんと動くか確認していないので、追って確認しようと思う。