はじめに
Rubyといえば、Web開発のイメージが強い方も多いでしょう。私自身、組み込み開発の経験はなく、今回のセッションでは、Rubyを組み込み開発(=マイコンなどの小さなコンピュータ向け開発)に応用する取り組みが紹介されていて、とても新鮮でした。
このセッションは、Fusic株式会社のIoTエンジニアである岡崎雄平(Yuhei Okazaki)氏によって発表されました。マイコンの世界でもRubyが使えるという可能性に驚きました。
PicoRubyとは
PicoRubyとは、組み込み環境でRubyを動かすために開発された、超軽量なRuby実装です。 通常のRuby(MRIやCRuby)は、PCやサーバのようなある程度リソースがある環境向けに作られています。一方、PicoRubyはマイコンのような小さな機械でも動作するように設計されています。
しかも、REPL(読み取り・評価・出力のループ)や簡単なスクリプトの実行など、Rubyらしいインタラクティブな体験がそのまま使えるというのが大きな魅力です。
ちなみにREPLとは「Read(読み取り)→Eval(評価)→Print(出力)→Loop(繰り返し)」の略です。Rubyの対話モード(irb)のように、1行ずつコードを入力してすぐに実行結果を確認できる仕組みです。 Web開発でいうと、Railsコンソールのようなものです。
PicoRubyはどこで動いていたのか
もともとPicoRubyは、Raspberry Pi Pico(RP2040)という小型マイコンボード上で動作するように開発されていました。RP2040は安価で扱いやすく、PicoRubyと相性の良いプラットフォームとして広く使われています。
ただし、開発者の関心やニーズはRaspberry Pi Picoに限らず、より多機能なマイコンにも向けられています。そこで今回のセッションでは、より汎用性の高いマイコンであるESP32でもPicoRubyを動かせるようにする取り組みが紹介されました。
これが「移植(ポーティング)」と呼ばれる作業です。
ESP32とは
PicoRubyが今回移植されたのは、ESP32(イーエスピースリーツー)というマイコンです。
これはEspressif Systemsというメーカーが作っている、Wi-FiとBluetoothを内蔵した高性能マイコンチップで、IoT(モノのインターネット)界隈では非常に人気があります。
ESP32には以下のような特徴があります。
- 手のひらサイズ
- 安価(数百円〜数千円)
- 電池でも動く
- 無線通信ができる
これらの特徴により、センサーと組み合わせて「温度をクラウドに送る」みたいな用途でよく使われています。
なぜ移植が必要なのか
PicoRubyは「組み込み向けRuby処理系」ですが、最初からすべてのマイコンで動くわけではありません。
たとえば、以下のような違いがあります:
- マイコンによってCPUアーキテクチャが違う(RISC-V、Xtensaなど)
- 周辺機能の使い方が違う(GPIOやUARTなど)
- 開発環境が異なる(ビルドシステム、クロスコンパイラなど)
これらの違いに対応するために、以下のような作業が必要になります:
目的 | 作業内容 |
---|---|
対象マイコン用のビルド設定 | build_config.rb の新規作成 |
puts などをESP32上で出力できるようにする | 標準出力関数 hal_write の実装 |
REPLやシェル機能などを動かすための機能追加 | 必要なmrbgemの対応 |
PCでESP32用バイナリをビルドできるようにする | クロスコンパイル環境の調整 |
これら一連の作業を通して、「PicoRubyをESP32で"動作可能"な形にする」ことが「移植(ポーティング)」です。
PicoRubyをESP32で動かす方法
このセッションでは、「どうやってPicoRubyをESP32に移植(ポーティング)したのか」が丁寧に説明されていました。
以下はその流れの概要です。
ステップ1:ビルド設定を整える
なぜビルド設定が必要なのか
私のような組み込み開発初心者にとって、「ビルド設定」という言葉自体がちょっと難しく感じました。調べてみると、通常のWeb開発と組み込み開発では、プログラムを実行可能な形にする過程が大きく異なります。
Webアプリ開発では、rails s
やnpm start
などのコマンドを実行するだけでアプリが動き始めますが、マイコン開発では「ビルド」という工程が必須です。これは、私たちが書いたコードをマイコンが理解できる機械語に変換し、マイコンのメモリに書き込むための準備をする作業です。
具体的に何をするのか
ESP32というマイコンでPicoRubyを動かすためには、以下のような設定ファイルを作成・調整する必要があります:
ESP-IDFの設定: ESP32を開発するための公式フレームワーク「ESP-IDF」を設定します。これは、ESP32のハードウェア機能(Wi-FiやBluetoothなど)を使うための基盤となるものです。
build_config.rbの作成: PicoRubyをESP32向けにビルドするための設定ファイルです。ここでは、ESP32の特性(メモリサイズやCPUタイプなど)に合わせた最適化設定をします。
Makefileの調整: ビルドコマンドの手順を定義するファイルです。「どのソースコードを、どんな順番で、どんなオプションでコンパイルするか」を指定します。
CMakeLists.txtの設定: より現代的なビルドシステムで、プロジェクトの構成や依存関係を管理するファイルです。
これらの設定は、普段Webアプリを開発している私には馴染みがなく、最初は戸惑いました。例えるなら、Railsのconfig/application.rb
やGemfile
のような役割を果たすものと考えられます。ただ、マイコン開発ではより低レベルな設定が必要で、ハードウェアの特性に合わせた細かい調整が求められます。
クロスコンパイルとは
もう一つ重要なポイントとして「クロスコンパイル環境の整備」があります。これは、自分のパソコン(例えばMacやWindows)上から、ESP32という全く異なるCPU向けプログラムをビルドするための仕組みです。
例えば、私たちが普段使っているパソコンはx86_64やARMなどのCPUアーキテクチャがありますが、ESP32はXtensaやRISC-Vという全く異なるCPUアーキテクチャを使っています。この違いを吸収し、パソコン上でESP32用のプログラムを作れるようにするのが「クロスコンパイル環境」です。
Web開発では、開発環境と本番環境でCPUが異なるということはあまり意識しませんが、組み込み開発ではこの違いが非常に重要だということを学びました。
ステップ2:標準出力を実装する
なぜ標準出力の実装が必要なのか
Rubyでプログラミングをする際、puts
やprint
などを使って画面に文字を表示することは基本中の基本です。しかし、ESP32のような小さなマイコンでは、これらの機能は最初から使えるわけではありません。
通常のパソコンでは、画面に文字を表示する仕組みはOSが提供していますが、マイコンにはそのような高度なOSがありません。そのため、「文字を表示する」という基本的な機能も自分で実装する必要があるのです。
具体的に何をするのか
ESP32上でRubyのputs
などを使えるようにするためには、C言語側で hal_write
という関数を実装します。この関数は「ハードウェア抽象化レイヤー(Hardware Abstraction Layer)」の一部で、Rubyから「文字を表示したい」という要求があったときに実際にハードウェアを操作する役割を担います。
以下のコードは、ESP32のUART(シリアル通信)を使って文字を出力するための実装例です:
int hal_write(int fd, const void *buf, int nbytes) { for (int i = 0; i < nbytes; i++) { putchar(((char*)buf)[i]); // 1文字ずつ出力 } fflush(stdout); // バッファをフラッシュして確実に出力 return nbytes; // 書き込んだバイト数を返す }
このコードにより、Rubyのputs "Hello, World!"
のような命令が実行されると、ESP32のシリアルポートから「Hello, World!」という文字列が出力されるようになります。これがないと、Rubyのプログラムは動いていても、その結果を見ることができません。
ステップ3:必要なmrbgemを移植する
なぜmrbgemの移植が必要なのか
通常のRubyでは、様々な機能を「gem」というパッケージで追加できます。PicoRubyでも同様に「mrbgem」という形で機能を追加できますが、ESP32のような限られたリソースのマイコンでは、すべてのgemがそのまま動くわけではありません。
マイコン向けに最適化されたgemを選び、必要に応じて調整(移植)することで、限られたメモリやCPUパワーの中でも快適にRubyを動かすことができるようになります。Web開発とは異なり、マイコンでは非常に限られたリソースの中で動作させる必要があるため、この最適化作業が重要なのだと学びました。
具体的に何をするのか
REPLやechoコマンド、シェル機能などの基本的な対話環境を実現するために、以下のmrbgem(PicoRubyの拡張機能)を移植・有効化します:
- picoruby-shell: コマンドラインシェルの機能を提供する(コマンド入力や履歴管理など)
- picoruby-env: 環境変数の管理機能である
- picoruby-io-console: キーボード入力の制御や画面出力の調整機能である
- picoruby-editor: 簡易的なテキストエディタ機能である
- picoruby-vfs: 仮想ファイルシステム(ファイル操作の基盤)である
ESP32のメモリは限られているため、必要最小限のgemだけを動かす構成にします。また、一部のgemが依存している機能で、ESP32では実装が難しいものについては、「ダミー定義」という形で代用します。これはESP32の制約に合わせた実装方法を採用しています。
このステップにより、ESP32上でもRubyらしい対話的なプログラミング体験が可能になります。
ステップ4:REPL起動用のRubyスクリプトを書く
なぜREPL起動用スクリプトが必要なのか
Rubyの魅力の1つは、対話的にコードを試せるREPL(Read-Eval-Print Loop)環境です。これは「コードを書いて→実行して→結果を見る」というサイクルをリアルタイムで繰り返せる機能で、プログラミングの学習や実験に非常に役立ちます。
ESP32上でもこのRuby体験を実現するためには、マイコンの起動時に自動的にREPL環境を立ち上げるスクリプトが必要です。これはマイコン起動時に自動的にRuby環境を利用できるようにするための仕組みです。
具体的に何をするのか
ESP32の起動時に実行されるRubyスクリプトを作成します。このスクリプトは、シェル機能を初期化し、ユーザーからの入力を受け付ける状態にするためのものです:
# 必要なライブラリを読み込む require "shell" # シェル機能(コマンドライン環境) require "machine" # マイコンのハードウェア制御機能 # 標準入出力を設定 STDIN = IO # キーボードからの入力 STDOUT = IO # 画面への出力 begin # エコーバック(入力文字の表示)を無効化 # これにより、カーソル移動などの特殊キー処理が可能になる STDIN.echo = false # シェルを初期化(clean: trueで起動時に画面をクリア) $shell = Shell.new(clean: true) # 起動メッセージを表示 puts "Starting shell...\n\n" # PicoRubyのロゴを表示 $shell.show_logo # シェルを起動(ここからユーザー入力の受付開始) $shell.start # エラーが発生した場合の処理 rescue => e puts "Not available" puts "#{e.message} (#{e.class})" # エラー内容を表示 end
このスクリプトにより、ESP32の電源を入れるだけで、Rubyのコマンドを入力して実行できる環境(REPL)が立ち上がります。まるでパソコン上でirbを使っているような体験ができるようになります。
これがあることで、ESP32上でRubyコードを書いて即座に実行結果を確認したり、対話的にハードウェアを制御したりすることが可能になります。例えば、led = Pin.new(13, :output); led.high
のようなコマンドを入力するだけで、LEDを光らせるといった操作ができるようになります。
苦労した点とトラブル事例
セッション内では、以下のようなトラブル事例も紹介されていました:
- ウォッチドッグタイマー:RTOSの制御を妨げて再起動を繰り返した
- requireが使えない:ファイルシステム非対応のため、load_irepに切り替えて解決
- スタックオーバーフロー:RTOSタスクのスタックサイズ不足によりクラッシュ
- CSI(カラーコード)が効かない:端末設定と出力文字列の整合性に課題
いずれもESP32の挙動に応じて丁寧にデバッグ・修正されていました。
デモ
実際のデモでは、PicoRubyが起動し、echoやputsなどが使える状態でREPLが動作していました。
最小構成のmrbgemと、main_task.rb
スクリプトで、対話型のRuby環境がESP32上に構築されていました。
特に印象的だったのは、ESP32上で動作するブロックゲームのデモです。シンプルなブロックを操作するゲームでしたが、これがRubyで書かれており、小さなマイコン上でも動作していることに驚きました。ハードウェア上で動くRubyのゲームを見るのは新鮮な体験でした。このデモは、PicoRubyの可能性を視覚的に示す素晴らしい例だと感じました。
感想
マイコンでRubyが動く様子を見て、組み込み開発に興味が湧きました。普段Web開発で使っているRubyで、LEDを光らせたり、ブロックゲームのようなアプリも動かせるのは驚きでした。
C言語のような低レベル言語が主流と思っていた分野で、Rubyのような親しみのある言語が使えるのは、とても魅力的だなと思いました。PicoRubyのような取り組みが広がれば、ハードウェア開発のハードルがもっと下がって、多くの人が気軽に挑戦できるようになると感じました。
関連リソース
- 発表スライド: Speaker Deck
- 公式セッションページ: RubyKaigi公式
- GitHub:
- スピーカー: Yuhei Okazaki(X: @Y_uuu, GitHub: @Y_uuu) / Fusic株式会社
Techouseでは、社会課題の解決に一緒に取り組むエンジニアを募集しております。 ご応募お待ちしております。