Bluemix
IoT
node-red
watsoniotplatform

Node-REDにダッシュボードを追加してセンサーデータを可視化する

Node-REDには入出力処理やさまざまな機能を実装した便利なノード(部品)があらかじめ用意されていますが、それ以外にもコミュニティや個人が作成したノードが多数公開されていて、用途に応じて追加で組み込んで使用ことができます。
nodered.orgのNode-RED Library を見ると、現時点(2017年8月時点)で1000個以上のノードが公開されているのですが、その中にnode-red-dashboardという、ダッシュボード(計器盤)が作れる便利な部品があります。
今回はこれを使って、センサーデータをグラフィカルに可視化するNode-REDフローを作ってみます。

手順は以下のとおりです。

  1. Node-REDにnode-red-dashboardを組み込む
  2. 温度センサーのデータを受信するフローを作成する
  3. 温度データをダッシュボードのtextノードに表示する
  4. ダッシュボードにGaugeノード、chartノードを追加する
  5. 湿度データ、物体温度データもダッシュボードに表示する

1. Node-REDにnode-red-dashboardを組み込む

Node-REDにはノードを追加する機能が用意されているので、これを使ってnode-red-dashboardをさっそく組込んでみましょう。画面右上の三本線をクリックして、プルダウンメニューから「パレットの管理」を選択します。
Screen Shot 2017-08-06 at 9.20.33.png

タブを「現在のノード」から「ノードを追加」に切り替えます。検索キーワードに「dashbo」くらいをタイプしたところで選択肢にnode-red-dashboardが現れるので、「ノードを追加」をクリックします。
Screen Shot 2017-08-06 at 9.22.44.png

確認画面が現れたら「追加」をクリックします。
Screen Shot 2017-08-06 at 9.27.22.png

「ノードを追加」ボタンが「追加しました」に変わったら追加完了です。
Screen Shot 2017-08-06 at 9.31.33.png

「閉じる」でパレットの管理を終了して元の画面に戻り、左側のパレットをスクロールダウンすると、追加されたdashboardの部品群が現れます。画面右側の「デバッグ」タブの隣には「dashboard」タブが追加されていました。
Screen Shot 2017-08-06 at 9.33.47.png

2. 温度センサーのデータを受信するフローを作成する

ダッシュボードに表示するデータの発生源として、IBMのIoTデモでよく使われる温度センサーシミュレータを使うことにします。シミュレータはhttps://quickstart.internetofthings.ibmcloud.com/iotsensor/ を開くと現れます。
シミュレータには温度(Temperature)、湿度(Humidity)、物体温度(Object Temperature)の3種類のセンサーがあり、画面の左右にある"<",">"で切り替えることができます。画面下にある"↓","↑"で温度等を上昇、下降することができます。センサーデータはWatson IoT Platformを経由してNode-REDのIBM IoTノードに毎秒送信されます。
センサー右肩にはセンサーID(ここでは3fbd1f674143)が表示されており、IBM IoTノードはこのIDでセンサーを識別します。
Screen Shot 2017-08-06 at 9.42.03.png

次に、このセンサーのデータを受信するフローを作成します。パレットの「入力」部品群から「ibmiot」を選択してキャンバスにドラッグ&ドロップします。
Screen Shot 2017-08-06 at 9.50.50.png

キャンバスの「IBM IoT」ノードをダブルクリックして設定画面を表示します。先程のセンサーIDを「Device Id」の欄にコピペし、他はデフォルトのまま「完了」をクリックしてキャンバスに戻ります。
Screen Shot 2017-08-06 at 9.52.49.png

ダッシュボードを作成する前に、まずはdebugノードを使ってIBM IoTノードの動作を確認します。パレットの「出力」部品群から「debug」を選択してキャンバスにドラッグ&ドロップし、「IBM IoT」と「msg.payload」の端子間をドラッグして接続します。
Screen Shot 2017-08-06 at 9.58.10.png

右側表示領域を「デバッグ」タブに切り替えておき、右上の「デプロイ」ボタンをクリックしてフローをデプロイします。
Screen Shot 2017-08-06 at 10.01.50.png

デプロイが完了すると同時に、デバッグ画面に1秒毎にメッセージが表示されるようになれば、フロー作成は成功です。
Screen Shot 2017-08-06 at 10.02.24.png

