Family mruby OS:FreeRTOSベースのMicroRubyマルチVM構想
この記事は、mrubyファミリ (組み込み向け軽量Ruby) Advent Calendar 2025の12/25の記事です。25日の枠が空いていたので、滑り込ませていただきました。
今日は私が最近取り組んでいる個人プロジェクトであるFamily mrubyとそこから発展したFamily mruby OSについて紹介したいと思います。

開発に至る経緯
Family mrubyは2019年から開発着手したプロジェクトです。2020のRubyKaigi Takeoutでも初期型について発表しました。
Family mrubyとは?

今は昔、子供が最初に触れるプログラミング言語といえば、BASICという時代がありました。 制約は多いですが、パソコン以外にも、MSXやファミコンでBASICができるFamily BASICという製品もあり、そこからプログラミングの面白さを知り、プログラマーになった方もたくさん居られると思います。
そして現在は無料で大抵のプログラミング言語の開発環境はパソコンにインストールすることができる時代になりましたが、できることが多すぎて何をしたらよいのかわからなかったり、Hello Worldの先のゲームを作ったりするまでの環境構築ハードルが高かったり、するような気がしています。
そこで、マイコン一つでちょっとしたゲームなどをスクリプト言語で作れる環境を作ってみたい、と思って開発したのが、Family mrubyです。
特徴
- mruby専用のエディタおよび実行環境をESP32上でスタンドアロンで実現
- PSRAM付きESP32上での動作を想定
- 専用の基板も設計
- 映像はVGAで出力
- 音声はアナログで出力
- PS/2のキーボードで入力
休眠期
コロナの流行や、私生活のあわただしさもあり、しばらく開発がストップしてました。でも頭の片隅にはプロジェクトをリブートしたいという思いがずっとありました。
PicoRuby/MicroRubyとの出会い
私がしばらくプロジェクトから遠ざかっているうちに、@hasumikinさんがPicoRubyを開発されたことを知り、大いに刺激を受けました。
私の初期型では、ESP32へ素のmrubyを移植して、自作のエディタを動かすまででしたが、PicoRubyでは、より省リソースになり、ファイルシステムやUSBデバイス化などユーザ体験を向上させるための機能が多数実装されてました。
この設計思想を一部受け継いで、自分なりに自分が求める新しいmrubyの開発実行環境をまた1から作り直したくなりました。
PicoRubyはmruby/cのVMがベースになっていましたが、MicroRubyではmruby VMベースになると聞いて、mrubyでの経験も生かせるのではと思いました。
ある程度私生活も落ち着いてきた2025年から本格的にプロジェクトをリブートして開発に着手しました。
初期Family mrubyと新Family mruby は何が違うのか
プロジェクト休眠期中に転職をしてPM寄りから、コーディングもする仕事になって、多少開発の勘も戻ってきたこともあり、腕試しの意味も込めて、やりたいこと全部やる方針で設計しました。
初期型の構成は↓の図のような感じです。

新型Family mrubyの構成は↓のようになってます。
mrubyで開発、実行できる世界を拡張できるようにOSとして再構築しました。
主な違いは以下の表にまとめてみました。PS/2キーボードやVGA出力がちょっと準備が面倒なので、USBに変えたり、Linux上でも開発できるようにしたり、と旧型で気になった部分を解消するように見直しています。

Family mruby OS
新型Family mrubyでは、USBマウスもつなぐことできるようになります。それを生かして、昔のWindows3.1や私が親しんだFM-TOWNS OSをオマージュして、シンプルなWindow GUIを提供したいと考えました。
ESP-IDFのベースになっているFreeRTOSを生かせば、単にmrubyのコードを実行する環境としてだけでなく、コンカレントに複数のVMを実行できるはずです。
この新しい環境をFamily mruby OSと呼ぶことにしました。
前提
Family mruby OSを動かすためには、PicoRubyなどと比較して何倍ものメモリが必要なることが見込まれます。そのため、PSRAMを数MB以上搭載したESP32を前提としました。

