Amazon Web Services ブログ

AWS IoT Eventsを利用してデバイスの位置情報を監視する

多数のIoTデバイスを持つ組織では、運用上の問題を特定して対処するために、複数のデバイス間で発生したイベントを追跡する効率的なソリューションが必要となります。この記事では、AcmeTrackerという架空の組織が、IoTデバイスを使用して車両のジオロケーションを追跡するサービスを提供しており、車両が予想されるジオロケーションの境界から外れるとサポートチームに通知する仕組みについて紹介します。AcmeTrackerはAWSのIoTサービスを使用してデバイスとそのデータを管理しています。各デバイスはAWS IoT CoreのFleet Provisioning機能を利用して固有のデバイスのクレデンシャル(証明書と秘密鍵)を取得し、各デバイスのジオロケーションはAWS IoT Eventsで監視しています。

今回の記事では、前述の車両監視ソリューションの運用概要を説明した後、以下のAWS IoTサービスのセットアップ方法について説明します。

  • AWS IoT Eventsを設定し、GPS座標を監視する
  • AWS IoT CoreのFleet Provisioningでユニークなクレデンシャルをデバイスに設定する

ソリューションの概要

とある複数の車両が特定のルートに従わなければならないシナリオを考えてみましょう。ジオロケーションの入力は、各車両を監視し、車両が予想される旅程に沿っていない場合に車両オペレータに通知するために使用されます。AcmeTrackerという組織は、プロビジョニング クレーム クレデンシャル(証明書と秘密鍵)を組み込んで製造されたIoTデバイスを提供しています。

デバイスはAWS IoT Device SDK for Pythonを使用してAWS IoTで認証するために、プロビジョニング クレーム クレデンシャルを使用します。Python以外のプログラミング言語もAWS IoT Device SDKでサポートされており、サポートされているプログラミング言語の一覧はこちらのページをご覧ください。

この車両監視ソリューションでは、以下のような一連のデータとメッセージのやり取りが行われます。

  1. デバイスは、MQTTトピックを介してAWS IoT Coreサービスに、固有のデバイスクレデンシャルの作成(証明書と秘密鍵の作成)を要求します
  2. デバイスは、AWS IoT Coreサービスで定義されたプロビジョニングテンプレートに基づいて、MQTTトピックを介してAWS IoT Coreサービスへの登録(固有のデバイスクレデンシャルの有効化)を要求します
  3. デバイスは衛星からGPS座標を取得します
  4. デバイスは、そのGPS座標をMQTTトピックを介してAWS IoT Coreサービスにpublishします
  5. AWS IoT Coreルールエンジンは、MQTTトピックからGPS座標を取得します
  6. AWS IoT Coreルールエンジンは、そのGPS座標をAWS IoT Eventsに送信します
  7. AWS IoT Eventsサービスには検知器モデルがあり、受信したIoTイベント(GPS座標)を監視して、デバイスが想定される境界線内にあるかどうかを検出します
  8. デバイスの状態が変化した場合(境界内か境界外か)、検知器モデルはAmazon Simple Notification Service(SNS)トピックにメッセージを送信します
  9. SNSトピックに登録しているエンドユーザーは、デバイスの状態変化を通知メッセージにて受信します

注:ステップ1とステップ2は、一意のデバイスのクレデンシャルがまだ生成されていないデバイスにのみ適用されます。固有のクレデンシャルを持つデバイスは、ステップ3からフローを開始します。

IoTデバイスのGPS座標の変化を監視し、それに対応するためにはAWS IoT Eventsをそれに応じて設定する必要があります(上記のステップ6-9で説明)。これについては、以下の「AWS IoT Eventsを設定し、GPS座標を監視する」の項で説明します。

IoTデバイスがAWS IoT Coreに接続するためには、Fleet Provisioning機能を有効にしておく必要があります(上記のステップ1、2、4、5で説明)。これについては、以下の「AWS IoT CoreのFleet Provisioningでユニークなクレデンシャルをデバイスに設定する」の項を参照してください。

