リアルタイムグラフ
MQTTサーバーに接続できるようになりました。次は本連載の第2回の気候センサーモジュールで計測した値をMQTTサーバーに送信し、ブラウザでリアルタイムにグラフ表示してみます。
計測データの送信
気候センサーからデータを取得する部分については、本連載の第2回を参照してください。ただし今回のサンプルでは、Promise機能を使ってコールバックのネストを減らしています。
3秒ごとに気候センサーから気温、湿度を取得してMQTTサーバーに送信します。
climate.on('ready', () => {
// 3秒ごとに、気温、湿度を取得して送信する
setImmediate(function loop() {
readAll();
setTimeout(loop, 3*1000);
});
});
// 気温、湿度のデータ送信
function readAll() {
// 気温、湿度のデータがそろったら送信する(3)
Promise.all([readTemperature(),readHumidity()]).then( (values) => {
// 現在時刻(エポック秒)
const now = require('moment')().unix();
// 計測結果をJSON文字列にする(4)
const json = JSON.stringify([
{ time : now, y : values[0] }, // 温度
{ time : now, y : values[1] } // 湿度
]);
client.publish('climate', json);
console.log(json);
})
.catch(function(e) {
console.log(e);
});
}
// 温度の取得(1)
function readTemperature() {
return new Promise( (resolve,reject) => {
climate.readTemperature('c', (err, temp) => {
if (!err) { resolve(temp); }
else { reject(err); }
});
});
}
// 湿度の取得(2)
function readHumidity() {
return new Promise( (resolve,reject) => {
climate.readHumidity( (err, humid) => {
if (!err) { resolve(humid); }
else { reject(err); }
});
});
}
温度の取得(1)と湿度の取得(2)では、Promiseオブジェクトを作成して返しています。
Promiseは非同期処理のデザインパターンのひとつです。「処理の途中」を意味するオブジェクトを利用することで、非同期処理をわかりやすく記述することができます。非同期処理が成功した場合はresolveメソッドを、失敗した場合はrejectメソッドを呼び出しています。
また、温度と湿度の取得、両方の非同期処理が完了してからデータを送信するためにPromise.allメソッドを用いています(3)。thenメソッドでは、それぞれの非同期処理の結果が配列として渡されます。その配列の値と現在時刻をJSON文字列にして(4)、MQTTサーバーに送信しています。なお、非同期処理のいずれかがrejectメソッドを呼び出してエラーを返せば、即座にcatchメソッドが実行されます。
このサンプルを実行すると、次のように送信したデータをコンソールにも出力します。
[{"time":1479021886,"y":26.836783447265624},{"time":1479021886,"y":46.417755126953125}]
時刻がtimeで、温度/湿度がyという名のキーです。yにしているのは次に解説するグラフ表示のためです。
グラフ表示
MQTTサーバーに計測データを送信できるようになったので、このデータをリアルタイムにグラフにしてブラウザ表示してみましょう。
グラフ表示は、EpochというJavaScriptのグラフライブラリを利用します。Epochは表示に関して細かいカスタマイズはあまりできないようですが、リアルタイムの表示はかんたんに行うことができます。
MQTTクライアント
MQTTクライアントはEclipse Paho JavaScript Clientを利用します。
ただしブラウザからは、まだ直接MQTTのプロトコルを扱うことができません。そのためWebSocketを通じてMQTTのメッセージを送受信します(MQTT over WebSocket)。WebSocketはウェブサーバーとブラウザ間で双方向通信を低コストで行うためのプロトコルです。MQTTと似ていますが別のものです。なおMQTT over WebSocketを使うには、MQTTサーバー側もWebSocketに対応している必要があります。前述のEMQはデフォルトで対応しています。
HTMLの用意
まずEpochグラフライブラリをダウンロードして解凍し、JavaScriptコードとスタイルシートを適当なところに配置しておきます(epoch.min.js、epoch.min.css)。
HTMLで指定する外部のJavaScriptとCSSコードは次のようになります。Epochはデータをブラウザで可視化するためのライブラリであるD3.js(のバージョン3)が必要なので、合わせて指定しています。なお、jquery、d3.js、MQTTクライアントは、公開されているCDNを利用しています。
<link rel="stylesheet" type="text/css" href="css/epoch.min.css"> <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js"></script> <script src="https://d3js.org/d3.v3.min.js"></script> <script src="js/epoch.min.js"></script> <script src="js/graph.js"></script>
グラフの表示部分のHTMLは、次のように<div>タグで指定しておきます。
<div id="myChart" class="epoch category10" style="width: 800px; height: 300px"></div>
classのcategory10は、D3.jsでのCategorical Colors(色分け)の指定です。温度と湿度の線を色分けして表示するために設定しています。
JavaScriptコード
JavaScriptのコードはグラフの初期表示とMQTTサーバーからの受信処理となります。まずグラフの初期表示です。
$(function(){
// グラフの範囲を設定する(1)
var leftRange = [-5, 40];
var rightRange = [0, 100];
// グラフのデータ設定(2)
var lineChartData = [
{ label: "温度", values: [], range: leftRange },
{ label: "湿度", values: [], range: rightRange }
];
// 折れ線グラフを生成する(3)
var chart = $('#myChart').epoch({
type: 'time.line',
data: lineChartData,
range: {
left: leftRange,
right: rightRange
},
axes: ['left','right','bottom'] // 表示する軸の指定
});
~中略~
});
最初にグラフ上Y軸の値の範囲(1)、表示するデータの設定(2)を行っています。データは動的に追加するため、ここでは空の配列を指定します。
epochメソッドでグラフを生成します(3)。ここではパラメータとして、グラフの種類、データ、範囲、軸の表示を指定しています。グラフの種類はリアルタイム表示の折れ線グラフ、time.lineとしました。
次はMQTTサーバーからの受信処理です。
$(function(){
// MQTTサーバーを指定してクライアントを生成する(1)
var client =
new Paho.MQTT.Client("xxx.xxx.xxx", Number(8083),
"clientId" + new Date().getTime());
// MQTTサーバーに接続する(2)
client.connect({onSuccess:onConnect, onFailure: failConnect});
// サーバーに接続したとき(3)
function onConnect() {
client.subscribe("climate"); // topicを指定する
}
// メッセージを受信したとき(4)
client.onMessageArrived = function(message) {
// 受信したJSON文字列をデコードする
const d = $.parseJSON(message.payloadString);
chart.push(d); // 折れ線グラフにデータを追加する
}
~中略~
});
最初にMQTTサーバーを指定してクライアントオブジェクトを生成します(1)。ポートはMQTTサーバーのWebsockets用のポートとします。3つめのパラメータはクライアントの識別コードです。
次にconnectメソッドでMQTTサーバーに接続します(2)。パラメータには正常に接続した時と失敗した時のコールバック先を指定しています。
サーバー接続時のコールバックでは、subscribeメソッドでtopicを指定しています(3)。
メッセージを受信したときのコールバックはonMessageArrivedプロパティに設定します(4)。受信したメッセージ本体は受信データオブジェクトのpayloadStringプロパティに格納されているので、それをデコードしてグラフオブジェクトに追加します。Tessel 2側で出力している値がグラフのデータフォーマットになっているので、そのままpushメソッドに渡しています。1つのデータはUNIXタイムとY軸に表示する値のペアで、それぞれtime、yという名のプロパティになっています。
ブラウザ表示
ブラウズで表示すると次のようなグラフ表示となります。データを受信するたびにリアルタイムに右から左にグラフが伸びていきます。
最後に
今回はMQTTサーバーを使い、Tessel 2で計測した値をリアルタイムに表示しました。オープンソースのサーバーやライブラリを活用すれば、このようなこともシンプルなコードで実現できます。
Tessel 2はRaspberry PiのようにOSのインストールから行う必要もなく、手軽にNode.jsでハードウェアを操作することができます。国内でも早く正式に販売されることを願っています。
