3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AWSとBLE人感センサーで会議室の利用状況を可視化してみる[第4話]

Posted at

概要

エヌシーアイ総合システムのtaguchyです。
普段は主にBtoCのWEBシステムや、BtoB向けWEB APIの開発などをしています。
最近はIoT関連でもお仕事させていただいております。

さて、前回はAWSIoT、Lambda、Firehoseを利用してElasticSearchとS3にセンシングデータを保存することを行いました。
(前回はこちら)

今回は保存したデータを使ってKibana上で会議室の使用状況を可視化します。

データの可視化

センサーの人感検知数を時系列でグラフ化する

時系列表示を行うため、Timelion(タイムライン)を利用します。
「Visualize」⇒「Create new visualization」⇒「Timelion」でTimelionの新規画面を開きます。

とりあえず、条件式を何も表示せずにグラフ表示をしてみます。

はい。なんかでました。
全センサーからあがってきているデータの件数が時系列ですべて表示されています。

以下の条件を入れてみます。

  1. センサーのMACアドレスを指定(3つのセンサーの内、1つを指定)
  2. 人感検知したステータスを指定
  3. なんとなくかっこいいから棒グラフで表示
  4. グラフに凡例を付ける

⇒出来上がった条件式
 .es(index=meeting-room-*,metric=count,q='status:04 AND sensor_mac:24718902B258').bars().label("検知数")

なかなか良いです。
時系列にて検知数をデータ化しました。

会議室の在室有無を可視化する

センサーの仕様上、センサー内で人が動いた場合、何度も検知データが送信されます。
その為、特定の時間範囲で検知数が1以上であった場合に”在室”扱いとします。

単純にデータをただ溜めるだけだと、Kibana側でどうしようもできなかったため、データ変換にひと工夫加えて、以下のようなことを行います。

  1. 新しい数値型のデータフィールドexistを作成
  2. センサーからあがったデータが人感検知した状態であった場合、exist1を設定
  3. 検知していない状態であった場合、exist0を設定
  4. Kibana側で時間範囲指定(例:30秒)と数値型フィールドの最大値を取得
    ⇒時間範囲の中で、一度でも検知していればexist1となる

Lambdaを変更

以下のコードに変更しました。

'use strict';
console.log('Loading function');
const csv = require('csvtojson');
const asyn = require('async');
require('date-utils');

exports.handler = (event, context, callback) => {
        // all record convert.
        asyn.map (event.records, function (record, callback) {
            // need to base64 decode.
            let sensor_data = Buffer.from(record.data, 'base64').toString('utf8');
            // "$GPRP" is not need.
            var sensor_data_non_gprp = sensor_data.replace(/^(\$GPRP,)/g, "");
            // split payload.
            let payload = sensor_data_non_gprp.match(/^(.{12},.{12},.*,)(.*)/);
            let split = payload[2].match(/^(02010612FF0D0082BC)(.{4})(.{2})(.{8})(.{4})(.{2})(.{6})/); 
            let fix = split[1];
            let battery = split[2];
            let status = split[3];
            let reserve = split[4];
            let user = split[5];
            let fix2 = split[6];
            let reserve2 = split[7];
            
            // make @timestamp
            let now = new Date();
            
            // make existFlg
            var existFlg = 0;
            if (status !== "00") {
                existFlg = 1;
            }

            
            let sensor_data_split_payload = payload[1]
                                + fix + "," + battery + ","
                                + status + "," + reserve + ","
                                + user + "," + fix2+ ","
                                + reserve2 + "," + now.toFormat('YYYY-MM-DDTHH24:MI:SS') + ","
                                + existFlg;
            csv({
                noheader: true,
                headers: [
                    'sensor_mac',
                    'gw_mac',
                    'rssi',
                    'fix',
                    'battery',
                    'status',
                    'reserve',
                    'user',
                    'fix2',
                    'reserve2',
                    '@timestamp',
                    'exist'],
                colParser:{
                    "exist":"number"
                }
            }).fromString(sensor_data_split_payload)
                .on('json',(jsonObj)=>{
                // Called for the number of rows.
                console.log(jsonObj);
                let jsonData = Buffer.from(JSON.stringify(jsonObj), 'utf8').toString('base64');
                callback (null, {
                                recordId : record.recordId,
                                result : 'Ok',
                                data : jsonData
                                }
                );
            });
        }, function (err, results) {
            if (err) {
                throw err;
            }
            console.log(results);
            callback (null, {records: results});
        });
};

Kibanaでデータを確認

Kibanaにてindexの作り直しが済んだら、グラフを作成します。
Gauge(ゲージ)を利用します。
「Visualize」⇒「Create new visualization」⇒「Gauge」⇒「From a New Search, Select Index」⇒「meeting-room-*」でGaugeの新規画面を開きます。

Timelionと同様、全データの件数が表示されました。

以下の条件を入れてみます。

  1. センサーのMACアドレスを指定(3つのセンサーの内、1つを指定)
  2. existの最大値を指定
  3. 表示ラベルを修正
  4. 目盛り線は削除

画面は以下のようになりました。

時間範囲内に人感を検知した場合に赤、それ以外は白となるゲージができました。
うん、微妙です。他にいい方法がないかもう少し研究したいところ。。

全会議室のグラフをDashboardにまとめる

Dashboardにまとめると以下のようになります。

うん、こんなものでしょうか。
左の検知数と右の在室有無表示の時間範囲の指定を変えたかったですが、それはKibanaの機能ではできませんでした。

検知数在室有無表示のDashboardをそれぞれ作り、iframeを取得し、自分で作成したHTMLページにそれぞれのiframeを埋め込みばいけそうです。

今回はKibanaを利用しての画面表示までが目標でしたので、ここで記事を終了します。
総じての感想は、nodeを触ったことが無かったため、コーディングには苦労しましたが、AWS操作に関しては参考記事も豊富にあり、苦労しませんでした。
ほぼノーコーディングでここまでのことができるのはすごいですね。
ただAWSの謳う

AWS IoT を利用した場合 ~5 分```
は私には無理でした。。今後精進いたします。

それでは、しばらくこの形で運用し、AWS料金が算出できたらまた記事を書こうと思います。
3
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?