ファームウェア開発は常に困難を伴うものだった。ツールチェーンの不整合、「自分のマシンでは動く」というビルド、そして既存製品の維持と新機能の提供との間の葛藤などが挙げられる。この記事では、DockerとDockerサンドボックスを使用してファームウェア開発、特にESP32プロジェクトの開発を容易にする方法について解説します。今日では、チームは複数のハードウェア改訂、複数のESP-IDFリリース、長期にわたる顧客展開をサポートすることになり、同時にWi-Fi 6、Matter、電力最適化などの新機能の反復開発も行っています。
公式のespressif/idf Dockerイメージは、再現性の問題を解決します。Dockerサンドボックス( sbx CLI)は、より新しい課題を解決します。それは、AIコーディングエージェントにノートパソコンの鍵を渡すことなく、ファームウェアをフルスピードで開発できるようにすることです。この記事では、クリーンビルド、新規ファームウェアと既存ファームウェアのための並列環境、そして安全な非監視型AIセッションを組み合わせた実践的なワークフローについて解説します。
パート1:ベースライン – 公式イメージを使用した構築
espressif/idfイメージには、フレームワーク自体、Xtensa/RISC-V ツールチェーン、Python 環境、CMake、ninja など、ESP-IDF の完全なインストール環境が固定された状態で同梱されています。ビルドには1つのコマンドが必要です。
docker run --rm -v $PWD:/project -w /project \
-u $UID -e HOME=/tmp \
espressif/idf:release-v5.4 idf.py build
盲信に陥るよりも、理解しておくべきいくつかの詳細事項:
-u $UID -e HOME=/tmpはコンテナをユーザーとして実行するため、build/内のビルド成果物は root の所有物ではなくなります。HOME=/tmpIDF ツールにキャッシュ用の書き込み可能なホームディレクトリを与えます。- タグをピン留めしてください。
latestトラックはマスターブランチにあり、最終的には壊れてしまいます。vX.Yタグは修正済みリリースを表し、release-vX.Yタグはリリースブランチを追跡し、バグ修正を受け取ります。保守中の製品については、正確なvX.YZタグが最も安全です。活発に開発中の製品については、release-vX.Yが適切なバランスです。 - マウントされたプロジェクトの所有者がコンテナ内のユーザーと異なる場合、Git は「所有権が疑わしい」というエラーを表示します。このイメージは、パスをホワイトリストに登録するための
-e IDF_GIT_SAFE_DIR='/project'をサポートしています(複数のパスを区切るには : を使用します)。 -e IDF_CCACHE_ENABLE= 1を使用してコンパイラキャッシュを有効にし、ボリュームをマウントすることで実行間でキャッシュを保持します。中規模プロジェクトの全面的な再構築にかかる時間が、数分から数秒に短縮される。
フラッシュとモニタリング
Linuxでは、シリアルデバイスを以下のようにパススルーします。
docker run --rm -it \
--device=/dev/ttyUSB0 \
--group-add $(getent group dialout | cut -d: -f3) \
-v $PWD:/project -w /project \
-u $UID -e HOME=/tmp \
espressif/idf:release-v5.4 idf.py flash monitor
--group-add必要なのは、root ではなく$UIDとして実行しており、デバイス ノードがdialoutに属しているためです。
macOSおよびWindowsでは、Docker DesktopはUSBデバイスをコンテナに渡すことができません。クリーンな回避策は、esptoolがネイティブにサポートするRFC2217を使用したネットワークシリアルブリッジです。ホスト側:
pip install esptool
esp_rfc2217_server -p 4000 /dev/cu.usbserial-1420
コンテナ内で、idf.py をネットワークポートに指定します。
idf.py --port 'rfc2217://host.docker.internal:4000?ign_set_control' flash monitor
これはハッキングのように見えるかもしれないが、実は機能なのだ。シリアルポートがネットワークエンドポイントになると、あらゆるものがそこにアクセスできるようになる。コンテナ、CIランナー、そして(後述するように)サンドボックス化されたAIエージェント。このコツを覚えておいてください。これはパート3の要です。
Makefileの背後に隠す
これらのコマンドを二度入力してはいけません。小さなMakefileを使うことで、配管構造が変更されてもインターフェースの安定性を維持できます。
IDF_IMAGE ?= espressif/idf:release-v5.4
PORT ?= /dev/ttyUSB0
DOCKER_RUN = docker run --rm -it \
--device=$(PORT) \
--group-add $(shell getent group dialout | cut -d: -f3) \
-v $(PWD):/project -w /project \
-v idf-ccache:/ccache -e CCACHE_DIR=/ccache -e IDF_CCACHE_ENABLE=1 \
-u $(shell id -u) -e HOME=/tmp -e IDF_GIT_SAFE_DIR=/project \
$(IDF_IMAGE)
build:
$(DOCKER_RUN) idf.py build
flash:
$(DOCKER_RUN) idf.py flash
monitor:
$(DOCKER_RUN) idf.py monitor
menuconfig:
$(DOCKER_RUN) idf.py menuconfig
shell:
$(DOCKER_RUN) bash
これで、 make buildすべての開発者と CI で同じように動作し、IDF バージョンの切り替えはmake build IDF_IMAGE=espressif/idf:release-v 5 . 3 . となります。
パート2:並列環境 – 新機能とレガシー機能の並置
ここからコンテナ方式は単なる利便性にとどまらず、働き方そのものを変革し始めるのです。各コンテナは完全に分離されているため、同じマシン上で、2つの異なるIDFバージョンを2つの異なるボードに対して同時に実行できます。
# Terminal 1 - new feature branch, IDF 5.4, experimental board
docker run --rm -it --device=/dev/esp32-experimental \
-v $PWD/new-feature:/project -w /project \
-u $UID -e HOME=/tmp \
espressif/idf:release-v5.4
# Terminal 2 - legacy firmware, IDF 5.3, production board
docker run --rm -it --device=/dev/esp32-production \
-v $PWD/legacy:/project -w /project \
-u $UID -e HOME=/tmp \
espressif/idf:release-v5.3
典型的な使用例:一方のボードに実験的なコードを書き込みながら、もう一方のボードでは長時間実行される耐久テストや顧客向けデモをそのまま維持する。ファームウェアのバージョン間で消費電力をA/B比較する。最新のツールチェーンで修正プログラムを開発しながら、旧バージョンのツールチェーンで現場のバグを再現する。
udev を使用した安定したデバイス名
/dev/ttyUSB 0と/dev/ttyUSB 1はプラグの順番によって入れ替わるため、最終的に間違ったボードにファームウェアを書き込んでしまう可能性があります。Linuxでは、アダプタのシリアル番号をキーとするudevルールを使用して、それらを固定します。
# find the serial numbers
udevadm info -a /dev/ttyUSB0 | grep '{serial}'
# /etc/udev/rules.d/99-esp32.rules
SUBSYSTEM=="tty", ATTRS{serial}=="A50285BI", SYMLINK+="esp32-experimental"
SUBSYSTEM=="tty", ATTRS{serial}=="B7743NM0", SYMLINK+="esp32-production"
udevadm control --reload実行すると、シンボリックリンクは再起動や再接続後も保持され、Makefile のターゲットは列挙による偶発的な操作ではなく、役割によってボードを参照できるようになります。
または、Composeでコード化する
2つの環境設定が永続的な場合、シェル履歴よりもcompose.yamlファイルの方がそれをより適切に文書化できます。
services:
new-feature:
image: espressif/idf:release-v5.4
volumes: ["./new-feature:/project"]
working_dir: /project
devices: ["/dev/esp32-experimental:/dev/ttyUSB0"]
stdin_open: true
tty: true
legacy:
image: espressif/idf:release-v5.3
volumes: ["./legacy:/project"]
working_dir: /project
devices: ["/dev/esp32-production:/dev/ttyUSB0"]
stdin_open: true
tty: true
docker compose run new-feature idf.py flash monitorと、ロールから物理ボードへのマッピングがバージョン管理されます。
パート3:Dockerサンドボックス – AIエージェントを監視なしで動作させる
Claude Codeのようなコーディングエージェントは、ファームウェア作業に非常に役立ちます。IDFバージョン間でコンポーネントを移植したり、ユニットテストを作成したり、 sdkconfigで構成のずれを追跡したりできます。しかし、それらが役立つためには、ビルド、フラッシュ、 pip install 、場合によってはDocker自体など、 様々な処理を実行する 必要がある。エージェントにホスト上で直接、権限をバイパスしたモードでそのような自由を与えることは、正当な理由から好ましくありません。
Docker Sandbox は、コンテナよりも強力な基本要素でこの問題を解決します。各サンドボックスは、独自のカーネル、ファイルシステム、ネットワークスタック、および独自のプライベート Docker デーモンを備えたマイクロ VMです。エージェントはパッケージのインストール、システム設定の変更、コンテナの構築と実行を行うことができますが、これらの操作はホストには一切影響を与えません。ワークスペースのディレクトリはサンドボックス内の同じパスに同期されるため、エラーメッセージ内のファイルパスは両方の環境で一致します。
CLIは小さくて分かりやすい。
# start Claude Code in a sandbox for the current project
sbx run claude
# work on a specific directory
sbx run claude ~/firmware/new-feature
# see what's running, resource usage, network requests
sbx
# list and clean up
sbx ls
sbx rm new-feature
ファームウェア開発においては、特に以下の3つの特性が重要となる。
- 使い捨て。エージェントは、esptoolのバージョン、パーティションテーブル、またはカスタムツールチェーンを試すことで、自身の環境を破壊する可能性があります。
sbx rmは、決して起こりませんでした。ホスト側のIDF設定(もし設定されている場合)は変更されません。 - ネットワークポリシー。サンドボックスは、オープン、バランス(事前に承認された開発者およびパッケージマネージャーのドメインに対してはデフォルトで拒否)、ロックダウンの 3 つのモードでホスト側のプロキシを介してトラフィックをルーティングします。ファームウェアを予期しない場所に
curlしようと決めたエージェントは、単純にそれができません。 - 認証情報の分離。APIキーとトークンはホスト側のプロキシによって送信リクエストに挿入され、サンドボックス自体はそれらを認識することはありません。即時投与された薬剤は、自身が持っていないものを体外に排出することはできない。
しかし、エージェントはどのようにしてボードをフラッシュするのでしょうか?
ここでパート1のRFC2217トリックが役に立ちます。サンドボックスは仮想マシンであり、USBパススルー機能はありません。しかし、ホストへのネットワーク経路は存在する。そこで、ホスト上でシリアルポートをネットワークサービスとして公開します。
esp_rfc2217_server -p 4000 /dev/esp32-experimental
そして、エージェント(プロジェクトのCLAUDE.mdまたは同等のファイル)に以下の内容をフラッシュするように指示します。
idf.py --port 'rfc2217://host.docker.internal:4000?ign_set_control' flash monitor
これで、エージェントのループ全体がサンドボックス内でエンドツーエンドで実行されます。編集、自身が生成したコンテナ内でのビルド、実際のハードウェアへの書き込み、モニター出力の読み取り、バグの修正。お使いのマシン上でアクセスできるのは、明示的に公開したシリアルポート1つだけです。これは非常に優れたトレードオフだ。完全なハードウェア・イン・ザ・ループの自律性と、最小限の爆発範囲を実現している。
ボードごとにサンドボックスを 1 つ実行すると、パート 2 のエージェント版の並列環境パターンが得られます。つまり、ポート 4000 を介して実験用ボードで反復処理を行うエージェントと、ポート 4001 を介して本番ボードを監視する 2 番目のロックダウンされたエージェントが存在します。
正直な注意点
サンドボックスはコンテナよりも新しい技術であり、その点は随所に表れている。MicroVM分離は、macOS(Apple Silicon)、Windows 11、およびKVMを搭載したLinuxで利用可能です。マイクロVM内でのビルドパフォーマンスは、ネイティブコンテナに比べて明らかに遅い。エージェントセッションには問題ないが、独自のタイトな内部ループでは煩わしい。また、このエージェントは設計上、権限バイパスモードで動作します。分離の仕組みは権限システムにあるため、他の貢献者と同様に、マージする前に差分を確認してください。
パート4:まとめる – 日々のワークフロー
- 通常の開発環境:
espressif/idfイメージ(およびコンテナ内のEspressif IDF拡張機能)を含むVS Code開発コンテナ。CIと同じイメージ、完全なIntelliSense、ネイティブコンテナ並みの速度。 - AIを活用した実験:
sbx run claude --branch <feature>.ブランチフラグを使用すると、エージェントのコミットがワークツリー上に保持されるため、チェックアウトがクリーンな状態に保たれます。完了したらレビューしてマージしてください。 - マルチボードテスト:デバイスごとに 1 つの並列コンテナ (ユーザー) または並列サンドボックス (エージェント) があり、udev-stable 名とボードごとに 1 つの
esp_rfc 2217 _serverあります。 - CI:公式の
espressif/esp-idf-ci-actionを使用したGitHub Actions。開発イメージと同じIDFバージョンに固定します。ローカル環境でのビルドが成功すれば、CI環境でも成功します。同じ部品です。
# .github/workflows/build.yml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { submodules: recursive }
- uses: espressif/esp-idf-ci-action@v1
with:
esp_idf_version: v5.4
target: esp32s3
プロのヒント
- 正確な画像タグをピン留めします (
release-v 5 . 4、latestではない場合)、リポジトリ(Makefile または compose ファイル)にタグを記録して、ツールチェーンのバージョンがコードレビューの一部となるようにします。 - 製品ラインごとに1つのプロジェクトフォルダ(
new-feature/、legacy/)があり、それぞれにピン留めされた画像が含まれています。IDFのバージョン間でbuild/ディレクトリを共有しないでください。 IDF_GIT_SAFE_DIR=/projectGit の所有権に関する警告を無効にします。IDF_CCACHE_ENABLEIDF_CCACHE_ENABLE= 1に加えて ccache ボリュームを使用すると、再構築時間が短縮されます。--deviceと-u $UIDを組み合わせる場合は、ダイヤルアウト GID に--group-add追加します。- macOS/Windowsでは、サンドボックス環境においては常にRFC2217がシリアルトランスポートです。ボード1枚につきサーバー1台、サーバー1台につきポート1つ。
CLAUDE.mdにフラッシュ/モニターコマンドとポートマッピングを記述することで、エージェントはセッションごとに指示されなくてもハードウェア設定を自動的に検出できるようになります。- チームが追加のツール (clang-tidy、cppcheck、特定の esptool) を標準化している場合は、セッションごとにインストールするのではなく、
FROM espressif/idf:release-v 5 . 4軽量のカスタム イメージを作成します。
結論
Dockerは、ESP32のビルドを、脆弱でマシン固有の手順から、信頼できるほど再現性の高いものへと変えた。並列コンテナを使用することで、1つのデスクが小さなハードウェアラボに変わり、従来型ファームウェアと次世代ファームウェアが摩擦なく共存できるようになります。そして、Docker Sandboxは最後のギャップを埋める。AIエージェントに実際のボードを与えて動作させることを、無謀ではなく合理的なものにするのだ。
もしあなたがまだ2026のホストマシンにESP-IDFを直接インストールしているなら、必要以上に手間をかけていることになります。今週は2枚の基板を使った構成を試してみてください。一方の基板で新しいファームウェアの改良を行い、もう一方の基板で安定版ファームウェアのテストを行います。次に、それらのエージェントの1つをサンドボックス環境のエージェントに渡して、どこまで進むか見てみましょう。
ハッピーハッキング!
詳しく見る
- ESP-IDF Dockerイメージガイド
- Docker Sandboxesのドキュメントを確認してください。
- Claude Codeやその他のコーディングエージェントを安全に実行する方法については、Dockerブログをご覧ください。
- ESP-IDFプロジェクトを構築するためのGitHubアクションを確認してください: esp-idf-ci-action