Claude Media
Raspberry Pi PicoのMicroPythonをClaude Codeで書く

Raspberry Pi PicoのMicroPythonをClaude Codeで書く

Claude CodeでRaspberry Pi Pico向けMicroPythonを書き、mpremote経由でREPLとGPIOを操作する手順をまとめます。

Raspberry Pi PicoはUSB接続だけでMicroPythonが動く安価なマイコンボードです。Claude CodeはPico本体を直接操作しません。ローカルのターミナルで動き、mpremoteという公式CLIをBashツール経由で呼び出してPicoと通信します。ファームウェアの書き込みからGPIO制御スクリプトの生成、mpremoteでの実行までを、手を動かす順に進めます。途中には、Claude Codeから呼ぶと詰まりやすい箇所が3つあります。

Raspberry Pi PicoのMicroPython開発でClaude Codeは何をするか

Claude Codeの役割は3つに絞られます。Pythonスクリプトの生成と編集、mpremoteコマンドの実行、そして実行結果の読み取りです。ハードウェアを直接見ているわけではありません。

mpremoteとは、USBシリアル経由でMicroPythonデバイスのファイル操作・REPL接続・スクリプト実行を行う公式CLIです。シリアル通信はmpremoteが担い、Claude CodeのBashツールはコマンドを呼ぶだけです。開発は「コードを書く、mpremoteを実行する、出力を読んで次の指示を出す」というループになります。

開発環境を準備する

用意するのはファームウェアとmpremoteの2つです。

手順

ファームウェアを書き込む

  1. 1

    BOOTSELモードで接続する

    PicoのBOOTSELボタンを押しながらUSBケーブルでパソコンに接続します。データ通信に対応したケーブルが必要です。REPLでMicroPythonがすでに動いているなら、machine.bootloader()の実行でも同じモードに入れます。

  2. 2

    .uf2をコピーする

    USBマスストレージとして現れたドライブに、micropython.orgのダウンロードページから取得した.uf2をコピーします。

  3. 3

    自動再起動を待つ

    書き込みが終わると自動で再起動し、Picoがシリアルデバイスとして見えるようになります。

ボードごとに.uf2は別配布です。PicoはRPI_PICO、Pico 2はRPI_PICO2のページに分かれており、取り違えないようダウンロード時にページ名のボード名を見ます。

続いてmpremoteをインストールします。Claude Codeのターミナルセッションでそのまま実行できます。

pip install --user mpremote

pipx install mpremoteでも入れられます。

インストール後は、デバイス一覧とmpremoteのバージョンを見ておくと、以降のコマンドが迷いません。Picoをつないでいない状態でv1.29.0を試すと、次のようになります。

mpremote --version
mpremote connect list
mpremote exec "print(1)"
mpremote 1.29.0
/dev/cu.Bluetooth-Incoming-Port None 0000:0000 None None
/dev/cu.debug-console None 0000:0000 None None
mpremote: no device found

connect listの各行は、ポート・シリアル番号・VID:PID・メーカー・製品名の順です。execのようにデバイスが要るコマンドは、connectを省くとconnect autoが補われ、USBシリアルポートだけが自動検出の対象になります。上の出力ではUSB機器が見つからないので、no device foundで終わりました。充電専用ケーブルではシリアルデバイスが現れず、USBシリアルが検出されないため、同じ表示になりやすくなります。

Claude CodeにGPIO制御スクリプトを書かせる

GPIO操作はMicroPythonのmachine.Pinクラスで行います。出力ピンならon()・off()・value()、入力ピンならvalue()で状態を読み取るのが基本の型です。「GP0にLEDをつないで1秒間隔で点滅させ、GP2のボタン入力も読む」と指示すると、次のようなスクリプトが返ります。

from machine import Pin
import time
 
led = Pin(0, Pin.OUT)
button = Pin(2, Pin.IN, Pin.PULL_UP)
 
while True:
    led.value(not led.value())
    print("button:", button.value())
    time.sleep(1)

Pin(0, Pin.OUT)でGP0を出力に、Pin(2, Pin.IN, Pin.PULL_UP)でGP2を内部プルアップ付きの入力に設定しています。後者はRP2クイックリファレンスのPin(4, Pin.IN, Pin.PULL_UP)と同じ書き方です。

