MicroCat.1でIoT温度監視システムを構築してみた

1. はじめに

今回は、MicroCat.1というRaspberry Pi Pico 2互換のIoTボードを使って、モバイルネットワーク経由で温度データをクラウドに送信するシステムを構築する実験を行いました。センサーからデータを取得し、LTE回線を使って通常のレンタルサーバーに送信するまでの一連の流れを実装し、実際に動作させることができました。

2. 実験の目的

3. 使用したハードウェア

3.1. MicroCat.1

3.2. ADT7410 温度センサー

4. システム構成

flowchart TD
    A[ADT7410温度センサー] -->|I2C通信| B[MicroCat.1<br/>RP2040]
    B -->|SIM7672モデム| C[1NCE LTEネットワーク]
    C -->|HTTP POST| D[Webサーバー<br/>PHP]
    D -->|ログ保存| E[temp_log.txt]

    style A fill:#e1f5ff
    style B fill:#fff4e1
    style C fill:#e8f5e9
    style D fill:#f3e5f5
    style E fill:#fce4ec

注意: 本実験では、1NCEのLTEネットワークを使用して通常のレンタルサーバー(PHPが動作するWebサーバー)にデータを送信しています。1NCEの専用サーバーやサービスは使用していません。

5. 実装の詳細

5.1. I2Cセンサーとの通信

ADT7410はI2Cインターフェースで通信します。MicroPythonのmachine.I2Cモジュールを使用して実装しました。

from machine import I2C, Pin

# I2Cバスの初期化
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=100000)

# ADT7410の初期化
sensor = ADT7410(i2c, address=0x48)

# 温度読み取り
temperature = sensor.read_temperature()

ポイント:

5.2. ADT7410ドライバーの実装

ADT7410のレジスタを直接操作するドライバークラスを実装しました。

class ADT7410:
    def __init__(self, i2c, address=0x48):
        self.i2c = i2c
        self.address = address

        # デバイスの存在確認
        if address not in i2c.scan():
            raise ValueError(f"ADT7410が見つかりません")

        # 16ビットモードに設定
        self.set_config(ADT7410_CONFIG_16BIT)
        time.sleep_ms(250)  # 初期化待機

    def read_temperature(self):
        """温度を摂氏で読み取る"""
        raw_value = self.read_temperature_raw()
        # 16ビットモード: 分解能0.0078℃
        temperature = raw_value / 128.0
        return temperature

実装のポイント:

5.3. モバイルネットワーク接続

SIM7672モデムを使って1NCEネットワークに接続します。

import SIM7672

def init_modem():
    """モデムを起動し、1NCEネットワークに接続する"""
    # モデムオブジェクトの作成
    m = SIM7672.modem()

    # モデムをアクティブ化
    m.active(True)
    time.sleep(10)  # モデムの起動待機

    # 1NCEネットワークに接続
    result = m.connect(
        apn="iot.1nce.net",
        user='',  # ユーザー名は不要(空欄)
        key='',   # パスワードは不要(空欄)
        pdp='IP',  # IPv4のみサポート
        security=1  # PAP認証方式(ユーザー名/パスワードは不要)
    )

    # 接続確認(リトライロジック付き)
    retry = 0
    max_retries = 30
    while retry < max_retries:
        if m.isconnected() and m.ifconfig()[0] != '0.0.0.0':
            print(f"接続成功! IP: {m.ifconfig()[0]}")
            return m
        retry += 1
        time.sleep(2)

    raise RuntimeError("ネットワーク接続に失敗しました")

接続パラメータの説明:

参考: 1NCE APN Setup Documentation

実装のポイント:

5.4. HTTP通信の実装

MicroPythonのrequestsライブラリを使用してHTTP POSTリクエストを送信します。

import requests

def send_data_to_server(temperature, modem=None):
    """サーバーへ温度データをPOST送信する"""
    payload = {
        "device_id": DEVICE_ID,
        "temperature": temperature,
        "unit": "Celsius"
    }

    try:
        response = requests.post(
            SERVER_URL,
            json=payload,
            timeout=30  # モバイル回線の遅延を考慮
        )
        print(f"送信完了。ステータスコード: {response.status_code}")
        response.close()  # メモリ解放
        return True
    except Exception as e:
        print(f"送信エラー: {e}")
        return False

実装のポイント:

5.5. 再接続機能の実装

ネットワーク接続が切断された場合に自動的に再接続する機能を実装しました。

