##0.はじめに
前回の続きです。今回はGoogle GaugeとWebSocketを使用し、センサデータの可視化を行います。
##1.必要なもの(今回準備したもの)
項目 | 数量 | メモ |
---|
##2.参考にさせていただいたところ
Node-REDを使ってセンサーデータをWebSocketで出力する
Github (karuru6225/bme280i2c)
##3.JSONデータにデバイスIDを追加
受信したJSONデータには、デバイスIDが入っていません。将来複数のデバイスが増えた場合を考慮して、デバイスIDを付加するようにします。
①デバイスIDを追加用のfunctionノードを追加する。
Node-REDの左側にあるfunctionからfunctionノードを選択する。
②functionノードを編集する。
Name項目に、「devid追加」を入力する。
Functionに、以下の内容を入力する。
devID = msg.deviceId;
msg.payload = {
"deviceid" : devID,
"timestamp": msg.payload.d.timestamp,
"temp" : msg.payload.d.temp,
"humid" : msg.payload.d.humid,
"press" : msg.payload.d.press
};
return msg;
##4.Websocket出力を追加
①websocket出力用のノードを追加する。
Node-REDの左側にあるoutputからwebsocketノードを選択する。
②websocket出力用ノードを編集する。
Type項目に、「Listen on」を選択する。
Path項目に、「/ws/sensor」を入力する。
Name項目に、「/ws/sensor」を入力する。
③センサ情報取得ノードとdevid追加ノードおよびwebsocket出力用ノードを接続する。また、DB蓄積前にdevidを追加するように変更する
##5.Google Gaugeテンプレートの作成
ここでは、HTMLで表示するGoogle GaugeのHTMLテンプレートを作成する。
①templateノードを追加する。
②templateノードを編集する。
Name項目に、「Google Gauge」を入力する。
Syntax Highlight項目に、「HTML」を選択する。
Fomart項目に、「Plain text」を選択する。
Template内には、下記を入力してください。
※var wsUrl = 'ws://XXXXXX.mybluemix.net/ws/sensor';
XXXXXXの部分は、自分の環境に合わせて変更してください。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>センサデータ リアルタイム表示</title>
<script type="text/javascript" src="//www.google.com/jsapi"></script>
<script type="text/javascript">
var wsUrl = 'ws://XXXXXX.mybluemix.net/ws/sensor';//XXXXXXのところは、自分の環境に合わせて変更してください。
var socket;
var temperatureData, humidityData, pressureData;
var temperatureOptions, humidityOptions, pressureOptions;
var temperatureChart, humidityChart, pressureChart;
// Google Gauge
google.load("visualization", "1", {packages:["gauge"]});
google.setOnLoadCallback(drawChart);
function drawChart() {
// Data for temperature chart
temperatureData = google.visualization.arrayToDataTable([
['Label', 'Value'],
['温度', 0]
]);
// Data for humidity chart
humidityData = google.visualization.arrayToDataTable([
['Label', 'Value'],
['湿度', 0]
]);
// Data for Pressure chart
pressureData = google.visualization.arrayToDataTable([
['Label', 'Value'],
['気圧', 0]
]);
temperatureOptions = {
width: 800, height: 240,
min: 0, max: 60,
redFrom: 50, redTo: 60,
yellowFrom:40, yellowTo: 50,
minorTicks: 5,
majorTicks: ["0", "10","20", "30", "40", "50", "60"]
};
// Option for humidity chart
humidityOptions = {
width: 800, height: 240,
min: 0, max: 100,
redFrom: 90, redTo: 100,
yellowFrom:75, yellowTo: 90,
minorTicks: 5,
majorTicks: ["0", "10","20", "30", "40", "50", "60", "70", "80", "90", "100"]
};
// Option for pressure chart
pressureOptions = {
width: 800, height: 240,
min: 0, max: 1100,
redFrom: 1050, redTo: 1100,
yellowFrom:1010, yellowTo: 1050,
minorTicks: 5,
majorTicks: ["0", "","", "", "", "", "", "700", "800", "900", "1000", "1100"]
};
temperatureChart = new google.visualization.Gauge(document.getElementById('temperatureChart'));
humidityChart = new google.visualization.Gauge(document.getElementById('humidityChart'));
pressureChart = new google.visualization.Gauge(document.getElementById('pressureChart'));
temperatureChart.draw(temperatureData, temperatureOptions);
humidityChart.draw(humidityData, humidityOptions);
pressureChart.draw(pressureData, pressureOptions);
connect();
};
function connect() {
socket = new WebSocket(wsUrl);
socket.onmessage = function(e) {
var sensorData = JSON.parse(e.data);
// Update temperature data
temperatureData.setValue(0, 1, sensorData.temp);
// Update humidity data
humidityData.setValue(0, 1, sensorData.humid);
// Update pressure data
pressureData.setValue(0, 1, sensorData.press);
temperatureChart.draw(temperatureData, temperatureOptions);
humidityChart.draw(humidityData, humidityOptions);
pressureChart.draw(pressureData, pressureOptions);
};
};
function disconnect() {
socket.close();
};
function clock() {
var nowTime = new Date();
var nowHour = ("0"+ nowTime.getHours()).substr(-2);
var nowMin = ("0"+ nowTime.getMinutes()).substr(-2);
var nowSec = ("0"+ nowTime.getSeconds()).substr(-2);
var msg = "現在時刻:" + nowHour + ":" + nowMin + ":" + nowSec;
document.getElementById("Clock").innerHTML = msg;
}
setInterval('clock()',1000);
</script>
</head>
<body>
<h2>温湿度・気圧 リアルタイム表示(Google Gauge)</h2>
<p id="Clock"></p>
<div>
<button onclick="connect()">Connect</button>
<button onclick="disconnect()">Disconnect</button>
</div>
<div>
<div id="temperatureChart" style="width: 800px; height: 0px;"></div>
<div id="humidityChart" style="width: 800px; height: 0px; position: relative; left: 230px;"></div>
<div id="pressureChart" style="width: 800px; height: 220px; position: relative; left: 460px;"></div>
</div>
</body>
</html>
③入力用HTTPノードを追加する。
Node-REDの左側にあるinputからhttpノードを選択する。
④入力用HTTPノードを編集する。
Method項目に、「GET」を選択する。
URL項目に、「/sensor」を入力する。
⑤出力用HTTPノードを追加する。
Node-REDの左側にあるoutputからhttp responseノードを選択する。
⑥入力用HTTPノードとGoogle Gaugeノードおよび出力用HTTPノードを接続する。
⑦右上のDeployをクリックし、変更を反映する。
##6.ブラウザで表示を確認
①任意のブラウザで下記URLに接続する。
http://XXXXXX.mybluemix.net/sensor ※XXXXXXは、自分の環境にあわせて変更してください。
②Websocketの接続状況を確認する。
下図の赤枠「connected 1」になっていれば接続されています。
※disconnectedになっている場合は、①の添付図にあるConnectボタンを再度クリックしてください。
③以前作成した、mqtt.jsを使用してデータを投入する。
pi@raspberrypi ~ $ node mqtt.js
④Google Gaugeに結果がリアルタイムに反映することを確認する。
⑤mqtt.jsをカスタマイズする。
このままですと、固定の温度・湿度・気圧のため、変化がないので、ランダムに値が変化するように
カスタマイズする。また、5秒置きにデータを発生させるように変更する。
var moment = require('moment');
var Client = require("ibmiotf").IotfDevice;
var config = {
省略
};
//ランダムデータを発生させる
var getRandomData = function(value){
return Math.floor(Math.random()*8 + (value-3));
}
var client = new Client (config);
client.connect();
client.on("connect", function () {
setInterval(function(){
var nowtime = moment().format("YYYY-MM-DD HH:mm:ss.SSS");
var temp_v = getRandomData(25);
var humid_v = getRandomData(40);
var press_v = getRandomData(1000);
payload = JSON.stringify({d : { timestamp : nowtime, temp : temp_v, humid : humid_v, press : press_v }});
console.log(payload);
client.publish ("eid", "json", payload);
}, 5000); //5000ms置き(5秒置きにデータを発生させる)
});
⑥再度mqtt.jsを実行して、Google Gaugeがリアルタイムに変化することを確認する。
##7.BME280センサデータの連携
①githubで公開されているソースファイルを入手する。
自力で作成する力が足りなかったため、ここGithub (karuru6225/bme280i2c)を利用させていただきました。
pi@raspberrypi ~ $ git clone https://github.com/karuru6225/bme280i2c.git
②index.jsを参考にbme280.jsを作成する。
index.jsをbme280.jsとしてコピーする。
pi@raspberrypi ~ $ cp bme280i2c/index.js bme280.js
次の部分を編集する。編集個所は、ファイルの後ろにあるsetIntaval部分の3か所を編集する。
(省略)
//setInterval(function(){ //この行をコメントとし、
exports.bme = function(p_callback) { //この行を追加する。
async.waterfall([
function(cb){
readRawData(function(raws){
cb(null, raws);
});
},
function(raws, cb){
var tbase = getTBase(raws.t, calib);
var temp = calibrateT(tbase, calib);
var pres = calibrateP(raws.p, tbase, calib);
var humi = calibrateH(raws.h, tbase, calib);
console.log('temp:' + (temp/100.0));
console.log('pres:' + (pres/100.0));
console.log('humi:' + (humi));
console.log('');
p_callback(temp/100.0, humi, pres/100.0); //この行を追加
cb();
}
]);
//},1000); //この行をコメントとし、
}; //この行を追加する。
③bme280.jsを呼び出し、温度・湿度・気圧データを取得するように、mqtt.jsをカスタマイズする。
カスタマイズ後のmqtt.jsは、下記の通り。
var f = require('./bme280.js');
var moment = require('moment');
var Client = require("ibmiotf").IotfDevice;
var config = {
(省略)
};
function mkJson(callback) {
f.bme(function(temp, humid, press){
var nowtime = moment().format("YYYY-MM-DD HH:mm:ss.SSS");
var temp_v = temp.toFixed(1);
var humid_v = humid.toFixed(1);
var press_v = press.toFixed(1);
payload = JSON.stringify({d : { timestamp : nowtime, temp : temp_v, humid : humid_v, press : press_v }});
//console.log(payload);
callback(payload);
});
}
var client = new Client (config);
client.connect();
client.on("connect", function () {
mkJson(function(payload) {
console.log(payload);
client.publish ("eid", "json", payload);
});
setInterval(function(){
mkJson(function(payload) {
console.log(payload);
});
client.publish ("eid", "json", payload);
}, 60000);
});
④mqtt.jsを実行する。
今回は、1分周期にしたため、適宜時間を変更して試してください。
以上で、Google GaugeとWebsocketを用いた可視化手順は終了です。
次(その9)は、chart.jsを利用しmongodbから取得した過去データを折れ線グラフで表示することを行います。