このwhile Trueが、次の節で詰まる原因になります。

無限ループのスクリプトでBashツールが返らなくなる

mpremote runとexecは、既定でスクリプトが終わるまで出力を追い続けます。終わらないループを流すと、mpremoteは終了せず、Bashツールの側もコマンドの完了を待つことになります。Bashツールのコマンドには既定で2分のタイムアウトがあります(BASH_DEFAULT_TIMEOUT_MSで変更でき、上限は10分)。達したコマンドはバックグラウンドへ移ることがあります。人ならCtrl-Cで止める場面です。

回避策は2つあります。1つは--no-followを付けることです。すぐに制御が戻り、スクリプトはデバイス側で動き続けます。

mpremote run --no-follow blink.py

もう1つは、Claude Codeに「確認用は有限回で終わるスクリプトにして」と頼むことです。while Trueをfor _ in range(5)に替えれば、printの出力まで読み取れます。動かしっぱなしにしたい本番用だけをwhile Trueにします。--no-followで起動したあとに別のmpremoteコマンドを呼ぶと、動いているプログラムは止まる点も覚えておきます。

mpremoteでPicoに書き込み、動作を確認する

書いたスクリプトの動かし方は、残すかどうかで分かれます。

くらべる

一時実行と永続化

一時実行

mpremote run

ローカルのファイルの中身だけをデバイスのRAMへ送って実行します。ファイルシステムにはコピーされません。実行前にソフトリセットが入るので、デバイスに保存済みのmain.pyは先に走らず、そのまま残ります。

永続化

mpremote cp

mpremote cp blink.py :main.pyのように、:で始まるパスをデバイス側として指定してコピーします。電源投入のたびにmain.pyが自動実行されるようになります。

ファイルの中身はmpremote fs cat :main.py、ディレクトリ一覧はmpremote fs lsで読めます。標準出力にテキストを返すだけなので、Claude Codeが結果をそのまま扱えます。cpやlsはそのまま書ける別名でもあります。

複数コマンドは+でつなげて1回の呼び出しにできます。公式の例にあるmpremote cp utils/driver.py :utils/driver.py + run test.pyは、コピーして実行するところまでを1行で済ませる書き方です。Bashツールの呼び出し回数と承認の回数を減らせます。

一方、対話的なmpremote replは、人がCtrl-Cやペーストモード(Ctrl-E)を使いながら触る用途のコマンドです。Bashツールは1回ごとに完了を待つ呼び出しなので、自動化ループの軸はrun・eval・exec・fsのような1回で終わるコマンドにします。

前回のimportが残る理由と、リセットの入れどころ

mpremoteは、デバイスを自分からソフトリセットしません。exec・eval・fs・mountは動いているプログラムを止めるものの、変数とインポート済みのモジュールは残ります。この状態は、mpremoteを呼び直したあとにも引き継がれます。runだけは実行前にソフトリセットが入るので例外です。

注意

状態が残る場面と対処

  • モジュールを書き換えて再実行

    import blinkは読み込み済みなら再読み込みされません。mpremote soft-resetを挟むと、ヒープとインタープリターが初期状態に戻ります。

  • 変数を前の実行から引き継ぎたい

    execで定義した変数は次の呼び出しでも使えます。センサー値を1回ずつ読みたい場面で、Claude Codeに状態を持たせられます。

  • 毎回まっさらにしたい

    設定ファイルにauto_soft_reset = Trueを書くと、デバイスが最初に必要になったときと切断のたびにソフトリセットします。旧来の挙動に戻す設定です。

ファイルコピーのたびに待たず反復する

編集のたびにcpで書き込み直すのは、修正が増えると手間になります。mpremote mountはローカルのディレクトリをPicoの/remoteとしてマウントし、カレントディレクトリもそこへ移すので、コピーなしでインポートや実行ができます。

mpremote mount . exec "import blink"