debug(msg.payload)ノードの右側のタブ(出っ張り)をクリックすると、メッセージ出力のON/OFFを切り替えることができます。クリックして薄緑色にするとメッセージ出力が止まります。
Screen Shot 2017-08-06 at 10.04.39.png

メッセージの{ d: object }は階層構造のjsonデータで、左側の三角形をクリックして展開できます。
実はIBM IoTノードの出力はmsgというjsonデータなのですが、このデータはmsgの下にpayload、payloadの下にdという階層構造になっていて、更にdの下に"name","temp","humidity","objectTemp"という4種類のデータが含まれていて、それがここに表示されています。このうち"temp","humidity","objectTemp"の値が、シミュレータの「温度(Temperature)」「湿度(Humidity)」「物体温度(Object Temperature)」のデータにそれぞれ対応しています。シミュレータの値を"↓","↑"で変化させると、これらの値も変化します。
Screen Shot 2017-08-06 at 10.10.24.png

3. 温度データをダッシュボードのtextノードに表示する

センサーのデータをNote-REDで受信できるようになったので、次にこのデータをダッシュボードに表示するフローを作成します。dashboard部品群のうち"text","gauge","chart"の3種類の部品を使います。
このうち"gauge"と"chart"は入力データがmsg.payloadに固定されているので、まずは"IBM IoT"の出力をmsg.payloadに変換するノードを作成します。
「機能」部品群から「change」を選択してキャンバスにドラッグ&ドロップします。
Screen Shot 2017-08-06 at 11.36.33.png

changeノード(「set msg.payload」と表示)をダブルクリックして設定画面を表示します。まず「対象の値」の「az」を「msg.」に切り替えます。
Screen Shot 2017-08-06 at 11.38.50.png

「対象の値」の「msg.」に続いて「payload.d.temp」と入力します。他の値はデフォルトのままでOKです。入力したら「完了」をクリックしてキャンバスに戻ります。
Screen Shot 2017-08-06 at 11.41.31.png

それではいよいよ「dashboard」部品群からノードを追加していきます。まずは「text」を選択してキャンバスにドラッグ&ドロップします。
Screen Shot 2017-08-06 at 11.55.15.png

「text」ノードをダブルクリックして設定画面を開き、まずはGroupを編集します。「新規にui_groupを追加...」の右側の鉛筆マークをクリックします。
Screen Shot 2017-08-06 at 11.56.32.png

Name(「Group」の名前)を「Default」から「温度」に変更します。更に続けてTabを編集します。「新規にui_tabを追加...」の右側の鉛筆マークをクリックします。
Screen Shot 2017-08-06 at 12.03.46.png

Tab名はDefaultのHomeのままでも動作上は問題ないのですが、ここではシミュレータの表示の「IoT Sensor」に合わせることにします。Nameの値を「Home」から「IoT Sensor」に変更し、追加をクリックして一つ前の画面(groupノード設定画面)に戻ります。
Screen Shot 2017-08-06 at 12.07.14.png

Tabの表示が「IoT Sensor」に変わったことを確認し、「追加」をクリックして一つ前の画面(textノード設定画面)に戻ります。
Screen Shot 2017-08-06 at 12.10.08.png

Groupの表示が「温度 [IoT Sensor]」に変わったことを確認します。更にLabelの値を「text」から「温度」に変更します。他の値はデフォルトのまま、「完了」をクリックしてキャンバスに戻ります。
Screen Shot 2017-08-06 at 12.15.35.png

キャンバスに戻ったら、右側の「dashboard」タブをクリックして画面を切り替えます。LayoutタブのTabs & Linksの下に先程定義した「IoT Sensor」(Tab名)、「温度」(Group名)などの名前が表示されています。Group名の「温度」をクリックして展開すると、textノード名の「温度」が現れます。
Screen Shot 2017-08-06 at 12.18.30.png

キャンバス上でIBM IoTノードとchangeノード、changeノードとtextノードの端子間をドラッグしてつなぎ、フローを作成します。フローができたら右上の「デプロイ」をクリックして、フローをデプロイします。
Screen Shot 2017-08-06 at 12.27.03.png

