実機なしで CAN → Greengrass → IoT Core の検証環境を作った
こんにちは産業支援グループ製造ビジネステクノロジー部のさかじです
先日CANデータを扱う事があり、気軽に実機を使わずCANデータを扱える環境を作りたいと思い本ブログを執筆してみました。合わせてAWS IoT Greengrassを触る機会がなかったので、今回初めて使ってみました。
車載のCANデータをクラウドに上げる仕組みとしては、以前は AWS IoT FleetWise という選択肢がありました。EC2 + vcan + Greengrass で FleetWise Edge Agent をコンポーネントとしてデプロイする構成は、以下の記事で紹介されています。
しかし、FleetWise は新規顧客の受付を終了しており、新機能の開発も行われません(既存顧客は継続利用可能)。新規に CAN データ収集の仕組みを作る場合は、Greengrass コンポーネントとして自前で実装する必要があります。
今回は、SocketCAN のデータを MQTT で IoT Core に publish する Greengrass コンポーネントを作って検証しました。実機(Raspberry Pi等のエッジデバイス)がすぐに用意できないので、EC2 + 仮想CAN(vcan)でエッジデバイスを模擬しています。
EC2 や Greengrass の構築手順は上記の記事を参考にしてください。本記事ではコンポーネントの実装と、デプロイ時の注意点に焦点を当てます。
やりたいこと
CAN データをクラウドに上げるプログラムを Greengrass コンポーネントとして実装します。必要な機能は以下の2つです。
- SocketCAN インターフェースから CAN フレームを読み取る
- 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 をサブスクライブすると、メッセージが届いていることも確認できました。

補足:Greengrass デプロイ時の注意点
自作コンポーネントをデプロイする際に知っておくと役立つ挙動を紹介します。
Thing デプロイは上書き(マージではない)
Greengrass インストール時に --deploy-dev-tools true を指定すると aws.greengrass.Cli コンポーネントがデプロイされます。その後、自作コンポーネントをデプロイしたら Cli が消えました。
Greengrass の Thing 単位のデプロイは、前回のデプロイをマージするのではなく上書きします。新しいデプロイに含めなかったコンポーネントは外れます。
デプロイスクリプトには、自作コンポーネントだけでなく aws.greengrass.Cli や aws.greengrass.LogManager など使いたいコンポーネントをすべて含める必要があります。
一方、コンポーネントの Configuration は逆にマージされます。上の recipe.yaml でいうと ComponentConfiguration > DefaultConfiguration の部分(accessControl など)です。ここの値を変更してバージョンアップしても、デバイス上の既存の設定値とマージされるため、変更が反映されません。
設定を recipe.yaml の DefaultConfiguration でリセットしたい場合は、デプロイ時に configurationUpdate で reset を指定します。
{
"com.example.CanBridgeDemo": {
"componentVersion": "1.0.1",
"configurationUpdate": {
"reset": [""]
}
}
}
reset: [""] でコンポーネントの全設定がレシピの DefaultConfiguration に戻ります。
まとめ
EC2 + vcan で、実機がなくても CAN → Greengrass コンポーネント → IoT Core MQTT の一連の流れを検証できました。
デプロイの上書き挙動や Configuration のマージなど、知っておくと戸惑わずに済むポイントも補足しましたので、同じ構成を試す方の参考になれば幸いです。