mrubyが当初想定していた富豪的マイコン環境がこれなのかなと思ってます。
将来的にはESP32-P4にも対応できたらいいなと思ってます。
特徴
MicroRubyを採用
MicroRubyを主たるVMとすることで、コンパクトでダイナミックなコンパイル機能や、各種pico-gemなど、たくさんの恩恵を受けることができます。
MicroRubyは絶賛開発中で内部的には破壊的変更も発生するので、Forkはしないでうまく追従していくように努力が必要です。
より安定しているPicoRubyの採用も最初考えましたが、ベースのmruby/cがグローバル変数が前提の実装になっており、メモリ空間の分割が難しそうだったため断念してます。
MicroRubyの提供するマルチタスク機能も有効なので、FreeRTOSによる機械語レベルの並行動作に加えて、バイトコードレベルでの並行動作も変わらず使えるはずです。(現在は、割り込みハンドラではなくて、専用タスクから各VMのTickを呼び出す形にしてます)
マルチVM
FreeRTOSは、リアルタイムOSであり、タスク機能を提供しています。タスクはいわゆるスレッド的なもので、それぞれがスタックをもって独立して動作することができます。
1タスクに1VMを載せ、タスクを複数生成することで、VMを並行で動かすことができます。そのためにはメモリ空間やHWリソースの分離などが必須になります。
VMをプロセスのように管理するために、必要な各種機能を実装しています。タスクのTLS(Thread Local Storage)を活用することで、VMのコンテキストを管理しています。
VMごとにメモリアロケータのハンドルを割り与えることで、1つのVMがアクセスできるメモリはあらかじめ確保したメモリの範囲に限定しています。こうすることで、メモリのフラグメンテーションがシステム全体に波及することを防いだり、1アプリのメモリが壊れても、他アプリへの影響しないようにしたり、などシステム全体の安定化に大きな効果があります。最悪、アプリの状態がおかしくなっても、タスクを強制的に終了させて、またメモリプールを割り与え直せば復帰できます。(もちろんC言語レベルでなにか不正なメモリアクセスしたり、HWの取り合いなど起こしたときは別の話になります)
性能が限られた環境でビジーループ的なことは極力避けたいので、イベント待ちはFreeRTOSのキュー機能で実装してます。アプリ側ではVM間イベントが届き次第即座に所定のメソッドがコールバックされるようしています。
基本的にメモリ分離、HWリソース分離ができて、バイナリサイズ、実行メモリサイズが適合すれば、mruby以外の言語も搭載可能です。現状はPoCとしてLuaを仮搭載してます。将来的にはMicroPythonも搭載してみたいです。
HWの抽象化
これだけ大規模な実装になるとマイコン上での開発はなかなか大変です。そのためLinuxでも実行できるようになると嬉しいです。そのために、HWやOSに関する機能は全て抽象化レイヤーを介してアクセスするようにしました。
その結果、Linux上でもESP32向けと同じコードを実行できるようになりました。ESP-IDFがLinux向けのビルドが可能なのと、描画用に採用しているLovyanGFXもSDL2 on Linuxでも実行可能なので、それを生かしています。
抽象化レイヤーをしっかり実装することで、移植性が向上するので、将来的にWASMで実行したり、新しい設計の基板で実行したり、応用の幅が広がることも期待しています。
課題としては、既存のペリフェラルに関するpico-gemは、メモリの管理など、Family mruby OSの抽象化レイヤーに必ずしも適合しない場合があります。また、そもそもマルチVM環境での動作も想定されてないので、複数VMで動かないようにするために、何らかの排他機構も必要になるはずです。
GPIO、I2Cあたりは、HW抽象化レイヤー(HAL)を使いつつ、スレッドセーフな実装に書き直すべきかなと思っています。
開発場所
以下のOrganazationで開発してます。
ドキュメント等まだ全然整理できてないので、徐々に整えていきたいと思ってます。
実行環境
Family mrubyは、専用の基板か、Linuxでの実行を想定しています。
専用基板
まだ量産版はありませんが、開発基板を設計して、JLCPCBのPCBAで生産してみました。しかし、うまく動かなかったので、現在デバック中です。
大まかなブロック図は以下のようになってます。半分やけくそでESP32を2つ搭載してます。将来、ESP32-P4で専用ディスプレイ使えば1チップ化もできるかな、と妄想してます。
HW設計データはすべて公開してます。
実際の開発基板の写真です。BOMも間違いなく、きれいに実装できました。スルーホール部品の実装までやってもらったことはなかったので、勉強になりました。
Linux
ESP-IDFのLinuxシミュレーションおよび、SDK2とLovyanGFXを利用することで、fmrb-coreはほぼそのままに、Linux上でも実行できるようできました。
サブESP32に相当するものとして、別プロセスを立ち上げてます。SPIの代わりにソケットで独自プロトコルで通信してます。
現状のデモ動画
Family mruby OSのデモ動画を撮りました。Linux環境でしか確認できてないので、基板のデバッグを完了させて、近いうちにこれをESP32でも同様に動かしていきたと考えています。
- OSの管理機能と、壁紙やシステムバーの画面と、疑似Shellアプリをそれぞれ独立したMicroRuby VMで実行してます。Luaも独立して実行してます。
- まだランチャーがないので、マウスのクリックに合わせて、疑似ShellとLuaのウィンドウを開いてます。
- 疑似Shellアプリは、Shellとしての機能はまだ実装されてませんが、キー入力は疑似Shellアプリが動くMicroRubyVMへ送信して、画面に反映してます。
- マウス操作もまだ実装不十分なので、十字キー入力で、Shellウィンドウを動かしてます。
デモ動画
Shell風アプリ(表示だけデモ)のコード
これは現状デモ用なので、STDIO対応などした上で将来大きく変更されると思います。どれくらいのコードでデモ動画のようなことが実現できているのかの参考になれば。
class ShellApp < FmrbApp
def initialize
super()
@current_line = ""
@history = [] # Line history
@cursor_x = 0
@cursor_y = 0
@char_width = 6
@char_height = 8
@prompt = "> "
@need_redraw = false
end
def on_create()
@gfx.clear(FmrbGfx::WHITE)
draw_window_frame
draw_prompt
@gfx.present
puts "[ShellApp] on_create called"
end
def on_update()
if @need_redraw
redraw_screen
@need_redraw = false
end
33 # msec
end
def draw_prompt
# Draw all history lines
@history.each_with_index do |line, i|
x = @user_area_x0 + 2
y = @user_area_y0 + 2 + (i * @char_height)
@gfx.draw_text(x, y, line, FmrbGfx::BLACK)
end
# Draw current input line
x = @user_area_x0 + 2
y = @user_area_y0 + 2 + (@history.length * @char_height)
@gfx.draw_text(x, y, @prompt + @current_line, FmrbGfx::BLACK)
end
def redraw_screen
# Clear user area
@gfx.fill_rect(@user_area_x0, @user_area_y0,
@user_area_width, @user_area_height, FmrbGfx::WHITE)
draw_window_frame
draw_prompt
@gfx.present
end
def on_event(ev)
puts "on_event: shell app"
p ev
if ev[:type] == :key_down
handle_key_input(ev)
elsif ev[:type] == :key_up
handle_key_up(ev)
end
end
def handle_key_up(ev)
keycode = ev[:keycode]
# Window position control with arrow keys
x = 0
y = 0
case keycode
when 82 # up
y = -10
when 81 # down
y = 10
when 80 # left
x = -10
when 79 # right
x = 10
end
if x != 0 || y != 0
set_window_position(@pos_x + x, @pos_y + y)
end
end
def handle_key_input(ev)
keycode = ev[:keycode]
puts "[Shell] keycode=#{keycode}"
# Enter key (keycode 13 = CR)
if keycode == 13
handle_enter
return
end
# Backspace key (keycode 8 = BS)
if keycode == 8
handle_backspace
return
end
# Ignore arrow keys (79-82)
if keycode >= 79 && keycode <= 82
return
end
# Convert keycode to character (ASCII printable range)
if keycode >= 32 && keycode <= 126
char = keycode.chr
@current_line += char
@need_redraw = true
end
end
def handle_enter
puts "[Shell] Command: #{@current_line}"
# Add current line to history
@history << (@prompt + @current_line)
# TODO: Execute command here and add output to history
# Clear current line
@current_line = ""
# Check if we need to scroll
max_lines = @user_area_height / @char_height
if @history.length >= max_lines - 1
# Remove oldest line to make room
@history.shift
end
@need_redraw = true
end
def handle_backspace
if @current_line.length > 0
@current_line = @current_line[0...-1]
@need_redraw = true
end
end
def on_destroy
puts "[ShellApp] Destroyed"
end
end
# Create and start the system GUI app instance
puts "[ShellApp] ShellApp.new"
begin
app = ShellApp.new
puts "[ShellApp] ShellApp created successfully"
app.start
rescue => e
puts "[ShellApp] Exception caught: #{e.class}"
puts "[ShellApp] Message: #{e.message}"
puts "[ShellApp] Backtrace:"
puts e.backtrace.join("\n") if e.backtrace
end
puts "[ShellApp] Script ended"これまでにやったこと
- OS Framework
- コア間連携の通信プロトコルの基礎
- 現状はソケットで実装
- 簡易な描画API
- LovyanGFXを前提としたRPCを実装
- Linux向け
- SDL2+LovyanGFXの実行環境
- Audioは未実装
- SDL2+LovyanGFXの実行環境
- 実機向け
- NESエミュレータからESP32へのAPUブロック移植
- fmrb-audio-graphicsの原理検証
- LovyanGFX(NTSC)とAPUの同時動作確認済み
- 基板
- 試作基板の設計実装
- ただし不具合あり
- 試作基板の設計実装
- アプリケーション
- 開発用の描画サンプルのみ
今後の主な開発アイテム
- OS Framework
- HIDのイベント管理/STDIOの差し替え
- Window管理(表示順、移動)
- アプリのライフサイクル
- フルスクリーンモード
- GUIデザインの刷新
- Linux向け
- Graphics系の拡充
- Audio系の実装
- 実機向け
- コア間通信の実装
- USB HOST対応(キーボード、マウス)
- サブESP32のファームウェア実装
- 基板
- ESP32ボードのデバッグ
- 量産型の設計、試作
- アプリケーション
- シェル
- ファイルマネージャ
- GPIOやI2Cデバイス連携のサンプルアプリ
- etc...
今後
今後は、まずはRubyKaigiへのCFP投稿、うまくいけば発表を目指して完成度を上げていきます。
開発アイテムは多岐にわたり、発展課題も含めると、とても数カ月で完了する内容ではないので、来年はずっと少しずつ開発を進めて完成度を上げていくつもりです。
ある程度、安定したら基板もセットで、イベントで販売したりしてみたいですね。
夢
このプロジェクトが形になってきたら、色々やってみたいことがあります。
- VM間通信の強化
- MicroPythonも取り込んで、MicroPython資産をどうにかして生かす
- ドット絵エディタや音楽エディタも実装して、ゲーム開発環境として成立させる
- GUIでI2CとかGPIOも操作できるようなアプリを作る
- WiFiもつないで、ネットでアプリをシェアしたりDLできる仕組みを作る
- Tab5やESP32-S3搭載の液晶パネル付きデバイスへの移植
- LCD対応して、ミニキーボードも搭載して、PicoCalc風のデバイスを作る
- Emscriptenでビルドできるようにして、ブラウザ上でも実行する。センサーとかの接続は、コンパニオンボードを開発してそれで行う
参考にしたプロジェクト
PicotronのFantasy Desktop
開発中に存在を知って、マイコン向けではないですが、UIのイメージが自分の理想にとても近かったです。
レトロな8bit風ゲーム開発環境です。

