Tech Sketch Bucket of Technical Chips by TIS Inc.

Javascript + highcharts.js + Arduino + Chrome Packaged Apps を使用したリアルタイムグラフ

Pocket

データの見せ方や伝え方の手法の一つに、取得するデータによってリアルタイムに変化していくグラフがあります。
この手法は株価や有害物質の測定値など、値の変化が重要なデータをグラフ化する場合に有効になります。
この記事ではHighCharts.jsというライブラリを用いてリアルタイムに変化していくグラフの実例を示し解説していきます。


はじめに

前回の記事 では D3.js を使用し、Tech-Sketchの記事毎のはてブ数をグラフ化しました。
このグラフは予めはてブ数を取得・加工してから描画しているため、描画以降にはてブ数が変化してもグラフに反映されません。
特定の日付時点でのデータを評価する場合や、値の変化が少ないデータなどは事前にデータを取得・加工し描画する方法で良いのですが、例えば毎日10分置きにグラフの更新を行いたい場合だと、その都度データを取得・加工・再描画という手間が必要になってしまいます。
そのためデータの値が変化しやすかったり、グラフを更新する頻度が高い場合は リアルタイムグラフ がオススメになります。
この記事では データ取得グラフ更新 をリアルタイムで行うWebアプリの例を説明します。

開発環境

ブラウザ Chrome 28.0.1500.29
ライブラリ jQuery 1.7.2
ライブラリ HighCharts.js
データ取得用ハードウェア Arduino Uno
データ取得用センサー 温度センサー LM61CIZ

データの取得方法

リアルタイムに変化するデータとして真っ先に思いついたのが 気温 でした。
気温データを取得する方法として Arduino温度センサー LM61CIZ を搭載し、1秒毎に室内の温度データを取得・シリアル通信でWebアプリに送信します。

データの処理方法

Arduinoはシリアル通信で取得したデータを送信するため、Webアプリ側ではシリアル通信機能が必要になります。
そこでシリアル通信機能が組み込まれている Chrome Packaged Apps API を使用するためにWebアプリを Chrome Packaged Apps 化する事にしました。
Chrome Packaged Apps APIの使用方法などは以前私が書きました こちらの記事 をご参照下さい。

データの描画方法

上記手段によって取得したデータを描画するライブラリとして HighCharts.js を選択しました。
なぜHighCharts.jsを選択したのかと言いますと、何よりも グラフの動的更新が簡単に行える という点が大きな選択理由になります。
他に グラフの細かい設定が可能 な事や、 グラフの種類が豊富 な事も選択理由になります。
詳細は 公式リファレンス 及び 公式デモ をご参照下さい。
HighCharts.jsは商用利用する際 有料ライセンス が必要になるので注意して下さい。
また、HighCharts.js側のバグでjQuery1.10.1を使用すると正しく動作しないバグがあるため、jQueryのバージョンにも注意して下さい。

温度計測を行うためのArduino設定解説

Arduinoを使用しデータを取得する準備としてまず回路を構築しなければなりません。
今回使用するArduinoではブレッドボードと呼ばれる基板上に回路を構築しました。
次にスケッチと呼ばれるArduino上で動作するプログラムを作成し、Arduinoのマイコンボードに書き込みます。
まずは回路の構築についてブレッドボード図及び回路図を参照しつつ説明していきます。

ブレッドボード・回路図

回路図を言葉だけで説明する事は難しいので、実際にブレッドボード上に作成した回路図を提示して解説を行っていきます。
以下の回路図は Fritzing というWindows/Mac/Linuxで動作し、回路図の設計からシミュレーションまでできるツールを使用し作成しました。

arduinoBreadboard.png

arduinoCircuitDiagram.png

回路を構築する際にできるだけ正確な温度情報を取得するために2つの工夫をしています。

工夫①

1つ目はバイパスコンデンサを使用する事で回路上の電圧を安定させ、取得できるデータの正確性を向上させています。
バイパスコンデンサを使用しない場合、回路上の電圧に波が出来てしまい同じ温度の場合でも取得できる値が変わってしまうのです。

工夫②

2つ目はAREFピンの使用になります。
Arduinoでは、取得した電圧のデジタル値( 0~1023の整数値 )をWebアプリ側に送信しています。
しかし、Webアプリ側で必要な値はデジタル値ではなくアナログ値( 0~基準電圧mV )であるため、Webアプリ側では取得した値に対してD/A変換を行っています。
このD/A変換を行うには、変換前と変換後の値の範囲が必要になります。
上記の場合0~1023、0~基準電圧を元にD/A変換処理を行うのですが、使用している温度センサー(LM61CIZ)の電圧範囲が300mV(-30℃)~1600mV(100℃)のため、デフォルト値である5000mVの基準電圧を使用するとD/A変換の精度が悪くなってしまい正確な値が取得できなくなってしまいます。
そこでAREFピンを使用します。
AREFピンとはアナログ値範囲の 基準電圧 を変更する場合に使用するピンの事になります。
今回の対象データは室温のため基準電圧を1000mV(40℃)に近い996mVに設定し、取得できる値の正確性を向上させます。

