正しく部屋の換気をするために、どうすべきか。

数字で見えるように、CO2の濃度センサではかって、表示してみる。

センサの調達

CO2 の濃度センサを探すと CCS811 というモジュールが見つかった。

スイッチサイエンスさんで、3000円近く。 個人のお試しで買うのはちょっと高いかも。

ということで、AliExpress で、500円台の同等モジュールを購入。 動くかどうかは自己責任で。

自分は4個購入して、同じ条件で2個同時に計測したら、そこそこぶれた。ただし、まだエイジング48時間を超えてないので、これからどうなるかは不明。

とはいえ、息を吹きかけたり、窓を開けて換気するとなかなかいい感じなので、ある程度信用してもいいと思う。

計測範囲

  • CO2 ... from 400ppm to 8192ppm
  • TVOC ... from 0ppb to 1187ppb

CO2 2000ppm で頭痛や眠気が発生するらしい。 試すと、外気で400ppm、ビニールに呼気を溜めて計測すると、1700ppm くらいだった。

真面目にやる人は、ちゃんとしたお店で買った方がいい。

センサの説明

Adafruit の説明をみてみる。

PIN説明
GNDGND
VCC3.3V から 5V(モノによる)
SCL/SDAI2C
INTセンサの読み取りを割り込みで知らせてくれるようなだが、未使用
RSTLOW->HIGHT センサのリセット
WAKELOW でセンサWAKE、その他でセンサSLEEP

WAKE をGNDに繋げておけば、実質I2CのPINの制御で動かせる。

M5StickC に配線

M5StickC 上部の GND , VCC , G26(SDA) , G0(SDL) に配線する。

CCS811 のライブラリインストール

Arduino の Manage Libraries... から ccs811 で検索し、 Adafruit CCS811 Library をインストールする。

アプリ仕様

CCS811 のサンプルプログラムを参考に、シリアルとM5StickC の画面に CO2 と TVOC を表示する。

warm up の待機時間

CCS811 の仕様をみると、Power on 後、20分はwarm up 時間なので、そのカウントダウンを表示する。

CO2濃度の画面の表示

濃度の時間遷移を絶対値とグラフで表示する。 グラフの背景色は、CO2濃度によって変更する。

1000ppm未満

1000ppm未満

1000ppm以上

1000ppm以上

2000ppm以上で赤色にした。

センサの取得&表示の切り替えは、2秒間隔。 M5StickCは、横置きにすると幅が160pixelあるので、2sec x 160pixel = 過去320秒のグラフ表示が可能。

TFT保護

一定時間で明るさを制限。ボタンAを押すと10秒だけ明るくなる。

code

自分が購入したモノは、たまたま I2Cアドレスが 0x5A だった。

ccs811.ino · Gistcpp
#include <Wire.h>
#include <M5StickC.h>

#include "SparkFunCCS811.h" //Click here to get the library: http://librarymanager/All#SparkFun_CCS811

//#define CCS811_ADDR 0x5B //Default I2C Address
#define CCS811_ADDR 0x5A //Alternate I2C Address

CCS811 mySensor(CCS811_ADDR);
uint16_t co2Buff[TFT_HEIGHT];
uint8_t co2Cursor;

int checkTime = millis();
int brightTime = checkTime + 30000;  // 30sec

void setup()
{
  Serial.begin(115200);

  M5.begin();

  M5.Axp.ScreenBreath(10);
  M5.Lcd.setRotation(3);      // displayの左を上にす
  M5.Lcd.setTextSize(1);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(DARKGREY);
  delay(500);

  M5.Axp.ScreenBreath(10);

  M5.Lcd.printf("CCS811 starting ");

  Wire.begin(0, 26); //Inialize I2C Hardware

  while (mySensor.begin() == false)
  {
    Serial.println("CCS811 error. Please check wiring. Freezing...");
    M5.Lcd.printf(".");
    delay(5000);
  }

  // CO2 Graph Buffer
  memset(co2Buff, 0, sizeof(co2Buff));
  co2Cursor = 0;
}

