RaspberryPiとAWS IoTを使用した温湿度の可視化[後編]

IoT

はじめに

お布団から脱出するのが難しい季節になってきました。GMO GSHDのringoです。
前回の記事ではAWS IoTにラズパイを接続しデータを送信するところまで書きました。今回はデータの保存からブラウザ上での可視化までを行いたいと思います。
可視化と言っても様々な方法で行うことができますが今回は、時系列データをブラウザ上でイイ感じに表示したい!がモチベーションです。そこで調べてみるとChart.jsを使う方法だとかmatplotlibを使う方法だとかzabbixだとか、様々な方法で実現可能ということが分かりました。
ただ、私自身そこまでソフトウェアに詳しいわけではないことと、できるだけ簡単かつ見栄えが良く実現したいということを踏まえてさらに調べると、時系列データ可視化のための InfluxDB、Grafana、AWS IoTの連携という記事を見つけました。この記事はAWS IoT Device Simulatorを使って仮想のデバイスが生成したデータをAWS IoTなどのサービスを連携させ、最終的に可視化までしています。ということはデバイスシミュレータの部分を現実のセンサーに置き換えたら簡単に可視化が実現できるな! ということで実践した記事になります。

全体構成

前回の記事では図の左側のAWSへデータを送信するところまでを行いました。今回の記事では右側の部分の構築を行います。
AWS IoT Coreから送られてきたデータをLambda関数によってEC2上にセットアップされたインスタンス内のDBへ挿入します。DBには時系列データを扱うためInfluxDBを使用し、データの可視化にはGrafanaを使用しています。

AWSのセットアップ

VPCの設定やEC2インスタンスの立ち上げ、InfluxDBとGrafanaのインストールまでします。基本的に参考にしたブログ記事と同じ手順で問題ないです。自分が初めてAWSを触るということもあり設定でつまづいたこともあったので追記しながら記事に起こしています。

EC2インスタンスの作成

VPCの中で動かすため以下の手順でインスタンスを生成します。
Amazon VPC の開始方法

マシンイメージはubuntu serverのt2.microを選択しました。

GrafanaとInfluxDBにアクセスするためにセキュリティグループの設定の画面でポート3000と8086を開放しておきます。
手順通りにElastic IPアドレスの割り当てまで完了したら、インスタンスを作成する際にダウンロードしたキーペアを使ってSSH接続できます。このときの初期ユーザ名はubuntuになります。

EC2にInfluxDBとGrafanaをセットアップ

ブログ記事のステップ2の手順にしたがってInfluxDBとGrafanaをインストールします。
インストールできたら http://EC2のパブリックDNS:3000 にアクセスします。EC2のアドレスはAWSのEC2 > インスタンスから作成したインスタンスをクリックすると確認することができます。
初期ユーザ名とパスワードはadminになっています。パスワードの変更を促されるので新しいパスワードを入力しましょう。

Grafanaにログインするとこの画面が現れます。
データベースを追加したいのでAdd Source -> InfluxDBを選択します。

URLにhttp://EC2のパブリックDNS:8086 を入力します。このときポート番号8086を忘れないよう気を付けます。
InfluxDBのdatabase名、ユーザ名、パスワードをセットし、Save & Testを選択しData source is workingが表示されたらセットアップ終了です。

Lmbda関数のセットアップ

ここではAWS Lambdaにソースコードをデプロイします。Lambda使うの初めてで個人的にかなり手こずったところです。

ソースコードのデプロイ

AWS LambdaはソースコードをLambdaへコピペしたら動くと思っていましたが、今回のプログラムはinfluxのライブラリが必要なので一度ローカルの環境でパッケージをインストールしてからLambdaへデプロイします。

関数名はsensorDataLambda2influxDBとしました。ランタイムはNode.js 10.xにします。
以下のソースコードをコピーして関数コード内のindex.jsに貼り付けます。

const Influx = require('influx');
//This code writes data from IoT core rule via Lambda into InfluxDB 
exports.handler = async (event,context,callback) => {
    var pressureInputValue = JSON.parse(event.pressure);
    var humidityInputValue = JSON.parse(event.humidity);
    var temperatureInputValue = JSON.parse(event.temperature);
    //Create clientID
    var deviceid = JSON.stringify(event.deviceid);
    // 不快指数の計算
    // DI=0.81T+0.01H×(0.99T−14.3)+46.3
    var thi = 0.81 * temperatureInputValue + 0.01 * humidityInputValue * (0.99 * temperatureInputValue - 14.3) + 46.3;

    var result = writeToInfluxDB (pressureInputValue, humidityInputValue, temperatureInputValue, thi, deviceid);
    callback(null, result);
  };

