AWS IoT MQTTクライアントとRESTful APIを利用してDevice Shadowサービスの動作を確認してみた

2019.12.27

こんにちは、CX事業本部の若槻です。

AWS IoTにはDevice Shadowサービスという機能があります。

デバイスのシャドウは、デバイスの現在の状態情報の保存と取得に使用される JSON ドキュメントです。Device Shadow サービスは AWS IoT に接続するデバイスごとにシャドウを維持します。シャドウを使用すれば、デバイスがインターネットに接続されているかどうかにかかわらず、MQTT または HTTP を介してデバイスの状態を取得および設定できます。各デバイスのシャドウは、対応するモノの名前によって一意に識別されます。

このDevice Shadowサービスの動作を理解したかったので良さげなドキュメントを探していたところ、以下のようなAWSドキュメントを見つけました。

読むだけでもDevice Shadowサービスの動作がよく分かるドキュメントですが、今回は実際にAWS IoTに対してMQTTクライアントとRESTful APIを利用したpub/subを行い、ドキュメントの手順の動作を確認してみました。

目次

  • やってみる
    • 環境準備
    • トピックのサブスクライブの準備
    • IoTエンドポイントの確認
    • Device Shadowサービスの動作を確認してみる
  • おわりに
  • 参考
  • 次回以降に参考にしたい

やってみる

環境準備

まずはAWS側およびローカル(デバイス)側の環境準備を行っていきます。

1. キーペアとデバイス証明書の作成

AWS CLIで以下のコマンドを実行し、2048ビットのRSAキーペアの作成と、発行された公開鍵を使用してX.509のデバイス証明書の発行を行います。

$ aws iot create-keys-and-certificate \
  --set-as-active \
  --public-key-outfile publicKey.pem \
  --private-key-outfile privateKey.pem \
  --certificate-pem-outfile cert.pem
{
    "certificateArn": "arn:aws:iot:ap-northeast-1:123456789012:cert/00c0479e991dcfa918d67b53762ecd95e1171c7ee984f44399d86a8c581ba817",
    "certificateId": "00c0479e991dcfa918d67b53762ecd95e1171c7ee984f44399d86a8c581ba817",
    "certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDWTCCAkGgAwIBAgIULkFh/IUQ6QlCW/2YEIyjihut0FswDQYJKoZIhvcNAQEL\n(中略)-----END CERTIFICATE-----\n",
    "keyPair": {
        "PublicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmWgt6TbmhVXxnr9mS+hw\n(中略)-----END PUBLIC KEY-----\n",
        "PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAmWgt6TbmhVXxnr9mS+hwXVz4//994SNPzY43rLb3us0hEWse\n(中略)-----END RSA PRIVATE KEY-----\n"
    }
}

ここでローカルに保存された秘密鍵privateKey.pemとデバイス証明書cert.pemは後の動作確認時にデバイス側で使用します。

2. ルートCA証明書の取得

以下のコマンドを実行し、デバイスがAWS IoTをサーバー認証するためのルートCA証明書をAmazonトラストサービスのリポジトリからダウンロードして取得します。1

$ curl -o rootCA.pem \
  -s https://www.amazontrust.com/repository/AmazonRootCA1.pem

ローカルに保存したルートCA証明書rootCA.pemは後の動作確認時にデバイス側で使用します。

3. IoTポリシーの作成

以下のiot-policy.jsonファイルを作成します。今回はすべてのアクションとリソースに対して有効なポリシーとします。2

$ cat iot-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action":  "*",
      "Resource": "*"
    }
  ]
}

AWS CLIで以下のコマンドを実行し、iot-policy.jsonをもとにIoTポリシーTestIotPolicyを作成します。

$ aws iot create-policy \
   --policy-name TestIotPolicy \
   --policy-document file://iot-policy.json
{
    "policyName": "TestIotPolicy",
    "policyArn": "arn:aws:iot:ap-northeast-1:123456789012:policy/TestIotPolicy",
    "policyDocument": "{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\":  \"*\",\n      \"Resource\": \"*\"\n    }\n  ]\n}",
    "policyVersionId": "1"
}

4. IoTポリシーをデバイス証明書へアタッチ

AWS CLIで以下のコマンドを実行し、IoTポリシーTestIotPolicyをデバイス証明書にアタッチします。ここで--principalに指定するのは、iot create-keys-and-certificateコマンド実行時の結果のcertificateArnの値です。

