こちらの記事はLambdaからIoT経由でArduino Yúnへ(前編)の後編で、実際にどんなことをしたかについて書いています。
AWS IoT Thingの準備
先日AWS IoTをArduino Yúnで使うで書いたようにまずはAWS IoTにThingや証明書を準備します。ここではThingの名前をs3thingとしました。(my-Yúnとかにするべきだったかも。)
- Thing
- Policy
- 証明書
を用意。
この証明書がArduino Yúnにセットアップされているものとして話を進めます。このあたりの設定については先日の記事に書いています。
このs3thingのペインを開いてMQTT Topicのところをメモしておきましょう。これを後に出てくるLambda関数で使います。
IAMロールの準備
AWS LambdaがS3やIoTにアクセスするための権限をAWS IAMロールとして用意する必要があるので、マネジメントコンソールのIAMの画面で新しいロールを作ります。今回はLambda_S3IoTという名前にしました。
作成画面のAWS サービスロールの選択はAWS Lambdaにします。
ポリシーのアタッチの画面で
- AmazonS3FullAccess
- AWSIoTFullAccess
- CloudWatchLogsFullAccess
を追加します。CloudWatchLogsFullAccessを追加することでLambdaのデバッグがCloudWatchのログで可能になります。
S3バケットの準備
後に出てくるLambda関数はS3にファイルをアップロードしたのをきっかけに呼び出したいので、S3にはそれ用のバケットを用意しておきます。
ここではsandbox.bucketという名前にしておきました。
Lambda関数の準備
Lambda関数は、マネジメントコンソールのLambda関数作成画面にリストされる「s3-get-object-python」ブループリントのコードをベースにしました。
これをlambda_function.pyという名前でファイルに保存しました。変更点は主にAWS IoTのためのコードを追加していることです。具体的には以下の3点です。
- Lambdaにデフォルトで含まれるバージョンではなく最新のboto3を使う
- Lambdaには初めから(デプロイパッケージをアップロードしなくても)AWSを操作できるPythonモジュールのboto3が利用可能になっていますが、そのバージョンにはAWS IoT向けのコードが入っていないようです。そのため後述の通り、別途最新のboto3を追加したLambdaデプロイパッケージを作ることになるのですが、単にboto3モジュールをデプロイパッケージに含めるだけだと、Lambda関数はそのboto3を無視してデフォルトのboto3を読み込んでしまいます。
そこで、LAMBDA_TASK_ROOT環境変数をPythonパスの一番先に追加して、デプロイパッケージのboto3を読み込ませるというわけです。
※このデフォルトのboto3を避ける方法はこちらを参考にさせてもらいました。 - AWS IoTで作ったThingのMQTT topicを指定する
- コードの中の、iot.publishのtopic部分には先ほどメモしておいた、$awsから始まるMQTT topicを入れます。
topic='$aws/things/s3thing/shadow/update'
- Thingのステータスをアップデートするためのリクエスト内容を用意する
- リクエスト内容はstateの中にdesiredがあり、desiredの中にアップデートしたいデータが入るという構造にします。(参考ページ:Device Shadow Document Syntax)
Lambdaデプロイパッケージ(zipファイル)の作成
Lambda関数で最新のboto3モジュールを使うために、boto3とLambda関数書かれたPythonファイルを1つのzipファイルにまとめます。
AWS Lambdaのデプロイパッケージの作成についてのドキュメントを見ると様々な方法が紹介されていますが、私の場合はMacOSでVirtualenvを使って、こんなターミナルコマンドでboto3のモジュールを用意しました。
# 適当なディレクトリを作る
mkdir awsiot
# そのディレクトリに入る
cd awsiot
# さらに適当なディレクトリ(ここではENV)を作る
mkdir ENV
# ENVディレクトリを仮想環境のための場所にする
virtualenv ENV
# 仮想環境に入る
source ENV/bin/activate
# boto3をインストールする
pip install boto3
# デプロイパッケージを置く適当なディレクトリ(ここではupload)を作る
mkdir upload
# 仮想環境に入っているモジュールをuploadディレクトリに移動する
mv ENV/lib/python2.7/site-packages/* upload/
ここまでやると
こんな風に、uploadディレクトリにモジュールが入っているはずです。
uploadディレクトリにLambda関数を書いたPythonファイルlambda_function.pyを追加します。
そしてuploadディレクトリの内容をZIPファイルに圧縮します。この時uploadディレクトリを圧縮するのではなく、uploadディレクトリの中身を圧縮します。これでLambdaのデプロイパッケージ(ZIPファイル)ができます。
Lambdaにデプロイ
マネジメントコンソールのLambdaの画面でLambda関数を作ります。
- RuntimeはPython
- Code entry typeはZIPファイルのアップロード
- Roleは先に作ったIAMロールLambda_S3IoTを指定
Lambda関数ができたらそれを呼び出すイベントソースの設定をします。Event sourcesタブを開いてAdd event sourceをクリックします。
LambdaのイベントソースはS3、バケットは先に作っておいたsandbox.bucket、イベントの種類はObject Createdを指定します。(ここではAllにしていますがPostだけでも良いと思います。)
Arduino Yúnのスケッチ
ここからは物理的なデバイス、Arduino Yúnの準備です。
前回はAWS IoT Arduino Yún SDKのサンプルスケッチBasicPubSubを試しましたが、今回はThingShadowEchoを使います。
先日同様、aws_iot_config.hファイルにはAWS IoTで証明書を作った時に出てきたdefine文をペーストしてあるものとします。
そしてThingShadowEcho.inoファイルのmsg_callback_delta関数の終わりには、
このような、S3のオブジェクト数と日時を取り出すコードを追加してみました。
スケッチをArduino Yúnに読み込ませ、Arduino IDEのシリアルターミナルを開いて、S3のバケットにファイルをアップロードすると、こんな風に、オブジェクト数と日時が表示されます。
ようやくできたよ!
7セグメントLEDを点けてみた
このIoTから届くS3のオブジェクト数をシリアル接続7セグメント4桁LED(赤)に表示させてみました。
当初思ったような、CloudWatchのアラートが発生したらArduino Yúnがモーターをぐるぐる回す、というところまではできていませんが、もう後はこれの延長なのだからできますよね?きっと。
気がついたことなど
AWS IoTから届くデータが大きすぎるとArduino Yúnがメモリ不足でエラーとなってしまいます。そのためThingのstateには少ししか情報を置きませんでした。