CC56

CC56: 567収束までに達成したい56リスト

Day-134 ArduinoとPythonで二酸化炭素センシング

おとといの記事で、酸素を摂るのも大事だが、部屋の中の二酸化炭素を減らすのも重要であると書きました。 ちょうど昨日、二酸化炭素センサ「MH-Z19B」が届いたので、空気中の二酸化炭素値[ppm]を取得してみることにしました。 これより安いものも売っていましたが、安物は届くのに1ヶ月以上かかるらしいので、早く届くものを買いました。

ちなみにppm(=パーツ・パー・ミリオン)とは、0.0001%のことです。二酸化炭素のppmが2,000だと空気中の0.2%が二酸化炭素、ということになります。

1,000ppmを超える辺りから、不調を起こす人もいるらしいです。 また、厚生労働省によると、二酸化炭素濃度が上がらないような設計を促す建築法があるようです*1

また、気象庁の資料によると、年々大気中の二酸化炭素濃度は上昇しており、現在だと約400ppmを前後しているようです *2。つまりいくら換気しても400ppmが最低値ということになります。

二酸化炭素センサ MH-Z19B

f:id:hyper-pigeon:20200818104450p:plain
MH-Z19B
左: オモテ / 右: ウラ

めちゃめちゃゴールデンなんだが。

入力線(5V)、グランド線(0V)、出力信号(PWM)の三端子で二酸化炭素濃度を取得できます。 普通にシンプルで使いやすいと思います。

ハンダ付けしないとブレッドボードに刺せないので、トゲみたいなパーツをつけるのに苦労しました。 ハンダ付けとかするの5年ぶりくらいだ。

内部の仕組みとしては、(1) 赤外線を発するライトと、(2) それを受信するセンサがあります。 そして、(3) 二酸化炭素は特定波長の赤外線スペクトルを吸収しやすいため、受信した(4) 光の減衰度により二酸化炭素濃度を計算するのです。

二酸化炭素を計測する手段は、他には化学反応を用いたものなどがありますが、赤外線を使うタイプが一番手っ取り早いはずです。 (ほこりなどに弱いらしいが)

f:id:hyper-pigeon:20200818112538p:plain
左: 回路全体 / 右上: CO2センサの配線 / 右下: Arduinoの配線

こんな感じになりました。

PWMについて

PWMに関してざっくり説明すると、1本の導線の電気信号 High(=1)/Low(=0) のみで情報を伝達するための方法です。 たとえば、二酸化炭素濃度[ppm]が薄い場合

 High → Low → Low → Low → ... → Low → ... → Low

という表現になります。逆に、二酸化炭素濃度[ppm]が濃い場合、

 HighHighHighHigh → ... → High → ... → Low

のように High が連続して続く感じです。モーターの回転角を制御するために使われたりもします。

プログラムについて

最低限のArduinoプログラムとpythonスクリプトをまとめたものを一旦GitHubに上げておきました。

github.com

いくつかのサイトのコードを参考にしたのですが、間違っていたので修正しました。

#define PINPWM 5
#define PINRED 13

long span_high, span_low, lasttime_low, lasttime_high, time_running;
long ppm;
int val_pwm_prev = LOW;
  
void setup() {
  Serial.begin(9600);
  pinMode(PINPWM, INPUT);
  pinMode(PINRED, OUTPUT);
}

void loop() {
  long time_running = millis();
  int val_pwm = digitalRead(PINPWM);
  
  if (val_pwm == HIGH) {
    digitalWrite(PINRED, HIGH);
    if (val_pwm != val_pwm_prev) {
      lasttime_high = time_running;
      span_low = lasttime_high - lasttime_low;
      
      val_pwm_prev = val_pwm;
    }
  }  else {
    digitalWrite(PINRED, LOW);
    if (val_pwm != val_pwm_prev) {
      lasttime_low = time_running;
      span_high = lasttime_low - lasttime_high;
      
      val_pwm_prev = val_pwm;
      /* 
        C_ppm = 2,000 x (T_H - 2[ms])/(T_H + T_L - 4[ms])
          T_H: PWM(span high)
          T_L: PWM(span low)
          2[ms]/4[ms]: criterion signal(?)
       */
      ppm = 2000 * (span_high - 2) / (span_high + span_low - 4);
      
      int intppm = (int) ppm;
      Serial.println(ppm);
    }
  }
  if(ppm >= 2000){
    val_pwm = 0;
  }
}

(本当はarduinoだがシンタックスハイライトのために.c扱いにしている)

二酸化炭素センサのマニュアル を読めばだいたい理解できました。

2000ppm以上はスペック上計測不能です。ドライアイスをばらまいたりしなければここまで行かないはずですが。

進捗

グラフ化までたどり着けたので結構満足しています。

f:id:hyper-pigeon:20200818105154p:plain
左側がmatplotで出力したCO2モニタリンググラフ。
右はそのときの状況を説明したもの

換気すると、たしかにゆっくりと400ppmくらいまで下がるのがわかります。

こんな感じでPythonで扱えるようになったので、もうあとは煮るなり焼くなりどうにでもなりそうです。

ハマりどころ

  • USB通信
    • 1つしか通信のパスが無いらしく、Arduinoのログを見ている状態だとPythonで情報を受け取れない(busy というエラーだけ返ってくる)
    • 通信で入ってくる変数の型が違うので、整えるのがめんどい
      • やり方: Arduino側で int で送信 → Pythonで UTF-8 にデコード、なぜかこれには改行コードがはいっている str 型のため、改行コードを弾いた上でもう一度 int にキャストする。
    • 何故かMac book Airの2つあるUSBポートのうち、片方でArduinoの書き込みが失敗する
  • Python
    • matplotの「分おきに横軸を刻む」 表示を作るのに90分くらいかかった。オイオイ…
    • シリアル通信をするためにimportするのは import serial だが、pipで入れるときは pip install pyserial。話それるけど、PyTorchのapexとかもここら辺間違えるのでちょっとどうにかしてほしい。

おわりに

とりあえず、

 CO2センサ → (PWM) → Arduino → (USB通信) → Python

とデータを流すことができたので、あとはAWSのDBに流したり、LambdaからAlexaを喋らせたりできるようにしたいです。 ついでに、Alexa → SwitchBot(リモコンを操作できる)→ エアーサーキュレータ と操作すれば、自動換気も可能かもしれないですね。

Raspberry Pi 使ったほうが携帯性と親和性が高かったかもしれない。ラズパイは今後使うかもしれません。

www.amazon.co.jp