Raspberry Pi PicoとAIを繋ぐ—IoTセンサー（CO₂）をLLMで使う

# はじめに

Raspberry Pi Picoのようなデバイスへセンサーを取り付け、その情報をLLM（大規模言語モデル）のAIで利用できるか試した。
MCP（Model Context Protocol）サーバーがどのような役割を果たすのか学びながら構築した。

Claude Desktopでの動作
![ezgif-556ef5a3169789.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/52b03e05-d4e5-4df9-b733-4da9264ffc17.gif)

デバイスの外観
![IMG_2216.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/035acfad-db8e-4d4c-963d-33329ade6c16.jpeg)
![IMG_2217.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/d040de22-7f5d-499d-bf25-5bfb25dc73a3.jpeg)


# Raspberry Pi PicoとCO₂センサー実装

今回は、Raspberry Pi Picoの無線が搭載されていないシンプルなボードを利用し、事例の多数あるセンサー読み取りのファームウェアを作成した。

CO₂センサーは、手元にあったMH-Z19Bを利用した。

#### MH-Z19Bの特徴

非分散型赤外線方式（NDIR）で動作し、比較的精度が高く安定している
測定範囲は0〜5000ppm、測定誤差は±50ppmまたは±5%（大きい方を採用）
センサー内部での自己校正機能（ABC：Automatic Baseline Correction）を備える
UART通信を使用して簡単に接続が可能

#### 回路図・接続時の注意点

MH-Z19BのVCCピンは5V、GNDピンは共通GNDへ接続
Raspberry Pi PicoのGPIOは3.3Vであるため、MH-Z19Bからの出力は3.3V対応（内部回路）で直接接続可能
RX、TXの接続ミスに注意（MH-Z19BのTXをPicoのRXに、MH-Z19BのRXをPicoのTXに接続）

#### PicoとCO₂センサーのコード実装
PicoとCo2センサーはシリアルを利用した通信を行うため、下記のようなコードを使い、デバイスを準備した。
これで、PC側へ、CO₂センサーの値がおよそ1秒ごとに送信される。

```python
from machine import Pin, UART
import utime

mhz19c = UART(0, baudrate=9600, tx=Pin(16), rx=Pin(17))
led = Pin(25, Pin.OUT)

while True:
    led.toggle()
    utime.sleep(0.1)
    led.toggle()
    data = bytearray([0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79])
    mhz19c.write(data)
    mhz19c.readinto(data,len(data))
    co2 = data[2] * 256 + data[3]
    print('CO2 (ppm):'+str(co2))

    utime.sleep(1)
```


接続としては以下の通り。

```
[PC]  <--USB-->  [Pico]
                   |
                   | シリアル (UART)
                   |
               [MH-Z19B]
```

# MCPサーバーによるIoT環境構築

#### MCPサーバーの基本概念と仕組み

MCP（Model Context Protocol）は、デバイスのセンサーデータや状態情報を標準化されたJSON形式でLLMへ渡す役割を持つ。

MCPはOpenAPIが定めた規格であり、JSON-RPC形式を使い、統一されたフォーマットでデータをリクエスト・レスポンスとしてやり取りする。

センサー情報をJSONに変換した後、MCPサーバーがその情報をHTTPまたはWebSocket経由でAIモデルへ伝える。

#### MCPプロトコルが持つ主な要素

1. Resource（リソース）: センサーやデバイスの状態情報を保持・管理するオブジェクトで、LLMへのプロンプト文脈として利用できるもの。今回のケースだと、温度センサーのデータなど

2. Tool（ツール）: 特定の機能（センサー値取得、状態変更、イベント通知など）を提供するためのAPI。今回のケースだと、温度センサーを呼び出すコマンド、デバイス状態を返すコマンドなど

3. Prompt（プロンプト）: ユーザーや他のLLMへ提示する指示や対話フローテンプレート。今回のケースでは使っていないが、例えば、ChatGPTで「あなたはXXの専門家です、その視点でXXをアドバイスしてください」などのプロンプトがそれにあたるという理解。

#### センサーデータ取得とMCPサーバーへの送信のコード実装
今回のMCPサーバーの構成は下記となる。

```
[センサーデータ]
       │
       │ シリアル通信
       ↓
[MCPサーバー]
       │
       │ 標準入出力(stdio)
       ↓
[MCPクライアント（Claude Desktop）]
```

MCPサーバーは実際は、Claude Desktopが起動するnode.jsの実行プロセスとして動作する。
そこへ標準入出力(stdio)でアクセスする形となる。
MCPサーバーとClaude DesktopなどのMCPクライアントとの間はほかにHTTP with SSE(Server-Sent Events)というリモートに対応できる接続方式もある。後日調査したい。

実装コードは[Github](https://github.com/kmwebnet/MCP-Server-for-sensor-device)に配置した。

コードは大まかに下記の構成となった：

* SerialPortライブラリを使ったシリアル通信の初期化
* シリアルポートが利用可能でない場合のセンサー値シミュレーション
* JSON-RPCリクエストによるデータ送信とエラー処理
* 定期的なセンサーデータ更新の仕組み
* センサー値（CO₂濃度）の取得と解析方法
* エラーコード管理とロギング（ログファイルへの書き込み処理）

# AI（LLM）連携による可能性と応用事例

リアルタイム換気制御：CO₂濃度が一定値（例：1000ppm）を超えたら、LLMが換気を推奨または自動で換気装置をオンにする。その他イベントドリブンで他のMCPサーバーと連携し制御の提案も行う。

環境快適性向上：オフィスや住宅内の快適度をLLMが評価し、アドバイスを提供。
などなど、Alexaのようなホームアシスタントが作れるのではないか。

# 課題と今後の展望

セキュリティやプライバシー保護の注意点
ローカルで遊ぶ分には問題はないだろうが、リモート環境のセンサーとの疎通などは配慮する必要がありそう。