デプロイが成功したら、画面右端の「Site」タブの右側にある四角に矢印のアイコンをクリックして、ダッシュボードを開きます。
Screen Shot 2017-08-06 at 12.27.44.png

温度をテキストで表示するだけのシンプルなダッシュボードが現れます。Tab名「IoT Sensor」がブルーに白抜きで、その下にGroup名「温度」が水色の文字で表示され、その下に「温度 17」と表示されています。
Screen Shot 2017-08-06 at 12.36.15.png

この17はシミュレータから届いたmsg.payload.d.tempをリアルタイムに表示しています。シミュレータの値を変更して、ダッシュボードの値の変化を確認してみましょう。
Screen Shot 2017-08-06 at 12.39.13.png

4. ダッシュボードにgaugeノード、chartノードを追加する

パレットのgaugeノードを選択して、キャンバスにドラッグ&ドロップします。
Screen Shot 2017-08-06 at 12.43.20.png

Gauteノードをダブルクリックして設定画面を開きます。
Group名はすでに「温度 [IoT Sensor]」になっているので、そのまま受け入れます。
下の方にあるRangeの項目で、値をmin=0、max=40にセットしておきます。さらにその下のSectorsを、「0 ... 30 ... 35 ... 40」にします。温度が0〜30の時は緑色、30〜35の時は黄色、35〜40の時は赤色で表示することを表しています。
その他の値はデフォルトのままで「完了」をクリックし、キャンバスに戻ります。
Screen Shot 2017-08-06 at 12.47.29.png

キャンバスに戻ったら、changeノードとGaugeノードの端子間をドラッグしてつなぎ、右上の「デプロイ」をクリックしてフローをデプロイします。
Screen Shot 2017-08-06 at 12.54.24.png

デプロイしたらダッシュボードの画面に切り替え、Gaugeが追加されていることを確認します。
Screen Shot 2017-08-06 at 12.57.29.png

シミュレータで温度を上げていくと、Gaugeの針が右に傾き、表示色が緑から黄、更には赤に変化します。
Screen Shot 2017-08-06 at 13.00.59.png

続いて同様の手順でchartノードも追加します。
Screen Shot 2017-08-06 at 13.03.32.png

chartノードをパレットからキャンバスにドラッグ&ドロップした後、ダブルクリックして設定画面を開きます。Y-axis(Y軸)の範囲をmin=0,max=40にセットし、他はデフォルトを受け入れて「完了」クリックします。
Screen Shot 2017-08-06 at 13.05.15.png

キャンバスに戻ったら、changeノードとchartノードの端子間をドラッグしてつなぎ、右上のデプロイをクリックしてフローをデプロイします。
Screen Shot 2017-08-06 at 13.07.41.png

デプロイしたらダッシュボードの画面に切り替え、chartが追加されていることを確認します。
Screen Shot 2017-08-06 at 13.10.32.png

シミュレータの温度を上下させると、chartのグラフも変化します。
Screen Shot 2017-08-06 at 13.12.23.png

この状態でNode-REDフローをエクスポートしたデータを以下に添付しておきます。

[{"id":"6a8b6204.b58e2c","type":"ibmiot in","z":"d29f2b73.bb856","authentication":"quickstart","apiKey":"","inputType":"evt","deviceId":"3fbd1f674143","applicationId":"","deviceType":"+","eventType":"+","commandType":"","format":"json","name":"IBM IoT","service":"quickstart","allDevices":"","allApplications":"","allDeviceTypes":true,"allEvents":true,"allCommands":"","allFormats":"","qos":0,"x":80.39999389648438,"y":79.16665649414062,"wires":[["49240739.1d131","73fb2c55.ccb794"]]},{"id":"49240739.1d131","type":"debug","z":"d29f2b73.bb856","name":"","active":false,"console":"false","complete":"false","x":347.40000915527344,"y":79.08332824707031,"wires":[]},{"id":"73fb2c55.ccb794","type":"change","z":"d29f2b73.bb856","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.d.temp","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":152.40000915527344,"y":155,"wires":[["c836c360.a02448","af08c9f1.fe20a","77db9d47.f45634"]]},{"id":"c836c360.a02448","type":"ui_text","z":"d29f2b73.bb856","group":"f6e58351.6ed218","order":0,"width":0,"height":0,"name":"","label":"温度","format":"{{msg.payload}}","layout":"row-spread","x":365.40000915527344,"y":157.4499969482422,"wires":[]},{"id":"af08c9f1.fe20a","type":"ui_gauge","z":"d29f2b73.bb856","name":"","group":"f6e58351.6ed218","order":0,"width":0,"height":0,"gtype":"gage","title":"Gauge","label":"units","format":"{{value}}","min":0,"max":"40","colors":["#00b500","#e6e600","#ca3838"],"seg1":"30","seg2":"35","x":368.40000915527344,"y":213.34999084472656,"wires":[]},{"id":"77db9d47.f45634","type":"ui_chart","z":"d29f2b73.bb856","name":"","group":"f6e58351.6ed218","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"40","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"x":369.40000915527344,"y":284.84999084472656,"wires":[[],[]]},{"id":"f6e58351.6ed218","type":"ui_group","z":"","name":"温度","tab":"6a2033b6.8a0594","disp":true,"width":"6"},{"id":"6a2033b6.8a0594","type":"ui_tab","z":"","name":"IoT Sensor","icon":"dashboard"}]

