センサ(CCS811)で、CO2濃度表示する(1/2 M5StickC編)

目次
正しく部屋の換気をするために、どうすべきか。
数字で見えるように、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 | 説明 |
|---|---|
| GND | GND |
| VCC | 3.3V から 5V(モノによる) |
| SCL/SDA | I2C |
| INT | センサの読み取りを割り込みで知らせてくれるようなだが、未使用 |
| RST | LOW->HIGHT センサのリセット |
| WAKE | LOW でセンサ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以上

2000ppm以上で赤色にした。
センサの取得&表示の切り替えは、2秒間隔。 M5StickCは、横置きにすると幅が160pixelあるので、2sec x 160pixel = 過去320秒のグラフ表示が可能。
TFT保護
一定時間で明るさを制限。ボタンAを押すと10秒だけ明るくなる。
code
自分が購入したモノは、たまたま I2Cアドレスが 0x5A だった。
#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/>
