【ラズパイ】DHT22、Node.js、Chart.jsでつくる温度ロガー

こんなこと、やります。

  • ラズパイでDHT22を使う
  • 一定時間おきに温度・湿度を測定してCSV保存する
  • Node.jsでWebサーバーを立ち上げる
  • Chart.jsでデータをグラフ化

【ラズパイ】DHT22、Node.js、Chart.jsでつくる温度ロガー

最近3DプリンタでABSフィラメントを使い始めたのですが、室内温度が低いためか造形に失敗してしまいます。ひび割れたり、造形物が剥がれたりと、ABSフィラメントで印刷するには温度管理が重要なようです。ようするに、ABSフィラメントを使う場合、室内を暖かくしてやらないといけないんですね。

そこで、本来なら3Dプリンタを箱に入れて断熱対策したいのですが、玄関先に置いてあるためあまり大げさなことはできません。とりあえず、こんな感じでビニールシートを被せ、保温対策してみました。

ビニールシートで熱対策
ビニールシートで熱対策

今回は、このビニールシートがどれほどの効果があるのかフィードバックを得たいと思い、ラズパイで温度ロガーを作ってみることにしました。

つかうもの

はじめに、この記事でつかうものをご紹介します。

ラズパイ

ラズパイは、3Dプリンタをラズパイで動かしてコードレス化するために、OctoPiをOSとしてインストールしています。OctoPiについて詳しくはこちらの記事をご覧ください。

DHT22

こちらのDHT22温度湿度センサモジュールを使用します。DHT22にはAM2302というチップが使われています。

1-Wireという通信方法でデータを受信するのですが、今回はDHTライブラリを使うので通信方法のことは意識せずにデータを受信できますのでご安心ください。

青色の温度・湿度センサDHT11もあります。

次の通りスペックが若干違います。DHT22の方が精度は高いです。

項目 DHT11 DHT22
電源 3V~5V 3V~5V
湿度 20~80%(5%) 0~100%(2.5%)
温度 0~50℃(±2℃) -40~80℃(±0.5℃)
測定レート 毎秒1回 毎秒2回

DHT22を使うための準備

ここでは、DHT22を使うための準備を行っていきます。

ラズパイを最新状態にする

まずは、ライブラリをインストールする前やる「おまじない」です。パッケージリストやインストールされているパッケージ類を最新状態にしておきます。

$ sudo apt-get update
$ sudo apt-get upgrade

python3とpipを使えるようにする

$ sudo apt-get install python3-dev python3-pip

setuptoolsとwheelとpipを最新状態にしておく

$ sudo python3 -m pip install --upgrade pip setuptools wheel

AdafruitのDHTライブラリをインストール

$ sudo pip3 install Adafruit_DHT

▼ こちらのAdafruitのDHTライブラリを使えば、ラズパイでカンタンに温度と湿度が取得できるようになります。

ラズパイとDHT22の配線

ラズパイとDHT22の配線は次のとおりです。

ラズパイとDHT22の配線
ラズパイとDHT22の配線

DATAピンは、4.7kΩの抵抗でプルアップする必要があります。また、センサのDATAピンは、ラズパイのGPIO14(物理番号8番)へつなぎました。DHT22の電源電圧は3V3へつなぎます。

▼ こちらの図のように、DHT22センサをステレオミニでラズパイへ配線できるようにしてみました。

センサとステレオミニ
センサとステレオミニ

Thingiverse

3Dプリンタのアルミニウムプロファイルの溝に設置できるよう、3Dプリンタで土台とM3のT型ナットを作成しました。土台とT型ナットは、ThingiverseからSTLファイルをダウンロードいただけます。

▼ Chassis for DHT22 sensor and Stereo 3.5mm mini phone on A 3D Printer

▼ T-nut M3 drop-in rotating V-Slot

▼ T型ナットのおかげで、温度ロガーを3Dプリンタのプロファイルに固定化できました。

温度ロガーをプロファイルに設置
温度ロガーをプロファイルに設置

PythonとDHT22で温度・湿度の表示

Vimなどで次のPythonプログラムを新規作成します。ここではdht22_test.pyとして、ホームディレクトリへ保存しました。

5秒おきに温度と湿度を取得します。

import Adafruit_DHT
from time import sleep

DHT_SENSOR = Adafruit_DHT.DHT22
DHT_PIN = 14

