カメラ
などで撮影した画像を AWS IoT
経由で S3
に保存してみたくて試してみました。
今回はホストPCからRaspberry Piにアップした画像を使っています。
プログラムはサンプルプログラムを使用しています。
まだAWSについて知識が浅いので、コメントいただけるとありがたいです!
【追記】
ちなみに、base64エンコードしなくてもバイナリデータのままで送信してS3に保存できます。
その方が、S3からダウンロードしてそのまま画像がみれます。
こちら参考にさせていただきました!
- AWS IoT Device SDK for PythonをRaspberryPiで動かす
- バイナリのデータをAWS IoT上でBase64エンコード&JSON化して、後方サービスへ流す(例えばAWS Lambdaとか)
前提など
制約事項
参考の方にも記載がありますが、AWS IoTとの1回の送信は 最大128KBの制約 ありますので、注意してください!
AWS IoTの制限
【2018/01/19 追記】
128KB超えデータのアップロードについては、ラージアップロードパターンというものを使用するとよさそうです。
これはアップロードする前に毎回、AWS IoT、STSを使用してトークンを取得し、そのトークンを使用してS3へデバイスから直接アップロードするパターンです。
これにより、アクセスキーやシークレットキーをデバイス側に持たなくて済み、AWS IoTの証明書のみでアクセス管理ができる。
参考URL:AWSIoT ラージデータアップロードパターン実装
AWS IoTを使用することのメリット
【2018/01/19 追記】
あくまでも個人的な意見ですが、
- AWS IoTをデバイスのIFにして、いくつもAWSへの送信パターン(※)がある場合
- AWS IoTに登録されている証明書のみでアクセスができる(キーをいくつも管理しなくてもよくなる)
もし、デバイスからAWSへのアクセス権をなくしたい場合(デバイスの盗難など)、AWSコンソールからAWS IoTで管理している証明書を無効にするだけで、アクセス権をなくせます(デメリットにもなくかもですが・・・)。
戻す場合は、証明書を有効化すればまたアクセス可能となります。
※例
センシングデータ → AWS IoT → DynamoDB
画像データ → AWS IoT → S3(ちなみに128KB)
イベント発生 → AWS IoT → Lambda実行
使用するもの
- AWS
- AWS IoT
- AWS IoT Device SDK for Python
- S3
- Raspberry
- ホストPC
- Macを使用してます
前提条件
- AWS側
- Raspberry Pi の接続が完了していること
- Raspberry Pi側
- OSインストールが完了し、ネットワーク、SSHの接続ができること
AWS 側の準備
準備を行います
- S3に保存先のバケットを作成
- AWS IoTでルールを作成
AWS IoTでルール、S3バケットを作成
AWS コンソールから「AWS IoT」をひらく
「ACT」をクリック
「属性:*」「トピックフィルター:sdk/test/Python」を入力
※トピックは送信時に設定するtopicです
S3アクションの詳細はこちらを参照
「新しいリソースを作成する」でS3バケットを追加
「S3バケット」横の更新ボタンをクリックして、作成したS3バケットを選択
「新しいロールの作成」も行う
「キー:${topic()}/${timestamp()}」を入力して「アクションの追加」をクリック
※キーはこちらを参照
※【追記】キーを「${topic()}/${timestamp()}.png」にすればS3で画像データとして認識されます
※画像は「更新」になっている理由:新規登録後、キーをかえるために更新を行ったためです
Raspberry Pi 側の準備
必要なライブラリ等をインストール
- 仮想環境を作成
# テスト用のディレクトリ作成
pi@chinopi:~ $ mkdir aws_iot_test
pi@chinopi:~ $
pi@chinopi:~ $ cd aws_iot_test/
pi@chinopi:~/aws_iot_test $
# 仮想環境
pi@chinopi:~/aws_iot_test $ sudo apt-get install python3-venv
Reading package lists... Done
Building dependency tree
Reading state information... Done
・
・
・
pi@chinopi:~/aws_iot_test $
# テスト用の仮想環境作成
pi@chinopi:~/aws_iot_test $ python3 -m venv env
pi@chinopi:~/aws_iot_test $
# 仮想環境のactivate
pi@chinopi:~/aws_iot_test $ source env/bin/activate
(env) pi@chinopi:~/aws_iot_test $
(env) pi@chinopi:~/aws_iot_test $ python -V
Python 3.4.2
(env) pi@chinopi:~/aws_iot_test $
- SSLのバージョン確認
AWS IoT Device SDK for Python
README の「Installation - Minimum Requirements」を参照
# SSLのバージョン確認
(env) pi@chinopi:~/aws_iot_test $ python
Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl
>>> ssl.OPENSSL_VERSION
'OpenSSL 1.0.1t 3 May 2016'
>>>
>>> exit()
(env) pi@chinopi:~/aws_iot_test $
(env) pi@chinopi:~/aws_iot_test $ pip install AWSIoTPythonSDK
Downloading/unpacking AWSIoTPythonSDK
Downloading AWSIoTPythonSDK-1.2.0.tar.gz (67kB): 67kB downloaded
Running setup.py (path:/tmp/pip-build-n3u557w8/AWSIoTPythonSDK/setup.py) egg_info for package AWSIoTPythonSDK
Installing collected packages: AWSIoTPythonSDK
Running setup.py install for AWSIoTPythonSDK
Successfully installed AWSIoTPythonSDK
Cleaning up...
(env) pi@chinopi:~/aws_iot_test $
- サンプルプログラムのclone、証明書等の配置ディレクトリの作成
(env) pi@chinopi:~/aws_iot_test $ git clone https://github.com/aws/aws-iot-device-sdk-python.git
Cloning into 'aws-iot-device-sdk-python'...
remote: Counting objects: 165, done.
remote: Total 165 (delta 0), reused 0 (delta 0), pack-reused 165
Receiving objects: 100% (165/165), 123.27 KiB | 170.00 KiB/s, done.
Resolving deltas: 100% (61/61), done.
Checking connectivity... done.
(env) pi@chinopi:~/aws_iot_test $
(env) pi@chinopi:~/aws_iot_test $
(env) pi@chinopi:~/aws_iot_test $ cd aws-iot-device-sdk-python/
(env) pi@chinopi:~/aws_iot_test/aws-iot-device-sdk-python $ ls
AWSIoTPythonSDK CHANGELOG.rst LICENSE.txt MANIFEST.in NOTICE.txt README.rst samples setup.cfg setup.py
(env) pi@chinopi:~/aws_iot_test/aws-iot-device-sdk-python $
# 証明書配置用のディレクトリ
(env) pi@chinopi:~/aws_iot_test/aws-iot-device-sdk-python $ mkdir certs
(env) pi@chinopi:~/aws_iot_test/aws-iot-device-sdk-python $
証明書等をRaspberry Pi に配置
- SCPで証明書を使用して転送
# ホストPCで実行(プログラム実行時に使用)
$ ls
XXXXXXXXXX-certificate.pem.crt XXXXXXXXXX-private.pem.key XXXXXXXXXX-public.pem.key root-CA.crt
$
# 証明書
$ scp ./XXXXXXXXXX-certificate.pem.crt pi@chinopi.local:/home/pi/aws_iot_test/aws-iot-device-sdk-python/certs
・・・
$
# プライベートキー
$ scp ./XXXXXXXXXX-private.pem.key pi@chinopi.local:/home/pi/aws_iot_test/aws-iot-device-sdk-python/certs
・・・
$
# ルートCA証明書
$ scp ./root-CA.crt pi@chinopi.local:/home/pi/aws_iot_test/aws-iot-device-sdk-python/certs
・・・
$
- テスト用の画像も転送(プログラム実行時に使用)
# ホストPCで実行
$ ls -la test.png
-rw-r--r--@ 1 chinoyuuji staff 37362 11 7 18:25 test.png
$
$ scp ./test.png /home/pi/aws_iot_test/aws-iot-device-sdk-python/samples/basicPubSub
・・・
$
プログラム準備、実行
画像をbase64に変更
(env) pi@chinopi:~ $ cd aws_iot_test/aws-iot-device-sdk-python/samples/basicPubSub/ $
(env) pi@chinopi:~/aws_iot_test/aws-iot-device-sdk-python/samples/basicPubSub $ cp basicPubSub.py basicPubSub_base64.py
(env) pi@chinopi:~/aws_iot_test/aws-iot-device-sdk-python/samples/basicPubSub $ ls -la
total 72
drwxr-xr-x 2 pi pi 4096 Nov 10 16:12 .
drwxr-xr-x 6 pi pi 4096 Nov 10 13:28 ..
-rw-r--r-- 1 pi pi 4414 Nov 10 13:28 basicPubSubAsync.py
-rwxr-xr-x 1 pi pi 3834 Nov 10 13:28 basicPubSub_CognitoSTS.py
-rwxr-xr-x 1 pi pi 4149 Nov 10 16:12 basicPubSub_base64.py
-rwxr-xr-x 1 pi pi 3874 Nov 10 13:28 basicPubSub.py
-rw-r--r-- 1 pi pi 37362 Nov 10 15:17 test.png
(env) pi@chinopi:~/aws_iot_test/aws-iot-device-sdk-python/samples/basicPubSub $ cb basicPubSub.py basicPubSub_copy.py
(env) pi@chinopi:~/aws_iot_test/aws-iot-device-sdk-python/samples/basicPubSub $ vi basicPubSub_copy.py
「★★・・・★★」部分を変更、追加
※【追加】bytearrayの部分をバイナリデータとすればそのままバイナリデータを送信できます
〜〜〜〜〜〜〜〜〜
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import logging
import time
import argparse
# ★★追加★★
import base64
〜〜〜〜〜〜〜〜〜
time.sleep(2)
# Publish to the same topic in a loop forever
# ★★コメントにする★★
# loopCount = 0
# while True:
# myAWSIoTMQTTClient.publish(topic, "New Message " + str(loopCount), 1)
# loopCount += 1
# time.sleep(1)
# ★★追加★★
binary = open('test.png', 'rb').read()
binary_base64 = base64.b64encode(binary)
myAWSIoTMQTTClient.publish(topic, bytearray(binary_base64), 1)
※ base64エンコードしたデータを送信しようとした時に以下エラーが発生したため、「bytearray」に変換しています
TypeError: payload must be a string, bytearray, int, float or None.
プログラム実行
実行形式はこちら
python basicPubSub_base64.py -e [エンドポイント] -r [ルートCA証明書] -c [証明書] -k [プライベートキー]
※ エンドポイントはこちらを参照:Raspberry Pi の接続
プログラム実行
(env) pi@chinopi:~/aws_iot_test/aws-iot-device-sdk-python/samples/basicPubSub $
(env) pi@chinopi:~/aws_iot_test/aws-iot-device-sdk-python/samples/basicPubSub $ python basicPubSub_base64.py -e XXXXXXXXXXXXXX.iot.ap-northeast-1.amazonaws.com -r ~/aws_iot_test/aws-iot-device-sdk-python/certs/root-CA.crt -c ~/aws_iot_test/aws-iot-device-sdk-python/certs/XXXXXXXXXX-certificate.pem.crt -k ~/aws_iot_test/aws-iot-device-sdk-python/certs/XXXXXXXXXX-private.pem.key
2017-11-11 12:33:45,590 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Initializing MQTT layer...
2017-11-11 12:33:45,609 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - Registering internal event callbacks to MQTT layer...
・
・
・
2017-11-11 12:33:48,378 - AWSIoTPythonSDK.core.protocol.internal.clients - DEBUG - This custom event callback is for pub/sub/unsub, removing it after invocation...
(env) pi@chinopi:~/aws_iot_test/aws-iot-device-sdk-python/samples/basicPubSub $
S3に保存されたデータを確認
S3 からダウンロード
AWS コンソールのS3をひらく
作成したS3バケット名をクリック
ホストPCでデコードして画像をみてみる
$
$ ls -la 1510409639497
-rw-r--r--@ 1 chinoyuuji staff 49816 11 11 23:16 1510409639497
$ python3
Python 3.6.1 (v3.6.1:69c0db5050, Mar 21 2017, 01:21:04)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import base64
>>>
>>> text = open('1510409639497', 'rt').read()
>>>
>>> binary = base64.b64decode(text)
>>>
>>> f = open('test_1.png', 'wb')
>>> f.write(binary)
37362
>>> f.close()
>>>
test_1.png
をひらいて画像が表示されればOK!