$ aws iot attach-principal-policy \
  --policy-name TestIotPolicy \
  --principal arn:aws:iot:ap-northeast-1:123456789012:cert/00c0479e991dcfa918d67b53762ecd95e1171c7ee984f44399d86a8c581ba817

5. thing(モノ)の作成

AWS CLIで以下のコマンドを実行し、AWS IoTのthing(モノ)としてmyLightBulbを作成します。

$ aws iot create-thing --thing-name myLightBulb
{
    "thingName": "myLightBulb",
    "thingArn": "arn:aws:iot:ap-northeast-1:123456789012:thing/myLightBulb",
    "thingId": "7f95d4af-bfec-4009-8faa-3d3dbba277fd"
}

6. デバイス証明書をモノへアタッチ

AWS CLIで以下のコマンドを実行し、モノmyLightBulbにデバイス証明書をアタッチします。ここで--principalに指定するのは、iot create-keys-and-certificateコマンド実行時の結果のcertificateArnの値です。

$ aws iot attach-thing-principal \
  --thing-name myLightBulb \
  --principal arn:aws:iot:ap-northeast-1:123456789012:cert/00c0479e991dcfa918d67b53762ecd95e1171c7ee984f44399d86a8c581ba817

ちなみに

参考までに、ここまで[環境準備]で行ってきた操作内容と操作対象を図に表すと下記のようになります。

image.png

さらに、デバイスとAWS IoT間の実際の通信動作をPublic Key Infrastructureの仕組みに当てはめてみると以下のようになります。

  • デバイス側(AWS IoTへデータ送信時)
    • AWSのルートCA証明書rootCA.pemで、AWSのサーバー証明書を検証し、サーバー認証
    • AWSのサーバー証明書に含まれる公開鍵で、送信データを暗号化
    • 秘密鍵privateKey.pemによる署名を送信リクエストに付与
    • デバイス証明書cert.pemを送信リクエストに付与
  • AWS IoT側(デバイスからデータ受信時)
    • デバイス証明書cert.pemに含まれる公開鍵(=publicKey.pem)で、デバイスからのリクエストに付与された署名を検証し、クライアント認証
    • AWSの秘密鍵でデバイスからの送信データを復号
    • デバイスからの送信データに応じて、AWS IoT側での必要な処理とデバイスへのレスポンスを実施

トピックのサブスクライブの準備

AWS IoTのマネージドコンソールの[テスト]を開きます。[トピックへサブスクライブする]を選択して[トピックのサブスクリプション]にトピック名を入力し[トピックへのサブスクライブ]をクリック、によるサブスクリプションの作成を以下の4つのトピックに対して行います。3

  • $aws/things/myLightBulb/shadow/update/accepted
  • $aws/things/myLightBulb/shadow/update/documents
  • $aws/things/myLightBulb/shadow/get/accepted
  • $aws/things/myLightBulb/shadow/update/delta

image.png

※ブラウザ画面を更新すると作成したサブスクリプションは消えてしまうので注意。

IoTエンドポイントの確認

以下のAWS CLIコマンドを実行して、AWS IoTのエンドポイントURLを確認します。4

$ aws iot describe-endpoint --endpoint-type iot:Data-ATS
{
    "endpointAddress": "xxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com"
}

myLightBulbのデバイスシャドウにRestful APIでアクセスする場合は、/things/myLightBulb/shadowのパスに8443ポートでアクセスすればよいので、https://<endpointAddress>:8443/things/myLightBulb/shadowというURL文字列として変数iotepに代入しておきます。

$ iotep="https://xxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com:8443/things/myLightBulb/shadow"

Device Shadowサービスの動作を確認してみる

ここまで準備した環境を利用して実際にDevice Shadowサービスの動作の確認を行っていきます。動作確認は前述の通り以下のAWSドキュメントの手順に沿って行っていきます。(ドキュメント中の手順は都度引用で記載します)

手順中での「デバイス(電球)」「アプリケーション」「Device Shadowサービス」によるpub/subの動作をシミュレートしたり動作結果を確認したりする方法は以下の通りとなります。

  • デバイス(電球):cUrlコマンド(RESTful API)
  • アプリケーション:マネージドコンソール(MQTTクライアント)
  • 各要素による動作結果の確認:マネージドコンソール(MQTTクライアント または モノの管理ページ)