5. 湿度データ、物体温度データもダッシュボードに表示する

まず湿度データをダッシュボードに追加します。
温度データの時と同様に、受信データを変換するchangeノード、ダッシュボード部品のtextノード、Gaugeノード、chartノードをパレットからキャンバスにドラッグ&ドロップします。
Screen Shot 2017-08-06 at 13.34.44.png

changeノードは、対象の値としてmsg.payload.d.humidityを指定します。
Screen Shot 2017-08-06 at 13.35.29.png

textノードは、Groupで「新規にui_groupを追加」を選択して鉛筆マークをクリックし、湿度グループを作成します。
Screen Shot 2017-08-06 at 13.36.58.png

Screen Shot 2017-08-06 at 13.39.03.png

グループ名が「湿度 [IoT Sensor]」に変わったことを確認し、Labelを「text」から「湿度」に変更して、「完了」をクリックしてキャンバスに戻ります。

Gaugeノードは、まずGroupを「湿度 [IoT Sensor]」に変更します。更にRangeをmin=0,max=100に、Sectorsを「0 ... 60 ... 70 ... 100」にセットし、他はデフォルトを受け入れて完了します。
Screen Shot 2017-08-06 at 13.52.54.png

chartノードも、まずGroupを「湿度 [IoT Sensor]」に変更します。更にY-axisをmin=0,max=100にセットし、他はデフォルトを受け入れて完了します。
Screen Shot 2017-08-06 at 13.55.08.png

キャンバスに戻ったら各ノードの端子間を接続してフローを作成し、デプロイします。
Screen Shot 2017-08-06 at 13.56.48.png

デプロイしたらダッシュボードの画面に切り替えます。湿度を表示するtext,Gauge,chartが追加されました。
Screen Shot 2017-08-06 at 14.00.53.png

同様に、物体温度データもダッシュボードに表示します。手順は省略しますが、以下にフローの最終型、ダッシュボードの最終型と、フローをエクスポートしたデータを添付しておきます。
Screen Shot 2017-08-06 at 14.13.33.png
Screen Shot 2017-08-06 at 14.14.15.png

