WebHID API は Firefox・Safari では利用できません。Chrome / Edge で開き直してください。
(Ruby のコンパイルと .ino / .urb のダウンロードはこのブラウザでも利用できますが、デバイス接続・SD 転送・HID コンソールは利用できません)
「Rubyで電子工作をやってみたい」——そう思っても、
高価なボード、複雑なセットアップ、専用ソフト…
とハードルを感じていませんか?
UIAPruby は、そのハードルをぐっと下げます。
UIAPduino(290円、CH32V003ベース)に
microSDカードアダプタ(300円ほど)を合わせて、ハードウェアは数百円。
あとは手持ちのChrome/Edgeブラウザだけ。
普段書いているRubyの文法でLEDを光らせ、ブザーを鳴らし、センサーを読み、
電源を切っても消えない変数($count)やドット絵の配列まで扱えます。
マイコンで Ruby といえば mruby。でも mruby のビルドには Ruby と rake が必要で、
Arduino IDE だけでは完結しません。
URB Lab は、その rake の役割をブラウザに移しました。
コンポーネント(mruby の mrbgems に相当)を選ぶと、
必要な機能だけを組み込んだ単一の .ino が生成され、Arduino IDE でそのまま書き込めます。
PC に mruby のビルド環境(rake の構成やクロスコンパイラ)を整える必要はありません。
Rubyコードのコンパイルもブラウザの中——@ruby/prism でコードを解析し、
独自の軽量バイトコード(.urb)に変換。
WebHIDでSDカードへ直接転送し、マイコン上のTinyVMが実行します。
インストール不要・クラウド不要。すべてブラウザの中で完結。
Rubyist が電子工作へ踏み出す入口を、
Wakayama.rb
が作りました。mruby ボード GR-CITRUS に続く、二つ目の扉です。
⚡ クイックスタート(4ステップ)
-
1
スケッチをダウンロードして書き込む(初回のみ)
この下の「コンポーネント選択」で使いたい機能を選び、UIAPrubyVm***.inoをダウンロード。 Arduino IDEでUIAPduinoに書き込みます。
書き込み時は、UIAPduino のボタンを押しながら USB ケーブルを挿し、 ブートローダーを起動した状態にしてから書き込んでください。
FQBN:UIAP_HID:ch32v:CH32V003:pnum=V14,usb=webhid,opt=oslto -
2
Rubyコードを書く
エディタにRubyコードを入力します。サンプルボタンですぐ試せます。
例:led = GPIO.new(2, GPIO::OUT)→ D2ピンをLED出力に設定
loop do led.toggle; sleep(0.5); end→ 0.5秒ごとに点滅 -
3
「コンパイル」ボタンを押す
ブラウザ内でRubyコードをAST解析し、TinyVM用.urbバイトコードへ変換します。
✓ OK と表示されれば生成完了です。 -
4
SDカードに送信 → RUN
USBケーブルでUIAPduinoを接続し「📤 SDカードに送信」をクリック。 未接続ならデバイス選択ダイアログが自動で開きます。転送後「▶ RUN」で実行開始。
💡 ファイル名をmain.urbにしておくと、 電源投入から 5 秒後に自動実行されます。
🔧 必要なハードウェア
- UIAPduino SD版(CH32V003ベース、microSDスロット搭載)
- MicroSDカード(FAT32フォーマット)
- USBケーブル(Micro-B または Type-C)
- LED / タクトスイッチ / ブザーなど(実験用)
💻 必要なソフトウェア
- Google Chrome または Microsoft Edge(最新版)
- このページ — WebHID対応ブラウザのみ動作
- Arduino IDE(スケッチ初回書き込み時のみ)
- UIAP_HID ボードパッケージ v1.2.7 以降(必須)
ボードマネージャURL:https://raw.githubusercontent.com/tarosay/board_manager_files/main/package_uiap_hid_index.json
UIAPduino SD 版 対応ピン一覧(クリックで開く)
| Arduinoピン番号 | CH32V003ポート | PWM | ADC | 用途・備考 | UIAPruby記法 |
|---|---|---|---|---|---|
| 2 | PC0 | ✅ | — | LED出力(基板上LED) | GPIO.new(2, GPIO::OUT) |
| 3 | PC1 | — | — | SDA (I2C) / HC-SR04 TRIG | Ultrasonic.new(3, 4) |
| 4 | PC2 | — | — | SCL (I2C) / HC-SR04 ECHO | Ultrasonic.new(3, 4) |
| 5 | PC3 | ✅ | — | PWMブザー(圧電ブザー推奨) | Tone.new(5) |
| 11 | PD1 | — | — | タクトスイッチ(PULL_UP使用) | GPIO.new(11, GPIO::IN | GPIO::PULL_UP) |
| 0, 1, 12, 15, 16 | PA1/PA2/PD2/PD5/PD6 | — | ✅ | アナログ入力(ADC対応ピン) | ADC.new(0) 〜 |
| 6 | PC4 | ✅* | ✅* | ⚠ SDカード SS専用 — 使用不可 | — |
| 7 / 8 / 9 | PC5 / PC6 / PC7 | — | — | ⚠ SDカード SCK/MOSI/MISO専用 — 使用不可 | — |
ピン6はPWM・ADC対応ですが、SDカード搭載版では使用不可です(* 印)。
選択内容が下のエディタのサンプルと .ino ファイルに反映されます。
UIAPruby — Ruby構文を @ruby/prism でAST解析し、UIAPduino TinyVM用 URB1 バイトコードへ変換します。
UIAPduino 上では Ruby VM を実行しません。
.rb ファイルをエディタにドラッグ&ドロップするか、「開く」ボタンで読み込めます。
require "name" または require_relative "name" で読み込めます。
⚠ D7(SCK) D8(MOSI) D9(MISO) D6(SS) は SD カード SPI 専用のため GPIO に使用できません。
自動実行が始まるのは、電源オンから 5 秒待ってからです。
USB 接続・RUN ボタン操作なしで単体動作するので、完成したスクリプトの運用に便利です。
ファイルヘッダー(8 バイト)
SD変数メタデータ(version = 2 のみ、ヘッダー直後)
$var: 電源を切っても値を保持){プログラム名}_{変数名}.urv に保存TinyVM 命令セット(リトルエンディアン)
レジスタ R0–R3 は int32_t。Q16.8 固定小数: 1.0=256, 0.5=128。
MUL_Q16 は int32 乗算 — 積が ±32768 未満まで正確(2026-07-12 以前のファームは int16 制限で ±128 以上のオペランドが壊れるバグあり。要再書き込み)。
GPIO / PWM / I2C / その他
| Ruby 記法 | 変換後命令 | Phase |
|---|---|---|
led = GPIO.new(2, GPIO::OUT) | GPIO_MODE pin=2 OUTPUT | 1 |
button = GPIO.new(11, GPIO::IN | GPIO::PULL_UP) | GPIO_MODE pin=11 INPUT_PULLUP | 1 |
led.on / led.high / led.write(1) | GPIO_WRITE pin, 1 | 1 |
led.off / led.low / led.write(0) | GPIO_WRITE pin, 0 | 1 |
led.toggle | GPIO_TOGGLE pin | 1 |
buzzer = Tone.new(5) | GPIO_MODE pin=5 OUTPUT | 1 |
buzzer.frequency(440) | TONE_FREQ pin, 440 | 1 |
buzzer.off / buzzer.frequency(0) | TONE_FREQ pin, 0 | 1 |
buzzer.tone(440, 0.2) | TONE_FREQ 440 → WAIT_MS 200 → TONE_FREQ 0 | 1 |
button.low? / button.high? | GPIO_READ pin, R0 + JNZ/JZ | 2 |
sleep(0.2) / wait_ms(200) | WAIT_MS 200 | 1 |
wait_ms n * 100 / sleep(n) — 変数の時間待ち | WAIT_MS_REG reg, 100(掛け算はファームウェア側) | 1 |
print / puts / p / putc | PRINT_STR | 3 |
warn d / warn sonar.read — DEVICE LOG へデバッグ出力(Sv 不要。Q1+Sv 構成のみ使用不可) | WARN_REG reg | 3 |
raise "msg" / raise unless cond | PRINT_STR + HALT | 4 |
def name … end | インライン展開 | 5 |
break / next | JMP loop_end / loop_start | 6 |
g = 0.1 * 0.5 / Q16.8 演算 | LOAD_Q16 + MUL_Q16 … | 7 |
if d < 15 — 比較演算(< > == >= <= !=)※Q1 必須 | CMP_LT/GT/EQ_Q16 + JZ/JNZ | Q1 |
case d / when 0, 10 — 値の列挙 ※Q1 必須 | CMP_EQ_Q16 + ジャンプ(OR 展開) | Q1 |
when 0...15 / when 1..3 — レンジ ※Q1 必須 | CMP_LT/GT_Q16 ×2 + ジャンプ(範囲比較) | Q1 |
f = ->(x) { x < 15 } — ラムダ式の定義 | コンパイル時登録(バイトコードなし) | — |
f.call(d) / when f / when ->(x){…} — ラムダ呼び出し | 本体をインライン展開(下記参照) | — |
require_relative "path" | compile-time include | 8 |
motor = PWM.new(2) | GPIO_MODE pin=2 OUTPUT | Pw |
motor.frequency(500) — 基準周波数(省略時 1kHz) | PWM_BASE_FREQ pin, 500 | Pw |
motor.duty(255) — リテラル 0〜255 | PWM_DUTY pin, 255(即値) | Pw |
motor.duty(speed) — 変数 ※Q1 必須 | PWM_DUTY_REG pin, reg | Pw |
servo.angle(90) — リテラル 0〜180 ※要 frequency(50)(50Hz・0.5〜2.4ms、分解能約7°) | PWM_DUTY pin, 19(コンパイル時計算) | Pw |
servo.angle(pos) — 変数 ※Q1 必須・要 frequency(50) | Q16 演算 + PWM_DUTY_REG pin, reg | Pw |
sensor = ADC.new(0) ピン: 0,1,12,15,16 | — (初期化なし) | Ad |
puts sensor.read / print sensor.read | ADC_READ pin,R0 + PRINT_REG | Ad |
sonar = Ultrasonic.new(3, 4) 第1引数=TRIG, 第2引数=ECHO(任意のGPIOピン) | GPIO_MODE trig OUT + GPIO_MODE echo IN | Us |
puts sonar.read / print sonar.read | ULTRASONIC_READ trig,echo,R0 + PRINT_REG | Us |
$count = $count + 1 — $変数は SD に永続保存 ※Q1 必須 | VAR_LOAD + ADD_Q16 + VAR_STORE | Ve |
| 数値変数の3つ目以降(R0/R1 あふれ)— SD に自動配置 ※Q1 必須 | VAR_LOAD / VAR_STORE | Ve |
数値配列 arr = Array.new(10) / arr[i]($付きで永続) ※Q1 必須 | VAR_LOAD_IDX / VAR_STORE_IDX | Ve |
puts $count / puts g — 数値出力 ※数値変数は Q1 必須(デバッグなら warn が Ve だけで使える) | VAR_LOAD + PRINT_REG / PRINT_REG | Sv |
n = rand(6) — 0〜5 の整数 | RAND 0,6,Rn | Rn |
n = rand(1..6) — 1〜6 の整数 | RAND 1,7,Rn | Rn |
srand(42) — シード設定 | SRAND 42 | Rn |
ラムダ式(コンパイル時インライン展開)
| 使い方 | 説明 | 必要 |
|---|---|---|
near = ->(x) { x < 15 } | 定義。本体は式1つだけ。バイトコードは出力されない | — |
case dwhen near / when ->(x) { x < 15 } | case 対象 d が引数 x に渡り、本体が条件として展開される | Q1 |
if between.call(d, 5, 10) | if / while / until / unless / 三項演算子の条件で使用可。複数引数・&& / || も OK | Q1 |
y = double.call(n) | 数値式として使用(戻り値 = 本体の式の値) | Q1 |
wait_ms half.call(500) | 引数がすべて定数ならコンパイル時に計算され、リテラルを書ける場所(sleep / servo.angle 等)ならどこでも使える | — |