2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

組み込み開発でもスマホAIコーディング!外出先からPlatformIO+Claude Codeやってみた

Posted at

こんなことができます

外出先から自宅のPC上で動いてるClaude Codeにアクセス
C0912_1.gif

TaskMaster AIでタスク管理してるから指示する内容に迷わない
DSC02642.jpg

自分のPCで動いてるから環境自由自在、PlatformIOのビルドもできる
DSC02644.jpg

4Gや5Gの電波が途切れてもエージェントの処理は途切れない
DSC02645.jpg

エージェントの作業が終わったら、code-server(Web版VSCode)でAIの変更を確認
20251128_195310.jpg

PCの電源をつけ忘れても、Wake on LANで起動できるから安心
C0912.gif

概要

AI時代、イベントに行ったり人に会ったりするのは非常に重要ですよね。
でも開発もしたい。

そこで、電車の中でもClaude Code等を使ったコーディングができる環境を構築してみました。
私がやりたいのがPlatformIOを使った組み込み開発なのでPlatformIOを使っていますが、CMakeでもGradleでもnpmでも、いろんな開発環境に応用できると思います。
また、Claude Codeに限らず、Codex CLI、Gemini CLI、GitHub Copilot CLI、Cursor CLI等でも使えそうです。

ポイントは下記です。

  • PlatformIOプロジェクトを(VSCodeの拡張機能に頼らず)CLIでビルドできるようにしておく
  • CLAUDE.md (またはAGENTS.md) にビルドの方法などを記載する
  • Tailscaleを使って、自宅PCとスマホをVPN経由で接続する
  • Moshとtmuxでセッション切れ対策
  • AndroidスマホからアクセスするならTermuxがよい
  • TaskMaster AI(タスク管理MCP)を入れて事前に実装したい機能リストを作っておく
  • code-serverを使ってブラウザから変更内容を確認
  • 余ったRaspberry PiをWake on LANデバイスにするとさらに快適、そして省電力

PCを用意する

まずClaude CodeやPlatformIOを動かすPCを用意します。
私はDeskMini A300にUbuntu 24.04 LTSを入れました。
Windowsでもいけるとは思うんですが、未検証です。
後述するMoshかSSHで接続できる必要があります。

基本の開発環境構築

Claude Codeをインストールします。ここでは省略します。

次にPlatformIOをインストールします。公式ドキュメントの内容に従いましょう。

curl -fsSL -o get-platformio.py https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py
python3 get-platformio.py

新しいターミナルで下記を実行して反応があることを確かめましょう。

source ~/.platformio/penv/bin/activate
pio --version

CLAUDE.md

今自分が開発中の、あるいはお好きなPlatformIOプロジェクトをgit cloneします。
なければ pio project init するのも良いかもです。

プロジェクトフォルダ(platformio.iniがあるディレクトリと同じディレクトリ)にCLAUDE.md(またはAGENTS.md)を作成します。
CLAUDE.mdではビルド手順について記載します。

## ビルドコマンド

### 事前準備

```bash
source ~/.platformio/penv/bin/activate
```

### ビルド

```bash
# 通常ビルド
PLATFORMIO_CORE_DIR=.pio pio run

# クリーンビルド
PLATFORMIO_CORE_DIR=.pio pio run -t clean
```

### 実機への書き込み

```bash
PLATFORMIO_CORE_DIR=.pio pio run -t upload
```

`shell` ツール実行時は `with_escalated_permissions: true``justification: "Firmware upload requires unrestricted USB device access beyond sandbox permissions."` を必ず指定する。

Linux環境では `Error: libusb_open() failed with LIBUSB_ERROR_ACCESS` というエラーが出ることがある。その際は 99-platformio-udev.rules を設定した後、デバイスを再度接続することをユーザーに求める。
また、brlttyのアンインストールが必要な場合もある。

```bash
# udev rulesの設定
curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/system/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules
sudo adduser $USER dialout
sudo service udev restart

# brlttyのアンインストール
sudo apt remove brltty
```

### テスト

```bash
# PC上でテスト
PLATFORMIO_CORE_DIR=.pio pio test -e native-test
```

### シリアルモニタ
```bash
# シリアルモニタを起動
pio device monitor
```

(私は最近はもっぱらESP-IDFのシリアルモニタばっかり使ってますがここでは割愛してPlatformIOのシリアルモニタを使うことにします)

これでClaude Codeが自分の力でPlatformIOプロジェクトのビルドを行い、ビルドエラーを確認できるようになりました。

