Raspberry PIとCO2センサー(MH-Z14A)を使用して簡易的なCO2インキュベーターを作る

#目的
研究対象としている微生物が大気中の微量成分を利用して生きている可能性があるためそれらを添加できる培養装置を制作する。

#RaspberryPi側でUARTを有効化する
・[Raspberrypi 3 でUART通信（コンソール＆汎用）](https://qiita.com/k_match/items/e1e60ec2fdd0e17cb727)
・[Raspberry Pi 3 Model B+ で二酸化炭素濃度を測る(MH-Z14A) -AWS IoT もあるよ-](https://qiita.com/watiko/items/5cfa2aedd5a67619add0)
を参考にした。

>`sudo raspi-config`
->"5 Interfacing Option"
->"P6 Serial"
>
シリアルでシェルにログインするか聞かれるので"いいえ"を選択．
"Would you like a login shell to be accessible over serial?"
->"いいえ"
>
シリアルポートを有効にするか聞かれるので”はい”を選択．
"Would you like the serial port hardware to be enabled?"
->"はい"
>
>Finishを選択で再起動．

続いて

>
```
$ whoami
pi
$ ls -la /dev/ttyS0
crw-rw---- 1 root dialout 4, 64 11月 29 10:42 /dev/ttyS0
$ sudo gpasswd -a pi dialout
```
>

#MHZ-14Aを使用するために作成したモジュール
[Raspberry Pi 3 Model B+ で二酸化炭素濃度を測る(MH-Z14A) -AWS IoT もあるよ-](https://qiita.com/watiko/items/5cfa2aedd5a67619add0)
を参考にした。

・[MH-Z14A NDIR CO2 SENSOR FOR CARBON DIOXIDE DETECTION](https://www.winsen-sensor.com/sensors/co2-sensor/mh-z14a.html)
データシートが更新されていたので最新のものを参照した。
このセンサーには以下の5つのコマンドが設定されている。
1. CO2濃度の測定
2. ゼロ点校正
3. スパン校正
4. 自動校正機能のオンオフ
5. 測定範囲の変更
校正には標準ガス(もしくは大気をCO2濃度400ppmとして使用する)が必要なので今回はこの機能は使用しない。CO2濃度の測定、自動校正のオフ、測定範囲を変更するコマンドを入れた。自動校正とは24時間に一度自動でゼロ点校正をする機能で、温室や農場、冷蔵庫で使用する場合にはこの機能を使用しないことを推奨するとのこと。

参考にしたコードを読む際に
・classの定義とinitやselfの意味
・pyserialの使い方
・モジュールの作成と`if __name__ == '__main__':`の意味
等をよく知らなかったたため、とりあえず動くものができたもののコードの正確さは自信が無い。

作成した`mhz14a.py`を`/usr/lib/python2.7/dist-packages/`に保存した。[参考; 3版p195](http://www.ric.co.jp/book/contents/book_1113.html)


```python:mhz14a.py
import serial
import time

class MHZ14A():
    PACKET = [0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79]
    RANGE1 = [0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x07, 0xd0, 0x8F]
    RANGE2 = [0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88, 0xCB]
    RANGE3 = [0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x27, 0x10, 0x2F]
    AUTOCALON = [0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6]
    AUTOCALOFF = [0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86]

    def __init__(self, ser):
        self.serial = serial.Serial(ser, 9600, timeout=1)
        time.sleep(2)

    def get(self):
        self.serial.write(bytearray(MHZ14A.PACKET))
        res = self.serial.read(size=9)
        res = bytearray(res)
        checksum = 0xff & (~(res[1] + res[2] + res[3] + res[4] + res[5] + res[6] + res[7]) + 1)
        if res[8] == checksum:
            return (res[2] << 8|res[3])
        else:
            raise Exception("checksum: " + hex(checksum))

    def close(self):
        self.serial.close()

#センサーからCO2濃度を取得して返す
def main():
    sensor = MHZ14A("/dev/ttyS0")
    try:
        return (int(sensor.get()))
    except:
        pass
    sensor.close()

#Self-calibration機能のオンオフ(返り値なし)
def autocal(x):
    sensor = MHZ14A("/dev/ttyS0")
    if x == 0:
        sensor.serial.write(bytearray(MHZ14A.AUTOCALOFF))
    elif x == 1:
        sensor.serial.write(bytearray(MHZ14A.AUTOCALON))
    sensor.close()

#測定範囲を変更するコマンドを送信(返り値なし)
def range(y):
    sensor = MHZ14A("/dev/ttyS0")
    if y == 1:
        sensor.serial.write(bytearray(MHZ14A.RANGE1))
    elif y == 2:
        sensor.serial.write(bytearray(MHZ14A.RANGE2))
    elif y == 3:
        sensor.serial.write(bytearray(MHZ14A.RANGE3))
    sensor.close

if __name__ == '__main__':
    main()
```

・main()はセンサーの値を返す
・autocal(x)は自動校正機能のオン(1)オフ(0)
・range(y)は測定範囲の変更、0~2000, 0~5000, 0~10000の三通りで機械の初期設定は5000


#CO2を取得して電磁弁をオンオフ
Lチカのコードを参考にして書いた。
自動校正機能をオフ、測定範囲を0~10000ppmにしている。

```python
import RPi.GPIO as GPIO
from time import sleep
import datetime
import mhz14a as MHZ

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)

MHZ.autocal(0) #self-calibration機能をオフ
MHZ.range(3) #測定範囲を0~10000ppmに設定

try:
    while True:
        ppm = MHZ.main() #センサーの値を取得
        if ppm < 9500: #電磁弁を開くCO2濃度
            GPIO.output(18, True)
            sleep(0.2) #電磁弁を開く時間(秒)
            GPIO.output(18, False)
            sleep(0.8)
        else:
            sleep(1)
        print(ppm, datetime.datetime.now()) #CO2濃度と日付時刻を表示
        sleep(60) #測定間隔(秒)
except KeyboardInterrupt:
    pass

GPIO.cleanup()
print("stop")
```

#電磁弁のオンオフ
・電磁弁　JPMV22NC(Amazonで買った)
・電磁弁付属の12V電源
・DC-DCコンバーター　SUS1R50505
・OMRON G5V-2(このサイズじゃないとブレットボードに刺さらない)
・トランジスタ S8050(Amazonの詰め合わせでパックで買った)
・1000Ω抵抗(Amazonの詰め合わせパックで買った)
・ダイオード(Amazonの詰め合わせパックで買った)
回路図描こうとしたらFiritzingが有料になってたのでブレットボード直撮り。
最初電磁弁が動作しなくて何もかも分からなくなって5VのGNDをラズパイのGNDに接続したら動いた。ぱそこんむずかしい・・・。
![IMG_3483_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271465/54ecdd69-fa28-3161-d72d-e0f2c9f7c5ec.png)


#とりあえず動かしてみた
![IMG_3485.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271465/0f15892c-ff48-acd6-f730-b9a44bf36ecb.png)
写真のようにセンサーとCO2の出口とファンをタッパーに詰めて軽く蓋をして(完全に密閉されていない)一晩動かしてみた結果が以下のグラフ

![co2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271465/46519357-9c48-2cdc-9fd4-3e01edd68bb5.png)
センサーの上限が10000ppmなので振り切っているが10000ppm前後を維持できていると思う。
欲を言えば10％程度まで測定範囲が欲しいが手頃なものがない。

#今後の課題
・ログの保存
・水素、メタン、一酸化炭素センサーを追加(センサーそのものは買ったがAD変換が必要)
　→Trace gasインキュベーター化