function writeToInfluxDB(pressureVar, humidityVar, temperatureVar, thi, sensorVar)
{
    console.log("Executing Iflux insert");

    const client = new Influx.InfluxDB({
        database: process.env.INFLUXDB,
        username: process.env.INFLUXDBUSRNAME,
        password: process.env.INFLUXDBPWD,
        port: process.env.INFLUXDBPORT,
        hosts: [{ host: process.env.INFLUXDBHOST }],
        schema: [{
            measurement: 'pressure',
            fields: {
                pressureValue: Influx.FieldType.FLOAT, 
                humidityValue: Influx.FieldType.FLOAT,
                temperatureValue: Influx.FieldType.FLOAT,
                thiValue: Influx.FieldType.FLOAT,
            },
            tags: ['sensorID']
        }]
    });

    client.writePoints([{
        measurement: 'pressure', fields: { pressureValue: pressureVar, humidityValue: humidityVar, temperatureValue: temperatureVar, thiValue: thi},
        tags: { sensorID: sensorVar}
    }])
    console.log("Finished executing");
}

ブログ記事のサンプルコードを今回使用するセンサーに合わせて変更したものです。
ラズパイから受け取ったデータをそのまま流すだけでは面白くないので、不快指数を計算する処理を追加しています。

そのままではモジュールが足りず動かないのでアクション -> 関数のエクスポートを選択しデプロイパッケージのダウンロードを選択します。ダウンロードしたファイルを解凍したディレクトリで npm install influxコマンドを実行しinfluxのモジュールを取得します。
取得したら再度zip形式に圧縮し アクション -> zipファイルのアップロードを行います。これでソースコードのデプロイは完了です。

環境変数の追加

自分の環境に合わせて環境変数を追加します。

環境変数のINFLUXDBHOSTにはインスタンスのプライベートDNSを設定します。

アクセス権限の設定

アクセス権限のタブを開き、実行ロールからロール名をクリックします。

IAM Manegemant Consollに移動するのでポリシーを追加します。

ポリシーのアタッチからAWSLambdaVPCAccessExecutionRoleポリシーを追加します。これを設定しないとVPCへアクセスすることができません。

VPCの編集

VPCとサブネットはInfluxDBが動いているインスタンスのVPCを設定します。

テストの実行

右上からテストイベントの設定を開きます。
以下のJSONを張り付けテストイベントを保存して実行します。

{
  "raspiId": "test_device",
  "temperature": " 27.12",
  "pressure": "1001.65",
  "humidity": " 63.44",
  "get_date": "2020-06-23",
  "get_time": "12:17:4"
}

実行結果:成功 と表示されたらLambda関数が正常に動いています。
サーバーにログインし以下のコマンドを実行してデータが挿入されていたら成功です。

$ influx
> use blogdatabase
> select * from pressure

AWS IoT Coreのリソース設定

ここまででLambdaからInfluxDBへデータを格納するところまで確認できました。
AWS IoTへ移動してLambdaがトリガーされるルールを追加します。AWS IoTのACT > ルールを開き作成を選択します。

アクションの追加からメッセージデータを渡すLambda関数を呼び出すを選択し、先ほど作成したLambda関数を選択します。
SQLには以下を入力します。

SELECT pressure AS pressure, humidity as humidity, temperature as temperature, raspiId as deviceid FROM 'pub/myhome'

Grafanaの設定

ここまでの設定が完了するとラズパイから取得したデータがDBに次々と挿入されるます。Grafanaのホーム画面を開いてダッシュボードを作成していきます。ダッシュボードの作成はGrafanaのパネルの作り方を参考にしました。
左のメニューから+ボタンを選択しChose Visualizationを選択します。

データのソースを設定します。温度と湿度の2軸グラフを作成しました。

縦軸の単位はAxesのUnitで編集できます。Grafanaでは様々な単位やパネルの種類が用意されているので用途に合わせてダッシュボードを作成することができます。

最終的に完成したダッシュボードがこちらになります。

上には不快指数、下には気圧を表示するようにしました。これは60以下だと青色、61~79だと緑色、80以上だと赤色で表示するようにしています。

おわりに

7月ぐらいに稼働させてからwifiが落ちたり電源が抜けたり等のアクシデントを除いてずっと稼働しています。これを作ったことでエアコンを入れる基準ができたことは生活の中のメリットの1つとなりました。
ダッシュボードを作ってみたことはいいものの、いちいちブラウザを開いてGrafanaにログインしないとグラフが見れないことが不便だと感じました。そこで今後の開発目標として現在の気温やグラフを返すAPIを作成することや、湿度が下がったらアラートを出すなどの機能を追加するのもいいかもしれません。少し考えてみると様々な方向に広げていけることがお家IoTの面白いことだと思います。

ここまでお読みくださってありがとうございました。