スマートフォンから接続できるようにする

Tailscaleの導入

Tailscaleは無料かつ簡単に構築できることで人気のVPNサービスです。
Ubuntu環境では公式で案内されているコマンドによってインストールできました。

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

出現するガイダンスに従ってログインすると完了です。
恐ろしく簡単です。

スマートフォンにもTailscaleをインストールしておきましょう。

Moshとtmuxの導入

MoshはMobile Shellの略で、SSHよりもセッション保持能力や、ネットワーク遅延の大きい状況での動作に優れています。
tmuxは端末多重化ソフトウェアで、ターミナルを閉じたとしてもセッションを保持してくれるのが特徴です。

これらを組み合わせることで、スマートフォンをスリープしても、ネットワークが途切れても、Claude Codeの処理を続行させることができます。

sudo apt install mosh tmux

ついでにtmuxをTrueColorに対応させておくとClaude Codeのあのキャラクターが正しく表示されて楽しいです。

~/.tmux.conf
set -g default-terminal "tmux-256color"
set -ag terminal-overrides ",*256col*:Tc"

Termuxで外部から接続

AndroidアプリのTermuxをインストールします。
他のMosh対応SSHクライアントも試したのですが、日本語入力の観点でこれが一番良かったです。

Unexpected Keyboardもオススメです。

TermuxでもMoshをインストールする必要があります。

pkg install openssh mosh

TailscaleアプリからIPアドレスをコピーします。

mosh wararyo@100.xxx.xxx.xxx # 実際のTailscale IPアドレスをここにペースト

自宅PCに入りますので、tmuxを起動します。

wararyo@wararyo-a300:~$ tmux

tmux内でPlatformIOプロジェクトに入り、Claude Codeを起動できることを確認します。

ちなみにTermuxでは ESC / - HOME などと並んでいるところを左にスワイプすると右から日本語入力パネルが現れます。

