CO2濃度計をRaspberry Pi Zeroで作ってみた

車の運転中にCO2濃度が高くなると集中力が落ち眠くなりやすくなるそうです。
そこで、車でCO2濃度が測定できるようにRaspberry Piを使って車載用にCO2濃度計を作ることにしました。
まだやりたいことが全部できたわけではないですが、個人的に使うには十分なところまでできましたので作成方法について紹介したいと思います。
f:id:cloudfish:20190529193915j:plain

機能

最終的に実装した機能は以下になります。

  • 定期的にCO2濃度を測定し小型液晶に表示
  • CO2濃度が閾値を超えていたら警告音を鳴らす
  • webで濃度の推移グラフ(1分ごと、1時間ごと)を可視化する
  • CO2濃度のログをAWSのS3に送信(とりあえず蓄積するだけ)

構成

f:id:cloudfish:20190611155546p:plain

必要部品

部品 数量 用途 金額(目安)
Raspberry Pi Zero W 1   1300円
MH-Z19 1 CO2センサー 3800円
PiOled 1 表示用小型液晶 3800円
圧電スピーカ 1 アラーム用 100円

各部品の接続

接続図

f:id:cloudfish:20190611151425p:plain

上記接続の通り各部品を接続していきます。

CO2センサー(mh-z19)接続

CO2センサーを接続し動作確認を行います。

uartの有効化

/boot/config.txtに以下を追加してraspberry piを再起動する

enable_uart=1
モジュールのインストール
pip install getrpimodel
接続確認

以下のプログラムを適当な名前で保存し実行します。

import sys
import serial
import time
import subprocess
import getrpimodel
import datetime
from time import sleep 
import RPi.GPIO as GPIO

if getrpimodel.model() == "3 Model B":
  serial_dev = '/dev/ttyS0'
  stop_getty = 'sudo systemctl stop serial-getty@ttyS0.service'
  start_getty = 'sudo systemctl start serial-getty@ttyS0.service'
else:
  serial_dev = '/dev/ttyAMA0'
  stop_getty = 'sudo systemctl stop serial-getty@ttyAMA0.service'
  start_getty = 'sudo systemctl start serial-getty@ttyAMA0.service'

def mh_z19():
  ser = serial.Serial(serial_dev,
               baudrate=9600,
               bytesize=serial.EIGHTBITS,
               parity=serial.PARITY_NONE,
               stopbits=serial.STOPBITS_ONE,
               timeout=1.0)

  while 1:
    result=ser.write("\xff\x01\x86\x00\x00\x00\x00\x00\x79")
    s=ser.read(9)
    if len(s)!=0 and  s[0] == "\xff" and s[1] == "\x86":
      return {'co2': ord(s[2])*256 + ord(s[3])}
      break

def main():

   subprocess.call(stop_getty, stdout=subprocess.PIPE, shell=True)
   now = datetime.datetime.now()
   now_ymdhms = "{0:%Y/%m/%d %H:%M:%S}".format(now)

   # Get Data
   value = mh_z19()
   co2 = value["co2"]
   print('CO2:' + co2)

   subprocess.call(start_getty, stdout=subprocess.PIPE, shell=True)

if __name__ == '__main__':
   main()

CO2の値が取得されていれば正しく接続できています。
室内だと通常400〜1000ppm程度かと思いますが、異常な値が取得されていれば以下のサイトを参考に補正してみてください。
qiita.com

液晶(PiOled)接続

PiOledを接続後、以下の設定を行います。
手順はRaspberry Pi 3 Model Bに Adafruit PiOLED を接続 - Qiitaを参考にしました。

カーネルモジュール自動ロード設定

/boot/config.txtに以下を追加してraspberry piを再起動する

dtparam=i2c_arm=on
必要ライブラリのインストール
sudo apt-get install python-dev
sudo apt-get install python-imaging
sudo apt-get install libffi-dev
sudo pip install smbus-cffi
sudo pip install RPi.GPIO 
カーネルモジュールのロード
sudo modprobe i2c-dev
$ dmesg | grep i2c

ロードされていることの確認
cat /proc/devices | grep i2c
 89 i2c

/dev/i2c-1 の作成

sudo mknod /dev/i2c-1 c 89 1
$ ls /dev/i2c-1
/dev/i2c-1
PiOledの接続確認

以下のように出力されていれば正しく接続されています。

sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
サンプルの実行確認
git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git

cd Adafruit_Python_SSD1306
sudo python ./setup.py build
sudo python ./setup.py install
python ./examples/stats.py &

液晶にIPアドレス、CPU、メモリ、ディスクが表示されていればOKです。

圧電スピーカ接続

接続後に以下のプログラムを実行してスピーカーから音が鳴ればOKです。

import RPi.GPIO as GPIO
import time

SOUNDER = 21

GPIO.setmode(GPIO.BCM)
GPIO.setup(SOUNDER, GPIO.OUT, initial = GPIO.LOW)

p = GPIO.PWM(SOUNDER, 6500)
p.start(50)
time.sleep(0.5)
p.stop()
time.sleep(0.5)

p.stop
GPIO.cleanup()

プログラムの配置

CO2濃度取得プログラムの配置

以下の通りプログラムを取得し適当なところに配置します。(ここでは/home/pi/program/配下に配置しました。)

git clone https://github.com/cloudfish7/co2_sensor.git

cronの設定
1分ごとと1時間ごとにデータを取得するように設定します。

*/1 * * * * sudo python /home/pi/program/co2_sensor/mh-z19.py minutes
0 * * * * sudo python /home/pi/program/co2_sensor/mh-z19.py hour

表示用Webプログラムの配置

以下の通りプログラムを取得し適当なところに配置します。(ここでは/home/pi/program/配下に配置しました。)
グラフ表示についてはOpen Source Image Charts Replacement | QuickChartを利用しているためraspberry piについてはインターネット接続されている必要があります。

git clone https://github.com/cloudfish7/co2_web.git

以下コマンドで実行します。(自動起動設定してないです)

python app_co2.py

ブラウザで以下アドレスにアクセスするとweb画面が表示されます。
IPアドレスは液晶にも表示されるようになっています。

http:ip_address:8080

車載にあたっての問題

車に積んだ際に電源が問題になります。
シガーソケットからUSBで電源を取得していた場合、エンジンを切ると電源供給が止まるためRaspberry Piが突然落ちることになります。そうするとSDカードへの書き込み途中の場合、ファイルが壊れてしまい最悪OSが起動しなくなる可能性があります。
組み込み系のデバイスの場合はROM化して書き込みを無くすようなので、Raspberry PiでもROM化できないか調べてみたところoverlayfsという仕組みを使うことでSDカードへの書き込みを制御できることが分かりました。
以下の記事に設定方法が詳しく書かれていましたが、設定後システムが不安定になる場合もあるようなので今回は諦めることにしました。
qiita.com
代わりにモバイルバッテリーを電源に使うことにしました。
少し面倒ですがとりあえずは当面これで使ってみたいと思います。OSのシャットダウンについてはWebの画面にシャットダウンボタンを付けて代用しようかと考えています。

まとめ

CO2濃度計を作ってから自宅、車と両方で使っています。
大気中のCO2濃度は約400ppmくらいなので、換気が行き届いている場合は同様の数値となりますが、窓を締めるとすぐに数値が上がっていきました。
実際に車で4人が乗り、内気循環にしていると30分程度で2000ppmを超える濃度となりました。2500ppmを超えるとパフォーマンスが落ち眠気を誘うとの研究もありますので、運転中は適度に換気したほうがよさそうですね。