実機なしで CAN → Greengrass → IoT Core の検証環境を作った

実機なしで CAN → Greengrass → IoT Core の検証環境を作った

FleetWise の新規受付終了に伴い、EC2 + vcan で CAN データを自作 Greengrass コンポーネント経由で IoT Core に MQTT publish する検証環境を構築しました。コンポーネントの実装とレシピの書き方、デプロイ時の注意点を紹介します。
2026.07.24

こんにちは産業支援グループ製造ビジネステクノロジー部のさかじです

先日CANデータを扱う事があり、気軽に実機を使わずCANデータを扱える環境を作りたいと思い本ブログを執筆してみました。合わせてAWS IoT Greengrassを触る機会がなかったので、今回初めて使ってみました。

車載のCANデータをクラウドに上げる仕組みとしては、以前は AWS IoT FleetWise という選択肢がありました。EC2 + vcan + Greengrass で FleetWise Edge Agent をコンポーネントとしてデプロイする構成は、以下の記事で紹介されています。

https://dev.classmethod.jp/articles/fleetwise-greengrass/

しかし、FleetWise は新規顧客の受付を終了しており、新機能の開発も行われません(既存顧客は継続利用可能)。新規に CAN データ収集の仕組みを作る場合は、Greengrass コンポーネントとして自前で実装する必要があります。

今回は、SocketCAN のデータを MQTT で IoT Core に publish する Greengrass コンポーネントを作って検証しました。実機(Raspberry Pi等のエッジデバイス)がすぐに用意できないので、EC2 + 仮想CAN(vcan)でエッジデバイスを模擬しています。

EC2 や Greengrass の構築手順は上記の記事を参考にしてください。本記事ではコンポーネントの実装と、デプロイ時の注意点に焦点を当てます。

やりたいこと

CAN データをクラウドに上げるプログラムを Greengrass コンポーネントとして実装します。必要な機能は以下の2つです。

  1. SocketCAN インターフェースから CAN フレームを読み取る
  2. Greengrass IPC SDK 経由で AWS IoT Core に MQTT publish する

構成

アーキテクチャ図

実機の代わりに EC2 上の vcan(仮想CANインターフェース)を使います。cansend コマンドで CAN フレームを送信すると、Greengrass コンポーネントがそれを読み取り、IoT Core に MQTT publish する流れです。

コンポーネントの実装

Python スクリプト(can_bridge_demo.py)

import json
import can
import awsiot.greengrasscoreipc
from awsiot.greengrasscoreipc.model import PublishToIoTCoreRequest, QOS

INTERFACE = "vcan0"
TOPIC = "demo/can-bridge/frames"

def main():
    ipc = awsiot.greengrasscoreipc.connect()
    bus = can.interface.Bus(channel=INTERFACE, interface="socketcan")
    print(f"Listening on {INTERFACE}, publishing to {TOPIC}", flush=True)

    try:
        while True:
            msg = bus.recv(timeout=1.0)
            if msg is None:
                continue

            payload = json.dumps({
                "arbitration_id": hex(msg.arbitration_id),
                "data": msg.data.hex(),
                "timestamp": msg.timestamp,
            })

            request = PublishToIoTCoreRequest(
                topic_name=TOPIC,
                qos=QOS.AT_LEAST_ONCE,
                payload=payload.encode(),
            )
            ipc.new_publish_to_iot_core().activate(request).result(timeout=5.0)
            print(f"Published: {payload}", flush=True)
    except KeyboardInterrupt:
        pass
    finally:
        bus.shutdown()

if __name__ == "__main__":
    main()

python-can で vcan0 から CAN フレームを受信し、Greengrass IPC SDK で IoT Core に publish するだけのシンプルな構成です。

レシピ(recipe.yaml)

RecipeFormatVersion: "2020-01-25"
ComponentName: com.example.CanBridgeDemo
ComponentVersion: "1.0.0"
ComponentDescription: Demo - CAN frames to IoT Core MQTT
ComponentPublisher: demo
ComponentConfiguration:
  DefaultConfiguration:
    accessControl:
      aws.greengrass.ipc.mqttproxy:
        com.example.CanBridgeDemo:mqttproxy:1:
          policyDescription: Allow publish to demo/can-bridge topic
          operations:
            - "aws.greengrass#PublishToIoTCore"
          resources:
            - "demo/can-bridge/*"
