📑

Arduinoで温度を計測 & chart.jsでリアルタイム表示

2022/08/14に公開

初投稿です。
Arduino で温度計測し、取得した温度をブラウザにリアルタイムでプロットできるようなものを作ります。

Requirements

  • Arduino
  • サーミスタ (MCP9700/9700A) : 秋月電子で購入しました。
  • 1 \muF のコンデンサ
  • ブレッドボード

Arduino の設定

サーミスタと Arduino を接続する

Arduinoとサーミスタの接続

  1. arduino、コンデンサ、サーミスタを図のように接続します。
  2. Arduino を PC と接続し、Arduino IDE で次のようなプログラムを Arduino に書き込みます。
const int vOutPin = 0;
void setup() {
    Serial.begin(57600);
    analogReference(EXTERNAL);
}

void loop() {
    int reading = analogRead(vOutPin);
    float voltage = ((long)reading * 3000) / 1024;

    float temp = (voltage - 500) / 10;
    Serial.print(temp);

    delay(1000);
}

これで Arduino からサーミスタで読み取った温度が出力されるようになりました。

プログラムの作成

ディレクトリ構成

作成するファイルはすべて同じディレクトリに入れて OK です。

temp_reading
├ reading_temperautre.py
├ websocket_server.py
└ monitor.html

Arduino から出力されている温度を読み取る

python の pyserial モジュールを利用して arduino のシリアル出力を読み取ります。
まずは以下コマンドで pyserial をインストールします。

pip3 install pyserial

以下のようなプログラムで python で温度を読み取ることができます。

reading_temperature.py
import serial # serial通信のライブラリ
import time
import re # 正規表現のライブラリ

def read_temperature() -> float:
  ser = serial.Serial("COM5", 57600)
  pattern = '\d'
  result = ser.readline().decode()
  matched = re.findall(pattern, result)
  if len(matched) > 0:
    temperature = matched[0]
  else:
    temperature = 0

  return float(temperature)

Python で websocket サーバーを実装する

次に websocket のサーバーを実装します。
まずは tornado をインストールします。

pip3 install tornado

以下のように websocket server を実装します。

webscoket_server.py

import json
import time

import tornado.websocket
import tornado.web
import tornado.ioloop

from reading_temperature import read_temperature # 先ほど作成した温度読み取りプログラム

class SendWebSocket(tornado.websocket.WebSocketHandler):
  def open(self):
    print('Session Opened. IP:' + self.request.remote_ip)
    self.ioloop = tornado.ioloop.IOLoop.instance()
    self.send_websocket()

  def on_close(self):
    print("Session Closed")

  def check_origin(self, origin):
    return True

  def send_websocket(self):
    self.ioloop.add_timeout(time.time() + 0.1, self.send_websocket)
    if self.ws_connection:
      message = json.dumps({
        'data': read_temperature(), # ここでarduinoで読み取った温度をwebsocketで送っている。
      })
      self.write_message(message)
app = tornado.web.Application([(r"/ws/display", SendWebSocket)])

if __name__ == "__main__":
  app.listen(8080)
  tornado.ioloop.IOLoop.current().start()

ブラウザで温度データをプロットする

arduino で取得した温度データをブラウザ上でグラフプロットします。
グラフのプロットにはChart.jsを利用しました。
また、chart.js でリアルタイムプロットを実現するためにchartjs-plugin-streamingを利用しました。(若干のスタイル調整のために Bootstrap も使っています。)

monitor.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
      crossorigin="anonymous"
    />
  </head>
  <body>
    <div class="container-md p-5">
      <h1>Temperature Monitor</h1>
      <div class="text-center p-4">
        <h2 id="Temperature">initializing ...</h2>
      </div>
      <canvas id="myChart"></canvas>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@3.3.2"></script>
    <script src="https://cdn.jsdelivr.net/npm/luxon@1.27.0"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.0.0"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@2.0.0"></script>

    <script>
      var ctx = document.getElementById("myChart").getContext("2d");
      var chart = new Chart(ctx, {
        type: "line",
        data: {
          datasets: [
            {
              data: [],
            },
          ],
        },
        options: {
          plugins: {
            streaming: {
              duration: 20000,
            },
          },
          scales: {
            x: {
              type: "realtime",
              realtime: {
                duration: 20000,
              },
            },
          },
        },
      });

      /*  以下でwebscoketで取得したデータの処理を行っている       */
      var connection = new WebSocket("ws://localhost:8080/ws/display");
      connection.onmessage = function (e) {
        /* 温度を文字で表示 */
        document.getElementById("Temperature").innerHTML =
          JSON.parse(e.data)["data"] + " degree";

        /* 温度データをグラフに追加 */
        chart.data.datasets[0].data.push({
          x: Date.now(),
          y: JSON.parse(e.data)["data"],
        });
        chart.update();
      };
    </script>
  </body>
</html>

完成と今後

python3 websocket_server.pyとターミナルに打ち込んだ後にmonitor.htmlを web ブラウザで開くと温度計測・プロットが開始します。
完成
必要最低限の構成ですが、とりあえず完成です。
ただ、今のままだと html を開いた瞬間に WebSocket が開いて測定が始まりますので、「測定開始」「測定中止」ボタンを作って測定の ON、OFF を実装出来たらいいなあと思ってます。
大学院のころに実験の測定・制御となると高価な Labview 使うしかなく困っていましたが、本手法を使うことで手軽に実験データーを表示させることができました。
高機能な Labview の代替までとはいきませんが、データをプロットするだけであれば無料で簡単にできますね。

参考記事

以下記事を参考に実装を行いました。

GitHubで編集を提案

Discussion