注: このソリューションの実装には、Amazon Simple Notification Service (SNS)、AWS IoT Core、AWS IoT Eventsの各サービスが利用可能なAWSリージョンを選択する必要があります。これらのAWSサービスが利用可能なAWSリージョンの一覧は、こちらのAWSリージョン表を参照してください。

AWS IoT Eventsを設定し、GPS座標を監視する

AWS IoT EventsではAcmeTrackerが以下のコンポーネントを作成し、車両監視ソリューションを実装します。

  • IoTデバイスからAWSクラウドに送信されたデータを表す1つの入力には、ジオロケーションの詳細(緯度と経度)が含まれています。
  • 2つの状態(および2つの遷移)を持つ検知器モデルで、デバイスが境界内にあるか境界外にあるかを検出します。検知器モデルは、入力データを使用してデバイスに関連付けられた状態を検出します。

以下のサブセクションでは、上記のコンポーネントの実装方法と、デバイスのジオロケーションデータをAWS IoT Eventsに送るためのAWS IoT Coreの設定方法について説明します。

AWS IoT Eventsの入力を作成

AWS IoT Eventsでは、入力を作成するためのガイドに従って入力を作成することができます。この例では、 AcmeTracker が以下の詳細で入力を作成しています。

  • AWS IoT Eventsの入力名は “gpsInput “に設定されています
  • JSONペイロード/イベントの例は以下の内容です。この内容をファイルの保存し、入力の作成でアップロードします
{
  "gpsDeviceID": "1",
  "gpsLat": 10.1,
  "gpsLng": 10.1,
  "gpsDatTm": "03/02/2020  08:03:20.200"
}

AWS IoT Eventsで検知器モデルを作成し、公開する

この例では、AcmeTrackerは以下の詳細を持つ検知器モデルを作成します。

  • 2つの状態(InBoundaryとOutOfBoundary)
  • デバイスを1つの状態から別の状態に移動させるための2つの遷移(NotInBoundaryTransitionとOutOfBoundaryTransition)

AWS IoT Eventsのコンソールで作成した検知器のモデルを以下に示します