1. デバイス(電球)が現在の状態("color": "red")をAWS IoTに送信する

電球はオンラインになると、$aws/things/myLightBulb/shadow/update トピックに MQTT メッセージを送信することで、Device Shadow サービスに現在の状態を送信します。 (中略) これをシミュレートするために、AWS IoT MQTT クライアントを使用して、$aws/things/myLightBulb/shadow/updateトピックに以下のメッセージをパブリッシュします。

  • シミュレート方法:cUrlコマンド(RESTful API)

payload.jsonファイルを作成します。

$ cat ./payload.json
{
    "state": {
        "reported": {
            "color": "red"
        }
    }
}

以下のコマンドを実行します。

curl \
  --tlsv1.2 \
  -X POST \
  --cert ./cert.pem \
  --key ./privateKey.pem \
  --cacert ./rootCA.pem \
  $iotep \
  -d @payload.json

{"state":{"reported":{"color":"red"}},"metadata":{"reported":{"color":{"timestamp":1577381275}}},"version":9,"timestamp":1577381275}

2.(1.をトリガーにして)Device Shadowサービスがupdate/acceptedにメッセージを送信する

Device Shadow サービスは $aws/things/myLightBulb/shadow/update/accepted トピックに以下のメッセージを送信することで応答します。 (中略) このメッセージは、Device Shadow サービスが UPDATE リクエストを受け取ってデバイスのシャドウを更新したことを示しています。

  • 確認方法:マネージドコンソール(MQTTクライアント)

AWS IoTのマネージメントコンソールで[テスト]より$aws/things/myLightBulb/shadow/update/acceptedをサブスクライブするMQTTクライアントを選択します。最新のトピックで以下のようなjsonが表示され、"state": {"reported":{"color": "red"}}となっていることが確認できます。

{
  "state": {
    "reported": {
      "color": "red"
    }
  },
  "metadata": {
    "reported": {
      "color": {
        "timestamp": 1577381275
      }
    }
  },
  "version": 9,
  "timestamp": 1577381275
}

3.(1.をトリガーにして)Device Shadowサービスがデバイスシャドウを作成する

Shadow が存在しない場合は作成されます。存在する場合、Shadow はメッセージ内のデータで更新されます。

  • 確認方法:マネージドコンソール(モノの管理ページ)

AWS IoTのマネージメントコンソールで[管理]-[モノ]より「myLightBulb」を選択します。

image.png

[シャドウ]を選択して、シャドウドキュメントが表示されていることを確認します。

image.png

4.(1.をトリガーにして)Device Shadowサービスがupdate/documentsにメッセージを送信する

加えて、Device Shadow サービスは $aws/things/myLightBulb/shadow/update/documents トピックに以下のメッセージをパブリッシュします。 (中略) デバイスのシャドウに対する更新が正常に実行されるたびに、/update/documents トピックにメッセージがパブリッシュされます。

  • 確認方法:マネージドコンソール(MQTTクライアント)

AWS IoTのマネージメントコンソールで[テスト]より$aws/things/myLightBulb/shadow/update/documentsをサブスクライブするMQTTクライアントを選択します。最新のトピックで以下のようなjsonが表示され、"current":{"state": {"reported":{"color": "red"}}}となっていることが確認できます。

{
  "previous": {
    "state": {},
    "metadata": {},
    "version": 8
  },
  "current": {
    "state": {
      "reported": {
        "color": "red"
      }
    },
    "metadata": {
      "reported": {
        "color": {
          "timestamp": 1577381275
        }
      }
    },
    "version": 9
  },
  "timestamp": 1577381275
}

5. アプリケーションが電球の現在の状態をリクエストする

電球とやり取りするアプリケーションがオンラインになり、電球の現在の状態をリクエストします。アプリケーションは $aws/things/myLightBulb/shadow/get トピックに空のメッセージを送信します。これをシミュレートするために、AWS IoT MQTT クライアントを使用して、$aws/things/myLightBulb/shadow/get トピックに空のメッセージ ("") をパブリッシュします。

  • シミュレート方法:マネージドコンソール(MQTTクライアント)

AWS IoTのマネージドコンソールの[テスト]を開きます。[トピックへの発行]を選択して[発行]にトピック名$aws/things/myLightBulb/shadow/getを入力し、メッセージコンソールに""を入力し、[トピックに発行]をクリックします。

image.png