[{"id":"6a8b6204.b58e2c","type":"ibmiot in","z":"d29f2b73.bb856","authentication":"quickstart","apiKey":"","inputType":"evt","deviceId":"3fbd1f674143","applicationId":"","deviceType":"+","eventType":"+","commandType":"","format":"json","name":"IBM IoT","service":"quickstart","allDevices":"","allApplications":"","allDeviceTypes":true,"allEvents":true,"allCommands":"","allFormats":"","qos":0,"x":80.39999389648438,"y":79.16665649414062,"wires":[["49240739.1d131","73fb2c55.ccb794","c156fe31.46178","6c2a1a1.f41d0e4"]]},{"id":"49240739.1d131","type":"debug","z":"d29f2b73.bb856","name":"","active":false,"console":"false","complete":"false","x":347.40000915527344,"y":79.08332824707031,"wires":[]},{"id":"73fb2c55.ccb794","type":"change","z":"d29f2b73.bb856","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.d.temp","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":152.40000915527344,"y":155,"wires":[["c836c360.a02448","af08c9f1.fe20a","77db9d47.f45634"]]},{"id":"c836c360.a02448","type":"ui_text","z":"d29f2b73.bb856","group":"f6e58351.6ed218","order":0,"width":0,"height":0,"name":"","label":"温度","format":"{{msg.payload}}","layout":"row-spread","x":295.4000244140625,"y":133.4499969482422,"wires":[]},{"id":"af08c9f1.fe20a","type":"ui_gauge","z":"d29f2b73.bb856","name":"","group":"f6e58351.6ed218","order":0,"width":0,"height":0,"gtype":"gage","title":"Gauge","label":"units","format":"{{value}}","min":0,"max":"40","colors":["#00b500","#e6e600","#ca3838"],"seg1":"30","seg2":"35","x":414.4000244140625,"y":132.34999084472656,"wires":[]},{"id":"77db9d47.f45634","type":"ui_chart","z":"d29f2b73.bb856","name":"","group":"f6e58351.6ed218","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"40","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"x":416.4000244140625,"y":173.84996032714844,"wires":[[],[]]},{"id":"c156fe31.46178","type":"change","z":"d29f2b73.bb856","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.d.humidity","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":153.39999389648438,"y":266.5,"wires":[["a3b0a5bf.fe734","220ed27.acd08ae","e86c0636.15a898"]]},{"id":"a3b0a5bf.fe734","type":"ui_text","z":"d29f2b73.bb856","group":"57a9f7d1.3aff4","order":0,"width":0,"height":0,"name":"","label":"湿度","format":"{{msg.payload}}","layout":"row-spread","x":295.40000915527344,"y":241.24998474121094,"wires":[]},{"id":"220ed27.acd08ae","type":"ui_gauge","z":"d29f2b73.bb856","name":"","group":"57a9f7d1.3aff4","order":0,"width":0,"height":0,"gtype":"gage","title":"Gauge","label":"units","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"60","seg2":"70","x":420.40000915527344,"y":241.34999084472656,"wires":[]},{"id":"e86c0636.15a898","type":"ui_chart","z":"d29f2b73.bb856","name":"","group":"57a9f7d1.3aff4","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"100","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"x":423.40000915527344,"y":283.84999084472656,"wires":[[],[]]},{"id":"6c2a1a1.f41d0e4","type":"change","z":"d29f2b73.bb856","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.d.objectTemp","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":150.39999389648438,"y":367.9000244140625,"wires":[["704db5dd.4a2d0c","6034919a.ca5a08","6513ac91.98922c"]]},{"id":"704db5dd.4a2d0c","type":"ui_text","z":"d29f2b73.bb856","group":"7a8177b1.4e8528","order":0,"width":0,"height":0,"name":"","label":"物体温度","format":"{{msg.payload}}","layout":"row-spread","x":304.40000915527344,"y":339.24998474121094,"wires":[]},{"id":"6034919a.ca5a08","type":"ui_gauge","z":"d29f2b73.bb856","name":"","group":"7a8177b1.4e8528","order":0,"width":0,"height":0,"gtype":"gage","title":"Gauge","label":"units","format":"{{value}}","min":0,"max":"40","colors":["#00b500","#e6e600","#ca3838"],"seg1":"30","seg2":"35","x":434.4000244140625,"y":338.3499755859375,"wires":[]},{"id":"6513ac91.98922c","type":"ui_chart","z":"d29f2b73.bb856","name":"","group":"7a8177b1.4e8528","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"40","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"x":421.40000915527344,"y":381.84999084472656,"wires":[[],[]]},{"id":"f6e58351.6ed218","type":"ui_group","z":"","name":"温度","tab":"6a2033b6.8a0594","disp":true,"width":"6"},{"id":"57a9f7d1.3aff4","type":"ui_group","z":"","name":"湿度","tab":"6a2033b6.8a0594","disp":true,"width":"6"},{"id":"7a8177b1.4e8528","type":"ui_group","z":"","name":"物体温度","tab":"6a2033b6.8a0594","disp":true,"width":"6"},{"id":"6a2033b6.8a0594","type":"ui_tab","z":"","name":"IoT Sensor","icon":"dashboard"}]

以上です。

参照:https://github.com/jeancarl/node-red-labs/tree/master/lab-sense-hat/node-red-dashboard