Raspberry PiとAWSを繋いでみる ~MQTT のデモ~

前回より詳しくMQTTに触れていきます。
2023.04.06

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。CX事業本部のKyoです。引き続きラズパイとAWS IoT Coreを触っていきます。

前回はこちら。

概要

前回AWS IoT Device Clientをセットアップしたラズパイにより詳細な設定を行い、MQTTでIoT Coreと双方向で通信します。

具体的には以下の手順を行います。

AWS IoT Device Client との MQTT メッセージ通信をデモンストレーションする

ステップ 1: Raspberry Pi を準備して MQTT メッセージ通信のデモンストレーションをする

このステップは前回とほぼ同じ内容でした。ラズパイへの証明書セットアップ、AWS IoTへのプロビジョニング、MQTT テストクライアントでのサブスクライブの準備、を行いました。

違いがあるとすれば、1つのデバイスに対して複数の証明書をセットアップした点です。本文にも以下のような表現がありました。

AWS IoT のモノのリソースはクラウド内のデバイスの仮想表現なので、AWS IoT に複数のモノのリソースを作成して、さまざまな目的で使用できます。これらすべてを同じ物理 IoT デバイスで使用して、デバイスのさまざまな側面を表すことができます。

また、AWS IoTポリシーの書き方もより限定的になっていました。

前回

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Publish",
                "iot:Subscribe",
                "iot:Receive",
                "iot:Connect"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

今回

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect"
      ],
      "Resource": [
        "arn:aws:iot:<リージョン>:<アカウントID>:client/PubSubTestThing"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish"
      ],
      "Resource": [
        "arn:aws:iot:<リージョン>:<アカウントID>:topic/test/dc/pubtopic"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Subscribe"
      ],
      "Resource": [
        "arn:aws:iot:<リージョン>:<アカウントID>:topicfilter/test/dc/subtopic"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Receive"
      ],
      "Resource": [
        "arn:aws:iot:<リージョン>:<アカウントID>:topic/test/dc/subtopic"
      ]
    }
  ]
}

ステップ 2: AWS IoT Device Client でのメッセージの発行をデモンストレーションする

AWS IoT Device Client を使用してデフォルトメッセージを発行する

まず、AWS IoT Device Client を使用してデフォルトメッセージを発行します。無事にHello Worldできています。ここまでが前回と同じ部分です。

AWS IoT Device Client を使用してカスタムメッセージを発行する

カスタムメッセージを発行していきます。まずはラズパイ側で以下のJSONファイルを作成します。これがカスタムメッセージのペイロードになります。

{
  "temperature": 28,
  "humidity": 80,
  "barometer": 1013,
  "wind": {
    "velocity": 22,
    "bearing": 255
  }
}

その後、デフォルトメッセージの設定ファイルを元に以下のような設定ファイルを作成します。作成したペイロードJSONのパスが含まれている点がポイントです。

{
  "endpoint": "<デバイスデータエンドポイント>",
  "cert": "~/certs/pubsub/device.pem.crt",
  "key": "~/certs/pubsub/private.pem.key",
  "root-ca": "~/certs/AmazonRootCA1.pem",
  "thing-name": "PubSubTestThing",
  "logging": {
    "enable-sdk-logging": true,
    "level": "DEBUG",
    "type": "STDOUT",
    "file": ""
  },
  "jobs": {
    "enabled": false,
    "handler-directory": ""
  },
  "tunneling": {
    "enabled": false
  },
  "device-defender": {
    "enabled": false,
    "interval": 300
  },
  "fleet-provisioning": {
    "enabled": false,
    "template-name": "",
    "template-parameters": "",
    "csr-file": "",
    "device-key": ""
  },
  "samples": {
    "pub-sub": {
      "enabled": true,
      "publish-topic": "test/dc/pubtopic",
      "publish-file": "<ペイロードファイルのパス>",
      "subscribe-topic": "test/dc/subtopic",
      "subscribe-file": "~/.aws-iot-device-client/log/pubsub_rx_msgs.log"
    }
  },
  "config-shadow": {
    "enabled": false
  },
  "sample-shadow": {
    "enabled": false,
    "shadow-name": "",
    "shadow-input-file": "",
    "shadow-output-file": ""
  }
}

両ファイルに適切なパーミッションが設定されていることを確認したら、以下のコマンドでカスタムメッセージを発行します。

./aws-iot-device-client --config-file ~/dc-configs/dc-pubsub-custom-config.json

テストクライアントでは以下のように受信できました。

ステップ 3: AWS IoT Device Client でメッセージのサブスクリプションをデモンストレーションする

単一の MQTT メッセージトピックをサブスクライブする

改めて先ほどの設定JSONを確認します。

subscribe-topictest/dc/subtopicに設定されていますね。これはtest/dc/subtopicというトピックを(ラズパイ側で)サブスクライブできることを示しています。