while True:
    humidity, temperature = Adafruit_DHT.read_retry(DHT_SENSOR, DHT_PIN)

    if humidity is not None and temperature is not None:
        print("Temp: {0:0.1f}C  Humidity: {1:0.1f}%".format(temperature, humidity))
        sleep(5)
    else:
        print("Failed.")

次のようにして、Python3でプログラムを実行します。

$ python3 dht22_test.py 
Temp: 19.8C  Humidity: 46.6%
Temp: 19.5C  Humidity: 46.4%
Temp: 19.5C  Humidity: 46.5%

このように、5秒おきに温度が出力できれば成功です。プログラムを終了する際は、control + Cを押します。

Vimがおかしい

ところで、ラズパイにデフォルトで入っているVimはVim-tinyのため、キー操作がVimと違い使いづらいです。次のようにして、Vimを再インストールしておきましょう。

$ sudo apt-get --purge remove vim-common vim-tiny
$ sudo apt-get install vim

Vimがつらい?

Vimに慣れていない方ですと操作が難解かもしれませんが、ポイントだけ押さえておけばすぐに慣れると思います。Linuxではよく使うので、Vimを覚えておいて損はないですよ。

筆者は、Vimを覚えるために一時期Vim訓練にはまっていました。その時は、キーボード以外を使わせないようにするため、タッチパッドを封印し、さらに十字キーをテープでおおって使えないようにしていました(笑)。Vimでの移動は、HJKLでしょ!

温度ロガーの制作

温度ロガーの制作
温度ロガーの制作

ここからは本題の、「温度ロガーの制作」を行っていきます。

Pythonで温度・湿度データをCSVファイルに保存する

先ほどのプログラムを改良して、Pythonで温度・湿度データをCSVファイルに保存します。

import Adafruit_DHT
from time import sleep
import datetime

DHT_SENSOR = Adafruit_DHT.DHT22
DHT_PIN = 14

file_path = "./dat/data.csv"
with open(file_path, mode='a+'):
    pass


def put_data(humid, temp):
    print("{} / {}".format(humid, temp))
    dt_now = datetime.datetime.now()
    # dt_today = datetime.date.today()
    count = 0
    datas = []
    
    with open(file_path, mode='r') as f:
        
        for l in f.readlines():
            l = l.replace("\n", "")
            datas.append(l)

        tmp_str = "{0:0.1f},{1:0.1f}".format(temp, humid)
        datas.append("{},{}".format(dt_now, tmp_str))
        datas = datas[max(0, len(datas)-3000):]

    with open(file_path, mode="w") as f:
        contents = ""
        for d in datas:
            contents += "{}\n".format(d)
        # print(contents)
        f.write(contents)


while True:
    humidity, temperature = Adafruit_DHT.read_retry(DHT_SENSOR, DHT_PIN)

    if humidity is not None and temperature is not None:
        put_data(humidity, temperature)
        sleep(60)
    else:
        print("Failed.")

仕様は次のとおりです。

  • 1分おきにデータを取得し、追記する
  • 3000件溜まったら、過去のデータを切り捨てる

ちなみに1分おきにデータを書き加えていくと、1日あたり1440件になります。ですから約2日分のデータを確認できます。

バックグラウンドジョブで実行する

バックグラウンドかつ、SSHをログアウトしてもプログラムを継続させるために、nohup&を使って次のようにプログラムを実行させます。

$ nohup python3 logger.py &
[1] 18117

プログラムを停止したい場合はpsコマンドでプロセスIDを調べてkillします

$ ps x
  PID TTY      STAT   TIME COMMAND
  319 ?        Ssl    1:09 /home/pi/oprint/bin/python3 /home/pi/oprint/bin/octoprint serve --h
  949 ?        Ss     0:00 /lib/systemd/systemd --user
  950 ?        S      0:00 (sd-pam)
  963 ?        S      0:03 sshd: pi@pts/0
  965 pts/0    Ss     0:03 -bash
18117 pts/0    S      0:01 python3 logger.py
19299 pts/0    R+     0:00 ps x

$ kill 18117

これでデータファイルの準備はできました。あとは、データを見やすくするために、Node.jsでWebサーバーを立ち上げ、Chart.jsで数値をグラフ化します。

Node.jsのインストール

Node.jsとパッケージ管理ツールのnpmをインストールします。

