換気が必要かどうか知りたい
最近は寒いので、エアコンをつけて部屋をしめっきしにしがちです。閉めっきりにすると二酸化炭素濃度が上がり、健康によくありません。以前MH-Z19で部屋や、ビルの空間の測定をしてみたことがあります。
結構変動していることがわかりました。
また、閉めっきりだと部屋の化学物質の量も気になります。以前ホルムアルデヒドが測定できる化学物質センサで、部屋や、お店などを測定したことがあります。
化学物質の量も場所によって結構違いました。
さて、今いる部屋はどうでしょう。わざわざ測定器を取り出して測定するのは面倒なのです。常に空気をモニターしていて、空気が悪くなったら教えてくれると便利です。
以前作った、スマホに通知してくれるはかり式離床センサーを作った時にように、
空気が悪くなったらスマホに通知、というのも面白いですね。
というわけで、M5StickCに装着できる、CO2と化学物質が測れるセンサHATを作りたいと思います。
CO2と化学物質が測れるセンサ
SGP30
CO2と化学物質別々のセンサで測ってもいいのですが、両方測定できる便利なセンサがありあす。SGP30というセンサです。
SGP30は、エタノールと水素に反応するセンサを搭載していて、これらの測定結果から、TVOC(総揮発性有機化合物)と、CO2の推定値を出力してくれます。
SGP30のデータシートはこちらに置いておきます。
このセンサ単体で測定してもいいのですが、気温と湿度の情報があると、より精度が上がるようなので、温湿度センサも用意します。
温湿度センサ
SGP30がI2C接続なので、同じI2CのセンサとしてBME280を使うことにしました。BME280は、気温、湿度、気圧を測定することができます。似た型番にBMP280がありますが、BMP280の方は湿度が測れないので注意してください。
プログラムを作る
SGP30とBME280をI2C接続して、M5StickCに配線します。ピンの割り当てはSDAをIO0、SCLをIO26とするとが一般的なようなので、それにならいました。電源ピンは、3V3とGNDを各モジュールのVINとGNDに接続します。I2C接続なので、4本だけで配線は完了です。
ライブラリ
SGP30とBME280は、それぞれAdafruitの便利なライブラリがあるので、それを利用させていただきます。
SGP30は「Adafruit SGP30」と検索すると出てくるライブラリをイントールします。
BME280は「Adafruit BME280」で検索すると出てくるライブラリをインストールします。
BME280の場合、このライブラリが動作に必要な、別のライブラリのインストールもするかメッセージが表示されるので、それらも一緒にインストールするようにしてください。
プログラム
SGP30とBME280のサンプルプログラムを合体させて、プログラムを作ってみました。
#include <M5StickC.h> #include <Wire.h> #include "Adafruit_SGP30.h" #include "Adafruit_Sensor.h" #include "Adafruit_BME280.h" Adafruit_SGP30 sgp; Adafruit_BME280 bme; /* return absolute humidity [mg/m^3] with approximation formula * @param temperature [°C] * @param humidity [%RH] */ uint32_t getAbsoluteHumidity(float temperature, float humidity) { // approximation formula from Sensirion SGP30 Driver Integration chapter 3.15 const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3] const uint32_t absoluteHumidityScaled = static_cast<uint32_t>(1000.0f * absoluteHumidity); // [mg/m^3] return absoluteHumidityScaled; } void setup() { M5.begin(); M5.Lcd.setRotation(3); M5.Lcd.setTextFont(4); M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK); M5.Lcd.fillScreen(BLACK); Serial.begin(115200); Serial.println("SGP30 test"); Wire.begin(0, 26); //SGP30 setup if (! sgp.begin()){ Serial.println("Sensor not found :("); delay(1000); ESP.restart(); } Serial.print("Found SGP30 serial #"); Serial.print(sgp.serialnumber[0], HEX); Serial.print(sgp.serialnumber[1], HEX); Serial.println(sgp.serialnumber[2], HEX); //BME280 seyup unsigned status = bme.begin(0x76, &Wire); if (!status) { Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!"); Serial.print("SensorID was: 0x"); Serial.println(bme.sensorID(),16); Serial.print(" ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n"); Serial.print(" ID of 0x56-0x58 represents a BMP 280,\n"); Serial.print(" ID of 0x60 represents a BME 280.\n"); Serial.print(" ID of 0x61 represents a BME 680.\n"); delay(1000); ESP.restart(); } // If you have a baseline measurement from before you can assign it to start, to 'self-calibrate' //sgp.setIAQBaseline(0x90A3, 0x8E8E); // Will vary for each sensor! } int counter = 0; void loop() { // If you have a temperature / humidity sensor, you can set the absolute humidity to enable the humditiy compensation for the air quality signals float temperature = bme.readTemperature(); // [°C] float humidity = bme.readHumidity(); // [%RH] sgp.setHumidity(getAbsoluteHumidity(temperature, humidity)); if (! sgp.IAQmeasure()) { Serial.println("Measurement failed"); return; } // M5.Lcd.setTextDatum(0); String str = " " + (String)sgp.TVOC + "[ppb]"; M5.Lcd.drawRightString(str,256,8,4); M5.Lcd.setCursor(0, 0, 2); M5.Lcd.println("TVOC"); str = " " + (String)sgp.eCO2 + "[ppm]"; M5.Lcd.drawRightString(str,256,32+16,4); M5.Lcd.setCursor(0, 32, 2); M5.Lcd.println("eCO2"); Serial.print("Temperature:");Serial.print(temperature); Serial.print("\tHumidity:");Serial.print(humidity); Serial.print("\tTVOC:"); Serial.print(sgp.TVOC);// Serial.print(" ppb\t"); Serial.print("\teCO2:"); Serial.println(sgp.eCO2);// Serial.println(" ppm"); */ if (! sgp.IAQmeasureRaw()) { Serial.println("Raw Measurement failed"); return; } //Serial.print("Raw H2:"); Serial.print(sgp.rawH2); Serial.print(" \t"); //Serial.print("Raw Ethanol:"); Serial.print(sgp.rawEthanol); Serial.println(""); counter++; delay(1000); counter++; if (counter == 60) { counter = 0; uint16_t TVOC_base, eCO2_base; if (! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) { Serial.println("Failed to get baseline readings"); return; } Serial.print("****Baseline values: eCO2: 0x"); Serial.print(eCO2_base, HEX); Serial.print(" & TVOC: 0x"); Serial.println(TVOC_base, HEX); } }
それぞれのセンサのサンプルプログラムをベースに、シリアルのスピードとI2Cの接続をM5StickC用に変更し、測定値をM5StickCの画面に表示するよう機能を追加しました。
実行
コンパイルして実行します。M5StickCの画面にTVOC、eCO2が、シリアルモニターに現在の、気温、湿度、TVOC、eCO2が出力されます。
最初の15秒くらいは、最低値の0と400が出力され、それ以降測定値が出力されます。TVOCの単位は[ppb]、eCO2は[ppm]です。
ベースラインの設定
SGP30に限らず、空気の質を調べるセンサ全般に言えることなのですが、原理上、センサ固有のオフセット値が測定値に加算されてしまっています。このオフセットを補正しないと正しい値が得られません。このセンサはこのオフセットのことをベースラインと呼んでいて、しばらく測定し続けることで、自動でベースラインを修正する機能が内蔵されています。しかし正しく補正されるまで長い時間が必要です。
そこで、あらかじめこのセンサ固有のベースラインを測定しておいて、プログラムの開始時にその値をセンサに設定することで、電源を入れらたすぐに測れるようにすることができます。
固有のベースラインの測定
ベースラインですが、シリアルモニターを見ていると1分に1回
****Baseline values: eCO2: 0xXXXX & TVOC: 0xYYYY
という文字が表示されます。このXXXXとYYYYの値が、センサの現在のベースラインの値です。しばらくの間、空気の綺麗な屋外にセンサを置いて測定していると、この値があまり変化しなくなります。(といっても一定というわけではなく、10くらいは増えたり減ったりします。)
この綺麗な空気の環境下で、あまり変化しなくなった時の値が、このセンサの固有のベースラインの値となります。
この値を初期値にセットするよう、プログラムを修正します。60行目の
//sgp.setIAQBaseline(0xXXXX, 0xYYYY); // Will vary for each sensor!
setIAQBaselineの()の中に、先ほどのベースラインの値をコピーします。そして、この行のコメントアウトを外し、コンパイルして書き込みます。
こうすることで、いつでも正しい値から測定が開始されるようになります。
測定してみた
Adruinoのシリアルプロットで、締め切った部屋で10分程度測定してみました。CO2は800[ppm]程度、TVOCは350[ppb]程度となりました。結構安定して測定できていますね。
SGP30 HATを作る
うまく動作するようになったので、SGP30とBME280をM5StickCのHATにしていきます。HATとはこの写真のように、M5StickCに刺さってM5StickCの機能を拡張するモジュールです。
Speaker HATのケースを利用
ケースとして、M5StickCのSpeaker Hatを利用したいと思います。
表面が網目模様なので、空気の出入りができて、空気を測定するセンサにぴったりです。
分解
六角のネジを外すと簡単に分解できます。
この中に、SGP30とBME280のモジュールを重ねて、それぞれのVCC、GND、SDA、SCLをつなぎます。
もともとついていたピンヘッダに、先ほどの4ピンをはんだ付けします。
ホットボンドで固めて蓋をつけたらSGP30センサHATの出来上がり。
先ほどのプログラムが動作するか確認して、完成です。
これで空気の状態がモニターできるようになりました。
2020.1.29追加 つづき:1日測定してみたしたが...
追加終わり
コメント