Claude Media
Claude CodeでPlatformIOのファームウェアを開発する手順

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. 1

    pio run -e <ボード環境>

    ターゲットのボード向けにビルドが通るかを見ます。書き込みはしません。

  2. 2

    pio check

    静的解析です。既定ではCppcheckが使われます。

  3. 3

    pio test -e native

    PCのGCCでテストをビルドして実行します。実機は不要です。

  4. 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 = unity

test_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.ini

test_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でこの動作は止められます。意図せず付けていないか確認します。

この記事を共有:XはてブLinkedIn