Claude CodeでESP32の開発とシリアルデバッグを進める手順
Claude CodeのBashツールからArduino CLIを呼び出し、ESP32ファームウェアのビルド・書き込み・シリアル出力の読み取りまでを一貫して進める手順をまとめます。
Claude CodeでESP32のファームウェアを開発するとき、変わるのは統合開発環境ではなく開発フローです。GUIのArduino IDEの代わりにArduino CLIを使い、コンパイル・書き込み・シリアル出力の確認までをClaude CodeのBashツールに任せます。この記事では環境構築から実際のデバッグループ、つまずきやすいポイントまでを順を追って説明します。
Claude CodeでESP32開発を始める前に
Claude Codeはターミナル常駐のエージェントで、GUI操作を前提にしたArduino IDEよりもコマンドラインで完結するArduino CLIと相性が良い構成です。ファイル編集はEditツール、ビルドや書き込みはBashツールが担当します。
対象になるのはArduino Core for ESP32です。取得時点の最新版はESP-IDF 5.5を基盤にしたバージョン3.3.12で、ESP32・ESP32-C3・ESP32-C5・ESP32-C6・ESP32-H2・ESP32-P4・ESP32-S2・ESP32-S3の各SoCを安定版としてサポートします。対応OSはWindows・Linux・macOSの3つで、公式にサポートされるIDEはArduino IDEのみです。
開発機がDevContainerやCodespacesの場合は注意が必要です。ESP32はUSBシリアル経由で書き込むため、リモートのコンテナ環境ではUSBパススルーの設定がない限りボードに直接アクセスできません。Claude Code CodespacesでDevContainer開発を始める手順で扱っている構成は、コンパイルのチェックまでに留めるか、USBデバイスを転送できるローカル環境と組み合わせるのが現実的です。
Arduino CLIでESP32のビルド環境を整える
最初の作業は、ESP32のボード定義をArduino CLIに登録することです。ESP32はArduino公式のデフォルトインデックスに含まれない3rdパーティコアなので、Espressifが公開するボードマネージャーのJSONを追加します。
arduino-cli config init
arduino-cli core update-index \
--additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
arduino-cli core install esp32:esp32 \
--additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json毎回--additional-urlsを書きたくない場合は、arduino-cli config initで生成された設定ファイルのboard_manager.additional_urlsにこのURLを追記しておくと、以降のコマンドで省略できます。
ボードをUSB接続したら、認識状況を確認します。
arduino-cli board list正しく認識されていれば、シリアルポートとFQBN(Fully Qualified Board Name)が一覧に表示されます。汎用のESP32開発ボードのFQBNはesp32:esp32:esp32です。これはボード定義ファイル上でボードIDがesp32(表示名は「ESP32 Dev Module」)として登録されていることに由来し、<パッケージ名>:<アーキテクチャ>:<ボードID>の形式でそのまま組み立てられます。手元のボードが認識されない場合は、arduino-cli board listall esp32で対応ボード名とFQBNの候補を検索できます。
Claude Codeにコンパイルと書き込みを任せる
スケッチの作成と編集はClaude CodeのEditツールで行い、ビルドと書き込みはBashツール経由でArduino CLIに委ねます。
arduino-cli sketch new blink_esp32
arduino-cli compile --fqbn esp32:esp32:esp32 blink_esp32
arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:esp32 blink_esp32compileは-b(--fqbnのエイリアス)、uploadは-p(ポート)と-bの組み合わせで、どちらもArduino CLIのサブコマンドが共通して受け付けるオプションです。uploadはビルド済みの成果物を書き込むだけでコンパイルは行わないため、コードを変更したらcompileから実行し直します。
書き込みが失敗して「Failed to connect to ESP32: Timed out waiting for packet header」のようなエラーになる場合、ESP32がダウンロードモードに入れていないことが原因です。EN(リセット)ピンをトリガーする瞬間にGPIO0をLOWに保つ必要があり、多くの開発ボードはBOOTボタンでこれを代行します。
書き込みコマンドを実行するたびにClaude Codeの権限確認が挟まって流れが止まる場合は、プロジェクトの.claude/settings.jsonに許可ルールを追記しておくとテンポが上がります。
{
"permissions": {
"allow": ["Bash(arduino-cli compile*)", "Bash(arduino-cli upload*)"]
}
}シリアル出力をClaude Codeに読ませてデバッグする
Claude Codeは対話中のターミナルに張り付いてログを流し見るツールではないため、シリアルモニターも「一定時間だけ実行してファイルに落とし、そのファイルを読ませる」形にするとデバッグループに組み込みやすくなります。
timeout 10 arduino-cli monitor -p /dev/ttyUSB0 --config 115200 > serial.log--configはシリアルポートの設定を<ID>=<値>の形式、または公式の例のようにボーレートだけを渡す形式で指定します。スケッチ側のSerial.begin()に指定した値と一致させないと、文字化けか無出力になります。
シリアルに何も出ない場合、ESP32の新しい世代ではUSB接続がUARTとUSB-CDCの2系統に分かれていることを疑います。USBコネクタ経由でつないでいるなら、書き込み設定の「USB CDC On Boot」を有効にし(-D ARDUINO_USB_CDC_ON_BOOT=1)、コード側はSerial.print()を使います。UART変換アダプタ経由でつないでいるなら「USB CDC On Boot」を無効にし、UART専用の出力にはSerial0.print()を使う必要があります。この対応関係を取り違えると、ケーブルもコードも間違っていないのに出力だけが届きません。
より詳細なログが欲しいときは、Arduino IDEの「Core Debug Level」に相当する設定をArduino CLIの--board-optionsで渡し、None・Error・Warning・Info・Debug・Verboseの6段階から選べます。原因が絞り込めない再起動ループの調査では、Verboseまで上げてからserial.logをClaude Codeに読ませると、スタックトレースの該当行を拾いやすくなります。
こうした「実行してログを取り、Claude Codeに読ませて直す」の反復は、Claude Codeでテスト駆動開発(TDD)を回す手順で扱う失敗テストからの修正ループと構造が近く、Stop hookなどで完了条件を明示する考え方もそのまま流用できます。
Arduino IDEとArduino CLIをどう使い分けるか
| 用途 | Arduino IDE | Arduino CLI(Claude Code) |
|---|---|---|
| 初回のボード認識確認 | Arduino IDE◎ 設定画面で状態が見える | Arduino CLI(Claude Code)△ board listの出力を読む必要がある |
| ライブラリの探索 | Arduino IDE◎ Library Managerで一覧できる | Arduino CLI(Claude Code)○ lib searchでも同等の情報は取れる |
| 繰り返しのビルド・書き込み | Arduino IDE△ クリック操作が挟まる | Arduino CLI(Claude Code)◎ コマンド1行で再現できる |
| CIやリモート開発機でのビルド確認 | Arduino IDE× GUI前提で組み込みにくい | Arduino CLI(Claude Code)◎ USBポートがなくてもコンパイルまでは検証可能 |
書き込みまで自動化したいならArduino CLI、初回のボード確認やライブラリ探索だけならArduino IDEのGUIの方が早いこともあります。Claude apps gatewayをCIやリモート開発機から使うにはで触れているような、物理ポートを持たないリモート実行環境では、arduino-cli compileによるビルド確認までを担当させ、実機への書き込みとシリアル確認はUSBが挿さったローカル環境に分けるとワークフローが破綻しません。
複数枚のESP32を同時に扱う(メッシュ通信のテストなど)場合は、ボードごとにシリアルポートが異なるため、ログの取得とコンパイルを並行して走らせたくなります。Claude Codeのサブエージェント完全活用で紹介されているTaskツールによる並列実行パターンは、ボードごとに独立したログファイルへ書き込ませる用途に応用できます。
よくあるつまずき
- 書き込めない: USBハブ経由ではなくPCに直接接続する、電源供給を確認する、TX/RXピンに何も接続されていないか確認する、の順で切り分けます。10μFのコンデンサをRSTとGND間に挟む対処も公式ドキュメントで案内されています。
- ボード自体が認識されない: USBドライバの有無、USBケーブルの種類(充電専用ケーブルでないか)、ボードの破損有無を確認します。
- SPIFFSのマウントに失敗する:
E (588) SPIFFS: mount failedのようなエラーが出た場合、SPIFFS.begin(true)のようにformatOnFailをtrueにして再フォーマットを許可すると解消することがあります。 - SDカードのマウントに失敗する: 配線の接触不良が大半の原因です。
SD_MMCライブラリを使う場合はD3ピンを含む全データピンに外付けの10kΩプルアップが必要で、ここが抜けていると1ビットモードでも失敗します。 - ESP32-S3が最小構成のスケッチでも再起動を繰り返す: PSRAMを搭載したWROOMモジュールで、Arduino IDE側のPSRAM設定(QSPIかOPIか)がモジュールの実装と食い違っていると発生します。モジュール側面の型番表記からPSRAM種別を確認し、Tools > PSRAMの設定を合わせます。
- 書き込み時に「flashを書き込めない」とcrashする: フラッシュサイズに対してPartition Schemeが大きすぎると発生します。使用しているボードのフラッシュ容量に合わせてTools > Partition Schemeを選び直します。
- BLEやBluetoothの初期化でESP-IDFのAPIを直接呼ぶと落ちる: Arduinoコアは起動時に未使用のBluetooth用メモリを解放して省メモリ化しますが、
nimble_port_init()やesp_ble_mesh_init()などESP-IDFのBluetooth APIを直接呼ぶスケッチでは、対応するヘッダ(esp32-hal-alloc-ble-mem.hまたはesp32-hal-alloc-bt-classic-mem.h)をどこか1つのソースファイルでincludeしておかないと、initArduino()がスタック初期化前にメモリを解放してしまいます。
まとめ
Claude CodeでESP32を開発する構成は、専用の統合機能ではなく、Bashツールで叩けるArduino CLIと、実行結果をファイル経由で読ませる工夫の組み合わせで成立します。ボード登録・コンパイル・書き込み・シリアル監視という4つの操作をコマンド化しておけば、コードの修正からログの確認までを1つのセッションの中で回せます。書き込みやシリアル出力でつまずいたときは、まずダウンロードモードへの入り方とUSB-CDC/UARTの系統の違いを疑うと、原因の多くに当たりが付きます。