また、このサブスクリプションから受信したペイロードはsubscribe-fileで指定されている~/.aws-iot-device-client/log/pubsub_rx_msgs.logに書き込まれることになります。

  "samples": {
    "pub-sub": {
      "enabled": true,
      "publish-topic": "test/dc/pubtopic",
      "publish-file": "",
      "subscribe-topic": "test/dc/subtopic",
      "subscribe-file": "~/.aws-iot-device-client/log/pubsub_rx_msgs.log"
    }
  },
  ...

AWS IoT Device Clientを実行し、テストクライアントからtest/dc/subtopicというトピック名のメッセージを発行します。

ラズパイのターミナルには以下のように表示されました。

2023-03-29T14:42:44.059Z [DEBUG] {samples/PubSubFeature.cpp}: Message received on subscribe topic, size: 57 bytes

また、書き込み先にはペイロードが書き込まれていることが確認できました。

$ tail ~/.aws-iot-device-client/log/pubsub_rx_msgs.log

{
"message": "AWS IoT コンソールからの挨拶"
}

なお、トピック名をtest/dc/subtopic2に変えるとメッセージの受信・ログへの書き込み共に行われないことも確認できました。

ワイルドカード文字を使用して複数の MQTT メッセージトピックをサブスクライブする

これまでの設定JSONを元に新しい設定JSONを作成します。subscribe-topictest/dc/# になっています。この#がワイルドカードを意味します。

{
  "endpoint": "",
  "cert": "~/certs/pubsub/device.pem.crt",
  "key": "~/certs/pubsub/private.pem.key",
  "root-ca": "~/certs/AmazonRootCA1.pem",
  "thing-name": "PubSubTestThing",
  "logging": {
    "enable-sdk-logging": true,
    "level": "DEBUG",
    "type": "STDOUT",
    "file": ""
  },
  "jobs": {
    "enabled": false,
    "handler-directory": ""
  },
  "tunneling": {
    "enabled": false
  },
  "device-defender": {
    "enabled": false,
    "interval": 300
  },
  "fleet-provisioning": {
    "enabled": false,
    "template-name": "",
    "template-parameters": "",
    "csr-file": "",
    "device-key": ""
  },
  "samples": {
    "pub-sub": {
      "enabled": true,
      "publish-topic": "test/dc/pubtopic",
      "publish-file": "",
      "subscribe-topic": "test/dc/#",
      "subscribe-file": "~/.aws-iot-device-client/log/pubsub_rx_msgs.log"
    }
  },
  "config-shadow": {
    "enabled": false
  },
  "sample-shadow": {
    "enabled": false,
    "shadow-name": "",
    "shadow-input-file": "",
    "shadow-output-file": ""
  }
}

AWS IoT ポリシーの変更も必要です。iot::Subscribeiot::ReceiveResourceをワイルドカードを含む形に書き換えます。ポリシーでのワイルドカードは#でないことに気をつけます。

    {
      "Effect": "Allow",
      "Action": [
        "iot:Subscribe"
      ],
      "Resource": [
        "arn:aws:iot:<リージョン>:<アカウントID>:topicfilter/test/dc/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Receive"
      ],
      "Resource": [
        "arn:aws:iot:<リージョン>:<アカウントID>:topicfilter/test/dc/*"
      ]
    }

更新後のポリシーです。バージョンが2になってます。

テストクライアントで#をサブスクライブします。また、ラズパイ側でもAWS IoT Device Clientを起動しておきます。

テストクライアントから二種類トピックを発行します。はじめにtest/dc/subtopic、次に(先ほどの設定ではサブスクライブできなかった)test/dc/subtopic2を発行します。

ラズパイ側では以下のようなログが表示されていました。

2023-03-29T15:03:35.493Z [DEBUG] {samples/PubSubFeature.cpp}: Message received on subscribe topic, size: 119 bytes
2023-03-29T15:04:06.148Z [DEBUG] {samples/PubSubFeature.cpp}: Message received on subscribe topic, size: 58 bytes

ログファイルも同様です。

tail -n 20 ~/.aws-iot-device-client/log/pubsub_rx_msgs.log

{
  "message": "AWS IoT コンソールからの挨拶"
}{
  "temperature": 28,
  "humidity": 80,
  "barometer": 1013,
  "wind": {
    "velocity": 22,
    "bearing": 255
  }
}
{
  "message": "AWS IoT コンソールからの挨拶2"

…よく見ると3件入っていますね。これについては以下の説明がありました。

受信したログには AWS IoT Device Client によって発行されたメッセージが表示されることもあります。これは、ワイルドカードトピックフィルターにそのメッセージトピックが含まれており、発行されたメッセージがサブスクライバーに送信される前にメッセージブローカーがサブスクリプションリクエストを処理することがあるためです。

おわりに

今回は前回の復習をしつつ、より詳しくMQTTの扱いを体験しました。 サブスクライブできるトピックはAWS IoT Device Clientの設定による制御とAWS IoT ポリシーによる制御が行われており、それぞれでワイルドカードも利用できる、というのがポイントでしょうか。