ArduinoでCO2濃度を計測する(MH-Z19)
ArduinoでCO2濃度を計測するためにいろいろやってみました。
使用パーツ
AE-ATMEGA328-MINI(Arduino Pro Mini互換)
秋月で780円だったので、使ってみることにしました。
USBシリアル変換
ロジックレベルを5Vと3.3Vで切り替えられて、便利なのでよく使ってます。
ブレッドボード
個人的にこれくらいのサイズが使いやすいです。サイズが足りなければ連結できます。
LCD
計測結果を表示するために用意しました。Arduino用のサンプルコードがあり、3.3Vと5Vで接続できます。
MH-Z19(CO2センサー)
今回のメイン、CO2センサーです。いろいろなサイトで探してみたのですが、高額だったり、大量ロットで注文しなければならなかったり、なかなか買えませんでした。
下記AliExpressという中国のサイトで見つけて安かったので、今回は試しに買ってみました。2週間ほどで無事に届きました。
CO2センサーからArduinoへデータ取得
CO2センサーからの情報の取得はシリアルとPWMが選択できます。今回はPWMで取得してみようと思います。
まずは、macとArduinoを接続します。ツールから以下を選択します。
- Board: “Arduino Pro or Pro Mini”
- Processor: “ATmega328 (5V, 16MHz)”
- Port: “/dev/cu.usbserial-A1032FAZ”
Arduinoとセンサーの接続はシンプルで以下の3本です。
センサーが正常に動作しているかを確認するため、シリアルにログを出力するコードを実行してみます。
#define PWM_PIN 3 int prev_val = LOW; long high_level_start = 0L; long high_level_end = 0L; long low_level_start = 0L; long low_level_end = 0L; void setup() { Serial.begin(9600); pinMode(PWM_PIN, INPUT); } void loop() { long cycle_start_time = millis(); int pin_in = digitalRead(PWM_PIN); if (pin_in == HIGH) { if (prev_val == LOW) { long time_high = high_level_end - high_level_start; long time_low = low_level_end - low_level_start; long ppm = 5000 * (time_high - 0.002) / (time_high + time_low - 0.004); Serial.println("PPM = " + String(ppm)); high_level_start = cycle_start_time; prev_val = HIGH; } else { high_level_end = cycle_start_time; } } else { if (prev_val == HIGH) { low_level_start = cycle_start_time; prev_val = LOW; } else { low_level_end = cycle_start_time; } } }
出力は以下のようになります。室内は少しCO2濃度が高めのようです。
PPM = 905 PPM = 906 PPM = 901 PPM = 941 PPM = 940 PPM = 936 PPM = 941 PPM = 941 PPM = 920 PPM = 916 PPM = 921
仕様書を読むと2000をかけると書かれているのですが、5000にしないと値があわないようでした。
http://clima-sensor.ru/files/MH-Z19_Manual_V2.pdf
ArduinoからLCD(ディスプレイ)へ結果の出力
センサーからの結果をLCDに出力するようにしてみます。
接続は以下の4本です。
I2Cのプルアップ抵抗は初めからArduino側で実装されていましたので、そのまま接続します。
プログラムは以下のようになります。(※ 見直し中)
#include <Wire.h> #define LCD_ADRS 0x3E #define PWM_PIN 3 int prev_val = LOW; long high_level_start = 0L; long high_level_end = 0L; long low_level_start = 0L; long low_level_end = 0L; int ppm = 0; boolean is_output_ready = false; char moji[] = "CO2 = "; void writeData(byte t_data) { Wire.beginTransmission(LCD_ADRS); Wire.write(0x40); Wire.write(t_data); Wire.endTransmission(); delay(1); } void writeCommand(byte t_command) { Wire.beginTransmission(LCD_ADRS); Wire.write(0x00); Wire.write(t_command); Wire.endTransmission(); delay(10); } void init_LCD() { delay(100); writeCommand(0x38); delay(20); writeCommand(0x39); delay(20); writeCommand(0x14); delay(20); writeCommand(0x73); delay(20); writeCommand(0x52); delay(20); writeCommand(0x6C); delay(20); writeCommand(0x38); delay(20); writeCommand(0x01); delay(20); writeCommand(0x0C); delay(20); } void setup() { Serial.begin(9600); pinMode(PWM_PIN, INPUT); Wire.begin(); init_LCD(); } void loop() { long cycle_start_time = millis(); int pin_in = digitalRead(PWM_PIN); if (pin_in == HIGH) { if (prev_val == LOW) { long time_high = high_level_end - high_level_start; long time_low = low_level_end - low_level_start; ppm = 5000 * (time_high - 0.002) / (time_high + time_low - 0.004); is_output_ready = true; high_level_start = cycle_start_time; prev_val = HIGH; } else { high_level_end = cycle_start_time; } } else { if (prev_val == HIGH) { low_level_start = cycle_start_time; prev_val = LOW; } else { low_level_end = cycle_start_time; } } if (is_output_ready) { writeCommand(0x00 + 0x01); for (int i = 0; i < 6; i++) { writeData(moji[i]); } String ppm_text = String(ppm); char ppm_print[4]; ppm_text.toCharArray(ppm_print, 4); for (int i = 0 ; i < 4 ; i++) { writeData(ppm_text[i]); } is_output_ready = false; } }
LCDへの文字の出力処理を実装し、Arduinoにアップロードします。
うまくいけば、以下のようにCO2の濃度が出力されます。