温度センサーから取得したデータをAWS IoT→DynamoDB経由でAmazon ESにデータを送信する＜2/2＞


#続き
https://qiita.com/0_1geek311/items/417fca6e6885282c14a4
こちらの記事の続きになります。

前回、ラズパイを使って温度データを取得するところまでは出来ため、次はその温度データをAWS IoT　→　DynamoDB　→　Amazon ESと流し、Kibanaによるグラフ表示まで実際に実装していきたいと思います。

#システム構成図
![demo.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/3f9b8db2-81a6-f17c-77af-ec1a8688ac54.jpeg)
※構成図は前回と同じ


#実装①　温度データ → AWS IoT → DynamoDB

システム構成図の①の部分を実装します。
ダミーデータ → AWS IoT → DynamoDBの実装は以下の記事で作成したのでこちらを流用します。
https://qiita.com/0_1geek311/items/93ac822b8561c6dbce3c
※設定方法は上記の記事を参照。

一通り設定が完了すると、
DynamoDB(テーブル名：iot_test)に以下のデータがAWS IoT経由で登録される状態まで設定できます。
・「time」(データ送信時間)
・「deviceNo」(デバイスNo)　※固定値
・「ttl」(データ有効時間)

次に、ダミーデータを送信していた部分をラズパイからの温度データを送信する設定に変更していきます。
設定したラズパイにAWS IoTで取得した以下のKeyファイルを任意のディレクトリに保管。
・XXX.crt
・XXX-private.pem.key
・XXX-public.pem.key
・AmazonRootCA1.pem

更にpipコマンドでAWSIoTPythonSDKをインストール。

```
pip install AWSIoTPythonSDK
```
<img width="360" alt="img_r1.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/a0e72073-4e59-67df-81a5-d2d05ac3e0f4.jpeg">

実際に実行するPGは以下の通り。

```ruby:awsTest.py
'''
Send Time Sample
'''

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import time
import json
import datetime
import logging

# Setup Param
host = "XXXXX-ats.iot.us-west-2.amazonaws.com"　★Rest APIエンドポイントを入力

# for Raspberry Pi
rootCAPath = "AmazonRootCA1.pem"  　　　　　　　★ダウンロードしたKeyを入力
certificatePath = "XXX-certificate.pem.crt"
privateKeyPath = "XXX-private.pem.key"

useWebsocket = False
clientId = "testRule"
topic = "testRule/time"
deviceNo = "031868f200ff"

# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)

# Init AWSIoTMQTTClient
myAWSIoTMQTTClient = None
myAWSIoTMQTTClient = AWSIoTMQTTClient(clientId)
myAWSIoTMQTTClient.configureEndpoint(host, 8883)
myAWSIoTMQTTClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)

# AWSIoTMQTTClient connection configuration
myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1)      # Infinite offline Publish queueing
myAWSIoTMQTTClient.configureDrainingFrequency(2)            # Draining: 2 Hz
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10)    # 10 sec
myAWSIoTMQTTClient.configureMQTTOperationTimeout(5)         # 5 sec

# Connect and subscribe to AWS IoT
myAWSIoTMQTTClient.connect()
# Initial
time.sleep(2)

try:
    while True:
        nowtime = datetime.datetime.now()
        nowsettime = int(time.mktime(nowtime.timetuple()))  # UNIX Time
        #send Command
        message = {}
        message['time'] = nowtime.strftime('%Y/%m/%d %H:%M:%S')
        message['deviceNo'] = deviceNo
        f = open("/sys/bus/w1/devices/28-031868f200ff/w1_slave", "r")
        line = f .readlines()[1].replace("\n", "")
        n = int(line.find("t="))
        message['temperature']  = int(line[n+2:]) * 0.001 
        f.close()
        messageJson = json.dumps(message)
        print (messageJson)
        myAWSIoTMQTTClient.publish(topic, messageJson, 1)
        time.sleep(60)
except:
    import traceback
    traceback.print_exc()

print ("Terminated")
```
こちらのPGもほぼ流用になりますが、ダミーデータを送っていたときと違うのが、
「ttl」(データ有効時間)を削除し、新たに「temperature」(温度データ)を追加しました。
温度データは以下のように指定のファイルに保管されているため、
<img width="313" alt="img_w1.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/3d5f1911-018c-25ea-8c4b-545c48e61360.jpeg">