Manifests:
  - Platform:
      os: linux
    Lifecycle:
      install:
        RequiresPrivilege: true
        Script: |
          dnf install -y python3-pip
          python3 -m venv /opt/can-bridge-demo/venv
          /opt/can-bridge-demo/venv/bin/pip install python-can awsiotsdk
      run:
        Script: /opt/can-bridge-demo/venv/bin/python3 {artifacts:path}/can_bridge_demo.py
    Artifacts:
      - URI: s3://YOUR_BUCKET/com.example.CanBridgeDemo/1.0.0/can_bridge_demo.py
        Unarchive: NONE
ComponentDependencies:
  aws.greengrass.TokenExchangeService:
    VersionRequirement: ">=0.0.0"
    DependencyType: HARD

レシピのポイントをいくつか紹介します。

install ステップの RequiresPrivilege: true

Greengrass コンポーネントはデフォルトではコンポーネント用のシステムユーザー(通常 ggc_user)で実行されます。RequiresPrivilege: true を付けると Nucleus を実行しているユーザー(通常は root)で実行されるため、dnf install のような特権が必要な操作が可能になります。これにより、OS パッケージのインストールまでレシピに含めてコンポーネントを自己完結させることができます。

venv の利用

Amazon Linux 2023 には RPM で awscrt がプリインストールされており、グローバル環境で pip install awsiotsdk すると依存パッケージのアップグレード時に競合してエラーになります(Cannot uninstall awscrt, RECORD file not found)。venv で隔離することで回避しています。

動作確認

EC2 に SSM Session Manager で接続し、cansend で CAN フレームを送信します。

$ cansend vcan0 123#DEADBEEF

コンポーネントのログに publish 成功が記録されます。

com.example.CanBridgeDemo: stdout. Listening on vcan0, publishing to demo/can-bridge/frames.
com.example.CanBridgeDemo: stdout. Published: {"arbitration_id": "0x123", "data": "deadbeef", "timestamp": 1784094894.3735676}.

IoT Core の MQTT test client で demo/can-bridge/frames をサブスクライブすると、メッセージが届いていることも確認できました。

IoT Core MQTT test client での受信確認

補足:Greengrass デプロイ時の注意点

自作コンポーネントをデプロイする際に知っておくと役立つ挙動を紹介します。

Thing デプロイは上書き(マージではない)

Greengrass インストール時に --deploy-dev-tools true を指定すると aws.greengrass.Cli コンポーネントがデプロイされます。その後、自作コンポーネントをデプロイしたら Cli が消えました

Greengrass の Thing 単位のデプロイは、前回のデプロイをマージするのではなく上書きします。新しいデプロイに含めなかったコンポーネントは外れます。

デプロイスクリプトには、自作コンポーネントだけでなく aws.greengrass.Cliaws.greengrass.LogManager など使いたいコンポーネントをすべて含める必要があります。

一方、コンポーネントの Configuration は逆にマージされます。上の recipe.yaml でいうと ComponentConfiguration > DefaultConfiguration の部分(accessControl など)です。ここの値を変更してバージョンアップしても、デバイス上の既存の設定値とマージされるため、変更が反映されません。

設定を recipe.yaml の DefaultConfiguration でリセットしたい場合は、デプロイ時に configurationUpdatereset を指定します。

{
  "com.example.CanBridgeDemo": {
    "componentVersion": "1.0.1",
    "configurationUpdate": {
      "reset": [""]
    }
  }
}

reset: [""] でコンポーネントの全設定がレシピの DefaultConfiguration に戻ります。

まとめ

EC2 + vcan で、実機がなくても CAN → Greengrass コンポーネント → IoT Core MQTT の一連の流れを検証できました。

デプロイの上書き挙動や Configuration のマージなど、知っておくと戸惑わずに済むポイントも補足しましたので、同じ構成を試す方の参考になれば幸いです。

この記事をシェアする

関連記事