二酸化炭素を測定してクラウド(oci)で見える化

September 27, 2021

コロナ禍のためずっと在宅勤務が続いていて、部屋に篭りきりで仕事をしていますが、集中したいので基本窓は閉め切って、部屋の扉も閉めて密閉空間の中にいます。一応、空気清浄機を買ってずっと動かしていますが、二酸化炭素は減らないのでどのくらい増えているのか気になっていました。そこで、こちらのページを参考に、二酸化炭素を測定してサーバに送り、どれくらいの濃度になっているのか調べてみました。

今回利用したセンサーはMH-Z19C (Amazonで3,300円)というもので、過去に別件で使ったESP-WROOM-02とFTDI社のUSBシリアル変換器(AE-UM232R)をブレッドボード上で結線して回路を組みました。プログラムの作成と書き込みのための開発ツールとしては前回同様、Arduinoを使用しました。センサーからの測定値はPWM端子から取得し、センサーに必要な5Vの電源はAE-UM232RのVccから供給しました。下の画像が結線が終わった状態の様子ですが、センサーのピンが微妙にブレッドボードの穴の位置に合わないようでしたので、使用しない方だけブレッドボードにさして、使用する方はオスメスのコネクタケーブルで直接結線しました。

ソフトの方は、20秒ごとにMQTTブローカーに測定したCO2の値をJSONに埋め込んで送る、というものです。最初、Wifiにつながらなくて何回も初期化に失敗したのですが、ルーターの設定をいろいろいじったらつながるようになりました。ソースコードは以下です。センサー値を送るためのPUBLISHのみ、行っており、SUBSCRIBEは今のところ行っていません。

#include <Arduino_JSON.h>

#include <ESP.h>
#include <ESP8266WiFi.h>

#include <PubSubClient.h>

#define PWMPIN  (12)

// WiFi Config
static const char *SSID = "SSID";
static const char *PASSWORD = "WIFIパスワード";

// MQTT Config
static const char *MQTT_SERVER = "サーバのIP";
static const uint16_t MQTT_PORT = 1883u;
static const char *MQTT_PUB_TOPIC = "topic name";
static const char *MQTT_USER = "mqtt user";
static const char *MQTT_PASS = "mqtt password";

unsigned long lastConnectionTime = 0; 
const unsigned long postingInterval = 20L * 1000L; // Post data every 20 seconds.

static WiFiClient wifiClient;
static PubSubClient client(wifiClient);

// WIFI
static void setup_wifi() {
  Serial.println(F("Connecting to access point ..."));
  WiFi.begin(SSID, PASSWORD);
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  Serial.println(F("Connected."));
}

// MQTT CLIENT
static void on_mqtt_message_received(const char *topic, byte *payload, unsigned int length) {
  
}

static void setup_mqtt(){
  client.setServer(MQTT_SERVER, MQTT_PORT);
  client.setCallback(on_mqtt_message_received);
}

static void mqtt_connect() {
  String clientId;
  while(!client.connected()) {
    Serial.print(F("Attempting MQTT connection..."));

    clientId = F("ESP8266-");
    clientId += String(random(0xffff), HEX);
    if(client.connect(clientId.c_str(), MQTT_USER, MQTT_PASS)) {
      Serial.println(F("MQTT connected"));
    }
    else {
      Serial.print(F("Connection failed, rc="));
      Serial.print(client.state());
      Serial.println(F(" retry in 5sec."));
      delay(5000);
    }
  }
}

static void publish() {
  JSONVar obj;
  char buf[256];

  obj["value"] = pulseIn(PWMPIN,HIGH,2000000);
  obj["devicename"] = "devicename";
  obj["propname"] = "co2";
  JSON.stringify(obj).toCharArray(buf, sizeof(buf));
  client.publish(MQTT_PUB_TOPIC, buf);
  lastConnectionTime = millis();
}

// PUBLIC API
void setup() {
  pinMode( PWMPIN, INPUT );
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  delay(1000);
  setup_wifi();
  setup_mqtt();
}

void loop() {
  if(!client.connected()) {
    mqtt_connect();
  } else {
    if (millis() - lastConnectionTime > postingInterval) 
    {
      publish();
      Serial.println(F("MQTT data sent"));
    }
  }
  client.loop();
}

サーバ側はmosquitto(MQTTブローカー)と、上記のトピックをSUBSCRIBEしてセンサー値を取得し、OCIのDBに送るプログラムをGoで実装して立ち上げました。OCIのDBへの接続はgo-oci8を使用してます。

DBに蓄積したデータを見える化する部分については、できるだけ楽をしたかったので、OCIのサービスの一つであるOracle Machine Learningのノートブックでグラフ化することにしました。このノートブックはApache Zeppelinがベースとなっているので、DBからのデータ取り込みもSQLを使えば簡単にできます。あと、OML4Pyを使えばpythonでもDBから取り込んだデータを扱えるようですが、今回はSQLのSELECTの結果をそのままノートブックの機能でグラフにしてみました(グラフのボタンを押すだけ)。その結果が以下です。

最初のほうに2つほどピークが表れてますが、これはセンサーに息を吹きかけてみたところです。期待通り上がってますね。このセンサー、高いだけあって、結構敏感に反応してくれるようです。なだらかに減少しているのは、部屋の扉を開けている時間帯です。やっぱり扉を開けるだけでも換気の効果はあるようです。ちなみに、縦軸の値は上にあげたソースのvalueの値をそのまま出してますので、特に単位はありません。

このデバイスと、モバイルバッテリーとテザリング可能なスマホがあれば、どこででも二酸化炭素を計測できるので、機会があれば外出先でも測ってみようと思います。