【第2回】IoT Core の三大機能で「使えるIoT」を完成させる ― そして外部連携まで一気に俯瞰する
IoT Core の実装第2回目。第1回で「Pi 3B+ → クラウドにデータが届く」までは到達しました。本記事では IoT Core が標準で持つ Rules Engine / Device Shadow / Jobs の3機能を実装中心にコンパクトに押さえ、後半で Matter / IFTTT / Homey との連携パターンと Greengrass との使い分け を比較表で俯瞰します。これを読み終える頃には、自分で IoT システム一式を設計できる感覚が掴めるはずです。
この記事のスタンス
ハンズオンの詳細は AWS 公式ドキュメント や AWS Skill Builder に良質なものが揃っているので、本記事では 「最小実装」と「動作確認の方法」 にフォーカスします。手順を1ステップずつ丁寧に追うというより、実装パターンを俯瞰できる ことを目指します。
前提:
- 第1回の構成(Pi 3B+ → IoT Core で MQTT Pub/Sub できている)
- 証明書、ポリシー、bridge.py が
/home/pi/に配置済み - Thing名:
raspi-bridge、トピック:hello/raspi
Part A:IoT Core の三大機能
1. Rules Engine ― 受信メッセージを後続サービスに流す
何をするものか
MQTT で受け取ったメッセージを SQL風の式でフィルタ・加工して、Lambda / DynamoDB / S3 / Kinesis / CloudWatch などに自動でルーティング する仕組み。「IoT Core に届いた後どうする?」の答えがこれです。
最小実装(CloudFormation で定義)
## [SPEC] iot-rule.yaml -- hello/raspi の受信を CloudWatch Logs に流す
AWSTemplateFormatVersion: '2010-09-09'
Resources:
TelemetryToCWLogs:
Type: AWS::IoT::TopicRule
Properties:
RuleName: HelloRaspiToCWLogs
TopicRulePayload:
RuleDisabled: false
Sql: "SELECT *, topic() AS source_topic, timestamp() AS ts FROM 'hello/raspi'"
AwsIotSqlVersion: '2016-03-23'
Actions:
- CloudwatchLogs:
LogGroupName: !Ref TelemetryLogGroup
RoleArn: !GetAtt IoTRuleRole.Arn
TelemetryLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /iot/hello-raspi
RetentionInDays: 7
IoTRuleRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal: { Service: iot.amazonaws.com }
Action: sts:AssumeRole
Policies:
- PolicyName: WriteLogs
PolicyDocument:
Statement:
- Effect: Allow
Action: ['logs:CreateLogStream', 'logs:PutLogEvents']
Resource: !GetAtt TelemetryLogGroup.Arn
## [SPEC] デプロイ
aws cloudformation deploy \
--template-file iot-rule.yaml \
--stack-name iot-rule-stack \
--capabilities CAPABILITY_IAM
動作確認
## [NOTE] Pi 側を起動した状態で、CloudWatch Logs を覗く
aws logs tail /iot/hello-raspi --follow
10秒おきにメッセージが流れてくれば成功。Rules で流す先を変えるだけで「保存」「通知」「集計」「他AWSサービスへの連携」がすべて実現できます。
ありがちな次の一手
| やりたいこと | Action タイプ |
|---|---|
| DynamoDB に時系列で保存 | DynamoDBv2 |
| Lambda で加工してから処理 | Lambda |
| 異常値で SNS 通知 |
Sns(SQL の WHERE で条件指定) |
| 大量データを S3 にバッチ書き出し |
Firehose → S3 |
| 時系列DBに永続化 | Timestream |
2. Device Shadow ― 状態の双方向同期
何をするものか
各 Thing に紐づく JSONドキュメント で、desired(クラウドが望む状態)と reported(デバイスが現状報告)の2区画があります。MQTT が途切れている間も差分は AWS 側に保持され、再接続時に同期されます。「ON/OFF」のような状態をやりとりするときの王道 です。
Shadow ドキュメントのイメージ
{
"state": {
"desired": { "led": "on" },
"reported": { "led": "off" }
}
}
desired.led と reported.led が違う = デルタが発生 している状態。デバイスはこのデルタを受け取って、reported を更新することで「指示を実行した」と伝えます。
最小実装(Pi 側)
## [SPEC] /home/pi/shadow.py -- Shadow の delta を受けて状態を切り替える
## [REF] https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-device-shadows.html
import json
from awscrt import mqtt
from awsiot import mqtt_connection_builder, iotshadow
THING_NAME = "raspi-bridge"
## [NOTE] MQTT接続は第1回と同じ mtls_from_path で確立済みとする
mqtt_conn = mqtt_connection_builder.mtls_from_path(
endpoint="xxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com",
cert_filepath="/home/pi/certs/cert.pem",
pri_key_filepath="/home/pi/certs/priv.key",
ca_filepath="/home/pi/certs/root-CA.pem",
client_id=THING_NAME,
)
mqtt_conn.connect().result()
shadow_client = iotshadow.IotShadowClient(mqtt_conn)
## [SPEC] delta を受けたら reported を更新する
def on_delta(delta):
desired_led = delta.state.get("led")
print(f"[DELTA] LED -> {desired_led}")
## [TODO] 実際のGPIO制御をここに書く(本記事では割愛)
## [NOTE] reported を更新してクラウドに「やりました」と伝える
update = iotshadow.UpdateShadowRequest(
thing_name=THING_NAME,
state=iotshadow.ShadowState(reported={"led": desired_led}),
)
shadow_client.publish_update_shadow(update, mqtt.QoS.AT_LEAST_ONCE)
shadow_client.subscribe_to_shadow_delta_updated_events(
request=iotshadow.ShadowDeltaUpdatedSubscriptionRequest(thing_name=THING_NAME),
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=on_delta,
).result()
print("[INFO] Shadow listener started")
import time
while True:
time.sleep(60)
動作確認
AWS コンソールで AWS IoT > すべてのデバイス > モノ > raspi-bridge > デバイスシャドウ > Classic Shadow を開き、エディタで以下を保存:
{ "state": { "desired": { "led": "on" } } }
Pi のログに [DELTA] LED -> on が出て、その直後にコンソールの reported が on に更新されれば成功。「クラウドから状態指示 → デバイス受信 → 実行報告」 が双方向に回りました。
3. IoT Jobs ― リモートからの任意処理実行
何をするものか
Shadow が「状態」を扱うのに対し、Jobs は「処理」を扱います。デバイス更新、診断、リブートなど、1回完結のタスク をクラウドから配信して結果を回収する仕組み。
最小実装
1. Job ドキュメントを S3 に置く
## [SPEC] s3://my-iot-jobs/job-restart-bridge.json
{
"operation": "restart_service",
"service": "raspi-bridge.service"
}
2. Job を作成
## [SPEC] raspi-bridge に Job を配信
aws iot create-job \
--job-id restart-bridge-001 \
--targets arn:aws:iot:ap-northeast-1:111122223333:thing/raspi-bridge \
--document-source s3://my-iot-jobs/job-restart-bridge.json \
--target-selection SNAPSHOT
3. Pi 側で Job を受信して実行
## [SPEC] /home/pi/jobs.py -- Job を受信して実行するエージェント(抜粋)
## [NOTE] 実コードでは IotJobsClient の subscribe / start_next / update を使う
import subprocess, json
from awsiot import iotjobs
def on_job_execution(job):
doc = job.execution.job_document
if doc.get("operation") == "restart_service":
svc = doc["service"]
subprocess.run(["sudo", "systemctl", "restart", svc])
## [NOTE] 実行結果を IoT Core に報告
return iotjobs.JobExecutionState.SUCCEEDED
return iotjobs.JobExecutionState.FAILED
## [TODO] iotjobs.IotJobsClient のサブスクライブ設定は AWS サンプル参照
## https://github.com/aws/aws-iot-device-sdk-python-v2/tree/main/samples
動作確認
## [SPEC] Jobの状態を確認
aws iot describe-job-execution \
--job-id restart-bridge-001 \
--thing-name raspi-bridge
## status が SUCCEEDED になれば成功
数台規模の運用Tips: Jobs を
target-selection CONTINUOUSで作成し、Thing Group をターゲットにすると、後から追加された Thing にも自動で配信されます。「数台のPiに同じ更新スクリプトを配る」運用はこれで十分まわります。
Part B:外部世界との接続を俯瞰する
ここまでで デバイス ↔ AWS は完成しました。次に AWS ↔ 外部のIoTエコシステム をどう繋ぐか疑問に思ったので 3つほど取り出して比較してみました。
と、いうのもそれぞれどう違うのか興味があったためです。
次のパターン表を見てもらうとわかりますが、それぞれのサービスは 種類が異なるのでどの層からのアプローチをしたいかなどによっても選択肢があります。
外部連携パターン表
| 連携先 | 種類 | 主な接続方式 | IoT Core 側の入口/出口 | 双方向性 | 主な用途 |
|---|---|---|---|---|---|
| Matter デバイス | プロトコル標準 | ローカル Matter Controller(Pi)経由 で MQTT に変換 | Pi側 Device SDK / 後述のブリッジ | ◎ | スマートホーム機器(プラグ、電球、センサ) |
| IFTTT | クラウドiPaaS | Webhooks (HTTPS) | API Gateway + Lambda | ○ | 他SaaSをトリガに/結果として接続 |
| Homey (Pro/SHS) | ハブ製品 | Webhook + Web API | API Gateway + Lambda | ◎ | Zigbee/Z-Wave機器をAWSに集約 |
各連携の最短アーキ
Matter 連携(第3回で詳細実装)
[Matter機器] ──Matter/Wi-Fi── [Pi 3B+: Matter Controller]
↓ MQTT
IoT Core
↓ Rule
Lambda / DynamoDB / etc.
ポイント: Matter は本質的にローカルプロトコル なので、Pi が「Matter ⇔ MQTT」の変換ハブになります。これが第3回のメインテーマ。
IFTTT 連携
受信(IFTTT → AWS):
[IFTTT Applet] ──Webhook──→ [API Gateway] → [Lambda] → IoT Core (Publish)
送信(AWS → IFTTT):
IoT Core (Rule) → [Lambda] ──Webhook──→ [IFTTT Maker URL]
https://maker.ifttt.com/trigger/{event}/with/key/{key}
ポイント: IFTTT は基本的に「人がトリガを書く」世界 なので、双方向にしたい場合も Webhook の URL を Lambda 側に登録するだけで済みます。
Homey 連携
受信(Homey → AWS):
[Homey Flow] ──Webhook──→ [API Gateway] → [Lambda] → IoT Core (Publish)
送信(AWS → Homey):
IoT Core (Rule) → [Lambda] ──HTTP(Bearer Token)──→ [Homey Web API]
http://<homey-ip>/api/manager/...
ポイント: Homey Pro はローカル Web API を持っているので、LAN内なら直叩きできる。クラウド経由なら webhook.homey.app を経由。
Part C:IoT Core をベースにしたいのですが IoTGreenGlass である理由を教えてください
「Greengrass って必要?IoTCoreはGreenGrassの中の一部なの?どっちがいいかおしえてほしい、これIoT使い始めてから聞かれるもので、ぱっとおなじもので後継サービスなのか?なんて誤解されます。
正直なところ
Greengrass を使う「べき」理由は意外と少ない、という正直な話
ここはちゃんと正直に答えたほうがいいテーマです。
結論から言うと、IoT Core だけで完結する構成は十分アリで、Greengrass は「特定の条件」を満たすときに初めてペイします。 名前の響きからか Greengrass の利便性を強調しすぎた感がある。
大まかに言えば、「工場などの閉域網で使いたい」「運転のフィードバックに対して即時性処理を要求する」と言った時の選択肢として頭に入れておく。
まず、IoT Core 単体でどこまでできるか
IoT Core は MQTT ブローカーだけだと思われがちですが、実はかなり広い機能を持っています:
| 機能 | できること | Greengrass 不要? |
|---|---|---|
| MQTT ブローカー | デバイス ↔ クラウド通信 | ✅ 不要 |
| Device Registry (Things / Groups) | デバイス管理、フリート索引 | ✅ 不要 |
| X.509 証明書認証 | デバイス認証 | ✅ 不要 |
| Rules Engine | 受信メッセージを Lambda/DynamoDB/S3/Kinesisへ | ✅ 不要 |
| Device Shadow | デバイス状態の同期(オフライン耐性あり) | ✅ 不要 |
| IoT Jobs | 任意のリモート処理実行(OTA含む) | ✅ 不要 |
| Fleet Provisioning | 大量デバイスの一括登録 | ✅ 不要 |
| Device Defender | セキュリティ異常検知 | ✅ 不要 |
| Secure Tunneling | NAT越しのSSH | ✅ 不要 |
| MQTT over WSS | WebSocket経由MQTT | ✅ 不要 |
つまり「クラウドにデータ送る・指示を受ける・OTAする・状態同期する」までは IoT Core だけで全部完結する。
Greengrass が本当に必要になる条件
Greengrass は「エッジコンピューティングランタイム」です。逆に言うと、エッジで処理する必要がない限り、選択から外すことが可能になります。
Greengrass が不要なケース(IoT Core で十分)
| シナリオ | IoT Core で実現する方法 |
|---|---|
| 単純にセンサ値を送りたい | Device SDK + MQTT Publish |
| クラウドから ON/OFF 指示を受けたい | Shadow + Device SDK |
| ファームウェア更新したい | IoT Jobs + 自前の更新スクリプト |
| デバイスを大量に登録したい | Fleet Provisioning |
| デバイスにSSH接続したい | Secure Tunneling |
| デバイスの異常を検知したい | Device Defender |
「Greengrass あり」と「なし」の本当の差分
| 項目 | IoT Core only | + Greengrass |
|---|---|---|
| MQTT 通信 | Device SDK | Greengrass経由(同じ) |
| 証明書管理 | 手動配置 | Connection Kit で簡略化 |
| デーモン化 | 自前 systemd unit |
greengrass-lite.target 付属 |
| アプリ更新 | IoT Jobs + 自前スクリプト | コンポーネントデプロイ |
| 複数台展開 | Fleet Provisioning | Thing Group へのデプロイ |
| オフライン耐性 | 自前で実装 | Stream Manager で標準対応 |
| 設定ファイル更新 | 自前 | デプロイの設定マージで対応 |
Greengrass が真価を発揮する条件(箇条書きだったもの)
これは元々表ではなく箇条書きでしたが、表にしておくほうがQiitaに貼ったときに見映えがすると思うので、表形式に整理し直したものも置いておきます。
機能で見る差分
| 機能 | IoT Core 単体 | + Greengrass |
|---|---|---|
| MQTT 送受信 | ◎ | ◎(同じ) |
| 証明書認証 | ◎ | ◎(Connection Kit で簡略化) |
| クラウド連携(Rules Engine) | ◎ | ◎(同じ) |
| 双方向制御(Shadow) | ◎ | ◎(同じ) |
| リモート処理(Jobs) | ◎ | ◎(同じ) |
| Fleet Provisioning | ◎ | ◎ |
| ローカル処理(ML推論など) | × | ◎ |
| オフライン動作・データバッファ | △(自前) | ◎(Stream Manager) |
| デバイス同士のローカル通信 | × | ◎(IPC) |
| コンポーネント単位のOTA | △(Jobs+自前スクリプト) | ◎ |
| ランタイム必要RAM | Python SDKで30〜50MB | Nucleus Lite で5MB |
ユースケースで見る使い分け
| シナリオ | おすすめ | 理由 |
|---|---|---|
| 個人で1〜数台、テレメトリと制御だけ | IoT Core単体 | Rules/Shadow/Jobs で十分 |
| 業務で数台〜十数台、安定したネットワーク | IoT Core単体 | 同上、運用工数も少ない |
| 工場・車載でネット切断中も動作必須 | + Greengrass | Stream Manager のオフライン耐性 |
| エッジでML推論・動画解析を回したい | + Greengrass | ローカル処理用ランタイム |
| 数百〜数千台で頻繁にコード更新 | + Greengrass | コンポーネントデプロイ |
| 親機 - 子機構成でローカル制御 | + Greengrass | IPC / Lambda local |
目安: 「数台規模 + 安定したインターネット」なら IoT Core 単体で困りません。逆に「オフライン耐性 / エッジ処理 / 大規模フリート」のいずれかが要件に出てきたら Greengrass の出番です。本連載の想定読者(数台規模)なら、まずは IoT Core 単体を使い倒せれば十分。
まとめ:ここまでで「一通りつくれる」状態へキタ
第1回と本記事で、IoT Core を中心とした以下の能力がすべて手に入った状態になりました。
| 能力 | 使う機能 | 第何回で扱った |
|---|---|---|
| デバイスからクラウドへデータを送る | MQTT + Device SDK | 第1回 |
| 受信データを保存・分析・通知に流す | Rules Engine | 第2回 |
| クラウドからデバイスを双方向制御する | Device Shadow | 第2回 |
| デバイスのコードを遠隔で更新する | Jobs | 第2回 |
| 他のIoTエコシステム(Matter/IFTTT/Homey)と繋ぐ | Rules + Lambda + Webhook | 第2回 |
| エッジ処理や大規模フリートにスケールする | Greengrass(必要に応じて) | 第2回(概要のみ) |
つまり、個人〜業務で数台規模の IoT システムなら、これだけの知識で一通り設計・実装できる という状態にたどり着きました🎉
第3回は学んだことの 実践編 として、Pi 3B+ を Matter Controller 化し、市販の Matter デバイスを IoT Core に繋ぐエンドツーエンドのサンプルを作ります。Rules Engine / Shadow / Jobs / Matter連携、すべてが1つの構成に集約される回になります。
お家にいるAlexa との連携とも、あとちょっと❤️
参考リンク
- AWS IoT デバイスシャドウサービス(公式)
- AWS IoT Jobs(公式)
- AWS IoT ルール(公式)
- AWS IoT Device SDK for Python v2 サンプル
- AWS IoT Core ワークショップ(ハンズオン)
この記事は IoT Core 連載シリーズ(全3回)の第2回です。次回「Pi 3B+ を Matter Controller 化して IoT Core と繋ぐ」もぜひ。