```
f = open("/sys/bus/w1/devices/28-031868f200ff/w1_slave", "r")
line = f .readlines()[1].replace("\n", "")
n = int(line.find("t="))
message['temperature']  = int(line[n+2:]) * 0.001 
f.close()
```
上記の部分で温度データが保存されているファイルを読み取り、「t=」以降の温度データを取得し、1000倍されている数値を正しく修正したあと、AWS IoTへ送信しています。

PGの実行

```
python awsTest.py
```
<img width="489" alt="img_r2.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/1dae101c-9681-342d-fa1c-a8f68d27be9c.jpeg">

dynamoDB  ※ひとまず60秒ごとに送信が行われるためおおよそ1分単位でデータが蓄積
<img width="521" alt="img_d1.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/0074e3eb-4ae7-a741-4ed6-0107792512e9.jpeg">


#実装②　DynamoDB → Lambda → Amazon ES
登録された温度データをAmazon ESに送るために、DynamoDBへデータ登録をトリガーにAWS Lumbdaを発火させます。

まずは、DynamoDBの「概要」でストリームの詳細にて「ストリームの管理」ボタンをクリックし、ストリームを有効化します。
<img width="482" alt="img_es5.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/d3d7f22e-0c99-aeae-237a-7e403ade45c2.jpeg">
表示タイプはデフォルトのままで、「有効化」をクリック。
<img width="252" alt="img_es6.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/7ef501b7-3628-310e-e244-428139cc228d.jpeg">
※「最新のストリーム ARN」項目にARNが表示されていればOK。



続いて Amazon ESの設定を行います。
Amazon ESのドメイン作成画面にて、下記の通り設定し、「次へ」をクリック。
※Kibanaのバージョン6.5になります。
<img width="605" alt="img_es1.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/0341d3e9-ecd6-ed15-bb75-9a55c905b526.jpeg">

Amazon ESのドメイン名、インスタンスタイプを設定。
<img width="613" alt="img_es2.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/bc979f7d-f482-69a0-1fb3-ce805722a1b4.jpeg">

ネットワーク構成は「パブリックアクセス」とし、アクセスポリシーの設定は「ドメインへのオープンアクセスを許可」を設定。画面下部の「次へ」ボタンをクリック。
※今回はテストのため制限は掛けず、パブリックとします。
<img width="611" alt="img_es3.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/a8d6da7b-88a1-3900-e541-18a25f5a2aa6.jpeg">

確認画面で内容を確認し、ドメインを作成。
※作成完了までしばらく時間がかかります。

作成が完了すると、「エンドポイント」「ドメインARN」「Kibana」が表示される。
※ここの値は、PGで使用するため、控えておく。
<img width="429" alt="img_es7.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/0eea735e-35fd-2096-b7b0-a5444244208e.jpeg">


次に、AWS Lambdaを開き、言語python3.6でLambda関数を作成します。※名前は任意
PGは以下の通り、

```ruby:lambda_function.py
import os
import requests
import json
from aws_requests_auth.aws_auth import AWSRequestsAuth
import string
import datetime
from urllib.parse import urlparse, parse_qs
 
def lambda_handler(event, context):
    for record in event['Records']:
        if record['eventName'] != 'INSERT':
            return
        strTime = (record['dynamodb']['NewImage']['time']['S'])
        strDevice = (record['dynamodb']['NewImage']['deviceNo']['S'])
        strTemperature = (record['dynamodb']['NewImage']['temperature']['S'])
        now = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S%z")

    auth = AWSRequestsAuth(aws_access_key= os.environ.get('AWS_ACCESS_KEY_ID'),
                           aws_secret_access_key= os.environ.get('AWS_SECRET_ACCESS_KEY'),
                           aws_token = os.environ.get('AWS_SESSION_TOKEN'),
                           aws_host='search-es-test-XXXXX.es.amazonaws.com', ※メモしたエンドポイントの値を「https://」無しで入力」
                           aws_region='us-west-2',
                           aws_service='es')                   
    url = ("https://search-es-test-xxxxx.us-west-2.es.amazonaws.com/kibana/sensor?pretty") ※メモしたエンドポイントを入力
    data = json.dumps({ "time": strTime, 
                        "deviceNo": strDevice, 
                        "temperature": float(strTemperature), 
                        "@timestamp": now })
    res = requests.post(url, 
                        data=data, 
                        headers={'Content-Type':'application/json'}, 
                        auth=auth)

    return {"result": "OK"}
```
※上記のPGファイルと、importライブラリをダウンロードした後、一つのファイルにzip化して
Lambdaへアップロード。
<img width="638" alt="img_es9.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/a4326617-337d-f052-82c3-14bbc3729c73.jpeg">


