Arduino + ESP-WROOM-02による気温測定。グラフにしてみる。D3.js


Arduino +ESP-WROOM-02で5分間隔で気温を測定するシステムを作りました。前回の記事をご覧下さい。Arduino +ESP-WROOM-02 気温の測定とネットへの送信この測定データをグラフにしてみます。
ESP-WROOM-02そ使った気温測定グラフ
測定データをD3.jsを使いグラフにしてみます。実際のページはこちら。D3.jsの公式ページはこちらD3.js。このライブラリはグラフの作成を簡単に行う物です。web上でグラフを表示するときの定番です。
グラフの作り方はこちらのサイト等を参考にさせて頂きました。「D3.jsの使い方とグラフを作成するサンプル」

回路は前回と同様です。LM60を使いArduinoのアナログ入力ピンを使い温度を読み取ってESP-WROOM-02で無線LANからサーバのPHPに送信します。PHP受信したデータをカンマ区切りでtxtファイルに保存します。作りとしては単純。

Arduino UNO + ESP-WROOM-02 + 温度センサLM60

前回の記事のphpでは時刻の書出しが12時間制でした。これではグラフにした時に午前と午後が見にくいので24時間表記に変更しました。

fwrite($fhandle,date("Y/m/d g:i"));

このgの部分を大文字にするだけです。

fwrite($fhandle,date("Y/m/d G:i"));

これで時間を24時間制で書き出す様になります。
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 );

つぎにファイルを読み込んでグラフにするhtmlです。Ajaxの非同期読み込みを使いtemparature.txtの内容を読み込み配列に入れます。配列に読み込んだデータをD3に渡してグラフにしています。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>

ポイントを抜き出して説明します。

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();
 }

読み込んだテキストデータxmlHttp.resposeTextを改行で区切って配列resArrayに入れます。resArray.length-1がデータの個数になります。最後の改行があるので-1という事になってます。
個数分のforループでカンマで区切ってtempArray[i]に入れて行きます。カンマの前が日付と時間、後が気温になります。ちなみにtempArrayの宣言はfunctionの外側で行っているのでグローバル変数で他のfunctionからも参照できます。グラフの描画はfunction drawGraphD3()で行っています。
drawGrahpD3()でグラフの描画を行いますが、ちょっと特殊な処理は時間の軸を作る所です。

//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]);

日付と時刻は「2015/09/15 15:00」といったフォーマットになっています。(phpで書き出す時にもっと単純な書式にしておけば良かった…)
これを年、月、日、時、分にばらして変数にいれます。scaleのdomainに入れるために必要な処理になります。
まず”/”でsplitして次に” “でsplitして最後に”:”でsplitします。
htmlのbodyは

<body onload="loadTemperature()">
<div id="result"></div>
表示期間
<div id="min"></div>
<div id="max"></div>
</body>

だけです。onloadで読み込み処理を開始するfunctionを呼び出しています。divが3つありますが、resultがグラフを表示するため、minが表示期間の最初の日付時刻を表示、maxが表示期間の最後を表示するためのものです。

 

過去のESP-WROOM-02関連の記事は次の通りです。