#概要
本コンテンツは、JAWS DAYS 2017のIoTハンズオン向けに作成したものです。
下記のようなデータフローを構築して、温度センサーデータをQuicksightで可視化します。
温度センサー -> RaspberryPi -> Kinesis Firehose -> S3 -> Athena -> Quicksight
本格的な分析や可視化にも耐えうる構成となっており、プロダクションへの応用も可能です。また、温度センサーの組み立てでは簡単な電子工作を体験できます。
##<前編>の範囲
<前編>では、温度センサー -> RaspberryPi -> Kinesis Firehose -> S3 の部分を説明します。成功すれば下記のようなJSONメッセージがS3バケットに格納されます。汎用的なJSONメッセージですので、後工程で様々に活用することが可能(ココ重要!)です。
{"time":"2017-02-04 19:14:33", "temperature":25.562}
{"time":"2017-02-04 19:14:43", "temperature":25.562}
{"time":"2017-02-04 19:14:53", "temperature":25.5}
{"time":"2017-02-04 19:15:03", "temperature":19.5}
{"time":"2017-02-04 19:15:13", "temperature":15.375}
{"time":"2017-02-04 19:15:23", "temperature":14.5}
##<後編>はこちら
#必要な機材
- PC
- RaspberryPi 2または3
- micro USB電源
- micro SDカード (8GB以上)
- 温度センサーセット
- DS18B20
- 2.2kΩカーボン抵抗
- ブレッドボード
- ジャンパワイヤ オスーメス 3本
#前提条件
- AWSアカウントをもっていること。アカウント開設手順はこちら
- RaspberryPiにRaspbianがインストール済みで、PCからSSHでログインまたはHDMI接続のモニタでターミナル操作ができること。手順はこちら。Raspbianは[Release date:2017-01-11]で動作確認しています
- RaspberryPiがインターネットに接続できていること
#<前編>で利用するAWSサービス
##Amazon Kinesis Firehose
多数のノードからデータを集約する場合、古くはFTPサーバを利用しました。現在であればHTTPベースのAPIサーバといったところでしょうか。数千~数万といった多数のIoTエッジデバイスノードを集約する場合、サーバのスケールや分散処理が大きな課題となります。Kinesisファミリーはストリーミングデータの集約に特化したフルマネージドサービスで、単一のエンドポイントで大量のストリーミングデータを処理することができます。
- データはクレデンシャルをつけてエンドポイントにHTTPSでPOSTする。CLIやSDKも利用可能
- 受信したデータはAmazon Kinesis Analytics、Amazon S3、Amazon Redshift、および Amazon Elasticsearch Serviceのどれかに配送される
- 配送先には設定したインターバル毎にデータが届く。最小のインターバルは60秒
- データ転送量による課金
- オートスケールで、1 秒あたり数ギガバイトを超える入力データレートにも対応
- さらなるリアルタイム性(遅延数秒以内)が求められる場合は、Kinesis Streamsを使う
##Amazon S3
一見ただのオブジェクトストレージ(ファイルサーバ)ですが、AWSの根幹をなすサービスと言え、非常に多くの機能をもっているので一言で説明することはできません。今回の利用用途に絞って説明します。
- API/CLI/SDKでHTTPSによるアクセス
- ストレージ容量とリクエスト数とデータ転送量(アウトバウンド)による課金。ストレージ容量1GBあたり3円/月程度と非常に低料金
- バケット毎にリージョンを指定でき、リージョン内で3箇所にコピーが作成される
- IoTにおけるユースケースとしては、データのハブとしての使い方が有効。S3にデータを集めておけばAWSのさまざまなサービスから利用可能となる。JSON形式のデータが望ましい
#[ToDo 0] RaspberryPiの準備
##インターネット接続確認
RaspberryPiをインターネットに接続し、PCから有線LANまたはWi-Fi経由でSSH接続します。
RaspberryPiのIPアドレスが分からない場合は、こちらを参考にGUIや**ifconfig
コマンド等で調べてください。下記の場合、192.168.0.209がRaspberryPiのIPアドレス(Wi-Fi)であることが分かります。
Wi-Fiで接続するのにwlan0
**にIPアドレスが割り当てられていない場合は、こちらを参考にWi-Fiアクセスポイントへの接続設定を行って下さい。
$ ifconfig
wlan0 Link encap:Ethernet HWaddr cc:e1:d5:4d:59:5e
inet addr:192.168.0.209 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: 2001:268:d006:d652::2/128 Scope:Global
**wget
**コマンドで適当なURLを打ってみればインターネットに接続できているか確認ができます。
$ wget https://google.co.jp
--2016-03-23 17:53:19-- https://google.co.jp/
Resolving google.co.jp (google.co.jp)... 2404:6800:4003:809::2003, 106.162.198.104, 106.162.198.123, ...
Connecting to google.co.jp (google.co.jp)|2404:6800:4003:809::2003|:443... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://www.google.co.jp/ [following]
--2016-03-23 17:53:30-- https://www.google.co.jp/
Resolving www.google.co.jp (www.google.co.jp)... 2404:6800:4007:805::2003, 106.162.192.173, 106.162.192.167, ...
Connecting to www.google.co.jp (www.google.co.jp)|2404:6800:4007:805::2003|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: ‘index.html’
index.html [ <=> ] 18.90K --.-KB/s in 0.1s
2016-03-23 17:53:32 (130 KB/s) - ‘index.html’ saved [19358]
##アップデートとvimのインストール
インターネット接続が確認できたら、下記を実行します。
$ sudo apt-get update
$ sudo apt-get install -y vim
#[ToDo 1] APIアクセス用のKeyを作成する(AWS)
Kinesis FirehoseにAPIでアクセスするために、AWSのIAM Access Keyを作成します。
今回作成するのは下記のユーザーおよびそれにひも付くKeyです。
ユーザー名 | アクセス権 | 使用箇所 |
---|---|---|
firehose-full | AmazonKinesisFirehoseFullAccess | RaspberryPiからKinesisへの書き込み |
- AWSにサインインする
- Identity and Access Management(IAM)コンソールを開く
※ 英語画面で説明するので日本語表示になっている場合は、画面左下の言語選択を「English」に変更する
- 「firehose-full」という名前でProgrammatic accessユーザーを作成する
- [Attach existing policies directly]をクリックし、[firehose]で検索して、[AmazonKinesisFirehoseFullAccess]にチェックを入れる
- [Next: Review]をクリックする
- [Create user]をクリックする
- 認証情報の書かれたCSVをダウンロードし、[Close]をクリックする
#[ToDo 2] Kinesis Firehoseの設定(AWS)
- Kinesisコンソールを開く
-
Kinesis Firehoseは2017年2月現在、東京リージョンでは提供されていないので、利用可能な北バージニア(N.Virginia)リージョンを選択する
2017年8月25日に東京リージョンで利用可能になりました!
- [Go to Firehose]をクリックする
- [Create Delivery Stream]をクリックする
- 配送先に[Amazon S3]を選択する
- 任意のStream Nameを入力し、ログを格納するS3バケットを選択(ここではともに「iotan-f」としている)する。バケットはここで作成することもできる(S3バケットのリージョンはお好みで)
- ログのファイル名はデフォルトでStream名+タイムスタンプとなるが、必要であればプレフィックスを指定する
- [Next]をクリックする
- Buffer interval(S3への送信間隔)を指定する。最小値は60秒。ハンズオンでは早く結果が分かるように60秒に設定する
- IAM roleで[Firehose Delivery IAM role]を選択する
- 他はデフォルトでOK
- そのまま[Allow]をクリックする
- そのまま[Next]をクリックする
- [Create Delivery Stream]をクリックする
- S3配送ストリームが作成された。[ACTIVE]になったら利用可能
#[ToDo 3] AWS CLIをインストールする(RaspberryPi)
##AWS CLIのインストール
pipコマンドでAWS CLIをインストールします。
$ sudo pip install awscli
(途中省略)
Successfully installed awscli botocore PyYAML docutils rsa s3transfer jmespath python-dateutil futures
Cleaning up...
##AWS CLIの設定
CLIを実行する際のリージョンや認証情報を設定します。認証情報は[ToDo 1]でダウンロードしたCSVを参照します。
項目 | 入力内容 |
---|---|
AWS Access Key ID | (CSV参照) |
AWS Secret Access Key | (CSV参照) |
Default region name | 北バージニアリージョンの場合は「us-east-1」 東京リージョンの場合は「ap-northeast-1」 |
Default output format | json |
$ aws configure
AWS Access Key ID [None]: ************
AWS Secret Access Key [None]: ************
Default region name [None]: us-east-1
Default output format [None]: json
#[ToDo 4] AWS CLIでKinesis Firehoseにput-recordしてみる(RaspberryPi / AWS)
##テストデータのput-record
AWS CLIでKinesis Firehoseにテストデータをput-recordしてみます。--delivery-stream-name
のあとの*****
には、[ToDo 2]で設定したストリーム名を入力します。
下記のような"RecordId"
のレスポンスが表示されたら成功です。データがS3に書き込まれるまで、最大で[ToDo 2]で設定したBuffer intervalの秒数だけかかります。
$ aws firehose put-record --delivery-stream-name ***** --record Data=blob
{
"RecordId": "PEMfHNCtnMGQ5+bCvkm7TgynKepLxf2awvfD5He1atikTPY3zg8Eo2o3LnGhEqYzlVFUEnbv4xT7z8t/AUOVMI8Fy4EcznypTh1xDLP1RnoBtB7MzXOa1094BJ3WhvSV9vWC0sxaHeTAMcDoe1hkSrDQ1BsVZje3iMGkxOuMpnCKw/wEAhPtFn3V3/safpY7sTm7dr8oGYWbPNGzxN7wc6K6VgTYzV6Z"
}
##S3バケットの確認
Quicksightでの分析時にエラーの原因となるので、テストデータを確認したら必ず削除するようにして下さい。
- S3コンソールを開く(新UIで説明)
- [ToDo 2]で設定したバケット -> 年 -> 月 -> 日 -> 時(UTC) の順に開く
- 1つだけファイルがあるはずなので、ダウンロードする
- テキストエディタで開くと送信内容が確認できる
- 確認後はバケットからファイルを削除しておく
##S3バケットにファイルが書き込まれない場合は・・・
Kinesis Firehoseのコンソール画面からデモデータが送信できるので、S3バケットに書き込まれるか確認することで原因の切り分けができます。
#[ToDo 5] 温度センサー回路を組み立てて温度を計測する(RaspberryPi)
##1-wireデジタル温度センサーとは
RaspberryPiのGPIOはデジタル信号しか受け付けませんが、多くのセンサー類はアナログ信号を出力しますので、ADコンバーターを使って信号を変換する必要があります。今回使うデジタル温度センサーDS18B20は、1-wire規格に準拠しており、1本の信号線だけで低速なデジタルデータ転送を行うことができるので、回路が非常にシンプルになります。
##温度センサー回路の組み立て
念のため、RaspberryPiをシャットダウンして回路を接続して下さい。
下記のパーツを図の通り配線し、RaspberryPiの電源をONにします。ジャンパワイヤの色は図の通りでなくてかまいません。
- DS18B20
- 2.2kΩカーボン抵抗
- ブレッドボード
- ジャンパワイヤ オスーメス 3本
##RaspberryPiの設定
- 1-wireデバイス用のモジュールを起動時に読み込むようにする
$ sudo vim /etc/modules
(最後尾に下記2行を追記する)
w1-gpio
w1-therm
- 1-wireデバイスのGPIOに関する設定を記述する
$ sudo vim /boot/config.txt
(最後尾に下記1行を追記する)
dtoverlay=w1-gpio-pullup,gpiopin=4
- リブート後、再度ログインする
$ sudo reboot
##温度を読み出す
DS18B20は温度をファイルに書き込む仕様となっています。再起動すると/sys/bus/w1/devices/28-************/
(28-************
は個体により異なるデバイスID)というディレクトリが作成されており、その中にw1_slave
というファイルが見つかるはずです。t=
に続く5桁の整数を1000で割った数が温度(摂氏)です。
デバイスID 28-************
は**[ToDo 6]で使うので控えておいてください**。
$ cd /sys/bus/w1/devices/
$ ls -l
total 0
lrwxrwxrwx 1 root root 0 Feb 6 01:28 28-************ -> ../../../devices/w1_bus_master1/28-************
lrwxrwxrwx 1 root root 0 Feb 6 01:28 w1_bus_master1 -> ../../../devices/w1_bus_master1
$ cd 28-************
$ cat w1_slave
a7 01 55 00 7f ff 0c 10 22 : crc=22 YES
a7 01 55 00 7f ff 0c 10 22 t=26437
#[ToDo 6] シェルスクリプトで温度データをKinesis Firehoseにput-recordする(RaspberryPi)
シェルスクリプトで一定時間ごとに温度を読み出し、AWS CLIを叩いてKinesis Firehoseにput-recordします。JSONは["
]をバックスラッシュでエスケープしなければならないことに注意が必要です。
本スクリプトはエラー処理を考慮していません。プロダクションの場合は、当然エラー処理を考慮する必要がありますので、fluentdなどのミドルウェアを使うとよいでしょう。
$ cd ~
$ vim temp-firehose.sh
-
deviceid=
のあとの28-************
には、[ToDo 5]で控えた28-
から始まるDS18B20のデバイスIDを入力する -
streamname=
のあとの*********
には、[ToDo 2]で設定したストリーム名を入力する
#!/bin/bash
# 第1引数がデータの送信間隔となる(デフォルトは15秒)
if [ "$1" = "" ]
then
interval=15
else
interval=$1
fi
# DS18B20のデバイスIDをdeviceidにセット
deviceid=28-************
# Kinesis Firehoseストリーム名をstreamnameにセット
streamname=*********
while [ 1 ]
do
(
# 日時をtimeにセット
time=`date '+%F %T'`
# 温度を読み取り、tempにセット
temp=$(awk -F= 'END {print $2/1000}' < /sys/bus/w1/devices/$deviceid/w1_slave)
# センサーデータが取得できたときのみデータを送信する
if [ -n "$temp" ] ; then
# 送信するJSON文字列を作る
payload='{\"time\":\"'$time'\", \"temperature\":'$temp'}'
# JSONを画面に表示
echo $payload
# AWS CLIでput-recordする
aws firehose put-record --delivery-stream-name $streamname --record="{\"Data\":\"$payload\n\"}"
fi
) &
sleep $interval
done
シェルスクリプトを実行します。停止する場合はctrl + C
。第1引数(下記では20)が送信間隔で、省略した場合は15秒間隔で送信されます。下記のようにJSONと"RecordId"
のレスポンスが交互に表示されたら成功です。送信間隔によってはレスポンスは遅れて表示されることもあります。
$ sh temp-firehose.sh 20
{\"time\":\"2017-02-06 01:00:10\", \"temperature\":26.187}
{
"RecordId": "sqyX8BaKpoWgWlYjJcPdxlp/sIxOIisujyk/cjbgnUO+CbigLVEhNk9UBL9VmmQL7QXtWu5c0Gi0yZF9hYi8rY5YBXoM9anaKsEowTHSfpjQSpx9/zink5b8WQvJYZtAazYolN7GfwQkWzCQHmTbGrUztmLwiZfECorKU4SCiHF0xbDkz5b+1W9F/ylabNHNbV8Fx4gEIJs45pX+LpWz7+zjsMHmZSun"
}
{\"time\":\"2017-02-06 01:00:30\", \"temperature\":26.062}
{
"RecordId": "sHadXs3jJTUnu9FLwPfwZzVDautdy7Vdy7k7u9dt1tYLlgF5MA4aVTYiNTh6yOtKn8H+OwWOIS3WmJTIO70kA+3snWWWG+a0fHFwQEXkIZTlu1ybXbPfXPS9566mMRkA1nLAxiBQSsXr8Wpv9XO9S6e3tf5CJYwOMaKbVVCKpr+u+uKqueyZVioLAKQz1TrF11jMBxNUdApdyVK8LQFvWu9c3V+WDcPl"
}
#[ToDo 7] S3バケットに温度データログファイルが書き込まれていることを確認する(AWS)
[ToDo 4]の手順でS3バケットにファイルが書き込まれていることを確認します。初回は書き込まれるまで少し時間がかかる場合があります。
ファイルを開くとJSONが書き込まれているはずです。センサーを保冷剤で冷やすとこんな感じで温度が下がります。
**正常に書き込まれている事を確認したら、データを蓄積するためにシェルスクリプトは実行したままの状態にしてください。**コマンドラインで他の操作をする場合は、別ウインドウでSSH接続しましょう。
#<後編>はこちら
#[おまけ] シェルスクリプトで温度データをDynamoDBにput-itemする(RaspberryPi)
NoSQL DBであるAmazon DynamoDBにもほぼ同じ方法で書き込むことができます。
IoT.kyoto VISで可視化すると良いかもです。(宣伝スミマセン)
##DynamoDBのテーブルを作成する
-
aws configure
コマンドで登録したIAMにDynamoDBの書き込み権限(AmazonDynamoDBFullAccess
等)を付与する - 以下の条件でDynamoDBのテーブルを作成する
項目 | 設定値 |
---|---|
リージョン | 北バージニア(us-east-1) |
Table Name | (任意のテーブル名) |
Partition key | id |
Sort key | time |
##スクリプトを実行する
$ vim temp-dynamodb.sh
-
deviceid=
のあとの28-************
には、[ToDo 5]で控えた28-
から始まるDS18B20のデバイスIDを入力する -
tablename=
のあとの*********
には、DynamoDBのテーブル名を入力する
#!/bin/bash
# 第1引数がデータの送信間隔となる(デフォルトは15秒)
if [ "$1" = "" ]
then
interval=15
else
interval=$1
fi
# DS18B20のデバイスIDをdeviceidにセット
deviceid=28-************
# DynamoDBのテーブル名をtablemnameにセット
tablename=*********
# ノードのデバイスIDをidにセット
id=id001
while [ 1 ]
do
(
# 日時をtimeにセット
time=`date '+%F %T'`
# 温度を読み取り、tempにセット
temp=$(awk -F= 'END {print $2/1000}' < /sys/bus/w1/devices/$deviceid/w1_slave)
# センサーデータが取得できたときのみデータを送信する
if [ -n "$temp" ] ; then
# 送信するJSON文字列を作る
payload='{"id": {"S":"'$id'"},"time": {"S":"'$time'"},"temperature": {"N":"'$temp'"}}'
# JSONを画面に表示
echo $payload
# AWS CLIでput-itemする(消費キャパシティのレスポンスなし)
aws dynamodb put-item --table-name $tablename --item="$payload"
# AWS CLIでput-itemする(消費キャパシティのレスポンスあり)
#aws dynamodb put-item --table-name $tablename --item="$payload" --return-consumed-capacity TOTAL
fi
) &
sleep $interval
done
シェルスクリプトを実行します。停止する場合はctrl + C。第1引数(下記では1)が送信間隔で、省略した場合は15秒間隔で送信されます。下記のようにエラーメッセージが表示されず、JSONが連続して表示されていたら成功です。
$ sh temp-dynamodb.sh 1
{"id": {"S":"id001"},"time": {"S":"2017-02-12 14:09:48"},"temperature": {"N":"30.437"}}
{"id": {"S":"id001"},"time": {"S":"2017-02-12 14:09:49"},"temperature": {"N":"29.937"}}
{"id": {"S":"id001"},"time": {"S":"2017-02-12 14:09:50"},"temperature": {"N":"29.562"}}
{"id": {"S":"id001"},"time": {"S":"2017-02-12 14:09:51"},"temperature": {"N":"29.312"}}