Systems Managerに登録したRaspberry Piを使用して、AWS IoT Device ClientのMQTTメッセージ通信をデモする
以下のページでRaspberry PiにAWS IoT Device Clientをインストールする方法をご紹介しました。

今回はその続きということで、以下の公式チュートリアルに従い、AWS IoT Device Clientを使用してメッセージの送受信を体験します。
構築する環境
基本的な構成は冒頭でご紹介したページと同様です。
変更点は2つです。
1点目はIoTポリシーです。
これはRasbperry Piに対応するAWS IoT Thing(モノ)に適用されるポリシーです。
上記のチュートリアルの内容に従い修正を加えます。
2点目はSSMドキュメントで実行するコマンドです。
SSMドキュメントをRaspberry Piに対して実行し、AWS IoT Device Clientインストール等のセットアップを行います。
こちらもチュートリアルに沿って修正します。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
テンプレートファイルのポイント解説
先述の通り、今回の構成は冒頭でご紹介した内容と大部分が同一ですから、変更点を中心に取り上げます。
IoTポリシー
Resources:
Policy:
Type: AWS::IoT::Policy
Properties:
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- iot:Connect
Resource:
- !Sub "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:client/${Thing}"
- Effect: Allow
Action:
- iot:Publish
Resource:
- !Sub "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topic/${PublishTopicName}"
- Effect: Allow
Action:
- iot:Subscribe
Resource:
- !Sub "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topicfilter/${SubscribeTopicName}"
- Effect: Allow
Action:
- iot:Receive
Resource:
- !Sub "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topic/${SubscribeTopicName}"
PolicyName: !Sub "${Prefix}-policy"
Code language: YAML (yaml)
上記の設定は以下のページを参考にして設定しました。
今回参考にしているチュートリアルでは、メッセージの送受信がテーマとなります。
ですから送信/受信用のトピックに対して適切な権限を設定する必要があります。
メッセージを送受信するために必要なポリシー設定に関しては、以下のページをご確認ください。
SSMドキュメント
Resources:
RunShellScriptAssociation:
Type: AWS::SSM::Association
Properties:
AssociationName: !Sub "${Prefix}-shellscript-association"
Name: AWS-RunShellScript
OutputLocation:
S3Location:
OutputS3BucketName: !Ref BucketName
OutputS3KeyPrefix: shellscript-association-log
Parameters:
commands:
- "apt-get -y install build-essential libssl-dev cmake unzip git python3-pip"
- !Sub 'su - ${UserName} -c "export PATH=$PATH:~/.local/bin"'
- !Sub 'su - ${UserName} -c "git clone https://github.com/aws/aws-cli.git"'
- !Sub 'su - ${UserName} -c "cd aws-cli && git checkout v2 && sudo pip3 install --break-system-packages -r requirements.txt"'
- !Sub 'su - ${UserName} -c "cd aws-cli && git checkout v2 && sudo pip3 install --break-system-packages ."'
- !Sub 'su - ${UserName} -c "mkdir ~/certs"'
- !Sub 'su - ${UserName} -c "curl -o ~/certs/AmazonRootCA1.pem https://www.amazontrust.com/repository/AmazonRootCA1.pem"'
- !Sub 'su - ${UserName} -c "chmod 745 ~"'
- !Sub 'su - ${UserName} -c "chmod 700 ~/certs"'
- !Sub 'su - ${UserName} -c "chmod 644 ~/certs/AmazonRootCA1.pem"'
- !Sub 'su - ${UserName} -c "git clone https://github.com/awslabs/aws-iot-device-client aws-iot-device-client"'
- !Sub 'su - ${UserName} -c "mkdir ~/aws-iot-device-client/build"'
- !Sub 'su - ${UserName} -c "cd ~/aws-iot-device-client/build && cmake ../"'
- !Sub 'su - ${UserName} -c "cd ~/aws-iot-device-client/build && cmake --build . --target aws-iot-device-client"'
- !Sub 'su - ${UserName} -c "mkdir ~/dc-configs"'
- !Sub 'su - ${UserName} -c "mkdir ~/messages"'
- !Sub 'su - ${UserName} -c "mkdir ~/certs/pubsub"'
- !Sub 'su - ${UserName} -c "mkdir ~/certs/jobs"'
- !Sub 'su - ${UserName} -c "chmod 745 ~"'
- !Sub 'su - ${UserName} -c "chmod 700 ~/certs/pubsub"'
- !Sub 'su - ${UserName} -c "chmod 700 ~/certs/jobs"'
- !Sub 'su - ${UserName} -c "chmod 745 ~/messages"'
- !Sub 'su - ${UserName} -c "sudo aws s3 cp s3://${BucketName}/${Thing}/${CertificateName} ~/certs/pubsub/"'
- !Sub 'su - ${UserName} -c "sudo aws s3 cp s3://${BucketName}/${Thing}/${PrivateKeyName} ~/certs/pubsub/"'
- !Sub 'su - ${UserName} -c "sudo aws s3 cp s3://${BucketName}/${Thing}/${PublicKeyName} ~/certs/pubsub"'
- !Sub 'su - ${UserName} -c "sudo chown ${UserName}:${UserName} ~/certs/pubsub/*"'
- !Sub 'su - ${UserName} -c "sudo chmod 644 ~/certs/pubsub/*"'
- !Sub 'su - ${UserName} -c "sudo chmod 600 ~/certs/pubsub/${PrivateKeyName}"'
- !Sub 'su - ${UserName} -c "mkdir ~/.aws-iot-device-client"'
- !Sub 'su - ${UserName} -c "mkdir ~/.aws-iot-device-client/log"'
- !Sub 'su - ${UserName} -c "chmod 745 ~/.aws-iot-device-client/log"'
- !Sub 'su - ${UserName} -c "echo " " > ~/.aws-iot-device-client/log/aws-iot-device-client.log"'
- !Sub 'su - ${UserName} -c "echo " " > ~/.aws-iot-device-client/log/pubsub_rx_msgs.log"'
- !Sub 'su - ${UserName} -c "chmod 600 ~/.aws-iot-device-client/log/*"'
- !Sub |
cat << EOF > /home/${UserName}/messages/sample-ws-message.json
{
"temperature": 28,
"humidity": 80,
"barometer": 1013,
"wind": {
"velocity": 22,
"bearing": 255
}
}
EOF
- !Sub 'su - ${UserName} -c "sudo chown ${UserName}:${UserName} ~/messages/sample-ws-message.json"'
- !Sub 'su - ${UserName} -c "chmod 600 ~/messages/*"'
- !Sub 'su - ${UserName} -c "chmod 745 ~/dc-configs"'
- !Sub |
cat << EOF > /home/${UserName}/dc-configs/dc-pubsub-custom-config.json
{
"endpoint": "${Endpoint}",
"cert": "~/certs/pubsub/${CertificateName}",
"key": "~/certs/pubsub/${PrivateKeyName}",
"root-ca": "~/certs/AmazonRootCA1.pem",
"thing-name": "${Thing}",
"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": "${PublishTopicName}",
"publish-file": "~/messages/sample-ws-message.json",
"subscribe-topic": "${SubscribeTopicName}",
"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": ""
}
}
EOF
- !Sub 'su - ${UserName} -c "sudo chown ${UserName}:${UserName} ~/dc-configs/dc-pubsub-config.json"'
- !Sub 'su - ${UserName} -c "chmod 644 ~/dc-configs/dc-pubsub-config.json"'
- !Sub 'su - ${UserName} -c "sudo mkdir /var/log/aws-iot-device-client"'
- !Sub 'su - ${UserName} -c "sudo chmod 745 /var/log/aws-iot-device-client"'
- !Sub 'su - ${UserName} -c "sudo chown ${UserName}:${UserName} /var/log/aws-iot-device-client"'
Targets:
- Key: InstanceIds
Values:
- !Ref InstanceId
WaitForSuccessTimeoutSeconds: !Ref WaitForSuccessTimeoutSeconds
Code language: YAML (yaml)
冒頭でご紹介したページからの主な変更点は3点です。
1点目は.aws-iot-device-clientディレクトリに関する処理です。
本ディレクトリはAWS IoT Device Clientを使用してメッセージの送受信時に生成されるログ用です。
本ディレクトリを作成後、ログファイルも併せて作成します。
2点目はsample-ws-message.jsonの作成です。
これはAWS IoT Device Clientを使用してAWS IoT Coreに送信するMQTTメッセージの内容に相当します。
3点目はdc-pubsub-custom-config.jsonの作成です。
元々はdc-pubsub-config.jsonを使用していましたが、これを修正する形で本ファイルを作成します。
元ファイルからの変更点ですが、メッセージ送信時はsample-ws-message.jsonの内容を送信し、メッセージ受信時は.aws-iot-device-clientディレクトリ配下のログファイル(pubsub_rx_msgs.log)に受信したメッセージを書き込むように設定します。
環境構築
Raspberry PiをSSMに登録する
1つ目のCloudFormationスタックを作成します。
AWS CLIを使用して、CloudFormationスタックを作成します。
上記のテンプレートファイルを任意のS3バケットに設置した上で、以下のコマンドを実行します。
$ aws cloudformation create-stack \
--stack-name fa-155-01 \
--template-url https://[bucket-name].s3.[region].amazonaws.com/fa-155/fa-155-01.yaml \
--capabilities CAPABILITY_IAM
Code language: Bash (bash)
スタック作成の詳細については、以下のページをご確認ください。
このスタックによって、主に以下のリソースが作成されます。
- クライアント証明書や鍵を配置するS3バケット
- SSMハイブリッドアクティベーション
- アクティベーション用のIAMロール
スタックの作成が完了すると、以下の通りハイブリッドアクティベーション用のコードとIDが取得できます。
Raspberry PiにSSM Agentをインストールする
Raspberry Piにおいて、以下のコマンドを実行してSSM Agentをインストールします。
sudo apt-get -y update
sudo apt-get -y upgrade
sudo apt-get -y install libc6:armhf
mkdir /tmp/ssm
curl https://amazon-ssm-ap-northeast-1.s3.ap-northeast-1.amazonaws.com/latest/debian_arm/ssm-setup-cli -o /tmp/ssm/ssm-setup-cli
sudo chmod +x /tmp/ssm/ssm-setup-cli
sudo /tmp/ssm/ssm-setup-cli -register -activation-code "[activation-code]" -activation-id "[activation-id]" -region "ap-northeast-1"
Code language: Bash (bash)
最後のコマンドに先ほど確認したアクティベーション用のIDとコードを埋め込みます。
手順の詳細については以下のページをご確認ください。
SSM Fleet Managerを確認すると、確かにRaspberry PiにSSMエージェントがインストールされて管理対象になっていることがわかります。
SSMドキュメントを使用して、Raspberry PiをAWS IoTに登録後、AWS IoT Device Clientをセットアップする
2つ目のCloudFormationスタックを作成します。
先ほどと同様にAWS CLIを使用してスタックを作成します。
$ aws cloudformation create-stack \
--stack-name fa-155-02 \
--template-url https://[bucket-name].s3.[region].amazonaws.com/fa-155/fa-155-02.yaml \
--capabilities CAPABILITY_IAM
Code language: Bash (bash)
このスタックによって、主に以下のリソースが作成されます。
- IoT Thing(モノ)
- IoTポリシー
- クライアント証明書と鍵
- クライアント証明書をモノおよびIoTポリシーにアタッチ
加えてSSMドキュメントによって、Raspberry Piにクライアント証明書や鍵がダウンロードされて、Raspberry Piをモノとして扱うことができるようになります。
そしてAWS IoT Device Clientを使用するために必要な手続きが実行されます。
マネージメントコンソールから作成されたリソースを確認します。
AWS IoTに関係するリソースを確認します。
モノ、証明書、ポリシーが正常に作成されています。
動作確認
AWS IoT Device Clientのインストール状況確認
準備が整いましたので、Raspberry Piにアクセスします。
アクセスはローカルマシンからSSHします。
証明書と鍵の有無を確認します。
awstut@raspberrypi:~ $ ls -l ~/certs/pubsub/
total 12
-rw-r--r-- 1 awstut awstut 1220 Mar 27 21:13 device.pem.crt
-rw------- 1 awstut awstut 1675 Mar 27 21:13 private.pem.key
-rw-r--r-- 1 awstut awstut 451 Mar 27 21:13 public.pem.key
Code language: Bash (bash)
確かにS3バケットから証明書および鍵がダウンロードされています。
つまりこのRaspberry PiはAWS IoTデバイスとして動作できます。
AWS IoT Device Clientのインストール状況を確認します。
awstut@raspberrypi:~ $ cd ~/aws-iot-device-client/build/
awstut@raspberrypi:~/aws-iot-device-client/build $ ./aws-iot-device-client --version
v1.9.2-cf76107
Code language: Bash (bash)
確かにAWS IoT Device Clientがインストールされて、使用できる状態であることがわかります。
AWS IoT Device Clientを使用してメッセージ送信
以下のページに従いメッセージを送信します。
awstut@raspberrypi:~/aws-iot-device-client/build $ ./aws-iot-device-client --config-file ~/dc-configs/dc-pubsub-config.json
2024-03-30T10:30:33.123Z [WARN] {FileUtils.cpp}: Permissions to given file/dir path '/home/awstut/dc-configs/dc-pubsub-config.json' is not set to recommended value... {Permissions: {desired: 640, actual: 644}}
2024-03-30T10:30:33.124Z [INFO] {Config.cpp}: Successfully fetched JSON config file: {
"endpoint": "a2oxckhng7gmur-ats.iot.ap-northeast-1.amazonaws.com",
"cert": "~/certs/pubsub/device.pem.crt",
"key": "~/certs/pubsub/private.pem.key",
"root-ca": "~/certs/AmazonRootCA1.pem",
"thing-name": "fa-155-thing",
"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": "~/messages/sample-ws-message.json",
"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": ""
}
}
2024-03-30T10:30:33.124Z [INFO] {FileUtils.cpp}: Successfully create directory /home/awstut/.aws-iot-device-client/sample-shadow/ with required permissions 700
2024-03-30T10:30:33.124Z [INFO] {Config.cpp}: ~/.aws-iot-device-client/sample-shadow/default-sample-shadow-document
2024-03-30T10:30:33.124Z [INFO] {Config.cpp}: Succesfully create default file: /home/awstut/.aws-iot-device-client/sample-shadow/default-sample-shadow-document required for storage of shadow document
2024-03-30T10:30:33.124Z [DEBUG] {Config.cpp}: Did not find a runtime configuration file, assuming Fleet Provisioning has not run for this device
2024-03-30T10:30:33.124Z [DEBUG] {Config.cpp}: Did not find a http proxy config file /home/awstut/.aws-iot-device-client/http-proxy.conf, assuming HTTP proxy is disabled on this device
2024-03-30T10:30:33.124Z [DEBUG] {EnvUtils.cpp}: Updated PATH environment variable to: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games:/home/awstut/.aws-iot-device-client:/home/awstut/.aws-iot-device-client/jobs:/home/awstut/aws-iot-device-client/build:/home/awstut/aws-iot-device-client/build/jobs
2024-03-30T10:30:33.124Z [DEBUG] {LockFile.cpp}: creating lockfile
2024-03-30T10:30:33.124Z [INFO] {Main.cpp}: Now running AWS IoT Device Client version v1.9.2-cf76107
2024-03-30T10:30:33.124Z [INFO] {SharedCrtResourceManager.cpp}: SDK logging is enabled. Check /var/log/aws-iot-device-client/sdk.log for SDK logs.
2024-03-30T10:30:33.124Z [DEBUG] {Retry.cpp}: Retryable function starting, it will retry until success
2024-03-30T10:30:33.174Z [INFO] {SharedCrtResourceManager.cpp}: Establishing MQTT connection with client id fa-155-thing...
2024-03-30T10:30:33.363Z [INFO] {SharedCrtResourceManager.cpp}: MQTT connection established with return code: 0
2024-03-30T10:30:33.363Z [INFO] {SharedCrtResourceManager.cpp}: Shared MQTT connection is ready!
2024-03-30T10:30:33.363Z [INFO] {Main.cpp}: Provisioning with Secure Elements is disabled
2024-03-30T10:30:33.363Z [INFO] {Main.cpp}: Config shadow is disabled
2024-03-30T10:30:33.363Z [INFO] {Main.cpp}: Jobs is disabled
2024-03-30T10:30:33.363Z [INFO] {Main.cpp}: Secure Tunneling is disabled
2024-03-30T10:30:33.363Z [INFO] {Main.cpp}: Device Defender is disabled
2024-03-30T10:30:33.363Z [INFO] {Main.cpp}: Sample shadow is disabled
2024-03-30T10:30:33.363Z [INFO] {Main.cpp}: PubSub is enabled
2024-03-30T10:30:33.363Z [INFO] {samples/PubSubFeature.cpp}: Creating Pub/Sub file: /home/awstut/messages/sample-ws-message.json
2024-03-30T10:30:33.363Z [INFO] {samples/PubSubFeature.cpp}: Creating Pub/Sub file: /home/awstut/.aws-iot-device-client/log/pubsub_rx_msgs.log
2024-03-30T10:30:33.363Z [INFO] {Main.cpp}: Sensor Publish is disabled
2024-03-30T10:30:33.363Z [INFO] {SharedCrtResourceManager.cpp}: Starting Device Client features.
2024-03-30T10:30:33.363Z [DEBUG] {FeatureRegistry.cpp}: Attempting to start Pub Sub Sample
2024-03-30T10:30:33.363Z [INFO] {samples/PubSubFeature.cpp}: Starting Pub Sub Sample
2024-03-30T10:30:33.363Z [INFO] {Main.cpp}: Client base has been notified that Pub Sub Sample has started
2024-03-30T10:30:33.399Z [DEBUG] {samples/PubSubFeature.cpp}: PublishCompAck: PacketId:(Pub Sub Sample), ErrorCode:0
2024-03-30T10:30:33.417Z [DEBUG] {samples/PubSubFeature.cpp}: SubAck: PacketId:(Pub Sub Sample), ErrorCode:0
Code language: Bash (bash)
ログを見ると、正常にMQTTメッセージを送信できたことがわかります。
続いてマネージドコンソールのMQTT test clientページを確認します。
確かにtest/dc/pubtopic向けにメッセージが送信されています。
中身を見ると、確かにsample-ws-message.jsonで指定した内容であることがわかります。
このようにAWS IoT Device Clientを使用して、任意のメッセージを送信することが可能です。
AWS IoT Device Clientを使用してメッセージ受信
続けてページに従いメッセージを受信します。
既にAWS IoT Device Clientを実行中ですので、マネージドコンソールのMQTT test clientページにて、test/dc/subtopic向けにメッセージを送信します。
するとAWS IoT Device Clientを実行しているRaspberry Pi側でメッセージの受信を知らせるメッセージが通知されます。
2024-03-30T10:40:51.339Z [DEBUG] {samples/PubSubFeature.cpp}: Message received on subscribe topic, size: 45 bytes
Code language: Bash (bash)
AWS IoT Device Clientを停止させて、ログファイルをチェックします。
$ cat ~/.aws-iot-device-client/log/pubsub_rx_msgs.log
{
"message": "Hello from AWS IoT console"
}
Code language: Bash (bash)
確かにMQTT test clientページで送信したメッセージです。
このようにAWS IoT Device Clientを使用して、メッセージを受信することが可能です。
まとめ
Raspberry Piにおいて、AWS IoT Device Clientを使用してメッセージを送受信する方法を確認しました。