Arduinoの測定値をNode.jsで受けてSocket.ioとchart.jsでリアルタイムにグラフ表示

#概要
Arduinoの測定値をリアルタイムにグラフ表示してみたかったので、Node.js諸々を用いてブラウザ上にグラフ表示してみました。
今回はひとまず光センサーを測定対象にしました。光センサーの抵抗値の変化を電圧としてArduinoのアナログ入力で測定しています。

普段コーディングしない人間のコードなので変な箇所が多々あるかもしれません。その辺はご了承ください。

##構成
ハードウェアとソフトウェアの構成を示します。Arduino周りの回路は[DEVICE PLUSの記事](https://deviceplus.jp/hobby/entry_005/)を参考にしてください。
###ハードウェア
- PC (Mac)
- Arduino UNO （PCとUSB接続）
- 光センサー回路
    - ブレッドボード
    - [光センサー](http://akizukidenshi.com/catalog/g/gI-00110/)
    - 抵抗(1kΩくらい)
    - ジャンパー線

###ソフトウェア
- Node.jsのフレームワークであるExpress
- Arduinoとシリアル通信するためのserialportライブラリ
- リアルタイム通信するためのSocket.ioライブラリ
- グラフ表示するためのchart.jsライブラリ

#ソースコード
ソースコードは以下の４つです。

- Arduino
    - serialCom.ino
- サーバ側
    - app.js
- クライアント（ブラウザ）側
    - index.html
    - index.js


```:ディレクトリ構造
.
├── app.js
├── index.html
├── node_modules
│   ├── @serialport
│　　：
│   └── yeast
├── package-lock.json
├── package.json
└── public
　　　└── index.js
```

##Arduino
```C:serialCom.ino
int analogPin=A3;
double aval=0;
double val=0;

void setup() {
    Serial.begin(9600);
}

void loop() {
    aval = analogRead(analogPin);
    val = 5 * aval / 1024;
    Serial.println(val);
    delay(100);
}
```
アナログ入力のA3ピンと５V出力を使用しています。（使用する端子は回路によって変わります。）
analogReadで得られる数値は10bitのA/Dコンバータの出力コードなので、電圧に変換しています。測定間隔は100msにしました。

##サーバ側
```Javascript:app.js
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
const SerialPort = require('serialport');
const Readline = require('@serialport/parser-readline');
const port = new SerialPort('/dev/cu.usbmodem141101', {
  baudRate: 9600
});
const parser = new Readline();
port.pipe(parser);

app.use(express.static('public'));

app.get('/', function(req, res){
  res.sendFile(__dirname + '/index.html');
});

// ブラウザ側とのコネクション確立
io.on('connection', (socket) => {
  console.log('a user connected');
  socket.on('disconnect', () => {
    console.log('user disconnected');
  });
});

//サーバ起動
http.listen(3000, function(){
  console.log('listening on *:3000');
});

//Arduinoからデータを受信したらクライアントへ送信
parser.on('data', (data) => {
  io.emit('graph update', (data));
});
```
必要なモジュールをインポートしてそれぞれ設定します。
Arduinoからのデータをシリアルポートで待ち受けます。（ポート名は環境によって変わります。）
httpサーバのポート3000で待ち受けます。
socket.ioでクライアントのブラウザと接続します。
Ardionoからシリアルポート経由でデータを受信したらブラウザへデータを送信します。

##クライアント側
```html:index.html
<!doctype html>
<html>
  <head>
    <title>グラフテスト</title>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.min.js'></script>
    <script src='//code.jquery.com/jquery-3.2.1.min.js'></script>
    <script src='http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.2/moment.min.js'></script>
    <script src='//cdn.socket.io/socket.io-1.4.5.js'></script>
  </head>
  <body>
    <canvas id="canvas" width="500" height="500"></canvas>
    <script src='index.js'></script>
  </body>
</html>
```
jquery, chart.js, moment.js, socket.ioを読み込んでいます。
（moment.jsをバンドルしたchart.jsもあるみたいですが、今回はそのまま。）
canvasタグ内にchart.jsでグラフが描画されます。
script部分は別ファイルにしています。

```javascript:index.js
const socket = io.connect();
var ctx = document.getElementById('canvas').getContext('2d');

// グラフの作成
var myChart = new Chart(ctx, {
  type: 'line',
  data: {
      labels: [],
      datasets: [{
          label: 'data-label1',
          data: [],
          backgroundColor: 'rgba(0,0,225,1)',
          borderColor: 'rgba(0,0,225,1)',
          borderWidth: 1,
          lineTension: 0,
          fill: false
      }]
  },
  options: {
      title: {
        display: true,
        text: 'CHART TITLE'
      },
      scales: {
          xAxes: [{
              ticks: {
                //autoSkip: true,
                maxTicksLimit: 10
              }
          }],
          yAxes: [{
              ticks: {
                  // beginAtZero:true,
                  // autoSkip: true,
                  // maxTicksLimit: 10,
                  min:0,
                  max:5,
                  stepSize:1
              }
          }]
      },
      // グラフサイズ固定
      responsive: false,
      //maintainAspectRatio: false
  }
});

$(() => {
  // サーバから値を受け取った時の処理
  socket.on('graph update', (recievedData) => {
    // 現在時刻の取得
    const time = moment();
    const outputTime = time.format('HH:mm:ss');
    // 追加するデータのラベルに時間を追加
    myChart.data.labels.push(outputTime);
    // グラフにデータを追加
    myChart.data.datasets[0].data.push(recievedData);
    // データ数が100以上なら一番古い要素を削除
    if (myChart.data.datasets[0].data.length > 100) {
      myChart.data.labels.shift();
      myChart.data.datasets[0].data.shift();
    };
    // グラフの表示を更新
    myChart.update();
  })
});
```
myChartがグラフの設定です。
その後に続くのがサーバからデータを受け取った時の処理です。
moment.jsでラベルに現在時刻を追加しています。
無限にデータが増え続けるので、データ数１００を上限にしています。

#結果
ArduinoをUSBで接続してから、ターミナルで`node app.js`と叩いてサーバを起動、ブラウザで`127.0.0.1:3000`へアクセスするとこんな感じのグラフが表示されます。
![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104368/997650f5-dc26-d49d-cff3-3495df013b86.png)
光センサーの上で手で光を遮ったりしたグラフになります。光を遮ると光センサーの抵抗値が上がるので電圧も上がります。
横軸がなぜか均等にならないのですが、気が向いたら改善します。