$ sudo apt-get install -y nodejs npm
$ npm -v
5.8.0

$ node -v
v10.24.0

次に、WebアプリケーションフレームワークのExpressをインストールします。

$ npm install --save express

Node.jsでWebサーバーの起動

次の内容の app.js ファイルを作ります。

var express = require('express');
var app = express();

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

var port = 8888;
app.listen(port,function(){
    console.log("Starting with port:%d mode:%s", port, app.settings.env)
});

www ディレクトリを作成します。ここでHTMLファイルを管理します。

$ mkdir www

www ディレクトリ内にindex.html ファイルをつくりましょう。

<!DOCTYPE html>
<html><head></head><body>
Hello world!
</body></html>

Node.jsでWebサーバーを起動します。

$ node app.js

ブラウザからhttp://<ラズパイのIPアドレス>:8888/へアクセスしてみましょう。「Hello world!」が表示されれば成功です。

SSHログアウト後も継続させるには、$ nohup node app.js &で実行しましょう。

ディレクトリツリー

ここまでで、ディレクトリツリーは次のような構成になっています。

$ tree
.
|-- app.js
|-- dat
|   `-- data.csv
|-- logger.py
|-- nohup.out
`-- www
    |-- dat -> /home/pi/dht22/dat
    `-- index.html

3 directories, 5 files

index.htmlにJavaScriptを実装しますが、JavaScriptからdata.csvへアクセスできるようにするため、シンボリックリンクを作成しています。

$ ln -s "$(pwd)/dat" www/dat

Chart.jsのインストール

あとは、温度・湿度データをWebページに表現すれば良いだけです。XMLHttpRequestを使ってCSVファイルを読み込み、Chart.jsでグラフ化します。

先ほどのindex.htmlを次のように書き換えて、データをグラフ化してみました。

<!DOCTYPE html>
<html>
<head><title>DHT22 Logger</title></head>
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<script
  src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.2.0/chart.min.js"
  integrity="sha512-VMsZqo0ar06BMtg0tPsdgRADvl0kDHpTbugCBBrL55KmucH6hP9zWdLIWY//OTfMnzz6xWQRxQqsUFefwHuHyg=="
  crossorigin="anonymous"></script>
<script
  src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@next/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
<body>
    <div style="width:100%">
        <canvas id="tempChart"></canvas>
        <canvas id="humidChart"></canvas>
      </div>
<script>
var date_datas = [];
var humidity_datas = [];
var temperature_datas = [];
    
function getCSV(){
    var req = new XMLHttpRequest();
    req.open("get", "./dat/data.csv", true);
    req.send(null);	
    req.onload = function(){

        var lines = req.responseText.split("\n");
        // console.log(req.responseText);

        for(var i=0; i<lines.length; i++){
            // console.log(lines[i]);
            var arr = lines[i].split(',');
            date_datas.push(arr[0]);
            temperature_datas.push(arr[1]);
            humidity_datas.push(arr[2]);
        }
        showChart();
    }
}

getCSV();

function showChart() {
    // console.log(date_datas);
    // console.log(humidity_datas);
    // console.log(temperature_datas);

    var ctx1 = document.getElementById('tempChart');
    var ctx2 = document.getElementById('humidChart');

    var tempChart = new Chart(ctx1, {
        type: 'line',
        data: {
        labels: date_datas,
        datasets: [{
            label: '温度',
            data: temperature_datas,
            borderColor: '#f88',
        }],
        },
        options: {
        scales: {
    y: {
      min: 0,
      max: 50,
    },
  }
      },
    });
    var humidChart = new Chart(ctx2, {
        type: 'line',
        data: {
        labels: date_datas,
        datasets: [{
            label: '湿度',
            data: humidity_datas,
            borderColor: '#48f',
        }],
        },
        options: {
        scales: {
    y: {
      min: 0,
      max: 100,
    },
  }
      },
    });
}

</script>
</body>
</html>

▼ このような感じで印刷をはじめると、3Dプリンタ周辺の温度が上昇していくのが分かりました。

温度ロガーでグラフ
温度ロガーでグラフ

果たしてABSフィラメントで、印刷成功するでしょうか?乞うご期待!

YouTubeで モノ作りチャンネル料理チャンネルベースチャンネル 始めました!
記事に関するご質問などがあれば、
Twitter または お問い合わせ までご連絡ください。
関連記事