while True:
    try:
        # 接続状態を確認
        is_connected = modem.isconnected()
        ip_info = modem.ifconfig()

        if not is_connected or ip_info[0] == '0.0.0.0':
            print("警告: ネットワーク接続が切断されました。再接続を試みます...")
            try:
                modem.active(False)
                time.sleep(2)
                modem = init_modem()
                print("再接続成功")
            except Exception as e:
                print(f"再接続失敗: {e}")
                time.sleep(10)
                continue

        # 温度読み取りと送信
        temp_c = sensor.read_temperature()
        send_data_to_server(temp_c, modem)

        time.sleep(60)  # 1分待機

    except KeyboardInterrupt:
        break
    except Exception as e:
        print(f"ループ内エラー: {e}")
        time.sleep(10)

実装のポイント:

6. サーバー側の実装

本実験では、通常のレンタルサーバー上でPHPでシンプルな受信スクリプトを実装しました。

<?php
// receive_temp.php
$json_data = file_get_contents('php://input');
$data = json_decode($json_data, true);

if ($data) {
    // ログファイルに保存
    $log = date("Y-m-d H:i:s") . " - Temp: " . $data['temperature'] . "C\n";
    file_put_contents("temp_log.txt", $log, FILE_APPEND);
    echo "Success";
} else {
    http_response_code(400);
    echo "Invalid Data";
}
?>

実装のポイント:

注意: 本実験では通常のレンタルサーバーを使用しています。1NCEの専用サーバーやサービスは使用していません。

2025-12-22 16:42:04 - Temp: 17.429688C
2025-12-22 16:43:06 - Temp: 17.492188C
2025-12-22 16:44:09 - Temp: 17.492188C
2025-12-22 16:45:12 - Temp: 17.507812C
2025-12-22 16:46:15 - Temp: 17.53125C
2025-12-22 16:47:18 - Temp: 17.414062C

temp_log.txtに記録された温度のログ

7. 設定ファイルの管理

機密情報を含む設定はconfig.pyとして管理し、.gitignoreに追加してリポジトリにコミットしないようにしました。

# config.py
SERVER_URL = "http://your-server.com/path/to/receive_temp.php"
DEVICE_ID = "MicroCat_01"

8. 実験結果

9. まとめ

MicroCat.1とADT7410温度センサー、SIM7672モデムを使って、モバイルネットワーク経由で温度データをクラウドに送信するシステムを構築しました。I2C通信、モバイルネットワーク接続、HTTP通信など、IoTシステムに必要な要素を一通り実装できました。

10. サンプル

microcat1 (ZIP, 20.62 KB)

11. 参考資料

12. FAQ

MicroCat.1は、通常のRaspberry Pi Picoと何が違うのですか?
MicroCat.1はRaspberry Pi Pico 2(RP2040/RP2350)と互換性を持ちつつ、SIM7672モデムを標準搭載している点が最大の違いです。これにより、Wi-Fi環境がない場所でもLTE回線を通じたモバイルデータ通信が単体で可能になっています。
なぜADT7410の初期化に250msもの待機時間を設けているのですか?
ADT7410のデータシートによると、電源投入後や設定変更後に動作が安定し、正確な温度データを取得できるようになるまで最大240msの時間を要するためです。この待機を入れないと、初期化直後のデータ読み取りでエラーが発生したり、不正確な値が返ってくる可能性があります。
温度データの精度を上げるために設定している「16ビットモード」とは何ですか?
ADT7410はデフォルトでは13ビット分解能(0.0625℃単位)ですが、レジスタ設定を変更することで16ビット分解能(0.0078125℃単位)で計測できるようになります。より微細な温度変化を捉えたい場合に有効な設定です。
1NCEのSIMカードを使用する際、ユーザー名やパスワードが空欄なのはなぜですか?
1NCEの仕様上、接続にはPAP認証を指定する必要がありますが、認証に使用する具体的なIDやパスワードは個別に設定されていないためです。APN(iot.1nce.net)が正しく設定されていれば、空欄のままでもネットワーク側で認証が行われます。
PDPタイプを「IP」に設定している理由を教えてください。
1NCEのネットワークがIPv4のみをサポートしているためです。IPV4V6などを選択すると、モデムやネットワークの仕様によって接続に失敗したり、接続確立まで時間がかかったりすることがあるため、明示的にIP(IPv4)を指定しています。
1NCEのSIM以外(他社の格安SIMなど)でもこの構成は動作しますか?
はい、基本的には動作します。ただし、そのSIMのキャリアが使用しているバンドをSIM7672モデムがサポートしていること、およびそのSIM専用のAPN設定、認証方式(PAP/CHAP)、ユーザー名/パスワードを適切に設定し直す必要があります。