サイトアイコン Blog AtoZ

【ラズパイ初心者】MH-Z19でCO2濃度をリアルタイムグラフ化

【RaspberryPi Zero】とMH-Z19

ラズパイ上で、MH-Z19のパッケージが使用できなかったため、MH-Z19で取得したデータをリアルタイムで可視化するプログラムを自作しました。結果は下図のような感じです。

このページの内容で出来ること

 ラズベリーパイとCO2センサー「MH-Z19(筆者が使ったのはMH-Z19Cで、MH-Z19B等、他の品番であっても同じものとして扱えます。)」を使って、CO2濃度をリアルタイムでグラフ化し、更に計測したデータをCSVファイルとして保存することができます。
 MH-Z19のラズパイへの配線方法は、他の紹介しているページに譲り、割愛させていただきます。

重要!!前提条件

ラズベリーパイにインストールするOSは、bullseye(full)を想定しています。lite版では、matplotlib等のライブラリがインストールされていないため、グラフが表示されず、エラーメッセージが表示されます。

コード紹介

 次のコードを、ラズパイの”Thonny”に張り付けて、ラズパイのデスクトップ上にpyファイルとして保存してください。

実行してしばらくするとグラフが表示されます。初回起動時は、グラフ化するためのソフトの起動に2~3分待機する必要があるかもしれません。

import serial
import serial
import time
import csv
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.dates as mdates
from multiprocessing import Process, Queue

#Set Serial Port
ser = serial.Serial('/dev/ttyS0', 9600, timeout=1)

#MH-Z19 Detection Range=5000
command = [0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88, 0xCB]

#Send Command
ser.write(bytearray(command))

#Read Response
response = ser.read(9)

#CSV File Name
csv_filename = time.strftime('%Y%m%d%H%M%S_') + 'co2_data.csv'

#Prepare Plot
fig, ax = plt.subplots()
x, y =[], []

#Error
last_co2 = None

#data Update Function
def get_data(q):
    global last_co2
    while True:
        #Get CO2
        ser.write(bytearray([0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79]))
        response = ser.read(9)
        co2 = response[2] * 256 + response[3]

        #400-5000
        if co2 < 400 or co2 > 5000:
            co2 = last_co2
        else:
            last_co2 = co2

        #Get Current Time
        now = time.gmtime()

        #Write CSV
        with open (csv_filename, 'a') as f:
            writer = csv.writer(f)
            writer.writerow([time.time(), time.strftime('%Y-%m-%d'), now.tm_hour, now.tm_min, now.tm_sec, int(time.time() * 1000) % 1000, co2])

        #Log
        print(f"Measurement time: {time.strftime('%Y:%m:%d:%H:%M:%S', now)}, CO2:{co2}ppm")

        #Que
        q.put((time.time(),co2))

        #Wait 1s
        time.sleep(1)

q = Queue()
p = Process(target=get_data, args=(q,))
p.start()

#Update Plot
def update(i):
    #Get Data From Queue
    while not q.empty():
        data = q.get()
        x.append(mdates.epoch2num(data[0]))
        y.append(data[1])

    #Update Plot
    ax.clear()
    ax.plot(x, y, '-')

    #Set X-Axis Format
    ax.xaxis.set_major_formatter(mdates.DateFormatter('%H,%M,%S'))
    plt.xticks(rotation=60)
    plt.xlabel('Time')

    #Set Y-Axis Format
    plt.ylabel('CO2(ppm)')

    #Plot Title
    plt.title('enter')

#Start Animation
ani = animation.FuncAnimation(fig, update, interval = 2000)

#Show Plot
plt.show()

工夫した点

はじめは、「グラフ化」と「1秒ごとのデータ保存」を一連の流れで処理していましたが、グラフ化の際にタイムラグが生じるため、「1秒ごとのデータ保存」がうまく行きませんでした。

そこで、multiprocessingで「グラフ化」と「1秒ごとのデータ保存」を並列処理させることで解決を図りました。

備忘録として、プログラムの各行には説明を記載しました。MH-Z19のパッケージを使った方法は検索するとすぐにヒットしましたが、パッケージを使わずにグラフ化できる点で、本記事を参考にしていただけますと幸いです。

参考としたセンサーのマニュアル

MH-Z19の製造メーカー「Winsen」のHPに掲載されているユーザーマニュアルを参照しました。

MH-Z19のユーザーマニュアル

モバイルバージョンを終了