検知器モデルを作成するには、このサンプル検知器モデルファイルをダウンロードし、以下の手順でAWS IoT Eventsにインポートします。

  1. SNSトピックを作成します。詳細については、Amazon Simple Notification Service Developer Guide、より具体的にはAmazon Simple Notification Service API Referenceのトピック作成のドキュメントを参照してください
  2. AWS マネジメントコンソール を使用してエンドポイントを Amazon SNS トピックにサブスクライブするにはのガイドに従って、SNSトピックをサブスクライブします
  3. 検知器モデルのIAMロールを作成します。詳細については、AWS IoT Eventsのパーミッションを設定するためのドキュメントを参照してください。Policyを追加する時に上記のSNSのトピックのARNで修正してください
  4. ここにあるリポジトリからサンプルの検知器モデルファイルをダウンロードします。
  5. ダウンロードしたファイルのすべてのSNSトピックのARNを更新します(値 “#snsTopicArn#”を以下の形式の値に置き換えます: arn:aws:sns:region:account:snsTopicName)
  6. 検知器モデルIAMロールのARNで、ダウンロードしたファイルを更新します(値 “#detectorModelRoleArn#”を以下の形式の値に置き換えます: arn:aws:iam::account:role/service-role/detectorRoleName)
  7. AWSコンソールに移動し、AWS IoT Eventsを選択します
  8. 検知器モデルの作成をクリックし、左側にある検知器モデルのインポートをクリックします
  9. インポートをクリックして、ローカルシステムからファイルを選択し、開くをクリックします
  10. 完了すると、検知器モデルが作成されます
  11. 検知器モデルが作成されたら、検知器モデルを作成するというガイドで示されている検知器モデルを公開する手順を参考に公開します。AcmeTrackerでは、複数のデバイスの状態を追跡する必要があるため、検知器生成モデルは、キーをgpsDeviceIDに設定して「キー値ごとに検知器を作成する」に設定します。検知器評価モデルは「バッチ評価」に設定されています。

検知器モデルにおける境界状態の設定

このサンプルでは、AcmeTrackerは受信した入力を評価した後、SNSトピックに通知するために、検知器モデルの状態を設定しています。受信した入力が、デバイスが境界内または境界外であることを示している場合、現在のデバイスの状態(それぞれ「InBoundary」または「OutOfBoundary」)をエンドユーザに通知するための通知がSNSトピックに送信されます。

以下に、2 つの状態の構成の概要を説明します。

  • どちらの状態も、イベント条件に基づいて SNS トピックに通知を送信する OnEnter イベントと OnInput イベントがあります
  • InBoundary状態のイベント条件の例
$input.gpsInput.gpsLat>=10.1 && $input.gpsInput.gpsLat<=10.2 && $input.gpsInput.gpsLng>=10.1 && $input.gpsInput.gpsLng<=10.2
  • OutOfBoundary状態のイベント条件の例
$input.gpsInput.gpsLat<10.1 || $input.gpsInput.gpsLat>10.2 || $input.gpsInput.gpsLng<10.1 || $input.gpsInput.gpsLng>10.2

注:上記の座標(緯度・経度)はあくまでも例示的なものです。

検知器モデルの境界遷移の設定

この例では、検知器モデルに遷移を設定することで、検知器の入力を条件(イベントの条件)に照らし合わせて評価した後に、検知器の状態を変化させるようにしています。

以下のポイントは、各トランジションの構成の概要を示しています。

  • どちらのトランジションにも、ある状態から他の状態に移行するための条件として使用される、イベントの条件が含まれています
  • InBoundaryTransitionトランジションのイベントの条件の例。
$input.gpsInput.gpsLat>=10.1 && $input.gpsInput.gpsLat<=10.2 && $input.gpsInput.gpsLng>=10.1 && $input.gpsInput.gpsLng<=10.2
  • OutOfBoundaryTransitionトランジションのイベントの条件の例
$input.gpsInput.gpsLat<10.1 || $input.gpsInput.gpsLat>10.2 || $input.gpsInput.gpsLng<10.1 || $input.gpsInput.gpsLng>10.2

注:上記の座標(緯度・経度)はあくまでも例示的なものです。

AWS IoT Coreルールの設定

AWS IoT Coreのルールで、AWS IoT Core(MQTTトピック)からAWS IoT Eventsにデバイスのジオロケーションデータを転送するように設定する必要があります。

  • AWSコンソールに移動し、AWS IoT Coreサービスを選択します
  • ACTルール の順にクリックし、作成をクリックします
  • ルールの名前を設定し、ルールのクエリ文をSELECT * FROM 'gpsTopic'に設定します
  • アクションの追加をクリックし、IoT Events 入力にメッセージを送信するを選択します
  • 前の手順で作成したAWS IoT Eventsの入力を選択します
  • ロールの作成をクリックして、ロール名を入力します
  • アクションの追加をクリックします
  • ルールの作成をクリックします

AWS IoT CoreのFleet Provisioningでユニークなクレデンシャルをデバイスに設定する

AcmeTrackerは、AWS IoT CoreのFleet Provisioning機能を使用して、同じ機能を共有するIoTデバイスのグループを、同じポリシーと同じAWS IoT Coreルールで管理できるようにしています。この例では、ソリューションの概要に記載されているように、プロビジョニング クレーム クレデンシャル(証明書と秘密鍵)を組み込んで製造されたIoTデバイスを提供しています。デバイスは、AWS IoTデバイスSDK for Pythonを使用してAWS IoT Coreで認証を行うためにプロビジョニングクレームクレデンシャルを使用します。Fleet Provisioningを利用することで、IoTデバイスはこれらのクレデンシャルをデバイス固有のクレデンシャルと交換して通常の運用を行うことができます。ここでは、プロビジョニング クレーム クレデンシャルの作成、プロビジョニングテンプレートの作成、AWS IoT Coreへの接続方法について説明します。

プロビジョニング クレーム クレデンシャル作成

プロビジョニング用の証明書は、以下のガイドに従って作成します。完了すると、証明書、秘密鍵、公開鍵、およびルート CA 証明書が作成されます。

  1. デバイス証明書の作成と有効化
  2. AWS IoT Coreのポリシー作成
  3. AWS IoT Coreのポリシーをデバイス証明書にアタッチします。以下のポリシーをデバイス証明書に添付します。

注:以下のポリシーでは、リージョンとアカウントの変数(region:account)を有効なリージョンとアカウント番号(例:us-east-1:123456789)に置き換えてください。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:region:account:client/clientid"
    },
    {
      "Effect": "Allow",
      "Action": ["iot:Publish","iot:Receive"],
      "Resource": [
        "arn:aws:iot:region:account:topic/$aws/certificates/create/*",
        "arn:aws:iot:region:account:topic/$aws/provisioning-templates/iotDeviceTemplateName/provision/*"
      ]
    },    
   {
      "Effect": "Allow",
      "Action": ["iot:Subscribe"],
      "Resource": [
        "arn:aws:iot:region:account:topicfilter/$aws/certificates/create/*",
        "arn:aws:iot:region:account:topicfilter/$aws/provisioning-templates/iotDeviceTemplateName/provision/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
         "iot:CreateProvisioningClaim"
      ],
      "Resource": [
         "arn:aws:iot:region:account:provisioningtemplate/iotDeviceTemplateName"
      ]
    }
  ]
}

