Claude Media
Claude Codeでembassyとprobe-rsを使う — flash権限とdefmtログの読ませ方

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

timeout は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が失敗するときの確認順

  1. Qprobe-rs list でプローブが見えるか?
    答えが見えない

    Linuxなら、udevルールを /etc/udev/rules.d に置き、udevadm control --reload と udevadm trigger を実行します。それでも見えなければ plugdev グループへの追加を試します。Windowsでは、プローブによってZadigでWinUSBドライバーを選ぶ必要があります。WSLではudevサービスの起動設定も必要です。

    答えがはい
  2. Q`--chip` の名前が probe-rs chip list の出力と一致するか?
    答えが違う

    .cargo/config.toml のrunnerの --chip を、probe-rs chip list の出力で見つけた名前に直します。

    答えがはい
  3. 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 で切り分けます。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. probe.rs/docs/overview/about-probe-rs
  2. probe.rs/docs/tools/probe-rs
  3. probe.rs/docs/tools/which-tool
  4. probe.rs/docs/getting-started/probe-setup
  5. embassy.dev/
  6. embassy.dev/book
  7. code.claude.com/docs/en/permissions
  8. doc.rust-lang.org/cargo/reference/config.html
この記事を共有:XはてブLinkedIn