AWS IoT Device SDK for Rubyを使って、AWS IoTとPublish/Subscribe/Shadow updateしてみたサンプルです。
前書き
aws_iot_device
というGemがあります。AWS IoT Device SDK for 〜系の非公式Ruby版ですが使用例が見当たらなく、下記Qiitaの使ってみた記事もスクリーンショットなど古い箇所が多いので、自分のログを記事として残すことにしました。
参考
AWS IoT Device SDK for Ruby on RubyGems
AWS IoT Device SDK for Ruby on GitHub
AWS IoT Device SDK for Rubyを使ってみた - Qiita
環境
ホスト : macOS Mojave 10.14.5
ゲスト(実動作環境) : Docker ruby:latest (2.6.3) コンテナ
ruby : 2.6.3p62
gem 'aws_iot_device' : 1.0.0
やったこと
- AWS IoTの設定
- AWS IoT Device SDK for Rubyのインストール
- AWS IoT Device SDK for Rubyを使ったPub/Subサンプル
- AWS IoT Device SDK for Rubyを使ったShadows updateサンプル
本来はRaspberry piなどの上で動かすものですが、動作環境に依存しないコードなのでMacOS上のDockerで動作させています。
1. AWS IoTの設定(モノの作成と証明書のダウンロード)
AWS IoTの設定(モノの作成と証明書のダウンロード)は他言語のSDKを使う場合と変わりません。
AWSはUIがよく変わるので公式のガイドを参考にするのが良い気がします。(画面キャプチャが英語版だったり項目名が違ったりしますが...。)
モノの作成(デバイスの登録)
以下ガイド通りに作成していきます。
証明書
証明書が作成されました!
画面からダウンロードできる証明書・プライベートキーはSDKに必要です。
画面にもあるとおり、ダウンロードし損ねるとプライベートキーは再取得出来ません。
またAWS IoT のルート CAもダウンロードしておきます。
X.509 証明書と AWS IoT - AWS IoT
curlでダウンロードする場合は以下のような感じで。
curl -O https://www.amazontrust.com/repository/AmazonRootCA1.pem
ポリシーの作成とアタッチ
ガイドには セキュア
と書かれていますが実際の項目名は 安全性
です。
AWS IoTデバイスには 管理 → モノ → 先ほど作ったモノの名前(ガイドだとMyIotThing)
の順でアクセス出来ます。
ガイド通りに作成すると、 MyIotThing
というモノができ手元に証明書・プライベートキー・ルートCAがあるはずです。
2. AWS IoT Device SDK for Rubyのインストール
RubyGemsにあるため gem install aws_iot_device
でインストール出来ます。
Gemfileで書いたり直接インストールしたりはお好みで。
今回はローカル環境を汚したくないのでDockerを使いました。
dockerコマンドを叩いたディレクトリをバインドし、先ほどダウンロードした証明書等をコンテナに渡しています。
※ --rm
オプションありで立ち上げているので一度コンテナから抜けるとコンテナが削除されます。
コンテナの立ち上げとバインドの確認
$ docker run --rm -it -v "$(pwd):/app" -w /app ruby:latest bash
root@ad0bd381c93c:/app# ls -1
certificate.pem.crt # 証明書
private.pem.key # プライベートキー
AmazonRootCA1.pem # CAルート証明書
これより下のコマンドは基本的に起動したDockerコンテナ上で実行しています。
AWS IoT Device SDK for Rubyのインストール
$ gem install aws_iot_device
...
$ gem list | grep aws_iot
aws_iot_device (1.0.0)
3. AWS IoT Device SDK for Rubyを使ったPub/Subサンプル
単なるPub/Subであれば ruby-mqtt
という別のgemでも可能でした。
次のShadowを使わないのであれば、こちらの方がスリムで良いかもしれません。(使い方は割愛)
エンドポイント
Pub/Subするためには先の証明書類とエンドポイントが必要です。
管理 → モノ → 先ほど作ったモノの名前(ガイドだとMyIotThing) → 操作 → HTTPS
で調べておきます。
aws cliが動くならコマンドでも取得出来ます。
$ aws iot describe-endpoint --endpoint-type iot:Data-ATS
{
"endpointAddress": "xxx-ats.iot.us-east-2.amazonaws.com"
}
AWS IoT Device SDK for Rubyは --endpoint-type iot:Data-ATS
付きで取得したエンドポイントでしか動きませんでした。
逆にruby-mqttはオプション無しで取得したエンドポイントでしか動かないようでした。環境依存かもしれませんがご参考に。
上記記載は間違いで使うCAルート証明書によってエンドポイントが変わります。
VeriSign エンドポイント (レガシー)
を使う場合は、 オプションを指定せず -ats
と付かないエンドポイントを使います。
Amazon Trust Services エンドポイント (推奨)
を使う場合は、 --endpoint-type iot:Data-ATS
を付け -ats
が含まれるエンドポイントを使います。
組み合わせを間違えるとconnectが失敗するので注意してください。
サンプルコード
AWS IoT Device SDK for Rubyにはサンプルコード(samples配下)が含まれています。
特に単なるPub/SubはReadmeだとさらっと流されているのでサンプルコードを読んだ方が早いかと思います。
Publish
AWS IoT Device SDK for Rubyからpublishし、別のデバイスでsubscribeするパターンです。
AWS IoT コンソールにはMQTTクライアントというsubscribeのテスト機能があるのでそれで確かめます。
AWS IoT MQTT クライアントでデバイスの MQTT メッセージを確認する - AWS IoT
topic名にはガイド通り my/topic
と入力しておきました。
publishのサンプルコードは以下のような感じです。
require 'aws_iot_device'
host = "xxx-ats.iot.us-east-2.amazonaws.com"
port = 8883
ca_file = "AmazonRootCA1.pem"
key_file = "private.pem.key"
cert_file = "certificate.pem.crt"
mqtt_client = AwsIotDevice::MqttShadowClient::MqttManager.new(host: host, port: port, ssl: true)
mqtt_client.config_ssl_context(ca_file, key_file, cert_file)
mqtt_client.connect
mqtt_client.publish("my/topic", '"Hello from AWS IoT SDK for ruby"')
sleep 1
mqtt_client.disconnect
上記サンプルコードを実行すると、MQTTクライアントに "Hello from AWS IoT SDK for ruby"
と表示されるはずです。
ここで送るメッセージはjson形式である必要があります。MQTTクライアントは警告を出しつつ表示してくれます。
ruby publish.rb
Subscribe
今後は逆にAWS IoT コンソールのMQTTクライアントからpublishされたメッセージをSDK側で受け取ります。
require 'aws_iot_device'
host = "xxx-ats.iot.us-east-2.amazonaws.com"
port = 8883
ca_file = "AmazonRootCA1.pem"
key_file = "private.pem.key"
cert_file = "certificate.pem.crt"
mqtt_client = AwsIotDevice::MqttShadowClient::MqttManager.new(host: host, port: port, ssl: true)
mqtt_client.config_ssl_context(ca_file, key_file, cert_file)
mqtt_client.connect
callback = proc do |message|
puts message.topic, message.payload
end
mqtt_client.subscribe("my/topic", 0, callback)
sleep 10 # subscribe待機時間
mqtt_client.disconnect
connectするところまではpublishと同じです。
メッセージを受け取ったときのcallbackを設定し10秒間subscribe待ちします。
ruby-mqttには client.get
というメッセージを受け取るまで待つ良い感じのメソッドがありますが、AWS IoT Device SDK for Rubyにはないようでした。
※ Python版にもなさそうですが、subscribeの使い方として主処理は無限ループで待ち続けて、メッセージが届いたらcallbackで処理するような感じになると思います。であれば自前でループ処理を書いた方が都合が良いのかもしれません。
上記サンプルコードを実行後、10秒の待ち時間以内にMQTTクライアントからpublishするとコンソールにメッセージが出力されます。
サンプルコードを実行 → 10秒以内にMQTTクライアントからpublish → コンソールにpublishしたtopicとメッセージが表示される。
$ ruby subscribe.rb
my/topic
{
"message": "Hello from AWS IoT console"
}
4. AWS IoT Device SDK for Rubyを使ったShadows updateサンプル
やっとモノ(Shadow)をupdateします。
Shadow操作は色々とできるはずですがupdateだけやってみます。
Thing名の確認
Shadow updateには先のPub/Subで使ったものに加えThing名が必要です。
管理 → モノ
に表示されているモノがThing名で、ガイド通りに作成していれば MyIotThing
になっています。
こちらもaws cliが使えれば aws iot list-things
でも確認出来ます。
$ aws iot list-things --profile=private
{
"things": [
{
"thingName": "MyIotThing",
"thingArn": "arn:aws:iot:xxx",
"attributes": {},
"version": 1
}
]
}
Shadow updateのサンプルコード
Shadow操作するときは AwsIotDevice::MqttShadowClient::ShadowClient
を使います。
MqttManagerと違いShadowClientの使い方はgemのReadmeに詳しく書いてあります。そちらを参照した方が良いでしょう。
最低限のコードであれば thing
が増えている程度でPub/Subとほぼ同じです。Shadowのmessageを実行時刻でupdateしてみます。
require "aws_iot_device"
host = "xxx-ats.iot.us-east-2.amazonaws.com"
port = 8883
thing = "MyIotThing"
ca_file = "AmazonRootCA1.pem"
key_file = "private.pem.key"
cert_file = "certificate.pem.crt"
shadow_client = AwsIotDevice::MqttShadowClient::ShadowClient.new
shadow_client.configure_endpoint(host, port)
shadow_client.configure_credentials(ca_file, key_file, cert_file)
shadow_client.create_shadow_handler_with_name(thing, true)
shadow_client.connect
shadow_client.update_shadow('{"state":{"desired":{"message":"shadow updated at ' + Time.now.to_s + '"}}}')
sleep 2
shadow_client.disconnect
ruby shadow_update.rb
Webコンソールでシャドウステータスを見る場合は、 管理 → モノ → 先ほど作ったモノの名前(ガイドだとMyIotThing) → シャドウ → シャドウステータス
から確認出来ます。
先ほどのサンプルコードを実行すると、シャドウステータスの message:
が実行時刻で更新されていきます。
今回はサンプルなので最低限のコードですが、ちゃんと使う場合はcallbackを設定したり、update_shadowのパラメータはto_jsonで生成したりしたほうが良いと思います。
さいごに
無事、Pub/SubとShadow updateの動作が確認出来ました。
最初に使ってみたときは設定ミスでconnectionでエラーになってしまったのですが、なにぶん参考にする記事がなかったので、他言語の記事を見たりgemのソースを読んだりpythonのSDKを読んだり苦労しました。
本記事が参考になれば幸いです。