AWS IoT Coreでプロビジョニングテンプレートを作成する

以下の手順でAWS IoT Coreでプロビジョニングテンプレートを作成します。

  • AWSコンソールに移動し、AWS IoT Coreサービスを選択します
  • 左側のメニューからオンボードフリート プロビジョニング テンプレート作成の順にクリックします

  • 開始方法をクリックしてください。
  • テンプレート名iotDeviceTemplateNameを入力します (他の名前にする場合は、PolicyやPythonのプログラムも修正してください)。
  • プロビジョニングロールで、ロールの作成をクリックし、名前をつけてロールの作成をクリックします
  • 次へをクリックし、AWS IoT ポリシーを作成するをクリックし、アドバンスドモードをクリックして、以下のポリシーをコピーペーストします。(ポリシーの中のregion:accountは自分の環境に合わせてリージョンとアカウントIDに修正します)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:region:account:client/clientid"
    },
    {
      "Effect": "Allow",
      "Action": ["iot:Publish","iot:Receive"],
      "Resource": "arn:aws:iot:region:account:topic/gpsTopic"
    },
    {
      "Effect": "Allow",
      "Action": ["iot:Subscribe"],
      "Resource": "arn:aws:iot:region:account:topicfilter/*"
    }
  ]
}
  • テンプレートを作成をクリック
  • テンプレートを有効にするをクリックします(プロビジョニングクレームを使用するセクションで証明書を選択する必要はありません
  • 閉じるをクリックし、作成を終了します

AWS IoT Coreに接続

プロビジョニングテンプレートが有効になったら、以下の手順でIoTデバイスをAWS IoT Coreに接続し、GPS座標をPublishすることができます。

以下のスニペットを実行するには、Python SDK (このページを参照) と AWS IoT Device SDK (このページを参照) をインストールする必要があることに注意してください。

  • ステップ 1: 以下のコードスニペットを使用して、キーと証明書を作成します。(configureEndpointは自分の環境に合わせて修正し、configureCredentialsは先の手順で作成した証明書を指定します)
import boto3
import json
import logging
import time
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient

logging.basicConfig(filename='pythonIotDeviceCreate.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s',level=logging.DEBUG)
logger = logging.getLogger('pythonIotDevice')
logger.info("pythonIotDevice")

global certificateOwnershipToken
global certificatePem
global privateKey
certificateOwnershipToken = ''
certificatePem = ''
privateKey = ''

#Connection to the AWS IoT Core with Root CA certificate and provisioning claim credentials (private key and certificates)

# For certificate based connection
myMQTTClient = AWSIoTMQTTClient("clientid")
# For TLS mutual authentication

#Provide your AWS IoT Core endpoint (Example: "abcdef12345-ats.iot.us-east-1.amazonaws.com")
myMQTTClient.configureEndpoint("your.iot.endpoint", 8883) 

#Set path for Root CA and provisioning claim credentials
myMQTTClient.configureCredentials("/root/ca/path", "/private/key/path", "/certificate/path") 

myMQTTClient.configureOfflinePublishQueueing(-1)
myMQTTClient.configureDrainingFrequency(2)
myMQTTClient.configureConnectDisconnectTimeout(10)
myMQTTClient.configureMQTTOperationTimeout(5)
 
logger.info("Connecting...")
myMQTTClient.connect()

#Create keys and certificate by publishing a request in a MQTT topic
 
logger.info("Publishing...")
myMQTTClient.publish("$aws/certificates/create/json", "{}", 0)

#Subscribe to a separate MQTT topic to retrieve the unique device credentials and certificate ownership token

def certCallback(client, userdata, message):
    jsonMessage = json.loads(message.payload)
    certificateOwnershipToken = jsonMessage['certificateOwnershipToken']
    certificatePem = jsonMessage['certificatePem']
    privateKey = jsonMessage ['privateKey']
    logger.info('certificateOwnershipToken=%s, certificatePem=%s, privateKey=%s', certificateOwnershipToken, certificatePem, privateKey)

logger.info("Subscribing...")
myMQTTClient.subscribe("$aws/certificates/create/json/accepted", 1,  certCallback)

#Wait until reception of subscription confirmation (sleep time set to 60 seconds)
time.sleep(60)

logger.info("Disconnecting...")
myMQTTClient.disconnect()
  • ステップ2: 生成されたログファイル(pythonIotDeviceCreate.log)を開きます。ログファイルの中のcertificateOwnershipTokenとデバイス様に作成されたユニークなクレデンシャル(certificatePemprivateKey)があります。certificatePemprivateKeyをそれぞれ別のファイルに保存します。
  • ステップ3: 以下のコードスニペットを使用してデバイスを登録します。コードの中の#certificateOwnershipToken#を、ログファイルに有ったcertificateOwnershipTokenで書き換えます(configureEndpointconfigureCredentialsは前の手順と同じ設定を使用します)
#Register the device (link the unique device credentials with the provisioning template) by publishing a request in a MQTT topic with the certificate ownership token

import boto3
import json
import logging
import time
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient

logging.basicConfig(filename='pythonIotDeviceRegister.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s',level=logging.DEBUG)
logger = logging.getLogger('pythonIotDevice')
logger.info("pythonIotDevice")

#Connection to the AWS IoT Core with Root CA certificate and provisioning claim credentials (private key and certificates)
 
# For certificate based connection
myMQTTClient = AWSIoTMQTTClient("clientid")
# For TLS mutual authentication

#Provide your AWS IoT Core endpoint (Example: "abcdef12345-ats.iot.us-east-1.amazonaws.com")
myMQTTClient.configureEndpoint("your.iot.endpoint", 8883) 

#Set path for Root CA and provisioning claim credentials (do not use the private key and certificate retrieved from the logs in Step 1 since those credentials are not yet activated)
myMQTTClient.configureCredentials("/root/ca/path", "/private/key/path", "/certificate/path") 

myMQTTClient.configureOfflinePublishQueueing(-1)
myMQTTClient.configureDrainingFrequency(2)
myMQTTClient.configureConnectDisconnectTimeout(10)
myMQTTClient.configureMQTTOperationTimeout(5)

logger.info("Connecting...")
myMQTTClient.connect()

jsonInput = {
    "certificateOwnershipToken": "#certificateOwnershipToken#", #Provide the Certificate Ownership Token previously retrieved from the logs in Step 1
    "parameters": {
        "SerialNumber": "Provide-A-Device-Serial-Number" #Provide a Serial Number (Example: 012)
    }
}
 
logger.info("Publishing...")
myMQTTClient.publish("$aws/provisioning-templates/iotDeviceTemplateName/provision/json", json.dumps(jsonInput), 0)

#Subscribe to a separate MQTT topic to retrieve the confirmation

def templateCallback(client,  userdata, message):
    logger.info("Confirmation received: ")
    logger.info(message.payload)
    logger.info("from topic: ")
    logger.info(message.topic)

myMQTTClient.subscribe("$aws/provisioning-templates/iotDeviceTemplateName/provision/json/accepted", 1, templateCallback);

#Wait until reception of subscription confirmation (sleep time set to 60 seconds)
time.sleep(60)

logger.info("Disconnecting...")
myMQTTClient.disconnect()
  • ステップ4:以下のコードスニペットを使用してGPS座標をPublishします(configureEndpointは前の手順と同じ値を使いますが、configureCredentialsの証明書と秘密鍵はログファイルから保存したものを指定します)
import boto3
import json
import logging
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient

logging.basicConfig(filename='pythonIotDevicePublish.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s',level=logging.DEBUG)
logger = logging.getLogger('pythonIotDevice')
logger.info("pythonIotDevice")

#Connection to the AWS IoT Core with Root CA certificate and unique device credentials (keys and certificate) previously retrieved

# For certificate based connection
myMQTTClient = AWSIoTMQTTClient("clientid")
# For TLS mutual authentication
myMQTTClient.configureEndpoint("your.iot.endpoint", 8883) #Provide your AWS IoT Core endpoint (Example: "abcdef12345-ats.iot.us-east-1.amazonaws.com")
myMQTTClient.configureCredentials("/root/ca/path", "/private/key/path", "/certificate/path") #Set path for Root CA and unique device credentials (use the private key and certificate retrieved from the logs in Step 1)
myMQTTClient.configureOfflinePublishQueueing(-1)
myMQTTClient.configureDrainingFrequency(2)
myMQTTClient.configureConnectDisconnectTimeout(10)
myMQTTClient.configureMQTTOperationTimeout(5)
 
logger.info("Connecting...")
myMQTTClient.connect()

#Publish gps coordinates to AWS IoT Core

myMQTTClient.publish("gpsTopic", "{\"gpsDeviceID\":\"1\",\"gpsLat\":10.1,\"gpsLng\":10.1,\"gpsDatTm\":\"03/02/2020  08:03:20.200\"}", 0)
  • ステップ5:事前に購読していたSNSトピックから、デバイスの状態(InBoundary)を反映した通知が届いたことを確認します。また、AWS IoT EventsでもデバイスがInBoundaryになっていることを確認できます。AWS IoT Eventsに移動し、Detectorのモデル名をクリックして、デバイスの現在の状態が下図のようになっていることを確認します。

  • ステップ6:先ほどのコードスニペットのpublish引数であるgps座標を以下の値に置き換えてGPS座標をPublishします。
myMQTTClient.publish("gpsTopic", "{\"gpsDeviceID\":\"1\",\"gpsLat\":10.0,\"gpsLng\":10.0,\"gpsDatTm\":\"03/02/2020  08:03:20.200\"}", 0)
  • ステップ7:事前に購読していたSNSトピックから、デバイスの状態(OutOfBoundary)を反映した通知が届いていることを確認します。また、AWS IoT Eventsでもデバイスが境界外になっていることを確認できます。AWS IoT Eventsに移動し、検知器のモデル名をクリックして、デバイスの現在の状態が下図のようになっていることを確認します。

注:その他のAWS IoT SDK (for Python)のサンプルについては、こちらのページをご覧ください。

まとめ

今回の記事では、AWS IoT Eventsサービスを利用してIoTデバイスフリートのジオロケーションを監視する方法と、Fleet Provisioning機能を利用してAWS IoT Coreに接続するためのユニークなデバイスのクレデンシャルを作成する方法を説明するユースケースをウォークスルーしました。

 

原文はこちら
翻訳はソリューションアーキテクト 市川が担当しました。