tmuxでは Ctrl + B でtmuxのコマンド受付モードに入ります。
その状態で [ を押すとスクロールモード(qで終わる)、d を押すとデタッチです。

さらに便利にする

code-server (ブラウザ版VSCode)

AIに指示して修正を加えてもらうことがあるかと思います。
修正内容を見る場合はcode-serverを使うのが分かりやすいです。
code-serverは自分のPCでVSCodeのサーバーを動かすもので、ブラウザからアクセスすることであらゆる環境でVSCodeを触れるようになります。

curl -fsSL https://code-server.dev/install.sh | sh
sudo systemctl enable code-server@$USER
~/.config/code-server/config.yaml
bind-addr: 0.0.0.0:8080 # 外部からアクセスできるようにする
auth: password
password: [何か良い感じのパスワードを入れる]
cert: false
sudo systemctl restart code-server@$USER

PWA対応なので、スマホから「ホーム画面に追加」を行えば、まるでネイティブに動作しているかのように使えます。

TaskMaster AI (タスク管理MCP)

外出先からClaude Codeにアクセス出来たところで、何を指示すればいいのか分からない…ということもあるかもしれません。
TaskMaster AIを使うと、「次のタスクを進めておいて」とだけ指示すれば事前に設定したタスクを進めておいてくれるようになります。

まずMCPとしてTaskMasterを追加します。

.mcp.json
{
	"mcpServers": {
		"task-master-ai": {
			"type": "stdio",
			"command": "npx",
			"args": [
				"-y",
				"task-master-ai"
			]
		}
	}
}

その後、「TaskMasterの初期化をしてください」とClaude Codeでお願いすると初期化してくれるはずです。
(私はなんか上手くいかなかったのでグローバルにインストールして task-master init しました)

途中、どのLLMを使うか質問されましたが、私はGemini CLIのGemini 3 Proを指定しておきました。
(Claude Codeを指定するとセッション履歴が汚染されるから&Gemini CLIならある程度は無料で使えるから)
APIキーが必要なLLMを使う場合はAPIキーを .mcp.json で設定します。

次に .taskmaster/docs 配下にprd.txtというドキュメントを作成します。
これはプロダクト要求仕様書と呼ばれるもので、これから作りたいものに対して仕様をまとめた文書です。
テンプレートが .taskmaster/templates/example_prd.txt にあるので、適宜コピーして編集します。
私が書いたprd.txtの例はこちら

そして最後に「TaskMasterの parse-prd を行ってください」とClaude Codeにお願いすると、いろいろあってTaskMasterのタスクリストにいくつかのタスクが起票されます。

ここまでやると、「次のタスクってどんなやつだっけ?」と質問したらTaskMasterの次のタスクを参照して答えてくれるようになります。やったね!

image.png

ぶっちゃけTaskMasterより良いやつがあるかもです。Claude Codeが直接起票してくれればいいのですが、Claude Codeが違うLLM(私の設定ではGemini)に聞いて、Geminiがタスク化するという構造っぽいので回りくどいです。

Claude Code UI

積極的にオススメするわけではありませんが、Claude Code UIをインストールしてもいいかもしれません。

これまでに行った会話の履歴を見られるほか、先述したTaskMasterのタスクをグラフィカルに確認できます。

image.png

(WebUIから会話することも出来るんですが、個人的にはCLIで直に使う方がしっくり来ました)

Wake on LAN端末を作る

ここまでの内容で結構快適に使えるんですが、自宅のPCを事前に起動しておかないといけないという欠点があります。
起動し忘れると外出先で使えませんし、このご時世、PCをずっと起動しておくのも電気代的に憚られます。

そこで、余ってた初代Raspberry PiをWake on LAN端末にしてみました。
下記の設定を行った後のRaspberry Piの消費電力は1W程度でした。

DSC02646.jpg

Raspberry Pi OSの導入

Raspberry Pi Imagerを使うと簡単です。
Raspberry Pi OS Lite (32-bit)をインストールしました。

Tailscaleの導入

手順は同じなので省きます。

Wake-On-LAN Serverの導入

こちらを使用させていただきました。

導入にあたって、いくつかハマった点がありましたので紹介します。
まずは公式ドキュメント通りに進めます。

Raspberry Pi
# install dependencies (ここは同じ)
sudo apt update
sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools
sudo apt install python3-venv nginx

次にリポジトリをクローンしてくるのですが、なぜかホームディレクトリに置くと動作しなかったので /var/www/ に置きました。

Raspberry Pi
sudo apt install git
cd /var/www/
git clone https://github.com/macoshita/wakeonlan-server.git
sudo chown [自分のユーザー名]:www-data
cd wakeonlan-server
/etc/systemd/system/wakeonlan-server.service (Raspberry Pi)
[Unit]
Description=wakeonlan server
After=network.target

[Service]
User=[ここにユーザー名を入力]:
Group=www-data
WorkingDirectory=/var/www/wakeonlan-server
Environment="PATH=/var/www/wakeonlan-server/env/bin"
ExecStart=/var/www/wakeonlan-server/env/bin/uwsgi --ini wol.ini

[Install]
WantedBy=multi-user.target
/etc/nginx/sites-available/wakeonlan-server (Raspberry Pi)
server {
    listen 80 default_server;

    location / {
        include uwsgi_params;
        uwsgi_pass unix:/var/www/wakeonlan-server/wol.sock;
    }
}

また、MACアドレスを固定にしないとエラーが発生するという報告を見かけました。
どうせWoLで起動するPCは一つだけなので、MACアドレスを固定しておきます。

wol.py (Raspberry Pi)
# 省略
@app.route("/wol", methods=['GET', 'POST'])
def wol():
    # addr = request.args.get('addr', '')
    addr = "00:00:00:00:00:00" # ここにPCのMACアドレスを入力
    message = ""

    if request.method == 'POST':
        send_magic_packet(addr)
        message = 'Sent magic packet'

    return render_template('wol.html', message=message, addr=addr)

# 省略

以降は公式ドキュメント通りです。

Raspberry Pi
sudo systemctl enable wakeonlan-server
sudo systemctl start wakeonlan-server

# setup nginx
sudo ln -s /etc/nginx/sites-available/wakeonlan-server /etc/nginx/sites-enabled
sudo unlink /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl restart nginx

Wake on LANを受け付ける設定(PC側)

本筋から外れますが、PC側でもWake on LANを受け付ける設定が必要でした。

まずBIOSの設定をします(ここでは割愛します)
その後、netstatの設定ファイルを書き、適用します。
これをするとUbuntuの設定アプリからGUIで設定したネットワーク設定は使われなくなってしまうので注意です。

/etc/netplan/02-enp51s0.yaml (PC)
network:
  version: 2
  ethernets:
    enp51s0: # ←環境に応じて適宜書き換えてください
      dhcp4: false
      dhcp6: true
      addresses: [192.168.1.16/24] # せっかくなのでこれを機にIPアドレスを固定しておく
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses: [192.168.1.1, 8.8.8.8, 8.8.4.4]
      wakeonlan: true # ←これを記述するとWake on LANが有効になる
PC
sudo shutdown -r now

以下のコマンドで、Wake on LANが有効になっていることを確認できます。
(ethtoolが入ってない場合は適宜apt installします)

PC
wararyo@wararyo-a300:~$ sudo ethtool enp51s0 | grep -i wake-on
    Supports Wake-on: pumbg
    Wake-on: g

この時点で、 http://[Tailscale上のRaspberry PiのIPアドレス]/wol にアクセスすることでWake on LANが機能します。

C0912.gif

unattended-upgradesの導入

WoL用Raspberry Piはメンテナンスしないまま放置することが予想されるので、セキュリティアップデートを自動でインストールするようにします。

Raspberry Pi
sudo apt install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades # ダイアログが出るのでYESを選択

アクセス制限の導入

少しでもセキュリティ上のリスクを減らすために、アクセス制限を導入します、これにより、

  • 原則すべてのアクセスが遮断されます
  • TCP80番(HTTP)の外部からのアクセスはTailscale経由でのみ許可します
  • デフォルトゲートウェイに行く通信は許可します(そうしないと自動セキュリティアップデートができない)
  • UDP9番ポートの外部へのブロードキャストは許可します (Wake on LANで使うから)
/etc/iptables/rules.v4 (Raspberry Pi)
# デフォルトポリシー:全拒否
*filter
:INPUT DROP
:OUTPUT DROP
:FORWARD DROP

# ループバックは許可
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT

# 確立済み接続の応答は許可
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Tailscale経由(tailscale0)は80番(HTTP)と53番(DNS)のみ許可
-A INPUT -i tailscale0 -p tcp --dport 80 -j ACCEPT
-A OUTPUT -o tailscale0 -p udp --dport 53 -j ACCEPT

# LAN側:デフォルトゲートウェイへは全許可(DNS, NTPなど必要)
-A OUTPUT -o eth0 -d 192.168.1.1 -j ACCEPT

# LAN側:WoLパケット(UDPブロードキャスト)の送信のみ許可
-A OUTPUT -o eth0 -p udp --dport 9 -d 255.255.255.255 -j ACCEPT

COMMIT
Raspberry Pi
sudo iptables-save > ~/iptables-backup # 一応バックアップを取っておく
sudo apt install iptables-persistent # 途中でrules.v4に今の設定を保存するか聞かれるのでNOを選択

SSH無効化

どうせ触らないのでSSHも無効化しちゃいましょう。

Raspberry Pi
sudo raspi-config
# 出現するUIから `Interface Options` → `SSH` → `<No>`を選択

Mosh接続中はサスペンドしない (PC側)

PC側は1時間程度でサスペンドしておくとより省電力になりますが、Mosh接続中でも問答無用でサスペンドになってしまいます。

接続中のMoshやSSH接続がある時はサスペンドしないようにします。

/etc/systemd/system/inhibit-sleep.service (PC)
[Unit]
Description=Inhibit sleep when ssh or mosh connected
Before=sleep.target

[Service]
Type=oneshot
ExecStart=/usr/bin/sh -c '! who | grep -qE "\([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+( via mosh \[[0-9]+\])?\)"'

[Install]
RequiredBy=sleep.target
PC
sudo systemctl enable inhibit-sleep.service

以上でWake on LAN環境の完成です。
Raspberry Piはどこか目立たないところに置いて、電源とLANだけ挿しておきましょう。


まとめ

これで外出先からいつでもClaude Codeできる環境の完成です。
スキマ時間を活用して頑張っていきましょう。

正直、もうちょっと改善できるところはある気がしています。
例えば、WebからClaude Codeを何不自由なく触れる、日本語入力問題なし、途中でページを閉じても処理が続行される!みたいな画期的なツールが登場すれば、CLIの代わりにそっちを使ってもいいかもしれません。
あるいは、組み込みでなければ、VPSを契約してそこでClaude Codeを動かすっていう択もあるのかもしれないですね。(組み込みだと実機への書き込みもしたいので、やっぱり手元にPCがあった方が良いです)
Web系ならClaude Code Webを使えば今回のような複雑なことはしなくていいのでしょうか。ちょっと羨ましい。

活用事例やより良いアイデアがありましたらぜひ教えてください!

C0912_1.gif

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?