Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TinyGo 開発サイクルを高速化する:Go で作るエミュレータ入門

TinyGo 開発サイクルを高速化する:Go で作るエミュレータ入門

2026/9/11に Go Conference 2026 で発表した登壇資料です。
https://gocon.jp/2026/
株式会社ZOZO
データ・AIシステム本部
検索基盤部 ZOZOAD開発ブロック
倉澤 大樹
#gocon #gocon26

Avatar for ZOZO Developers

ZOZO Developers PRO

September 11, 2026

More Decks by ZOZO Developers

Other Decks in Technology

Transcript

  1. 自己紹介 株式会社ZOZO 検索基盤部 ZOZOAD開発ブロック 倉澤 大樹 X: @kurasawah • バックエンドエンジニア

    • ZOZOTOWNの検索連動型広告システムの開発をしています • ZOZOではバックエンドの開発言語にGoを採用しています 余談 • © ZOZO, Inc. 生まれも育ちも中野なので今日話せることに縁を感じています 2
  2. このテーマを話そうと思ったきっかけ • Go Conference mini in Sendai をきっかけにTinyGoに入門してみました • まずはマイコン(Wio

    Terminal)を購入 • いざコードを書いて実機に書き込んでみると、色々詰まった • もっと手軽に動作確認できる方法が欲しくなった © ZOZO, Inc. 3
  3. 目次 1. TinyGo とは 2. 実機開発の課題 3. 今回作成したエミュレータの紹介 4. 最初の設計とその限界

    5. 設計の刷新: build tag x net/rpc x Ebitengine 6. 設計から得た学び 7. まとめ © ZOZO, Inc. 6
  4. Wio Terminalとは Wio Terminalの特徴 • カラーディスプレイ搭載のマイコンボード • Wi-Fi/Bluetooth内蔵 • 5方向スイッチ・3ボタン搭載

    • 内蔵センサー・拡張ポートが豊富 • TinyGoが公式に対応しているボード TinyGoでいろいろ作ってみたい方に、おすすめなマイコンです 画像引用元:秋月電子通商 https://akizukidenshi.com/catalog/g/g115275/ © ZOZO, Inc. 10
  5. 実機開発の課題① トラブルシューティングが難しい • ビルドは通っているのに実機に反映されない • (私の環境では)tinygo flashがセキュリティ制限(read-only mount)で使 えない •

    エラーメッセージがTinyGo固有で検索してもヒットしない コードが悪いのか、フラッシュが失敗しているのか、 わからない状態になる © ZOZO, Inc. 12
  6. 実機開発の課題② 開発サイクルが遅い 01 コード編集  02 ビルド  03 フラッシュ

     04 実機確認   _ デスクトップで確認できたら、どれだけ楽になるか 実機への書き込み(フラッシュ)をスキップし、開発サイクルを劇的に高速化 m c © ZOZO, Inc. 13
  7. 今回作成したエミュレータ 1. Wio Terminalの画面を デスクトップ上に再現 2. TinyGoのコードを ほぼそのままで動かせる 3. ©

    ZOZO, Inc. The Go gopher was designed by Renée French. Illustrations by avocadoneko. go run .するだけで反映 14
  8. エミュレータの使い方 1. エミュレーターの起動 $ wio-emu 2. ユーザーコード側からラッパーパッケージをimportするように書き換える import "github.com/kurakura967/wiodisplay/machine" 3.

    アプリケーションを実行 #エミュレータで確認 $ go run ./your_app/ #wio terminalへ反映させたい場合もそのままビルド可能 $tinygo flash -target wioterminal ./your_app/ © ZOZO, Inc. 17
  9. 最初のアイデア: ユーザーは何も変えなくていい PC向けのスタブに差し替え ユーザのTinyGoコード import ( $ wio-emu main.go import

    ( "machine" "machine/stub" "ili9341" "ili9341/stub" ASTによる書き換え ) func main() { コードを構文木として importを差し替え ) func userMain() { // 画面描画など } © ZOZO, Inc. // 同じロジック } 20
  10. ASTによるimportの自動書き換え // 差し替え前(TinyGo向け) // 差し替え後(PC向け) import ( "machine" "tinygo.org/x/drivers/ili9341" )

    import ( // PCでボタン入力を再現 "machine_stub" // PCでディスプレイ描画を再現 "ili9341_stub" ) これをASTによって自動で行うのが最初の設計 © ZOZO, Inc.
  11. EbitengineでWio Terminal をPC上に再現する Wio Terminal はディスプレイとボタンを持つデバイス • PC上で再現するには「描画」と「入力処理」の仕組みが必要 Ebitengine(Go製ゲームエンジン)はこの両方を持つ 毎フレーム

    Update() → Draw()の順で処理が走る • Update() : キーボード・マウスの入力を読み取る • Draw() : 画面に描画する • Layout() : ウィンドウサイズを定義する © ZOZO, Inc. 25
  12. 差し替え先の実装: 描画はどう画面に反映されるのか スタブ > ピクセルバッファ > EbitengineがWio Terminalの液晶部分に描画 1. ili9341スタブパッケージ(自作)がピクセルバッファに書き込む

    2. ピクセルバッファ(320x240の共有バッファ)に一時的に保持される 3. EbitengineのDraw()が毎フレーム、ピクセルバッファの内容を LCDエリアにスケールして描画する テキスト © ZOZO, Inc.
  13. 限界が積み上がった • 対応パッケージがハードコード ◦ 未対応のパッケージを使うと即ビルドエラー ◦ ユーザ側には手の打ちようがない • 起動のたびにgo mod

    tidyが走る ◦ 通信コストで起動が遅い • ホットリロードでウィンドウが毎回閉じる ◦ 再起動のたびに画面がリセットされる © ZOZO, Inc.
  14. 課題①: 対応パッケージがハードコード 差し替えるパッケージが限定されている var importReplacements = map[string]string{ "machine": "github.com/kurakura967/wiodisplay/machine", "tinygo.org/x/drivers/ili9341":

    "github.com/kurakura967/wiodisplay/driver/ili9341", } 対応パッケージを追加するにはエミュレータ自体を変更するしかない import "tinygo.org/x/drivers/ws2812" // LED ドライバ // → リストにない → PC 上でビルドエラー © ZOZO, Inc.
  15. 課題②: 起動のたびにgo mod tidyが走る 起動のたびに一時プロジェクトを生成して実行する 01 02 wio-emu main.go 一時フォルダ

    go.mod生成 03 go mod tidy 毎回ネット通信 (ボトルネック) 04 05 go build 実行 cコードを変更するたびに、この重いサイクルが繰り返される © ZOZO, Inc.
  16. 課題③: ホットリロードでウィンドウが毎回閉じる 01 02 wio-emu main.go (再実行) 新しいプロセスで Ebitengine ウィンドウ起動

    03 04 前のウィンドウが 閉じる 新しいウィンドウ が開く 開発中の画面状態が毎回リセットされ、開発のリズムが途切れる © ZOZO, Inc.
  17. ver.2のエミュレータの全体設計 クライアントプロセス $ go run . • ユーザーコードを実行する • ボタン入力・描画命令などをnet/rpcでサーバーへ送る

    サーバープロセス $ wio-emu . • EbitengineでWio Terminalの画面を常時表示 • ボタン状態を返し、描画命令を受け取って画面に反映する © ZOZO, Inc.
  18. build tagで実装を切り替える(エミュレータ側) ユーザーがimportする wiodisplay/machine は、内部で実装が分かれている tinygo flashもgo run.も同じmain.goで動く tinygo flash

    の時 実機の machine パッケージを 再エクスポート go run . の時 net/rpc を介してPC上のサーバー へ問い合わせる //go:build tinygo import "machine" //go:build !tinygo func (p Pin) Get() bool { type Pin = machine.Pin client.Conn.Call(....) } © ZOZO, Inc.
  19. net/rpcによるプロセス間通信 別プロセスで動かすことでwio-emuを起動したままユーザーコードを何度でも実行できる クライアントプロセス ユーザーのコード net/rpc サーバープロセス エミュレータ(wio-emu) $ go run

    . $ wio-emu . ➔ コード変更のたびに再実行 ➔ 起動したまま(常駐) 別プロセス化(net/rpc)による絶大なメリット • • • ウィンドウが閉じない — エミュレータ画面が維持されるため、実行の度に開き直すストレスがありません。 go mod tidy が毎回走らない — 依存関係の再検証プロセスをスキップでき、起動が極めて高速になります。 ver.1の課題が全て解決する — ユーザーの自由度と、エミュレータ開発の高速なフィードバックループを両立。 © ZOZO, Inc.
  20. 例えば、キー入力のAPIはほぼ同じ // koebiten (TinyGo) // Ebitengine (デスクトップ) koebiten.IsKeyPressed(koebiten.Key0) ebiten.IsKeyPressed(koebiten.Key0) •

    koebiten を ebitenに変えるだけ この対称性があるから 「実機用のコードをそのままデスクトップで確認する」体験が成立する © ZOZO, Inc.
  21. ver.2で解決した3つの課題 ver.1の課題 ① 対応パッケージがハードコード ver.2での解決アプローチ importを差し替えるだけ ユーザーによる自由な拡張を可能に設計変更 wio-emuは常時起動のまま維持 ② 起動のたびに

    go mod tidy が走る ③ ホットリロードの度にウィンドウが閉じる © ZOZO, Inc. 不要な依存解決ステップをスキップし、起動を極め て高速化 別プロセス化によりウィンドウは開いたまま 実行の度に画面を開き直すストレスを完全に解消
  22. 普通のGoと同じ開発スピード 開発の基本サイクル 開発速度を向上する工夫 1 wio-emu 起動 (最初の1回だけ) 2 go run

    . ウィンドウが閉じない 別プロセス化により、ホットリロード時も エミュレータ画面が開いたまま維持され、 開発が中断されません。 3 エミュレータ上で即確認 4 コード変更 5 即確認 © ZOZO, Inc. go mod tidy が毎回走らない 起動のたびに実行されていた不要な依存 解決ステップをスキップ。通常のGo開発 と同等の瞬時の起動を実現します。
  23. 設計から得た学び: パッケージ境界で止める ver.1: 内側に深く入り込む ver.2: パッケージ境界で止まる エミュレータがソースコードの中まで入り 込む設計 エミュレータは完全に境界の外で構える 設計

    • AST(抽象構文木)でユーザーコードを走 査する複雑な処理 • 純粋なパッケージを提供するだけのシン プルな依存関係 • 直接import文を書き換えるトリッキーな 挙動 • ユーザーコードには一切手を触れない安 全な実行環境 ユーザーの自由度がゼロ © ZOZO, Inc. ユーザーが自由に拡張できる