Arduino의 측정값을 Node.js에서 받아 Socket.io와 chart.js로 실시간으로 그래프 표시

개요



Arduino의 측정치를 실시간으로 그래프 표시하고 싶었으므로, Node.js 여러분을 이용해 브라우저상에 그래프 표시해 보았습니다.
이번에는 일단 광 센서를 측정 대상으로 했습니다. 광센서의 저항값의 변화를 전압으로서 Arduino의 아날로그 입력으로 측정하고 있습니다.

평소 코딩하지 않는 인간의 코드이므로 이상한 부분이 많이 있을지도 모릅니다. 그 근처는 양해 바랍니다.

구성



하드웨어 및 소프트웨어 구성을 나타냅니다. Arduino 주위의 회로는 DEVICE PLUS 기사 를 참고해 주세요.

하드웨어


  • PC (Mac)
  • Arduino UNO (PC와 USB 연결)
  • 광 센서 회로
  • 브레드 보드
  • 광 센서
  • 저항 (1kΩ 정도)
  • 점퍼 라인


  • 소프트웨어


  • Node.js의 프레임 워크 인 Express
  • Arduino와 직렬 통신을위한 직렬 포트 라이브러리
  • 실시간 통신을위한 Socket.io 라이브러리
  • 그래프 표시를위한 chart.js 라이브러리

  • 소스 코드



    소스 코드는 다음의 4개입니다.
  • 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



    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 핀과 5V 출력을 사용하고 있습니다. (사용하는 단자는 회로에 따라 바뀝니다.)
    analogRead로 얻을 수 있는 수치는 10bit의 A/D컨버터의 출력 코드이므로, 전압으로 변환하고 있습니다. 측정 간격은 100ms로 했습니다.

    서버측



    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에서 직렬 포트를 통해 데이터를 받으면 브라우저로 데이터를 보냅니다.

    클라이언트측



    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 부분은 별도 파일로 하고 있습니다.

    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에서 라벨에 현재 시간을 추가하고 있습니다.
    무한하게 데이터가 계속 증가하기 때문에, 데이터수 100을 상한으로 하고 있습니다.

    결과



    Arduino를 USB로 연결한 후 터미널에서 node app.js 라고 두드려 서버를 시작하고 브라우저에서 127.0.0.1:3000에 액세스하면 이런 느낌의 그래프가 표시됩니다.

    광센서 위에서 손으로 빛을 차단한 그래프가 됩니다. 빛을 차단하면 광센서의 저항값이 올라가므로 전압도 올라갑니다.
    가로축이 왠지 균등하게 되지 않습니다만, 마음이 향하면 개선합니다.

    좋은 웹페이지 즐겨찾기