AWS IoTのマネージメントコンソールで[テスト]より$aws/things/myLightBulb/shadow/get/acceptedをサブスクライブするMQTTクライアントを選択します。最新のトピックで以下のようなjsonが表示され、"state": {"reported":{"color": "red"}}となっていることが確認できます。

{
  "state": {
    "reported": {
      "color": "red"
    }
  },
  "metadata": {
    "reported": {
      "color": {
        "timestamp": 1577381275
      }
    }
  },
  "version": 9,
  "timestamp": 1577383774
}

6. アプリケーションが電球の色の変更 (赤から緑への変更) をリクエストする

アプリケーションではこの情報がユーザーに表示され、ユーザーは電球の色の変更 (赤から緑への変更) をリクエストします。そのためには、アプリケーションは $aws/things/myLightBulb/shadow/update トピックにメッセージをパブリッシュします。 (中略) これをシミュレートするために、AWS IoT MQTT クライアントを使用して、$aws/things/myLightBulb/shadow/update トピックに先ほどのメッセージをパブリッシュします。

  • シミュレート方法:マネージドコンソール(MQTTクライアント)

AWS IoTのマネージドコンソールの[テスト]を開きます。[トピックへの発行]を選択して[発行]にトピック名$aws/things/myLightBulb/shadow/updateを入力し、メッセージコンソールに以下のjsonを入力し、[トピックに発行]をクリックします。

{
    "state": {
        "desired": {
            "color": "green" 
        } 
    }
}

7.(6.をトリガーにして)Device Shadowサービスはupdate/acceptedにメッセージを送信する

Device Shadow サービスは $aws/things/myLightBulb/shadow/update/accepted トピックにメッセージを送信することで応答します。

  • 確認方法:マネージドコンソール(MQTTクライアント)

AWS IoTのマネージメントコンソールで[テスト]より$aws/things/myLightBulb/shadow/update/acceptedをサブスクライブするMQTTクライアントを選択します。最新のトピックで以下のようなjsonが表示され、"state": {"desired":{"color": "red"}}となっていることが確認できます。

{
  "state": {
    "desired": {
      "color": "green"
    }
  },
  "metadata": {
    "desired": {
      "color": {
        "timestamp": 1577384203
      }
    }
  },
  "version": 10,
  "timestamp": 1577384203
}

8.(6.をトリガーにして)Device Shadowサービスはupdate/deltaにメッセージを送信する

$aws/things/myLightBulb/shadow/update/delta トピックにもメッセージを送信します。 (中略) Device Shadow サービスは、シャドウの更新が受け入れられ、結果としてシャドウの desired 状態と reported 状態とで異なる値が含まれるときに、このトピックにメッセージをパブリッシュします。

  • 確認方法:マネージドコンソール(MQTTクライアント)

AWS IoTのマネージメントコンソールで[テスト]より$aws/things/myLightBulb/shadow/update/deltaをサブスクライブするMQTTクライアントを選択します。最新のトピックで以下のようなjsonが表示され、"state":{"color": "green"}となっていることが確認できます。

{
  "version": 10,
  "timestamp": 1577384203,
  "state": {
    "color": "green"
  },
  "metadata": {
    "color": {
      "timestamp": 1577384203
    }
  }
}

9.(6.をトリガーにして)Device Shadowサービスはupdate/documentsにメッセージを送信する

Device Shadow サービスは、$aws/things/myLightBulb/shadow/update/documents トピックにもメッセージをパブリッシュします。

  • 確認方法:マネージドコンソール(MQTTクライアント)

