M5Stackで二酸化炭素の濃度を監視してみた

コロナ渦で二酸化炭素(CO2)濃度に注目

最近、近所のスーパーに行った時に、店舗内のCO2濃度をモニターで表示しているのを見かけました。
また、別の日に母親の病院に付添った際、待合室にCO2センサーが設置されていました。

このコロナ渦で、十分に換気が出来ているかの指標としてCO2濃度に注目が集まっていますが、身近なスーパーや病院でも具体的な取り組みを目にする機会が増えた気がします。

確かに、CO2濃度が低ければ、それだけ換気が十分に行われていることの証明となりますし、施設を利用する側としても安心材料になるわけです。
私もCO2濃度が可視化されている施設のほうが、安心して利用できると思います。

自宅のCO2濃度は適正なのか

しかし、利用する施設のCO2が気になるのであれば、普段生活している自宅のCO2濃度は果たして問題ないレベルなのか?
非常に気になるところです。

特に我が家は機密性の高い造りになっており、24時間強制換気のシステムが導入されていますが、果たして日常生活に支障の無いCO2濃度になっているのかどうか気になってきました。

仮にコロナが無かったとしても、CO2濃度が増えれば、脳に十分な酸素が運ばれなくなることで集中力の低下や、様々な健康への影響が懸念されます。
私も含め、皆さん意外とCO2濃度への関心が薄かったのではないでしょうか。

というわけで、どうにかして自宅のCO2濃度を計測し、問題が無いか検証してみたいものです。
今はコロナ渦ということもあり、色々なCO2濃度計が出回っているらしく、手っ取り早くそれを購入しても良かったのですが、やはりIoTエンジニアを目指す身としては、自身でCO2センサーからデータを取得して濃度を調べてみたいと思います。

センサーでCO2濃度の計測してみる

そこで、今回はスイッチサイエンスさんで購入した「Grove – SCD30搭載 CO2・温湿度センサ(Arduino用)」とM5Stack を使用してCO2濃度を計測してみようと思います。

一つのモジュールでCO2、温度、湿度を測定できるGroveのArduino用3-in-1センサです。

センサーを接続するゲートウェイはM5Stack basicを使用します。

今回、機能としては以下を実装してみたいと思います。

  • M5Stackのモニター上にリアルタイムでCO2濃度を表示する。
  • 濃度に応じてモニターの背景色を変える(黒:問題無し、黄:換気を推奨、赤:今すぐ換気)
  • Google Apps Script(GAS)を使ってWi-fi経由でGoogleスプレッドシートに一定時間おきに濃度データを蓄積
  • スプレッドシート上でCO2濃度推移をグラフ表示

自宅のCO2濃度を把握するのに上記の機能があればとりあえず事足りるかなと思います。

今回の実装内容

では今回実装したコードです。
今回は、3分おきにセンサーデータを送信し、スプレッドシートに蓄積するようにしてみました。
まずはM5stackから。

#include <M5Stack.h>  // M5StackBasic
#include <WiFi.h> 
#include "SCD30.h"
#include <HTTPClient.h>
#include <Ticker.h>
#include <ArduinoJson.h>
#include <ArduinoHttpClient.h>

Ticker ticker1;

int NOTIFICATION_INTERVAL_SEC = 3 * 60;

//volatile long count = 0;
int count = 0;

void countdown_ticker_for_notification() {
  count--;
  if (count < 0) count = 0; // Avoiding minus
}

void reset_count() {
  count = NOTIFICATION_INTERVAL_SEC - 1; // Zero origin
}

// Wi-FiのSSID
char *ssid = "********************";
// Wi-Fiのパスワード
char *password = "********************";

// GoogleスプレッドシートのデプロイされたURLを設定
const char* published_url = "***************************************************************";

void setup_wifi(){ 
  Serial.println("Connecting to ");
  Serial.print(ssid);
  // WiFi接続性改善のため、いったん切断
  WiFi.disconnect();
  delay(500);
  // WiFi開始
  WiFi.begin(ssid, password);
  // Wi-Fi接続待ち
  while (WiFi.status() != WL_CONNECTED){
    delay(500);
    //M5.Lcd.print(".");
  }
  // WiFi接続成功メッセージの表示
  Serial.println("\nWiFi Connected.");
  //M5.Lcd.setCursor(10, 40);
  M5.Lcd.setTextColor(WHITE);
  //M5.Lcd.setTextSize(2);
  M5.Lcd.println("WiFi Connected.");
  // M5StackのIPアドレスを表示
  M5.Lcd.println("IP address: ");
  M5.Lcd.println(WiFi.localIP());
  delay(2000);  
}
 
void setup() {
  M5.begin();
 
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.clear(BLACK);
 
  M5.Lcd.setCursor(0,0);
  M5.Lcd.setTextSize(3);
  M5.Lcd.println("[Seeed SCD30]");
  M5.Lcd.println("-----------------");
  M5.Lcd.println("> start");
   
  Wire.begin();
  Serial.begin(115200);
   
  scd30.initialize();
  //Calibration for minimum 7 days,after this ,close auto self calibration operation.
  scd30.setAutoSelfCalibration(1);

  // Wi-Fi処理の開始
  setup_wifi();

  reset_count();
  ticker1.attach_ms(1000, countdown_ticker_for_notification);
}
 