zipのアップロード完了後、Lambdaの設定画面で、
下記のように、トリガーにDynamoDBを設定し、テーブルを「iot-test」を選択。
<img width="646" alt="img_es8.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/3331d5ed-b683-a7f9-6d8c-31aff3982456.jpeg">

Lambda「実行ロール」はDynamoDBの読み書きの権限を付与したものを設定、
加えて「基本設定」のタイムアウトを念の為3 → 15くらいに変更しておきます。
変更が完了後、画面上部の「保存」ボタンをクリック。
<img width="643" alt="img_es10.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/a3bb516a-1ac9-c9a1-1e39-1f166d3b1645.jpeg">

Lambdaの設定は以上なります。

設定が完了後、ラズパイから送信されている温度データが、DynamoDB → Lambdaを経由して、Amazon ESへ追加されます。


#③確認　Amazon ES → Kibana、グラフ作成
送信されたデータを確認するため、Kinabaを表示し、確認していきます。

Amazon ESのダッシュボードを開き、ドメイン「es-test」をクリック。
「Kibana」に表示されているリンクURLをクリックする。
<img width="429" alt="img_es11.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/53455244-f0d7-7d4a-ff67-f4954bc845dc.jpeg">

Kibanaの初期画面が表示されますので、「Management」をクリック。
<img width="648" alt="img_es12.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/738461c9-ab7e-d6b6-7689-4c654654cb4f.jpeg">

ボタン「Index Patterns」→ 「Create index pattern」とクリックし、
Create index pattern画面を表示。index patternに「kibana」と入力し、
有効化された「Next step」ボタンをクリックする。
<img width="703" alt="img_es13.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/48511d27-033d-3b42-436c-e90bd9bc0796.jpeg">


次に「Time Filter field naem」のリストから送信したデータの１つの「@timestamp」を選択肢、「Create index pattern」ボタンをクリックし、indexを作成する。
<img width="700" alt="img_es14.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/8c1141a3-e329-2e52-776d-5a0c4e5fc474.jpeg">
※作成完了後の画面で、「★kibana」と表示されていればOK。

indexが登録できたので、グラフ表示のための設定を行っていきます。
今回は「折れ線グラフ」をつかったグラフ表示を行います。
まず、画面左のメニュー「Discover」をクリックします。
![img0.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/17ce1d85-5a55-4f40-828e-9bcad4b0835d.jpeg)


「Selected fields」の一覧にある「temperature」にカーソルを合わせ、表示された「Add」ボタンをクリックする。クリック後、Selected fieldsにtemperatureが追加される。
![img0-1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/fec83f93-9d75-d5eb-9a66-4a207235d3af.jpeg)

データを確認後、画面左メニューの「Visualaize」をクリック。
<img width="618" alt="img_es15.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/b6f4f487-2e42-9826-fa97-288f96ba81e4.jpeg">

次の画面で「From a New Search, Select Index」のNameで「kibana*」をクリック。
<img width="641" alt="img_es16.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/a66eca82-c6ad-8015-68c0-60b885e51355.jpeg">

画面が切り替わり、以下のような画面になります。
ここの赤枠の部分でX軸、Y軸を設定し、折れ線グラフを表示させていきます。
<img width="708" alt="img_es17.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/cdb8c770-8828-3276-1050-0851b2b4c573.jpeg">

「Y-Axis」と「X-Axis」を以下の通り設定し、反映ボタンをクリックします。
<img width="141" alt="img_es18.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/44f7ced7-8f1d-6484-d7c0-26d5a32c912d.jpeg">

これで、グラフ表示ができました。
画面をリロードすれば最新の温度データが表示されます。
![img2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367782/8fc352a0-e7c0-2507-2aa7-f97860256a92.jpeg)

※↑温度が一部急に上昇しているのは試しに温度センサーを指で挟んだため。
※もっとデザインを凝りたい場合はkibana canvasなどの希望に合ったpluginを入れることで実装ができます。