void serial_output(int eCO2, int TVOC) {
    
    Serial.print("CO2[");
    Serial.print(eCO2);
    Serial.print("] tVOC[");
    Serial.print(TVOC);
    Serial.print("] millis[");
    Serial.print(millis());
    Serial.print("]");
    Serial.println();

}

void sensor(int eCO2, int TVOC) {

    serial_output(eCO2, TVOC);
    
    // display values on LCD
    M5.Lcd.setTextSize(2);
    M5.Lcd.setTextColor(DARKGREEN);
    M5.Lcd.fillRect(0, 0, TFT_HEIGHT, 36, BLACK);

    M5.Lcd.setCursor(0, 1);
    M5.Lcd.printf("CO2 : %4d", eCO2);

    M5.Lcd.setTextSize(1);
    M5.Lcd.setCursor(128, 1+4);
    M5.Lcd.printf("ppm");

    M5.Lcd.setTextSize(2);
    M5.Lcd.setCursor(0, 2 * 8 + 4);
    M5.Lcd.printf("TVOC: %4d", TVOC);
    
    M5.Lcd.setTextSize(1);
    M5.Lcd.setCursor(128, 2 * 8+8);
    M5.Lcd.printf("pp");

    // power on after 20sec, ideling
    if (millis() / 1000 < 20*60) {
        M5.Lcd.setCursor(8, 7 * 8);
        M5.Lcd.setTextSize(2);
        M5.Lcd.setTextColor(DARKGREY);
        M5.Lcd.fillRect(0, 7 * 8, 160, 32, BLACK);
        M5.Lcd.printf("wait %2d sec\r\n", 20*60 - (millis() / 1000));

    } else {
        // graph TFT_HEIGHTx45
        co2Buff[co2Cursor] = eCO2;
  
        int backcolor = DARKCYAN;
        if (eCO2 > 1000) {
            backcolor = MAGENTA;
            brightTime = millis() + 60*1000; // 1min
        } else if (eCO2 > 2000) {
            backcolor = RED;
            brightTime = millis() + 3600*1000; // 1hour
        }
        M5.Lcd.fillRect(0, 7 * 5, TFT_HEIGHT, 45, backcolor);
        
        for (int x = 0; x < TFT_HEIGHT; x++) {
            int cursorx = co2Cursor - x;
            if (cursorx < 0) {
                cursorx = TFT_HEIGHT-1;
            }
            uint16_t value = co2Buff[cursorx];
            if (value < 400) {
                value = 400;
            }
            uint8_t height = (( value-400 ) /30/*graph height factor*/);
    
            int line_color = WHITE;
            M5.Lcd.drawLine(x, 80-height, x, 80, line_color);
        }
        if (co2Cursor++ >= TFT_HEIGHT) {
            co2Cursor = 0;  
        }
    }
}

void loop()
{
    static int sensor_catch_time = millis();

    int nowtime = millis();
    
    // pinの状態など更新
    M5.update();

    // TFT の明るさ調整
    int pressed = M5.BtnA.wasPressed();
    if (pressed) {
      Serial.println("press!! ");
      brightTime = nowtime + 10000; // 10sec
    }

    if (brightTime > nowtime) {
      M5.Axp.ScreenBreath(10);
    } else {
      M5.Axp.ScreenBreath(8);
    }

    // CCS118 のチェック
    if (!mySensor.dataAvailable()) {
        delay(100);
        return;
    }

    // 2秒に1回のセンサーチェックタイム
    if (sensor_catch_time < nowtime) {
        mySensor.readAlgorithmResults();
    
        int eCO2 = mySensor.getCO2();
        int TVOC = mySensor.getTVOC();
  
        sensor(eCO2, TVOC);
        sensor_catch_time =  nowtime + 2000;   // 1time/2sec
    }

    delay(100);
}

続き

</692/>