Arduino +ESP-WROOM-02で5分間隔で気温を測定するシステムを作りました。前回の記事をご覧下さい。Arduino +ESP-WROOM-02 気温の測定とネットへの送信この測定データをグラフにしてみます。
測定データをD3.jsを使いグラフにしてみます。実際のページはこちら。D3.jsの公式ページはこちらD3.js。このライブラリはグラフの作成を簡単に行う物です。web上でグラフを表示するときの定番です。
グラフの作り方はこちらのサイト等を参考にさせて頂きました。「D3.jsの使い方とグラフを作成するサンプル」
回路は前回と同様です。LM60を使いArduinoのアナログ入力ピンを使い温度を読み取ってESP-WROOM-02で無線LANからサーバのPHPに送信します。PHP受信したデータをカンマ区切りでtxtファイルに保存します。作りとしては単純。
前回の記事のphpでは時刻の書出しが12時間制でした。これではグラフにした時に午前と午後が見にくいので24時間表記に変更しました。
[php]
fwrite($fhandle,date("Y/m/d g:i"));
[/php]
このgの部分を大文字にするだけです。
[php]
fwrite($fhandle,date("Y/m/d G:i"));
[/php]
これで時間を24時間制で書き出す様になります。
phpの全文は次の様になります。
[php]
<?php
date_default_timezone_set(‘Asia/Tokyo’);//タイムゾーンの設定
//文字コードの設定
mb_language("Japanese");
mb_internal_encoding("UTF-8");
$temperature = $_GET["t"];
$fhandle = fopen("./temperature.txt", "a+");//読み込み/書き出し用でオープンします。 ファイルポインタをファイルの終端
//現在時刻の取得
fwrite($fhandle,date("Y/m/d G:i"));
fwrite($fhandle,",");
fwrite($fhandle,$temperature);
fwrite($fhandle, "\n");
fclose($fhandle );
[/php]
つぎにファイルを読み込んでグラフにするhtmlです。Ajaxの非同期読み込みを使いtemparature.txtの内容を読み込み配列に入れます。配列に読み込んだデータをD3に渡してグラフにしています。htmlの内容は次の通りです。
[html]
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>ESP-WROOM-02 Temperature Graph</title>
<style type="text/css">
body {
background-color: #EEE;
}
#result {
width: 1000px;
height: 500px;
background-color: #FFF;
}
#result svg {
position: absolute;
top: 0;
left: 0;
}
#result .axis path,
#result .axis line {
fill: none;
stroke: #666;
}
</style>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script type="text/javascript">
var xmlHttp;
var tempArray = [];
function loadTemperature(){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = checkStatus;
xmlHttp.open("GET", "./temperature.txt", true);
xmlHttp.send(null);
}
function checkStatus(){
if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
var resArray = xmlHttp.responseText.split("\n");
for( var i=0 ; i<resArray.length-1 ; i++ ) {
tempArray[i] = resArray[i].split(",");
}
//D3を使ったグラフの描画
drawGraphD3();
}
function drawGraphD3(){
var w = 980;
var h = 500;
var padding = 10; // グラフの余白
var xAxisPadding = 40; // x軸表示余白
var yAxisPadding = 100; // y軸表示余白
var displayNum = tempArray.length – 1; // 表示期間
var dayWidth = (w – xAxisPadding – padding * 2) / displayNum; // 一目盛り
// SVG作成
var svg = d3.select("#result")
.append("svg")
.attr("width", w)
.attr("height", h);
//y軸の最小と最大を表示用に取得する。
//tempArray[0]最小値
var node = document.getElementById("min");
var minDateTime = tempArray[0][0].split("/");
var year1 = minDateTime[0];
var month1 = minDateTime[1];
var dayTemp = minDateTime[2].split(" ");
var day1 = dayTemp[0];
var timeTemp = dayTemp[1].split(":");
var hour1 = timeTemp[0];
var min1 = timeTemp[1];
node.innerHTML = "開始" + year1 + "/"+ month1 + "/" + day1+ " " +hour1+ ":" +min1;
//tempArray[tempArray.length-1]最大値
var node = document.getElementById("max");
var minDateTime = tempArray[tempArray.length-1][0].split("/");
var year2 = minDateTime[0];
var month2 = minDateTime[1];
var dayTemp = minDateTime[2].split(" ");
var day2 = dayTemp[0];
var timeTemp = dayTemp[1].split(":");
var hour2 = timeTemp[0];
var min2 = timeTemp[1];
node.innerHTML = "終了" + year2 + "/"+ month2 + "/" + day2+ " " +hour2+ ":" +min2;
// 軸
var xScale = d3.time.scale()
.domain([
new Date(year1, month1-1,day1,hour1,min1),
new Date(year2, month2-1, day2,hour2,min2)
])
.range([padding, w – xAxisPadding – padding]);
var yScale = d3.scale.linear()
.domain([30, 0])
.range([padding, h – yAxisPadding – padding]);
var xAxis = d3.svg.axis()
.scale(xScale)
.tickFormat(d3.time.format("%m/%d %H:%M"))
.ticks(10);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + xAxisPadding + ", " + (h – yAxisPadding) + ")")
.call(xAxis)
.selectAll("text")
.attr("x", 10)
.attr("y", -5)
.attr("transform", "rotate(90)")
.style("text-anchor", "start");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + xAxisPadding + ", 0)")
.call(yAxis);
// 折れ線グラフ
var line = d3.svg.line()
.x(function(d, i){
return (i * dayWidth) + xAxisPadding + padding;
})
.y(function(d){
return h – padding – yAxisPadding – ((h – yAxisPadding – padding * 2) / 30 * d[1] );
});
svg.append("path")
.attr("d", line(tempArray))
.attr("stroke", "#ed5454")
.attr("fill", "none");
}
}
</script>
</head>
<body onload="loadTemperature()">
<div id="result"></div>
表示期間
<div id="min"></div>
<div id="max"></div>
</body>
</html>
[/html]
ポイントを抜き出して説明します。
[html]
function checkStatus(){
if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
var resArray = xmlHttp.responseText.split("\n");
for( var i=0 ; i<resArray.length-1 ; i++ ) {
tempArray[i] = resArray[i].split(",");
}
//D3を使ったグラフの描画
drawGraphD3();
}
[/html]
読み込んだテキストデータxmlHttp.resposeTextを改行で区切って配列resArrayに入れます。resArray.length-1がデータの個数になります。最後の改行があるので-1という事になってます。
個数分のforループでカンマで区切ってtempArray[i]に入れて行きます。カンマの前が日付と時間、後が気温になります。ちなみにtempArrayの宣言はfunctionの外側で行っているのでグローバル変数で他のfunctionからも参照できます。グラフの描画はfunction drawGraphD3()で行っています。
drawGrahpD3()でグラフの描画を行いますが、ちょっと特殊な処理は時間の軸を作る所です。
[html]
//y軸の最小と最大を表示用に取得する。
//tempArray[0]最小値
var node = document.getElementById("min");
var minDateTime = tempArray[0][0].split("/");
var year1 = minDateTime[0];
var month1 = minDateTime[1];
var dayTemp = minDateTime[2].split(" ");
var day1 = dayTemp[0];
var timeTemp = dayTemp[1].split(":");
var hour1 = timeTemp[0];
var min1 = timeTemp[1];
node.innerHTML = "開始" + year1 + "/"+ month1 + "/" + day1+ " " +hour1+ ":" +min1;
//tempArray[tempArray.length-1]最大値
var node = document.getElementById("max");
var minDateTime = tempArray[tempArray.length-1][0].split("/");
var year2 = minDateTime[0];
var month2 = minDateTime[1];
var dayTemp = minDateTime[2].split(" ");
var day2 = dayTemp[0];
var timeTemp = dayTemp[1].split(":");
var hour2 = timeTemp[0];
var min2 = timeTemp[1];
node.innerHTML = "終了" + year2 + "/"+ month2 + "/" + day2+ " " +hour2+ ":" +min2;
// 軸
var xScale = d3.time.scale()
.domain([
new Date(year1, month1-1,day1,hour1,min1),
new Date(year2, month2-1, day2,hour2,min2)
])
.range([padding, w – xAxisPadding – padding]);
[/html]
日付と時刻は「2015/09/15 15:00」といったフォーマットになっています。(phpで書き出す時にもっと単純な書式にしておけば良かった…)
これを年、月、日、時、分にばらして変数にいれます。scaleのdomainに入れるために必要な処理になります。
まず”/”でsplitして次に” “でsplitして最後に”:”でsplitします。
htmlのbodyは
[html]
<body onload="loadTemperature()">
<div id="result"></div>
表示期間
<div id="min"></div>
<div id="max"></div>
</body>
[/html]
だけです。onloadで読み込み処理を開始するfunctionを呼び出しています。divが3つありますが、resultがグラフを表示するため、minが表示期間の最初の日付時刻を表示、maxが表示期間の最後を表示するためのものです。
過去のESP-WROOM-02関連の記事は次の通りです。