窓を開けて新鮮な空気をいれよう！Raspberry Pi でCO2 濃度を測ろう

# English version of this document is available [here](https://monitorserviceatelierueda.blogspot.com/2018/11/how-to-measure-room-co2-concentration.html).

# 始めに
その会議が眠いのはテーマや議事運営が眠いからというのはもちろんそうなのかもしれませんが、[二酸化炭素濃度が上がっている](http://nemgym.com/201411193560/)ことも原因なのかもしれません。
[CO2濃度が2500ppmをこえたあたりから急激に思考力が低下する](http://www.nagano-c.ed.jp/ina/B_educationalinfo/2013/03/2013_kadaikenkyuuhappyou/2013CO2naka.pdf)なんて報告もありますが、人間のCO2排出量って結構すごくて、**会議室に数人あつまっただけでかなりCO2濃度は高く**なります。冬の閉め切った室内でちょっと**灯油ストーブとかつけるとすぐに凄いCO2濃度**になったりします。

**建設的な会議を進めるために、まずは現状のCO2 濃度を把握してみませんか？**

# [MH-Z19](http://www.winsen-sensor.com/products/ndir-co2-sensor/mh-z19.html)センサー
CO2センサーは non-dispersive infrared (NDIR) といって、赤外線の吸収率がCO2濃度によって変わることを利用して測るのだそうです、かっこいい。 こういうセンサーではスウェーデンの **SenseAir 社の [K-30](http://www.senseair.com/products/oem-modules/k30/)** がポピュラーです。使っていらっしゃる方の報告がいくつもありますし、Raspberry Pi でつかうための[アプリケーショノート](http://co2meters.com/Documentation/AppNotes/AN142-RaspberryPi-K_series.pdf) も通販屋さんから出てるので繋いですぐに使える手軽さと安心感が魅力ですが、[安い通販で買っても](http://www.co2meter.com/products/k-30-co2-sensor-module)**かなり高い**です

なんか安いセンサーがないかなと探していて見つけたのが<a target="_self" href="http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575391936&toolid=10001&campid=5338307275&customid=&icep_item=183008290612&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg">&#12371;&#12385;&#12425;</a><img style="text-decoration:none;border:0;padding:0;margin:0;" src="http://rover.ebay.com/roverimp/1/711-53200-19255-0/1?ff3=2&pub=5575391936&toolid=10001&campid=5338307275&customid=&item=183008290612&mpt=[CACHEBUSTER]">です。送料込みで20$代は安いです。ちなみにポチった頃（先月末）よりさらに値段が下がってる気が...

# センサー外観
届いたセンサーはあのずっしりとたくましい K-30 とちがってもの凄くちっちゃくて軽いです。開発元サイトをみると**15g** だそうです。本当に動くのか、ケースの中にちゃんと物が入っているのか心配になるレベルの軽さです ^^;;;
金属光沢がありますが、**樹脂性のケースにメッキ**がしてあるようです

![スクリーンショット 2015-12-22 19.02.07.png](https://qiita-image-store.s3.amazonaws.com/0/46544/1579f9db-0f40-76e8-05f3-993a24c47d18.png "スクリーンショット 2015-12-22 19.02.07.png")

# 接続
どうやって使うんだろうと思って mh-z19 と Raspberry とかで検索してもめぼしいものがでてきません。ますます不安になります。とはいえ、みつけた[ドキュメント](http://eleparts.co.kr/data/design/product_file/SENSOR/gas/MH-Z19_CO2%20Manual%20V2.pdf)を見ていると **PWM**(!) と **UART** のインターフェースがあるようなので、下図のように普通にRaspberry Pi のUART のGPIOと4線でつなぎます。

※2018.09.04 近況
これを書いた2015年末は検索しても本当になにもない状況だったのですが今は随分賑やかになってるみたいで、ちょっと最近の状況とあってないですね ^^/
PWM にびっくりマークを付けたのはそれまで知らなくて「デジタルデータをわざわざ擬似アナログで送るの？」っていう驚きだったんですけど、個人的な感想なので蛇足だったです

![mz-h19_ブレッドボード.jpg](https://qiita-image-store.s3.amazonaws.com/0/46544/08283014-68d2-c3de-1644-7c8d9b7b62fd.jpeg "mz-h19_ブレッドボード.jpg")

# 事前準備(UARTを空ける)
以前、[ToCoStick で Raspberry Pi 同士を SLIP で繋ぐ](http://qiita.com/UedaTakeyuki/items/e8bee104af280c8c018c)にも書きましたが、Raspberry Pi は起動時に自動的に uart でコンソールを開いてくれている（のでシリアルでバッグができます）ので、uart が塞がっています。
そこで uart を空けるために、事前に下記のように /etc/inittab の末尾の、ttuUSB0 で tty を開く行をコメントアウトしてから起動する必用があります

```bash:/etc/inittab
#Spawn a getty on Raspberry Pi serial line
# T1:23:respawn:/sbin/getty -L ttyUSB0 115200 vt100　※これをコメントアウト
```

## jessie の補足
※2016.07.08 補足
この辺りが jessie で変わっていて、inittab で止めておかなくても systemctl で tty を止めたり再開したり出来るようになってました

```bash:
sudo systemctl stop serial-getty@ttyAMA0.service
sudo systemctl start serial-getty@ttyAMA0.service
```

## RaspberryPi 3 の補足
※2016.11.24 補足
**Raspberry Pi 3** だと **下記2点**の **変更**が必要です

- デフォルトで **UART が無効**（SoC が2個持ってる内の簡単な方が BT に map?)になっているので、**raspi-config で UART を enable にする**か、**/boot/config.txt** に **enable_uart=1** を追加するかして UART を有効にする
![スクリーンショット 2016-11-24 10.06.40.png](https://qiita-image-store.s3.amazonaws.com/0/46544/07961adb-a8e9-8540-d113-411bfb3ba373.png)
![スクリーンショット 2016-11-24 10.07.07.png](https://qiita-image-store.s3.amazonaws.com/0/46544/da2909fe-55b5-97ab-03eb-e49930e455bf.png)
![スクリーンショット 2016-11-24 10.07.20.png](https://qiita-image-store.s3.amazonaws.com/0/46544/6ea7d0b1-91a2-ab71-47eb-46c6b18e82a8.png)

またはこんな感じ↓

![スクリーンショット 2016-11-24 10.29.15.png](https://qiita-image-store.s3.amazonaws.com/0/46544/9d98ce5e-2454-c96f-2b78-d8d4669dc257.png)


- tty が **ttyAMA0** じゃなくて **ttyS0** になっているので、下記のように tty を開け閉めをし、

```bash:
sudo systemctl stop serial-getty@ttyS0.service
sudo systemctl start serial-getty@ttyS0.service
```

**/dev/ttyAMA0** ではなく **/dev/ttyS0** に読み書きする

```python:
def mh_z19():
#    ser = serial.Serial('/dev/ttyAMA0',
    serial_dev = '/dev/ttyS0'
    ser = serial.Serial(serial_dev,
                        baudrate=9600,
                        bytesize=serial.EIGHTBITS,
                        parity=serial.PARITY_NONE,
                        stopbits=serial.STOPBITS_ONE,
                        timeout=1.0)
```

# コード
そして **Gas consentration reading** のコマンドを投げます。Return Value の Byte2 * 256 + Byte3 が CO2 濃度になるとのことなので、下記のようにシリアルを開いてコマンドなげて結果を読むだけの簡単なコードであっさり動きました。マジックナンバーだらけのコードでどうもすみません ^^;;;

```Python:mh_z19.py
# http://eleparts.co.kr/data/design/product_file/SENSOR/gas/MH-Z19_CO2%20Manual%20V2.pdf
import serial
import time

def mh_z19():
  ser = serial.Serial('/dev/ttyAMA0',
                      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 s[0] == "\xff" and s[1] == "\x86":
      return {'co2': ord(s[2])*256 + ord(s[3])}
    break

if __name__ == '__main__':
  value = mh_z19()
  print "co2=", value["co2"]
```
※2016.2.12 遅ればせながらこちらに[gist](https://gist.github.com/UedaTakeyuki/bfe8b20c80e6f09c7105)を御用意いたしました
※2018.9.13 pre-require モジュールのインストール、設定、[MONITOR™](https://monitor.uedasoft.com)への送信など、必要なものをまとめて[こちら](https://github.com/UedaTakeyuki/mh-z19)に用意しました
これまで用意してなくて大変申し訳ありませんでした
※2018.11.06 遅ればせながらパッケージを [pypi](https://pypi.org/project/mh-z19/) にご用意いたしました。本当に必要なのはこれだと気がつくのに３年も掛かってしまい我ながら情けないです ^^;;;
  

## jessie の補足
※2016.07.08 補足
上の補足の通り，jessie だと systemctl コマンドで tty を開け閉めできるので

```Python:
import subprocess

stop_getty = 'sudo systemctl stop serial-getty@ttyAMA0.service'
start_getty = 'sudo systemctl start serial-getty@ttyAMA0.service'


if __name__ == '__main__':
  subprocess.call(stop_getty, stdout=subprocess.PIPE, shell=True)
  value = mh_z19()
  subprocess.call(start_getty, stdout=subprocess.PIPE, shell=True)
```

みたいに、UART 読む時だけ tty を閉めてしまうのでもいいのかもしれません

## RaspberryPi 3 の補足
※2016.11.24 補足
起動時に **RPi3 かどうかチェックして、使う tty を切り替える処理を入れたバージョンを[github](https://github.com/UedaTakeyuki/slider/blob/master/mh_z19.py)に用意いたしました**
中で import してる RPi のモデルチェックモジュール ~~getpimodel.py も[github](https://github.com/UedaTakeyuki/slider/blob/master/getpimodel.py)に用意してあります
これはこれで他にも使い所がありそうなので、別途 package を作ろうとおもってます(futur works)~~
getrpimodel は、他にもいろいろ使い所がありそうでしたので大変僭越ではございますが[pypi のパッケージ](https://pypi.python.org/pypi/getrpimodel/0.1.3)にさせていだだきました。pip でインストールしていただければ恐縮でございます
```
pip install getrpimodel
```
パッケージのソースはご参考までに[こちら](https://github.com/UedaTakeyuki/getrpimodel)にご用意させていただきました

# 表示
測ったデータはサーバに POST して(mqttが本手なんでしょうけど 1883通してくれないネットワークが結構あるんですよね) Chart.js でグラフにすると(websocket が本手なんでしょうけど 9000通してくれないネットワークが沢山あるのでポーリングで更新)見やすくて便利です。
![スクリーンショット 2015-12-22 17.53.21.png](https://qiita-image-store.s3.amazonaws.com/0/46544/f6bfeae5-0a10-5939-b2b9-75bdfc9d9016.png "スクリーンショット 2015-12-22 17.53.21.png")

ついでに**温湿度**も測って**[飽差](http://lib.ruralnet.or.jp/genno/yougo/gy087.html)**も計算してつけてしまうと会議室だけじゃなくてビニールハウスの**トマト栽培のCO2施肥管理**でもつかえそうです

※2018.09.04 補足
こちらも、[MONITOR](https://monitor.uedasoft.com)としてサービスを公開しております
使い方などのご紹介を[こちらのブログ](https://jpmonitorserviceatelierueda.blogspot.com/2019/01/raspberry-pi-20-mh-z19.html)(English version is also available [here](https://monitorserviceatelierueda.blogspot.com/2018/11/how-to-measure-room-co2-concentration.html))にご用意いたしましたので、もしよろしければお時間のある時にでも御覧いただけますことが叶えば至福の至りに存じます

こんな感じのサービスです
![ss.2018-11-20.11.25.43.png](https://qiita-image-store.s3.amazonaws.com/0/46544/cff67946-1f3d-0750-71c4-96c23f0bcb1a.png)

使っていただけたりしようものならもう大感謝で言葉もないです！

※2020.04.26
[Ken Matsui](https://medium.com/@matken.jp)様、[Raspberry Pi 3B+でCO2濃度を計測してグラフ表示](https://medium.com/matken-blog/measure-co2-concentration-with-raspberry-pi-3b-plus-and-display-it-as-a-graph-8190234dab45) で Monitor をご紹介いただきまして誠にありがとうございました。設定方法とか私のドキュメントよりもわかりやすくて脱帽いたしました。


# 謝辞
Qiita の [Raspberry Pi アドベント](http://qiita.com/advent-calendar/2015/raspberrypi)、ぼーっとしてたら速攻で埋まってしまって登録できず、立て逃げみたいな事になってしまいまして誠に申し訳ありませんでした
登録された方の書かれてた記事がとても参考になり、ありがたく勉強させて頂きました事をこの場を借りてお礼もうしあげさせていただきます

# future works
私の所に届いた個体はちょっと高めに出ている気がします。校正が必用みたいです
※2016.02.18 屋外で400ppmで校正しました。こちらもコマンド一発なので簡単でした
あと、時定数というか応答時間というかこれが結構長いです

※2018.09.05 訂正
応答時間が長いんじゃなくて、プレヒート中は正確な値がとれない、ですね。馬鹿な事をもうしまして申し訳ありませんでした。訂正させていただければ幸いです。訂正が遅れましたことも申し訳ありませんでした。


# references
- [会議中の眠気は二酸化炭素濃度が上がっていることが原因かも？！](http://nemgym.com/201411193560/) 高い給料の人が集まって寝ててもしかたないですので、きれいな空気にいれかえましょう

- [室内CO2濃度と眠気・集中力の関係](http://www.nagano-c.ed.jp/ina/B_educationalinfo/2013/03/2013_kadaikenkyuuhappyou/2013CO2naka.pdf) 伊那北高校の生徒の方々によるリサーチ。NDIR の解説がわかりやすいです。教室のCO2濃度と寝ている人の数を計測して回帰直線を計算されているのが興味深かったです

- [MH-Z19](http://www.winsen-sensor.com/products/ndir-co2-sensor/mh-z19.html) 開発元のサイトです
- [MH-Z19のドキュメント](http://eleparts.co.kr/data/design/product_file/SENSOR/gas/MH-Z19_CO2%20Manual%20V2.pdf) 私はこれ以外のドキュメントを見つけられていません

- [AliExpressのショップ](http://www.aliexpress.com/item/1PCS-module-MH-Z19-infrared-co2-sensor-for-co2-monitor-Free-shipping/32371956420.html) ちなみにいろんな人がいろんな値段で出品してます。<a target="_self" href="http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575391936&toolid=10001&campid=5338307275&customid=&icep_item=183008290612&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg">eBay</a><img style="text-decoration:none;border:0;padding:0;margin:0;" src="http://rover.ebay.com/roverimp/1/711-53200-19255-0/1?ff3=2&pub=5575391936&toolid=10001&campid=5338307275&customid=&item=183008290612&mpt=[CACHEBUSTER]"> にもありました
- 必要なインストールや設定を行うスクリプトを含めて、MH-Z19 を Raspberry Pi で使うソフトウェア一式は[こちら](https://github.com/UedaTakeyuki/mh-z19)に、単に読み取りを行うだけのモジュールは[こちら](https://pypi.org/project/mh-z19/)です

- (※2019.01.23 追加) [David](https://github.com/kostaldavid8) さんから[教えて](https://github.com/UedaTakeyuki/mh-z19/pull/3)いただいた[RevSpace](https://revspace.nl/MHZ19#Setting_the_measurement_range)。全てのコマンドをためしてみた記録が秀逸。range 設定が DataSheet の記載と違う、とか
- (※2020.12.14 追加) [nincube8](https://github.com/nincube8) さんに[教えていただいた](https://github.com/UedaTakeyuki/mh-z19/issues/22#issuecomment-683393350)んですけどケーブリングに問題なく UARTのドライバの準備も問題なさそうなのに値がとれない場合は[1-5kΩ](https://github.com/UedaTakeyuki/mh-z19/issues/26#issuecomment-744039360)ぐらいの抵抗でプルアップすると読めることがそうです。私は出会ったことがないのですが信号のレベルが足りてないデバイスがあるみたいですね
- (※2021.4.4 追加) 申し遅れましたが[PWMサポート](https://github.com/UedaTakeyuki/mh-z19/wiki/PWM-support.) を追加してます。**UART 準備するのめんどくさい** <sub>接点インタフェースなので生のGPIOでOK</sub> **デュポンケーブル４本も用意するのは嫌だ** <sub>３本で済みます</sub> **なんかしらんけどエラーになる** <sub> TTL レベルに届いてなくてもなんでコレだと読めるのか不思議なのですが</sub> とかいう場合の **2nd chance** にご検討いただければ幸いです