Raspberry Pi で AWS IoT Core にデータを Publish する

2023/08/16に公開

Raspberry Pi から AWS IoT Core に接続してデータを Publish した時のメモです。
AWSコンソールの Connect one device で行う作業に相当しますが、今回は aws-shell を使ってセットアップを行ってみます。

ポリシーの作成

最初にクライアントが IoT Core へのアクセスを許可するためのポリシーを作成します。このポリシーをクライアント証明書、もしくは、グループにアタッチすることで、クライアントが IoT Core への接続や、topic へのアクセスが可能になります。

aws> iot create-policy --policy-name rpi-policy --policy-document file://rpi-policy.json

作成するポリシーは以下のようにします。証明書がアタッチされたクライアントからの接続のみを許可します。また、トピック rpi/<ThingName> に対して Publish, Receive, Subscribe を許可します。

rpi-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["iot:Connect"],
      "Resource": "arn:aws:iot:ap-northeast-1:<YOUR ACCOUNT ID>:client/${iot:Connection.Thing.ThingName}",
      "Condition": {
        "Bool": {
          "iot:Connection.Thing.IsAttached": "true"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": ["iot:Publish", "iot:Receive"],
      "Resource": "arn:aws:iot:ap-northeast-1:<YOUR ACCOUNT ID>:topic/rpi/${iot:Connection.Thing.ThingName}"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": "arn:aws:iot:ap-northeast-1:<YOUR ACCOUNT ID>:topicfilter/rpi/${iot:Connection.Thing.ThingName}"
    }
  ]
}

グループの作成

Thing を所属させるグループを作ります。グループにポリシーをアタッチすることで、そのグループに所属する Thing にそのポリシーが適用されます。(もしくは、クライアント証明書にポリシーをアタッチすることもできます。)

aws> iot create-thing-group --thing-group-name rpi-group
aws> iot attach-policy --target "arn:aws:iot:ap-northeast-1:487501577678:thinggroup/rpi-group" --policy-name "rpi-policy"

Thing の作成

Thing を作成し、グループに所属させます。ThingName として Raspberry Pi のシリアル番号を使用することにします。

Raspberry Pi 上で以下のコマンドを実行し、シリアル番号を調べます。

$ cat /proc/cpuinfo | grep Serial
Serial          : 100000007517acf3

aws-shell 上で Thing を作成します。

aws> iot create-thing --thing-name "rpi-100000007517acf3"

作成した Thing をグループに所属させます。

aws> iot add-thing-to-thing-group --thing-group-arn "arn:aws:iot:ap-northeast-1:487501577678:thinggroup/rpi-group" --thing-arn "arn:aws:iot:ap-northeast-1:487501577678:thing/rpi-100000007517acf3"

証明書の作成

クライアント証明書を作成します。

aws> iot create-keys-and-certificate \
	--set-as-active \
	--certificate-pem-outfile "rpi-100000007517acf3.cert.pem" \
	--public-key-outfile "rpi-100000007517acf3.public.key" \
	--private-key-outfile "rpi-100000007517acf3.private.key"

証明書が作成されました。

ls -l rpi-100000007517acf3.*
-rw------- 1 shasegawa shasegawa 1224 Aug 16 00:43 rpi-100000007517acf3.cert.pem
-rw------- 1 shasegawa shasegawa 1675 Aug 16 00:43 rpi-100000007517acf3.private.key
-rw------- 1 shasegawa shasegawa  451 Aug 16 00:43 rpi-100000007517acf3.public.key

証明書を Thing にアタッチします。

aws> iot attach-thing-principal --thing-name "rpi-100000007517acf3" --principal arn:aws:iot:ap-northeast-1:487501577678:cert/XXXXXX

Amazon の CA もダウンロードします。

curl -O https://www.amazontrust.com/repository/AmazonRootCA1.pem

クライアントからの接続

あらかじめ、Raspberry Pi 上に証明書をコピーしておきます。

Raspberry Pi 上に仮想環境を作成します。

python -m venv .venv
. .venv/bin/activate

必要となるパッケージをインストールします。

pip install awsiotsdk

AWS IoT SDK を用いてトピックにメッセージを publish します。

client.py
import os
import json
import time
import random
from awscrt import io, mqtt
from awsiot import mqtt_connection_builder


def main():
    ENDPOINT = os.environ.get('ENDPOINT')
    CLIENT_ID = os.environ.get('CLIENT_ID')
    PATH_TO_CERT = os.environ.get('PATH_TO_CERT')
    PATH_TO_KEY = os.environ.get('PATH_TO_KEY')
    PATH_TO_ROOTCA = os.environ.get('PATH_TO_ROOT')

    event_loop_group = io.EventLoopGroup(1)
    host_resolver = io.DefaultHostResolver(event_loop_group)
    client_bootstrap = io.ClientBootstrap(event_loop_group, host_resolver)
    mqtt_connection = mqtt_connection_builder.mtls_from_path(
        endpoint=ENDPOINT,
        cert_filepath=PATH_TO_CERT,
        pri_key_filepath=PATH_TO_KEY,
        client_bootstrap=client_bootstrap,
        ca_filepath=PATH_TO_ROOTCA,
        client_id=CLIENT_ID,
        clean_session=False,
        keep_alive_secs=6
    )

    connect_future = mqtt_connection.connect()
    connect_future.result()
    print("Connected.")

    topic = f"rpi/{CLIENT_ID}"
    message = {
        "ts": round(time.time() * 1000),
        "value": random.uniform(0, 100)
    }
    mqtt_connection.publish(topic=topic, payload=json.dumps(message), qos=mqtt.QoS.AT_LEAST_ONCE)
    print(("Published: " + json.dumps(message) + " to the topic: " + topic))

    disconnect_future = mqtt_connection.disconnect()
    disconnect_future.result()
    print("Disconnected.")


if __name__ == '__main__':
    main()

AWS コンソールの [ AWS IoT ] - [ MQTT test client ] の [Subscribe to a topic ] で rpi/# を subscribe します。

メッセージを publish すると、test client でメッセージを受信できることが確認できます。

まとめ

クライアントからの AWS IoT Core にメッセージを publish するまでの確認しました。大まかな作業は以下の通りでした。

準備作業

  1. ポリシーの作成
  2. グループの作成
  3. ポリシーをグループアタッチする

クライアント毎の作業

  1. Thing の作成
  2. Thing のグループへの登録
  3. 証明書の作成
  4. 証明書の Thing へのアタッチ

この後は、ルールを作成してメッセージを DB や S3 に保存することになります。

参考資料

Discussion