AREFピンを使用する上での注意点

AREFピンを使用するにあたり二つ注意点があります。
一つ目はAREFピンに電圧を供給しただけでは基準電圧は変わらないという事です。
Arduinoのスケッチ上で AREFピンを使用する という設定を行わないといけません。
二つ目はAREFピン自体に内部抵抗値32kΩがあるため、電圧計算時にそれを考慮しなければいけません。
この32kΩを計算に入れないと基準電圧にズレが出てしまい、取得するデータの正確性が大幅に落ちてしまいます。

AREFピンを使用する場合の基準電圧計算式は こちらのサイト を参照して下さい。

スケッチ

次にスケッチについてソースを参照しつつ説明します。
スケッチとはArduinoのマイコンボード上で動作する Arduino言語 で書かれたプログラムの事を指します。
Arduino言語はC/C++をベースにしており、C言語のすべての構造と、いくつかのC++の機能をサポートしています。
また、 AVR Libc にリンクされていて、その関数を利用できます。

今回は温度センサーの使用及びシリアル通信に必要な機能のみを説明します。
他の機能が知りたい方は有志の方が翻訳した 日本語版リファレンス をご参照下さい。

以下に今回作成したスケッチを示します。

[1] 基準電圧設定

基準電圧としてAREFピンに供給される電圧を使用する設定(= EXTERNAL )を行っています。
「AREFピンを使用する上での注意点」で書きました スケッチ上でAREFピンを使用するという設定 とはここの事を指しています。

[2] データ転送レートの設定

シリアル通信のデータ転送レートをbps(baud)で指定しています。
データ転送レートは自由に設定できるわけではなく、
300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200
の中から選択しなければいけません。

[3] アナログピンからのデータ取得

引数として指定したアナログピンから値を取得し、戻り値として 0から1023 の整数値が返されます。
今回はこの戻り値をwebアプリ側に送信し温度に変換してグラフ化します。

[4] 書式指定変換

取得したデータに区切り文字としてカンマを挿入しています。
Webアプリ側でパース処理を行うためにカンマを挿入しています。

[5] シリアルポート出力

人が読むことのできる形式でデータをシリアルポートへ出力します。
Arduino側からポートを指定する事は出来ません。

取得した温度データをグラフ化するJavaScript解説

次に HighCharts.js を使用したグラフの描画方法を説明します。

グラフの生成

まず今回作成したアプリのソースを例にグラフ生成方法を説明します。

[1] Highcharts.Chart(options)

引数として渡したオプションを元にグラフを作成する関数です。
optionsの設定をいくつかピックアップして解説を行っていきます。

[2] chart

  • renderTo ... 表示領域に指定する要素のID
  • spacingRight ... 表示領域右側の空白幅

グラフ全体の表示領域について定義しています。
グラフ領域のサイズは renderTo で指定した要素のサイズが適用されます。

[3] stops

stopsは配列で定義し、要素も配列で定義します。
要素配列は識別数値と色情報を定義します。
色情報は「#FFFFFF」の様な書き方でも良いのですが、今回は視覚的にも温度がわかりやすくするため、温度が高いと不透明な赤色に、低いと無色透明に、中間は半透明なオレンジ色になるよう設定しています。

[4] linearGradient

inearGradientはstopsで設定した色情報をどのようにグラフに反映させるか定義しています。
x1は右方向、x2は左方向、y1は上方向、y2は下方向の情報になります。
色情報の指定方法はstopsで設定した識別数値で行います。

[5] series

  • type ... グラフの種類
  • pointInterval ... グラフ上のポイント表示間隔
  • data ... グラフデータ初期値

グラフに追加するシリーズについて一次元配列で定義しています。
シリーズとはデータ系列、つまり同じグラフ上に表示するデータグループの事です。
描画するグラフの種類は、グラフ上の数値に関連して視覚的効果を付与するためにエリアグラフを選択しました。
エリアグラフとは、データ系列の内側が塗りつぶされる折れ線グラフの事を指します。
また、サンプルのようなリアルタイムにデータを取得しグラフに反映される場合でも data の宣言を行ってください。
宣言を行わずにデータを追加していくとエラーになります。

データの取得・変換、グラフへの反映

次にArduinoから受け取ったデータをグラフ上に反映させる方法を説明します。

[1] readPort()

chrome.serial.readでシリアルポートからデータを読み取り配列に格納しています。
読み取ったデータはバイナリ形式のためUint8Arrayオブジェクトに変換後String.fromCharCode.apply関数を使用し文字列に変換しdata変数に格納しています。
格納時に加算代入している理由はupdateSeries関数で説明します。

[2] getTemperature()

Arduinoから取得できる数値は温度ではなくセンサーが出力した電圧値のため、温度に変換する必要があります。
今回使用しているセンサーは300mVが-30℃、1600mVが100℃をあらわしているため、センサーが出力した電圧→D/A変換→温度の順に変換していきます。
温度センサーには取得する温度に若干の誤差があるため、電圧から温度へ変換する際に実際の温度計を元に補正値を計算しそれを加算してグラフの正確性を向上させます。

