DCDevicecamp
メニュー

通常のPythonでセンサーゲートウェイを構築。

定期送信には依存関係のないHTTPS、常駐ゲートウェイにはPaho MQTTを選べます。生成版にはデバイス専用の認証情報が入ります。

1. 通信方式を選ぶ

HTTPはPython標準ライブラリだけで動作し、定期実行に最適です。MQTTはTLS常時接続、QoS 1送信、last will、再接続待ちを利用します。

2. SDK不要のHTTP

デバイスを作成し、Raspberry Pi / PythonでHTTPを選びます。read_sensor()をGPIO、I2C、シリアル、USBセンサーの読み取りへ置き換えます。

#!/usr/bin/env python3
"""Devicecamp Raspberry Pi / Linux HTTP starter."""

import json
import time
import urllib.error
import urllib.request

DEVICECAMP_URL = "https://api.devicecamp.app/v1/devices/DEVICE_ID/telemetry"
DEVICECAMP_SECRET = "DEVICE_SECRET"
PUBLISH_INTERVAL_SECONDS = 60


def read_sensor() -> dict[str, float]:
    # Replace these values with reads from your GPIO, I2C, serial, or USB sensor.
    return {"temperature": 22.4, "humidity": 51.0}


def publish(reading: dict[str, float]) -> None:
    request = urllib.request.Request(
        DEVICECAMP_URL,
        data=json.dumps(reading).encode("utf-8"),
        headers={
            "Authorization": f"Bearer {DEVICECAMP_SECRET}",
            "Content-Type": "application/json",
        },
        method="POST",
    )
    with urllib.request.urlopen(request, timeout=15) as response:
        if response.status not in (200, 202):
            raise RuntimeError(f"Devicecamp returned HTTP {response.status}")


while True:
    try:
        reading = read_sensor()
        publish(reading)
        print(f"Published metric keys: {', '.join(reading)}")
    except (OSError, RuntimeError, urllib.error.HTTPError) as error:
        # Do not print credentials or the full telemetry payload.
        print(f"Publish failed: {error}")
    time.sleep(PUBLISH_INTERVAL_SECONDS)

3. ゲートウェイ向けMQTT

MQTTを選び、一度だけ表示される認証情報を生成します。ダウンロードしたスクリプトを実行する前にpython3 -m pip install "paho-mqtt>=2,<3"でPahoを導入します。

#!/usr/bin/env python3
"""Devicecamp Raspberry Pi / Linux MQTT/TLS starter.

Install the only dependency with:
    python3 -m pip install "paho-mqtt>=2,<3"
"""

import json
import ssl
import time

import paho.mqtt.client as mqtt

MQTT_HOST = "mqtt.devicecamp.app"
MQTT_PORT = 8883
MQTT_CLIENT_ID = "MQTT_CLIENT_ID"
MQTT_USERNAME = "MQTT_USERNAME"
MQTT_PASSWORD = "MQTT_PASSWORD"
TELEMETRY_TOPIC = "v1/devices/DEVICE_ID/telemetry"
STATUS_TOPIC = "v1/devices/DEVICE_ID/status"
PUBLISH_INTERVAL_SECONDS = 60


def read_sensor() -> dict[str, float]:
    # Replace these values with reads from your GPIO, I2C, serial, or USB sensor.
    return {"temperature": 22.4, "humidity": 51.0}


client = mqtt.Client(
    callback_api_version=mqtt.CallbackAPIVersion.VERSION2,
    client_id=MQTT_CLIENT_ID,
    protocol=mqtt.MQTTv311,
)
client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
client.tls_set(tls_version=ssl.PROTOCOL_TLS_CLIENT)
client.reconnect_delay_set(min_delay=1, max_delay=60)
client.will_set(STATUS_TOPIC, '{"status":"offline"}', qos=1, retain=True)
client.connect(MQTT_HOST, MQTT_PORT, keepalive=30)
client.loop_start()
client.publish(STATUS_TOPIC, '{"status":"online"}', qos=1, retain=True)

try:
    while True:
        reading = read_sensor()
        result = client.publish(
            TELEMETRY_TOPIC,
            json.dumps(reading, separators=(",", ":")),
            qos=1,
        )
        result.wait_for_publish(timeout=15)
        if result.is_published():
            print(f"Published metric keys: {', '.join(reading)}")
        time.sleep(PUBLISH_INTERVAL_SECONDS)
finally:
    client.publish(STATUS_TOPIC, '{"status":"offline"}', qos=1, retain=True)
    client.disconnect()
    client.loop_stop()

4. 常時運転

手動確認後は専用ユーザーとsystemdサービスで実行します。生成ファイルには実際の認証情報が含まれるため、そのユーザーだけが読める権限にしてください。

[Unit]
Description=Devicecamp sensor gateway
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=devicecamp
Group=devicecamp
WorkingDirectory=/opt/devicecamp
Environment=PYTHONDONTWRITEBYTECODE=1
ExecStart=/usr/bin/python3 /opt/devicecamp/devicecamp_sensor.py
Restart=on-failure
RestartSec=10
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict

[Install]
WantedBy=multi-user.target

5. サービスをインストール

上のunitをdevicecamp-sensor.serviceとしてダウンロード済みスクリプトと同じ場所へ保存します。コマンドはログイン不可のユーザーを作成し、認証ファイルを保護してログを表示します。

sudo useradd --system --home /opt/devicecamp \
  --shell /usr/sbin/nologin devicecamp
sudo install -d -o devicecamp -g devicecamp -m 750 /opt/devicecamp
sudo install -o devicecamp -g devicecamp -m 600 \
  devicecamp_raspberry_pi_http.py /opt/devicecamp/devicecamp_sensor.py
sudo install -m 644 devicecamp-sensor.service \
  /etc/systemd/system/devicecamp-sensor.service
sudo systemctl daemon-reload
sudo systemctl enable --now devicecamp-sensor.service
sudo journalctl -u devicecamp-sensor.service -f