PicoRuby向けのM5Unified/GFX mrbgemを作る話
この記事は、mrubyファミリ (組み込み向け軽量Ruby) Advent Calendar 2025に参加しています。
本日の記事では、先日のTokyuRuby会議16 でLTした内容をブログ記事としても残しておきたいと思います。
ESP32とPicoRuby
PicoRubyはもともと、@hasumikin さんがRaspberry Pi Picoをターゲットとしてスタートしたプロジェクトです。
その後、ESP32への対応も@Yuuuさんの尽力もありなされています。(picoruby-esp32)
PicoRubyをESP32のcomponentsとしてビルドしてリンクするような形でポーティングされています。
現在は主なESP32シリーズで、PicoRubyを動かすことができるようになっています。
M5Stackシリーズを使いたい
ESP32を使いたい理由の一つとして、市場にいろいろなバリエーションのESP32をコアとしてパッケージングされた製品があるという点があると思います。

M5Unified/GFXを使いたい
M5シリーズにはディスプレイやボタンやスピーカーなど色々なデバイスが標準搭載されていますが、機種を変えるたびにAPIの仕様が微妙に異なることがあったのを解決するために、機種をまたいだAPIとして、M5Unified(グラフィクスはM5GFXが担当)というライブラリがあります。Arduino以外にもESP-IDF環境でも利用可能です。言語はC++です。
機種を違いを意識しないでプログラミングするには、これを利用しない手はないわけです。
しかし当然PicoRubyには対応していないので、PicoRubyから使うには各メソッドのバインディングを書く必要があります
どうやって大量のバインディングをさばくか
メソッドの数が少なければ、手作業でバインディング書けばよいところではあるのですが、幅広い機能を抽象化しているため、メソッドの数が数百もあります。
これを手作業で書くのはあまり現実的ではないというか、あまりやりたくない作業です。
たとえばCRubyであれば、Ruby-FFIというものがあり、C言語のライブラリをダイナミックリンクして、各関数のインタフェースを自動で定義する機能があります。
こういう場面で役に立つ機能なのですが、マイコン上で動くmrubyではそのままではFFIを動かすことはできません。
そのため、Claude Codeでどこまでやれるか試してみることにしました。
AIに仕事をやらせるための手順
これまでClaude Code使ってきた感覚として、漠然と大量の仕事をやらせても、途中でコンテキストが切れて、一貫性のある作業が難しくなります。
そのため、作業をステップに分割しつつ、進捗もドキュメントに書きつつ、途中でとまって再開できるようなことを意識しつつ、作業してもらう必要あります。
今回は以下のような手順でやってもらいました。
- 実装対象の関数をC++ヘッダからスクリプトで抽出
- 抽出した結果をYAMLにまとめる
- YAMLからmrubyバインディングのスケルトン関数を生成
- スケルトン関数を段階的に実装
中間生成物
作業する中で、各メソッドは以下のようなYAMLで抽出されます。
classes:
- name: M5Unified
namespace: m5::m5
file: M5Unified.hpp
methods:
- name: getPin
return_type: int8_t
parameters:
- type: pin_name_t
name: name
modifiers: []
line: 250
- name: getButton
return_type: Button_Class&
parameters:
- type: size_t
name: index
modifiers: []
line: 252
- name: getDisplay
return_type: M5GFX&
parameters:
- type: size_t
name: index
modifiers: []
line: 256
- name: getDisplayCount
return_type: std::size_t
parameters:
- type: void
name: ''
modifiers:
- const
line: 260
...これベースにをCのmruby/c用のバインディングの作法に則った形のスケルトン関数にします。
結果
まずは一番ニーズがあるであろう描画系を試してみました。
よく使うであろうメソッドから対応して、100メソッドほど実装しました。
全体: 99/180 (55%)
M5Unified Core: 20/20 (100%) ✅
- M5GFX Color: 12/12 (100%) ✅
- M5GFX Draw: 30/30 (100%) ✅
- M5GFX Fill: 3/13 (23%)
- M5GFX Text: 9/31 (29%)
- M5GFX Image: 2/13 (15%)
- M5GFX Display: 9/29 (31%)
- M5GFX Low-level: 4/18 (22%)
- M5GFX Utility: 0/13 (0%)
サンプルコード
以下のコードを、/home/m5.rb として保存して、R2P2を起動させて実行してみます。
r# M5Unified Basic Display Example
# Based on actual C extension implementation
require 'm5unified'
puts "Initializing M5Unified..."
# Initialize M5
M5.begin
disp = M5.Display
w = disp.width
h = disp.height
puts "Display: #{w}x#{h}"
# Clear screen to black
disp.fill_screen(0x000000)
# Set text properties and display title
disp.set_text_color(0xFFFFFF) # White
disp.set_text_size(2)
disp.set_cursor(10, 10)
disp.print("Hello M5!")
# Draw shapes with RGB888 colors
puts "Drawing shapes..."
# Red rectangle
disp.fill_rect(10, 40, 100, 60, 0xFF0000)
# Green circle
disp.fill_circle(w/2, h/2, 40, 0x00FF00)
# Blue line
disp.draw_line(0, 0, w-1, h-1, 0x0000FF)
# Yellow triangle (now implemented!)
disp.fill_triangle(w-80, h-40, w-30, h-60, w-10, h-20, 0xFFFF00)
# Cyan text label
disp.set_text_color(0x00FFFF)
disp.set_text_size(1)
disp.set_cursor(10, h-20)
disp.print("PicoRuby on M5")
puts "Drawing complete!"
puts "Press Ctrl+C to exit"
loop do
M5.update
sleep 0.1
endm5.rb
ターゲットデバイス
M5Stack FireとM5StickC Plust2でさっきのサンプルコード試してみました。

無事、同じコードで、同じような描画がされましたね。
M5Unifiedは自動でデバイス認識する機能があるため、特に機種を指定することなく違うLCDでも正しく動作します。
課題
おそらくこの調子でやれば、全メソッド制覇もできそうではあるのですが、そのメソッドのコードレビューと動作確認をするのがかなり大変です。
また何かバージョンアップがあった時の追従も大変そうです。
個人的に使う分にはいいのですが、継続的なメンテナンスが厳しそうなので、今後どうしたものか悩ましいです。
またFFI的なアプローチもあるかなと思ったのですが、M5UnifiedがC++なので、Cと違って、シンボルテーブルを解析して、FFI的なものを自作するのも厳しそう、となっています。
以下は、現在の実装です。一応、導入方法も記載したので、試して頂くこともできるかと思います。