[3] updateSeries()

readPort関数で文字列に変換したデータをsplit関数を使用し、スケッチ上でデータに追加したカンマを分割基準として分割します。
処理したデータは配列に格納し、シリーズに追加するための処理を行っていきます。
グラフ領域に設定されているオプションを取得し、HighCharts.jsの addPoint 関数を使用してデータを追加しています。
この関数はシリーズ内のデータ配列最後尾に引数のデータを追加します。
次にデータ削除処理を行います。
毎秒データが追加されていくこのグラフでは、時間が経てば経つほど処理が重くなっていきます。
そのため、データの最大描画数を設定し、その数を超えた場合古いデータから削除処理をしていきます。
今回のグラフでは最大60個のデータを同時に表示します。
最後に配列の0番目要素をdata変数に格納しているのは、送信途中のデータが残っていた場合削除してしまうとグラフ上に正しいポイントが追加できなくなるのを避けるためです。
このためreadPort関数では取得したデータを加算代入し、途中送信されたデータを正しいデータになるよう結合しています。

実行結果

こちらが作成したパッケージになります。

また、下図は実際に温度を取得するために使用したArduinoになります。

arduino.JPG

下図はWebアプリを起動してから60秒経過したグラフです。
60秒では大きく気温が変わらないためグラフの変化がわかりません。

temp_result_01.JPG

そこで人工的に温度を変化させるためにセンサーを指でつまみます。
すると、下図のようにグラフが変化し気温が上昇しているのがわかります。

temp_result_02.JPG

センサーから指を離し時間が経つと、温度は下がりグラフは元の平らな状態に戻っていきます。

temp_result_03.JPG

同じデータを別手法を用いて表現

アナログ温度計っぽく表示してみる

ここまで温度の変化をリアルタイムにグラフ表示する例を見てきました。リアルタイムグラフはデータの起伏が激しい場合は変化が追えて良いのですが、気温のようになだらかな変化をする対象の観測には向いているとはいえません。1分前からどれだけ気温が変化したのかを知りたいシーンは少ないでしょう。気温ではなく、温度の変化がそれなりにある揚げ物をする際の油の温度の観測などには向いている気がします。

現在の気温が分かれば良いといっても数値で表示しても面白くないので、HighCharts.jsのサンプルにある スピードメーター を参考にアナログ温度計のような見た目で表示してみました。

thermometer.png

Arduinoやスケッチは同様のものを利用しています。
Arduinoから値を取得するJavaScriptについてはほぼそのままですが、グラフの特性が異なる為一部修正しています。
※ポイントの追加・削除 - [3]で説明した箇所

リアルタイムグラフでは取得した値を追加していく形式で温度の推移をグラフで表しましたが、温度計で表示する場合は現在の温度が表示出来れば良いので、追加ではなく更新にしています。ちなみに、追加のままにすると針がどんどん増え続けます。

次に、実際にグラフ描画している箇所について説明していきます。

[1] 描画箇所、グラフタイプなどの設定

renderToで表示領域に指定する要素のIDを指定しています。
typeにはメーターの様な見た目にする為に gauge を設定しています。これにより、関連のオプションが利用出来るようになります。

[2] 温度計の外周部分の設定

温度計に立体感を出すために影を付けたり、線にグラデーションを入れています。

[3] 温度計内部の設定

温度計の目盛や針、最低、最高温度の設定などを行なっています。

  • min : 最低気温
  • max : 最高気温
  • minorTick : 短針の目盛(1℃単位)
  • tick : 長針の目盛(5℃単位)
  • label : 目盛上に表示するラベル(温度)の設定
    • step : いくつの長針の目盛ごとにラベルを表示するかの設定。2を設定しているので、10℃ごとにラベルが表示されています。
    • rotation : ラベルの表示向き
  • title : 温度計の真ん中上くらいに表示されるテキスト。今回は単位(℃)に設定
  • plotBands : 範囲に対する設定。from℃からto℃までの範囲に対して色を設定しています。
[4] ツールチップとデータの設定

ツールチップに表示する内容とデータの設定をしています。データはArduinoを接続していない状態でも針を表示するように、初期値として15℃を与えています。

感想

同じデータを、異なる表現方法で表示してみました。
同じ気温や温度を表示するにしても、温度の起伏を見たいのか、現在の温度だけ分かれば良いのかでは適した表現方法が変わってきます。取得するデータの特性だけで無く、なにを、どう伝えたいのかを考え、表現方法を選択すると良いでしょう。

まとめ

リアルタイムに変化していくデータをグラフ化する最大のメリットは 今起こった変化がすぐ確認できる という事です。
例えば株価のような値の変化が重要なデータや、タスクマネージャのCPU使用率のような一定の値を越えたら危険なデータなどのグラフ化に向いていると言えます。
今回は表示しているシリーズは1つでしたが、複数のシリーズを組み合わせてリアルタイムグラフを作成し、相互作用の確認などを行うのも楽しそうです。

次回は場所に関するデータの表現方法についての記事になります。

エンジニア採用中!私たちと一緒に働いてみませんか?