Claude CodeでPlatformIOのファームウェアを開発する手順
Claude CodeにPlatformIOのビルドとnativeテストまで任せ、書き込みとシリアル確認は人が握る範囲の切り方を、platformio.iniとpermissionsの設定例でまとめます。
Claude CodeでPlatformIOのファームウェアを開発するなら、pio runによるビルドと、PCだけで動くpio test(nativeテスト)までをClaude Codeに回させ、書き込みとシリアル確認は人が握る分担が扱いやすくなります。実機が手元に無くても検証は進み、実機をつなぐのは最後の一手だけで済みます。
この記事では、その分担をplatformio.iniの環境分け、CLAUDE.mdの規約、permissionsの許可と拒否の3点で固定する方法を説明します。
検証を実機の手前で止める4段の梯子
PlatformIOの検証は、実機が要らない段と要る段に分かれます。Claude Codeに任せる範囲は、この境目で切るのが素直です。
Claude Codeに回させる順番
- 1
pio run -e <ボード環境>
ターゲットのボード向けにビルドが通るかを見ます。書き込みはしません。
- 2
pio check
静的解析です。既定ではCppcheckが使われます。
- 3
pio test -e native
PCのGCCでテストをビルドして実行します。実機は不要です。
- 4
書き込みとシリアル確認(人が行う)
pio run -t uploadとpio device monitor、組み込み向けテストはここに入ります。
pio testは、テストをどこで走らせるかで2種類に分かれます。PlatformIOのテストランナーは、ホストマシン(native)と、接続した実機の両方を対象にできます。nativeテストはハードウェアに依存しない部品向けで、組み込みテストは実機にファームウェアを書き込み、シリアル経由で結果を集めます。
実機向けの流れは、専用ファームウェアのビルド、書き込み、シリアル接続、出力の回収の順です。この流れに入った時点で、ポートの占有と書き込みが絡みます。Claude Codeを放し飼いにする場所ではありません。
platformio.iniにnative環境を足す
nativeテストを回すには、platformio.iniにnative環境を用意します。ボード向けの環境はそのまま残します。
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
[env:native]
platform = native
test_framework = unitytest_frameworkの既定値はunityなので、unityと書くのは明示のためです。環境名のnativeは慣習で、platform = nativeが実体です。
native環境はシステムのGCCに依存します。PlatformIOはnative用のツールチェーンを自動では入れません。gcc --versionが通らない環境では、先に入れる必要があります。
gcc --version入れ方はOSごとに異なります。Linuxはsudo apt install build-essential、macOSはxcode-select --install、WindowsはMSYS2を入れてC:\msys64\mingw64\binなどをPATHに加えます。Claude Codeに任せる前に、人が1回確かめておく部分です。
nativeで試せるコードをlib/に出す
ここが設計の肝です。src/のコードは、既定ではテストと一緒にビルドされません。テストから呼びたいロジックは、lib/配下のコンポーネントに切り出します。LDF(Library Dependency Finder)が自動で見つけるので、テストからも本体からも#include <calculator.h>のように読めます。
project_dir
├── lib
│ └── ThermoMath
│ ├── include/thermo_math.h
│ └── src/thermo_math.cpp
├── src
│ └── main.cpp
├── test
│ ├── native
│ │ └── test_thermo_math
│ │ └── test_main.cpp
│ └── embedded
│ └── test_gpio
│ └── test_main.cpp
└── platformio.initest_build_src = yesでsrc/をテストと同時にビルドする手もありますが、公式の説明では推奨されない方法です。main()やsetup() / loop()を#ifndef PIO_UNIT_TESTINGで囲む必要も出ます。Claude Codeに新規機能を足させるときは、「センサー値の変換や状態遷移はlib/に書き、src/は配線だけにする」と指示する方が、テストできる範囲が広がります。
テストはtest/直下のtest_で始まるフォルダ単位で1つのテストスイートになり、各スイートが自分のmain()を持ちます。Arduinoならsetup() / loop()、ESP-IDFならapp_main()です。フォルダをnativeとembeddedに分けておくと、次のフィルタが使えます。
pio testをnativeだけに絞って回す
テストの実行は、環境とフィルタで範囲を絞ります。
pio test -e native
pio test -e native --filter "native/*"
pio test -e native --ignore "embedded/*"--filterはtest_dirからの相対パスがパターンに一致するスイートだけを実行し、--ignoreは一致するスイートを除外します。platformio.iniに書く場合は、test_filterとtest_ignoreが対応します。
[env:native]
platform = native
test_filter = native/*組み込み側の環境にもtest_ignore = native/*を入れておくと、実機向けのテストがnative専用スイートを巻き込みません。逆も同様です。
結果をClaude Codeに読ませる出力
Claude Codeがテスト結果を読んで直す反復には、機械可読なレポートが向きます。--junit-output-pathでJUnit XMLを、--json-output-pathでJSONを書き出せます。親フォルダは先に存在している必要があります。
mkdir -p .pio-reports
pio test -e native --junit-output-path .pio-reports/native.xml
pio test -e native --list-tests --json-output-path .pio-reports/tests.json--list-testsは、テストを実行せずに一覧だけ出します。どのスイートが存在するかをClaude Codeに先に把握させたいとき、実行より軽く使えます。失敗の原因が出力から読み取れないときは、-vでテストフレームワークの生出力を、-vvでビルドの詳細を見られます。
Unityのmainがぶつかるとき
Unityのテストファイルは、末尾にmain()を置きます。nativeではint main(void)、Arduinoならsetup()とloop()、ESP-IDFならapp_main()です。Unityの手順には、不要なmain実装を消すよう警告があります。全フレームワーク分を並べたままにすると、環境によってはエントリポイントが重複します。Claude Codeにテストを書かせるときは、対象環境を先に伝えて、不要な入口を残させないようにします。
Arduinoのテストでは、setup()の先頭でdelay(2000)ほど待ってからUnityを走らせる形が示されています。ボードのシリアルとの接続確立を待つためです。nativeにはこの待ちは要りません。
ビルドはpio runで環境ごとに確かめる
ターゲット環境のビルド確認は、環境名を明示してpio runを使います。
pio run --list-targets
pio run -e esp32dev -s
pio run -e esp32dev -j 4--list-targetsは使えるターゲットの一覧、-sは進捗表示の抑制、-jは並列ビルド数の指定です。-sで出力を絞ると、Claude Codeのコンテキストに入るログが減ります。
注意したいのは、-t(--target)で渡すターゲットです。monitorは、ビルド成功後にpio device monitorを自動で起動します。uploadのような書き込みターゲットと合わせて、この2つは人が実行する側に置きます。シリアルモニターは接続を保ったまま待ち続けるツールで、対話の途中にClaude Codeが開く用途には向きません。
静的解析はpio checkに分ける
ビルドが通ったらpio checkで静的解析に進めます。--fail-on-defectに重大度を渡すと、その重大度の欠陥があれば非ゼロで終了します。
pio check -e esp32dev --fail-on-defect=high -s--skip-packagesを付けると、サードパーティのパッケージを検査対象から外し、自分のソースだけを見られます。フレームワークのヘッダを解析器が読み込めず、レポートが空になるときの切り分けに使えます。
CLAUDE.mdで検証の順序を決める
3段の梯子は、CLAUDE.mdに書いておけばセッションをまたいで保てます。
# ファームウェア開発の規約
- ロジックは lib/ に置き、src/ は配線だけにする
- 変更後は次の順で確認し、全部通ってから完了とする
1. `pio run -e esp32dev -s`
2. `pio test -e native`
- `pio run` の `-t upload` / `-t monitor`、`pio device monitor`、
`pio test` の実機環境は実行しない(人が行う)
- `pio test` は必ず `-e native` を付ける最後の行には理由があります。pio testの-eは「指定した環境を処理する」オプションです。環境を絞らずに実行すると、実機環境が対象に入りかねません。実機環境が含まれると、書き込みとシリアル待ちが始まります。
許可と拒否の細かい書き方は、次節とClaude Code settings.json完全ガイドにあります。
permissionsで書き込みを止める
規約は「お願い」で、強制力はありません。書き込みやモニターは、.claude/settings.jsonのpermissionsで止めます。
{
"permissions": {
"allow": [
"Bash(pio run -e *)",
"Bash(pio test -e native*)",
"Bash(pio check *)",
"Bash(gcc --version)"
],
"deny": [
"Bash(pio run *upload*)",
"Bash(pio run *monitor*)",
"Bash(pio device monitor*)",
"Bash(pio test *--upload-port*)"
]
}
}ルールの評価順は、deny、ask、allowです。denyに一致した呼び出しは、より細かいallowがあっても許可されません。つまりBash(pio run -e *)を許可しても、pio run -e esp32dev -t uploadは*upload*のdenyで止まります。
allowだけでは書き込みを防げない理由は、ここにあります。pio run -e *のワイルドカードは、-t uploadの付いた呼び出しも飲み込むからです。末尾の*の前に空白を置くと「そのコマンドだけ」と「それ以降の引数」の両方に一致し、空白を省くと前方一致が広がる点も、書くときの注意です。
denyは文字列の一致なので、--target uploadとも書けるし-t uploadとも書けます。上の*upload*は両方に効きますが、すり抜けを絶対に許せない現場では、PreToolUse hookでpioを含むコマンドを検査し、終了コード2でブロックする方法もあります。hooksの仕組みはClaude Code Hooks完全ガイドで扱っています。
実機の確認だけを人に戻す
nativeテストが通り、ターゲット向けのビルドも通った段階で、人の番になります。実機でしか見えないものがあるからです。
- ピンの電気的な挙動や、タイミングに依存する不具合
- Wi-FiやBLEなどの無線の挙動
- 書き込み後のシリアル出力
人が書き込んでシリアル出力を貼り付ければ、そこからはClaude Codeが読んで原因を絞れます。シリアルログの読ませ方は、Arduino系ならClaude CodeでESP32の開発とシリアルデバッグを進める手順、ESP-IDF系ならClaude CodeでESP-IDFのファームウェアを開発する手順が参考になります。
PlatformIOは、組み込みテストをシリアルで回収する場合に--test-portでポートを指定し、未指定なら自動検出します。ポートの一覧はpio device listで確認します。この確認は人側の手順に置き、Claude Codeには任せません。
よくあるつまずき
nativeテストがビルドできない。GCCがPATHに無いことが最多の原因です。gcc --versionで確認します。
テストからsrc/の関数が見えない。 src/は既定でテストと同時にビルドされません。lib/に切り出すのが推奨の直し方です。
テストが実行されない。Unityでは、テスト関数ごとにRUN_TESTの呼び出しが要ります。関数を足してRUN_TESTを忘れると走りません。Claude Codeにテストを足させたら、--list-testsと実行結果で件数が増えたかを確かめます。
ボード向けとnative向けで同じファイルが衝突する。テストフォルダをnativeとembeddedに分け、test_filterとtest_ignoreで各環境に割り当てます。
設定を変えたのに反映されない。 pio runは、platformio.iniやsrc_dirを変更するとbuild_dirを自動で掃除します。--disable-auto-cleanでこの動作は止められます。意図せず付けていないか確認します。