void loop() {
  StaticJsonDocument<500> doc;
  char pubMessage[256];
  float result[3] = {0};
 
  if (scd30.isAvailable()) {    
    scd30.getCarbonDioxideConcentration(result);
    M5.Lcd.clear(BLACK);
    M5.Lcd.setTextColor(WHITE);      
    M5.Lcd.setCursor(0,0);

    if(result[0] > 1500){
      M5.Lcd.fillScreen(RED);
      M5.Lcd.setTextColor(WHITE);      
      M5.Lcd.println("-----------------");
      M5.Lcd.print("CO2 : ");
      M5.Lcd.print(result[0]);
      M5.Lcd.println(" ppm");
      M5.Lcd.println("-----------------"); 
      M5.Lcd.println("Open WIndow");
    }else if(result[0] > 1000){
      M5.Lcd.fillScreen(YELLOW);
      M5.Lcd.setTextColor(BLACK);
      M5.Lcd.println("-----------------");
      M5.Lcd.print("CO2 : ");
      M5.Lcd.print(result[0]);
      M5.Lcd.println(" ppm");
      M5.Lcd.println("-----------------"); 
      M5.Lcd.println("Notice");
      M5.Lcd.println("Over 1000ppm");
    }else{
      M5.Lcd.fillScreen(BLACK);
      M5.Lcd.setTextColor(WHITE);
      M5.Lcd.println("-----------------");
      M5.Lcd.print("CO2 : ");
      M5.Lcd.print(result[0]);
      M5.Lcd.println(" ppm");
      M5.Lcd.println("-----------------"); 
      M5.Lcd.println("CLEAN AIR");
    }

    M5.Lcd.print("Left:");
    M5.Lcd.println(count);
  }

  if(count == 0){
    // JSONメッセージの作成
    JsonArray idValues = doc.createNestedArray("ID");
    idValues.add(result[0]);
    
    
    serializeJson(doc, pubMessage);

    // HTTP通信開始
    M5.Lcd.println("Send to cloud...");

    HTTPClient http;

    Serial.print(" HTTP通信開始 \n");
    http.begin(published_url);
   
    Serial.print(" HTTP通信POST \n");
    int httpCode = http.POST(pubMessage);
   
    if(httpCode > 0){
      //M5.Lcd.printf(" HTTP Response:%d\n", httpCode);
   
      if(httpCode == HTTP_CODE_OK){
        //M5.Lcd.println(" HTTP Success!!");
        String payload = http.getString();
        Serial.println(payload);
      }
    }else{
      //M5.Lcd.println(" FAILED");
      Serial.printf(" HTTP failed,error: %s\n", http.errorToString(httpCode).c_str());
    }
   
    http.end();
    reset_count();
  }  
 
  delay(2000);
}

つづいて、GASのコードです。

function doPost(e) {

  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート1');
  
  // 送信されてくるJSONデータ {"CO2":XXXX} から、各要素を取り出す
  var params = JSON.parse(e.postData.getDataAsString());
  var id = params.ID;
  var temp = params.temp;
  
  
  // データをシートに追加
  sheet.insertRows(2,1);  // 2行1列目に列を挿入する
  sheet.getRange(2, 1).setValue(new Date());     // 受信日時を記録
  sheet.getRange(2, 2).setValue(id);     // CO2を記録
   
 
}

実際に動かしてみました。
M5Stackのモニターは、CO2濃度によって以下のように背景色が変化します。

1000ppm未満:黒
1000ppm以上~1500ppm未満:黄
1500ppm以上:赤

1000ppm未満の場合

1000ppm以上~1500ppm未満の場合

1500ppm以上の場合

CO2濃度に関しては、一般的に室内においては1000ppm以下に保つことが推奨されているようです。
そこで1000ppm以上となった場合に換気を推奨する意味での「黄色」とし、さらに1500ppm以上となった場合は「すぐに換気しよう」という意味で「赤」としてみました。

で、スプレッドシートには以下のように蓄積されていきます。
濃度の推移がわかるように、折れ線グラフを表示しています。

計測してわかったこと

で、このセンサーとM5Stackを使って、自宅の部屋ごとにCO2濃度を計測しまくってみました。
一つ分かったことは、我が家は就寝中にCO2濃度が1000ppmを超えていることが多いという事でした。
色んな部屋で計測しましたが、おおむね就寝中は1200~1300ppmの間で推移しています。

で、日中は子供が学校に行ったり、妻がパートで働きに出たりと、在宅人数が減るために1000ppm未満となる傾向にあります。
夏場、室温が高くなる時期については、日中ほとんど窓をあけているため、CO2濃度は600-800台となっています。
ただ、これから冬になると、札幌は真冬はほぼマイナス気温なので窓を空けての換気は大変難しくなります。
東京とは寒さのレベルが違います。

ただ、今シーズンに関しては、自宅の換気システムの換気風量を上げるなどして少しでもCO2濃度を1000PPM以下に保てるように意識したいと思います。

何事もきちんと数値にして可視化することって大事ですね。