カレントディレクトリをマウントしたうえで、Pico側からblink.pyをimport blinkとして読み込んで実行しています。mountのあとに別のコマンドを続けなかったときは、replが自動で補われます。対話用のreplは人が抜けるまで終わらないため、Bashツールからは返ってこない呼び出しになります。mountにはexecかrunを続けます。

REPL上でCtrl-Dを押すと、mountは保ったままソフトリセットされます。ただしデバイスに起動時から動くmain.pyがあるときは再マウントできず、Ctrl-A、Ctrl-D、Ctrl-Bの順で戻す手順が必要です。Claude Codeでの反復では、mountとexecを毎回1行で呼び直すほうが扱いやすいでしょう。

同じ土台でPWMやADCを扱う

明るさやモーターの回転数のように段階的な出力が欲しいときは、machine.PWMに切り替えます。MicroPythonのrp2ポートでは、PWMジェネレーターが8個あり、それぞれ2チャンネルを持ちます。2チャンネルは同じ周波数で動き、デューティ比だけを別々にできます。

from machine import Pin, PWM
 
pwm0 = PWM(Pin(0), freq=2000, duty_u16=32768)
pwm0.freq(1000)        # 周波数を変更
pwm0.duty_u16(200)     # デューティ比を0〜65535の範囲で変更

隣り合うGP0とGP1は同じスライスに入ります。同じスライスのピンは周波数を共有するので、別々の周波数は指定できません。LEDとブザーのように周波数が違う部品は、GP0とGP2のようにスライスをまたいで割り当てるようにClaude Codeへ指示します。

PinからPWMやADCへ切り替えても、インポート元はmachineのままです。MicroPythonは既定でCPUを125MHzで動かし、machine.freq()で変更もできます。

mpremoteコマンドの使い分け早見表

コマンドBashツールに戻るとき前回の変数やimport
mpremote run <file>Bashツールに戻るときスクリプトが終わったとき前回の変数やimport実行前に消える
mpremote run --no-follow <file>Bashツールに戻るとき起動した直後前回の変数やimport実行前に消える
mpremote cp <file> :main.pyBashツールに戻るときコピーが終わったとき前回の変数やimport変わらない
mpremote fs ls / fs catBashツールに戻るとき出力を返し終えたとき前回の変数やimport残る
mpremote mount . exec "..."Bashツールに戻るときexecの処理が終わったとき前回の変数やimport残る
mpremote soft-resetBashツールに戻るときリセットが終わったとき前回の変数やimport消える

よくあるつまずき

  • デバイスが見つからない: mpremote: no device foundと出たら、まずmpremote connect listでPicoが並ぶか見ます。USBシリアルポートだけが自動検出の対象です。ケーブルが充電専用でないか、BOOTSELモードのままになっていないかも切り分けます
  • 複数台つないでいる: connect autoは最初に見つかったUSBシリアルポートを選びます。mpremote connect id:<シリアル番号>のようにconnect listの2列目で指定すると、対象を固定できます
  • 承認プロンプトで毎回止まる: Bash(mpremote:*)のような許可ルールを設定していないと、コマンドのたびに確認が挟まります。許可範囲を絞りたいときはallowedTools/disallowedToolsでの制御も選択肢です
  • サンドボックス環境でmpremoteが動かない: sandbox.enabledを有効にした環境で動かないときは、sandbox.enabledの挙動で設定の影響を切り分けます
  • 書いたはずのコードが再起動で消える: runで動かしただけでは、デバイスのファイルシステムにコードは残りません。再起動後にmpremote fs lsを実行してmain.pyの有無を見て、無ければmpremote cp blink.py :main.pyでコピーします
  • BOOTSELモードに入れない: 充電専用のUSBケーブルでは、PCにドライブとして現れないことがあります。データ通信に対応したケーブルに替えて試します

まとめ

Claude Codeからmpremoteを使うときの判断は、呼び出しがBashツールに戻るかどうかです。戻らないwhile Trueとmount単独は避け、古いimportが気になるときだけsoft-resetを挟みます。

対話が必要な場面だけ人がREPLに入り、反復はClaude Codeに任せる分担にすると、「書いて、焼いて、確かめる」ループが短くなります。権限設計や拡張機構を広く知りたい場合はClaude Code完全ガイドも参考にしてください。

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