Claude Codeでembassyとprobe-rsを使う — flash権限とdefmtログの読ませ方
Embassyのマイコン開発で、cargo runのrunnerをprobe-rsにして実機書き込みとdefmtログまで一気通貫にする設定と、Claude Codeに実機を触らせる権限の線引きをまとめます。
Embassyのプロジェクトは、.cargo/config.toml のrunnerをprobe-rsにしておくと、cargo run だけで書き込み・起動・ログ表示までが終わります。Claude Codeに任せるなら、この1コマンドの前後に線を引きます。ビルドと静的チェックは自由に回させ、実機に触れるコマンドだけ毎回こちらの承認を挟む形です。
この記事では、runnerの設定、権限の分け方、defmtログを読ませる手順、失敗したときの切り分け、CLAUDE.md に置く内容を順に説明します。ホスト向けのRustでcargo checkやclippyを回す話はAxumのRust API開発にあります。ここで扱うのは no_std のファームウェアです。
cargo runが書き込みとログ表示まで担う仕組み
probe-rsは、デバッグプローブ経由でマイコンにファームウェアを書き込み、デバッグするためのツール群です。ARMとRISC-Vを対象に、書き込み、メモリの読み書き、RTTとdefmtによるログ取得、panic時のスタックトレース表示ができます。C言語のファームウェアにも使えます。
Claude Codeにとって重要なのは、probe-rs run がcargoのtarget runnerとして動く点です。次の2行を .cargo/config.toml に置くと、cargo run が書き込み・起動・ログ表示を1回で行います。
[target.thumbv7em-none-eabi]
runner = 'probe-rs run --chip STM32G474RETx'--chip に入れる名前は、probe-rs chip list の出力から探せます。Embassy Bookも、probe-rs chip list | grep -i stm32g474re のように絞り込んで、出てきた名前を設定に写す手順を示しています。ターゲット名の thumbv7em-none-eabi や STM32G474RETx は例で、自分のボードに合わせて変えます。
Embassyのプロジェクトで最初に用意するファイルは次のとおりです。
.cargo/config.toml: ターゲットとrunnerを書くbuild.rs: defmtとリンカースクリプトを組み込む。チップごとに違うので、同じチップのexampleをコピーするCargo.toml: HALやexecutorのfeatureを選ぶmemory.x: 必要なチップだけ。フラッシュとRAMの配置を書くrust-toolchain.toml: Rustのバージョンとターゲットを固定する
Claude Codeに新規プロジェクトを組ませるときも、ゼロから書かせるより、使うチップのexampleをコピーさせてから差分を直させるほうが外しません。Embassy Bookが推す進め方でもあります。
ログはdefmtでRTT経由
DEFMT_LOG は .cargo/config.toml の [env] に書きます。Embassy Bookの最小例は trace で、info、warn、error にも変えられます。実機ログが長くなりすぎてClaude Codeに読ませにくいなら、info に下げるのが最初の一手です。
panic時の出力は、Embassy Bookが開発中の定番として挙げる defmt-rtt と panic-probe の組み合わせで、ターミナルに流れます。ホストのRustでRUST_BACKTRACEを読ませる流れとは出どころが違うので、混同しないでください。ホスト側の読み方はRustのpanicとbacktraceの調べ方に分けてあります。
cargo-embedではなくprobe-rs runを前提にする
probe-rsには cargo-flash と cargo-embed もあります。使い分けの案内は次のとおりです。
| ツール | できること | 位置づけ |
|---|---|---|
probe-rs run | できること書き込み、リセット、RTTとdefmtの表示、panic表示 | 位置づけほとんどのプロジェクトで最初に選ぶ |
cargo-embed | できること同等の機能に加え、対話的なRTT端末 | 位置づけ将来廃止される見込みと案内されている |
cargo-flash | できること書き込みだけ | 位置づけチップに書くだけで足りるとき |
Claude Codeに渡す手順は probe-rs run に揃えます。対話的なRTT端末は、Claude Codeのシェルからは操作できません。ログをファイルに残す使い方にも合いません。
実機に触れるコマンドだけ承認を挟む
ここからがClaude Codeの話です。cargo run が実機に書き込む構成だと、「テストを回して」という指示でも、条件によってはフラッシュまで走ります。cargoのrunnerは cargo run だけでなく cargo test と cargo bench にも適用されるためです。
そこで、コマンドを3段に分けます。
| 段階 | コマンド | 設定 |
|---|---|---|
| 自由に実行 | コマンドcargo build、cargo check、cargo clippy、probe-rs list、probe-rs chip list | 設定allow |
| 毎回承認 | コマンドcargo run、cargo test、cargo bench、cargo flash、cargo embed、probe-rs run、probe-rs attach、probe-rs info | 設定ask |
| 使わせない | コマンド遠隔操作用のサーバーを立てる probe-rs serve | 設定deny |
probe-rs info をaskに入れるのは、実機への接続を伴うからだけではありません。Embassy Bookは、RP2040のデバッグプローブ(Pico Probe)で probe-rs info を実行すると、プローブが使えない状態になる既知の問題を挙げています。抜き挿しで復帰しますが、自動で何度も呼ばせたいコマンドではありません。
probe-rs attach は、リセットも書き込みもせずに接続します。動作中の状態を変えずにログを見たいときに向きますが、それでも実機への接続なので、ここではaskに置いています。
probe-rs serve は、サーバーを立てて別のマシンからプローブとメモリを操作できるようにします。リモートアクセス用の機能で、手元のボード1枚で開発する場面では要らないため、denyにして外します。
{
"permissions": {
"allow": [
"Bash(cargo build *)",
"Bash(cargo check *)",
"Bash(cargo clippy *)",
"Bash(probe-rs list)",
"Bash(probe-rs chip list *)"
],
"ask": [
"Bash(cargo run *)",
"Bash(cargo test *)",
"Bash(cargo bench *)",
"Bash(cargo flash *)",
"Bash(cargo embed *)",
"Bash(probe-rs run *)",
"Bash(probe-rs attach *)",
"Bash(probe-rs info *)"
],
"deny": [
"Bash(probe-rs serve *)"
]
}
}これを .claude/settings.json に置けば、同じリポジトリを開く全員に同じ線引きが効きます。自分のボードだけで試すなら .claude/settings.local.json でも構いません。
落とし穴
Bashルールは別の書き方を止めない
denyとaskのBashルールは、Claudeが書いたコマンド文字列に対して照合されます。/usr/bin/cargo run や sh -c 'cargo run' のような別形式は、同じ表で止まるとは限りません。権限ルールは境界ではなく、通常のコマンドを止める安全網と考えてください。厳密に止めたいなら、サンドボックスやPreToolUseフックを組み合わせます。
もうひとつ確認しておきたいのが「承認の出し方」です。承認プロンプトで「Yes, and don't ask again」を選ぶと、そのコマンド接頭辞のルールがその場で保存されます。cargo run * を保存してしまうと、以後は書き込みも黙って走ります。作業中だけ緩めたいなら、保存を選ばず1回ずつ許可する運用が安全です。
同じ「allow/denyで範囲を切る」設計は、OpenTofuを使うときのplanだけ許可する設定やDenoのtask権限でも使われています。
defmtログをファイルに落として読ませる
probe-rs run はログを流し続けます。コマンドが自分では終わらないので、そのまま実行させると待ち続けることになります。そこで、時間で区切ってファイルに保存し、そのファイルを読ませます。
timeout 30 cargo run --release > target/run.log 2>&1timeout はBashルールの照合時に取り除かれる接頭辞のひとつなので、Bash(cargo run *) のask判定はそのまま効きます。macOSの標準環境に timeout はないので、coreutilsを入れて gtimeout を使うか、同等のコマンドに置き換えます。
実行後は、probe-rs のプロセスが残っていないかを確認させます。残っていると、次の書き込みで接続に失敗しうるためです。
ログができたら、次の順で依頼します。
target/run.log を読んで、次の3点を答えて。
1. 起動直後から最後までに出た WARN と ERROR を、時刻順に
2. panic があれば、└─ の次の行にある src/ 以下のファイルと行
3. 期待するログ「Hello World!」「high」「low」が出ているか
ファイルを書き換える前に、原因の仮説を先に述べて。defmtの出力は、ログ1件につき1行目にメッセージ、次の行に └─ で発生箇所が付く形です。Embassy Bookが示す出力例では、次のようになっています。
0 INFO Hello World!
└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:18
1 INFO high
└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:23先頭の数字はタイムスタンプで、defmt-timestamp-uptime を有効にすると起動からの秒数になります。この例はドキュメント上のサンプルで、手元のボードでは出る行もタイムスタンプも変わります。
発生箇所が出ないとき
ログの └─ が <invalid location: defmt frame-index: 14> のように出る場合は、リリースビルドにデバッグ情報が入っていません。Embassy Bookは、Cargo.toml に次を足すよう案内しています。
[profile.release]
debug = 2デバッグ情報はチップのフラッシュに書き込まれません。バイナリサイズを心配して消す必要はありません。
失敗したときの切り分け
実機の失敗は、Claude Codeがソースを読んでも原因が見えないことが多いです。次の順で確かめさせると、遠回りが減ります。
cargo runが失敗するときの確認順
- Qprobe-rs list でプローブが見えるか?答えが見えない答えがはい
Linuxなら、udevルールを
/etc/udev/rules.dに置き、udevadm control --reloadとudevadm triggerを実行します。それでも見えなければplugdevグループへの追加を試します。Windowsでは、プローブによってZadigでWinUSBドライバーを選ぶ必要があります。WSLではudevサービスの起動設定も必要です。 - Q`--chip` の名前が probe-rs chip list の出力と一致するか?答えが違う答えがはい
.cargo/config.tomlのrunnerの--chipを、probe-rs chip listの出力で見つけた名前に直します。 - QCargo.tomlのHALのfeatureが同じチップを指しているか?答えが違う答えがはい
embassy-stm32のstm32g474reのようなfeatureが、--chipと別のチップになっていないかを見ます。Embassy Bookはexampleを別のチップで動かすとき、runnerの--chipとHALのfeatureを揃えるよう案内しています。
書き込みは通るがログが乱れる
debug = 2 を入れたリリースビルドで再実行します。
Embassy Bookには、cargo add probe-rs を間違って実行して依存クレートに入れてしまい、std が見つからないという長いエラーが出る例も載っています。Claude Codeにセットアップを任せるなら、probe-rsは cargo add ではなくインストーラーやbrewで入れることを、最初に伝えておきます。
デバッグプローブを持っていないRP2040やRP235xでは、picotoolをrunnerにする方法がEmbassy BookのFAQにあります。この場合はdefmtのログをRTTで受け取れないため、この記事の読ませ方はそのまま使えません。
CLAUDE.mdに書いておく内容
毎回の依頼文に書かなくて済むよう、リポジトリの CLAUDE.md に実機のルールを置きます。
## マイコン実機のルール
- チップは STM32G474RETx。runner は .cargo/config.toml の probe-rs run
- 実機に書くコマンド(cargo run / test / bench)は、人が承認してから実行する
- 実行は `timeout 30 cargo run --release > target/run.log 2>&1` の形にし、
結果は target/run.log を読んで判断する
- 実行後に probe-rs のプロセスが残っていないか確認する
- ログの発生箇所が `<invalid location>` なら [profile.release] の debug = 2 を確認する
- 実機の挙動はログで裏取りする。LED や配線の見た目は人が確認する最後の1行は、実機特有の線引きです。ソースを正しく直しても、LEDが点くかどうかはClaude Codeには見えません。ログに「high」「low」が出たかどうかを判定基準にして、物理的な見た目は人に返します。
まとめ
Embassyでは、runnerをprobe-rsにした時点で、書き込みとログ確認は cargo run の1コマンドになります。Claude Codeに任せるときの要点は2つで、実機に触れる cargo run と cargo test を毎回承認にすることと、ログを時間で区切ってファイルに落とすことです。プローブが見えない問題はudevやドライバーの話なので、コードを直させる前に probe-rs list で切り分けます。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。