AWS IoTのマネージメントコンソールで[テスト]より$aws/things/myLightBulb/shadow/update/documentsをサブスクライブするMQTTクライアントを選択します。最新のトピックで以下のようなjsonが表示され、"current": {"state": {"desired": {"color": "green"},"reported": {"color": "red"}}となっていることが確認できます。

{
  "previous": {
    "state": {
      "reported": {
        "color": "red"
      }
    },
    "metadata": {
      "reported": {
        "color": {
          "timestamp": 1577381275
        }
      }
    },
    "version": 9
  },
  "current": {
    "state": {
      "desired": {
        "color": "green"
      },
      "reported": {
        "color": "red"
      }
    },
    "metadata": {
      "desired": {
        "color": {
          "timestamp": 1577384203
        }
      },
      "reported": {
        "color": {
          "timestamp": 1577381275
        }
      }
    },
    "version": 10
  },
  "timestamp": 1577384203
}

10. デバイス(電球)はupdate/deltaをサブスクライブして、状態を変更(電球の色を赤から緑に変更)し、現在の状態("color": "green")をAWS IoTに送信する

電球は $aws/things/myLightBulb/shadow/update/delta トピックにサブスクライブし、そのトピックへのメッセージを受信すると、電球の色を変更し、その新しい状態をパブリッシュします。これをシミュレートするために、AWS IoT MQTT クライアントを使用し、$aws/things/myLightBulb/shadow/update トピックに以下のメッセージをパブリッシュして、Shadow の状態を更新します。

  • シミュレート方法:cUrlコマンド(RESTful API)

payload.jsonファイルを更新します。

$ cat ./payload.json
{
    "state":{
        "reported":{
            "color":"green"
        },
        "desired":null
    }
}

以下のコマンドを実行します。

curl \
  --tlsv1.2 \
  -X POST \
  --cert ./cert.pem \
  --key ./privateKey.pem \
  --cacert ./rootCA.pem \
  $iotep \
  -d @payload.json

{"state":{"reported":{"color":"green"},"desired":null},"metadata":{"reported":{"color":{"timestamp":1577385174}},"desired":{"timestamp":1577385174}},"version":12,"timestamp":1577385174}

11.(10.をトリガーにして)Device Shadowサービスはupdate/acceptedにメッセージを送信する

レスポンスで、Device Shadow サービスは $aws/things/myLightBulb/shadow/update/accepted トピックにメッセージを送信します。

  • 確認方法:マネージドコンソール(MQTTクライアント)

AWS IoTのマネージメントコンソールで[テスト]より$aws/things/myLightBulb/shadow/update/acceptedをサブスクライブするMQTTクライアントを選択します。最新のトピックで以下のようなjsonが表示され、"state": {"reported": {"color": "green"},"desired": null}となっていることが確認できます。

{
  "state": {
    "reported": {
      "color": "green"
    },
    "desired": null
  },
  "metadata": {
    "reported": {
      "color": {
        "timestamp": 1577385174
      }
    },
    "desired": {
      "timestamp": 1577385174
    }
  },
  "version": 12,
  "timestamp": 1577385174
}

12.(10.をトリガーにして)Device Shadowサービスはupdate/documentsにメッセージを送信する

$aws/things/myLightBulb/shadow/update/documents トピックにもメッセージを送信します。

  • 確認方法:マネージドコンソール(MQTTクライアント)

AWS IoTのマネージメントコンソールで[テスト]より$aws/things/myLightBulb/shadow/update/documentsをサブスクライブするMQTTクライアントを選択します。最新のトピックで以下のようなjsonが表示され、"current": {"state": {"reported": {"color": "green"}}となっていることが確認できます。

{
  "previous": {
    "state": {
      "desired": {
        "color": "green"
      },
      "reported": {
        "color": "red"
      }
    },
    "metadata": {
      "desired": {
        "color": {
          "timestamp": 1577384203
        }
      },
      "reported": {
        "color": {
          "timestamp": 1577384746
        }
      }
    },
    "version": 11
  },
  "current": {
    "state": {
      "reported": {
        "color": "green"
      }
    },
    "metadata": {
      "reported": {
        "color": {
          "timestamp": 1577385174
        }
      }
    },
    "version": 12
  },
  "timestamp": 1577385174
}

おわりに

AWSドキュメントを利用してDevice Shadowサービスの動作を実際に確認することができました。今回はトピックをpub/subするMQTTクライアントはマネージドコンソールを利用しましたが、CLIを利用する方法もあるので次回以降はこちらの動作の確認もしてみたいです。参考になれば幸いです。

参考

次回以降に参考にしたい

以上


  1. AWS IoTは、iot-Dataiot:Data-ATSの2つの異なるデータエンドポイントをサポートしています。iot-DataエンドポイントはVeriSign、iot:Data-ATSエンドポイントはAmazonトラストサービスによって署名されたサーバー証明書を提供します。現在利用が推奨されているのはiot:Data-ATSエンドポイントとなります。(参考:サーバー認証) 
  2. ポリシーの有効範囲を限定したい場合はAWSドキュメント AWS IoT ポリシー が参考になります。 
  3. 各トピックの仕様を確認したい場合はAWSドキュメントシャドウの MQTT トピックが参考になります。 
  4. 脚注1に同じ。