PR 記事には広告が含まれています
スポンサーリンク
Translate

M5StickC用のTVOC(総揮発性有機化合物)&CO2センサHATを作って換気が必要か知りたいな

スポンサーリンク

スポンサーリンク

換気が必要かどうか知りたい

最近は寒いので、エアコンをつけて部屋をしめっきしにしがちです。閉めっきりにすると二酸化炭素濃度が上がり、健康によくありません。以前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を利用したいと思います。

M5StickC Speaker Hat(PAM8303搭載)
スイッチサイエンス

表面が網目模様なので、空気の出入りができて、空気を測定するセンサにぴったりです。

分解

六角のネジを外すと簡単に分解できます。

この中に、SGP30とBME280のモジュールを重ねて、それぞれのVCC、GND、SDA、SCLをつなぎます。

もともとついていたピンヘッダに、先ほどの4ピンをはんだ付けします。

ホットボンドで固めて蓋をつけたらSGP30センサHATの出来上がり。

先ほどのプログラムが動作するか確認して、完成です。

これで空気の状態がモニターできるようになりました。

2020.1.29追加 つづき:1日測定してみたしたが...

追加終わり