LPWA(Sigfox)で二酸化炭素濃度を測ってみた

技術情報

2020年5月25日の緊急事態宣言解除、6月11日東京アラートの解除を受け、日々の生活に戻りつつあるものの、同時に感染者数も増加し、「正解のないニューノーマル」にどう立ち向かえば良いのか、悩みは尽きません。
オフィスや商業施設も活気を戻しているものの、本当にこのままで良いのだろうか?という疑問も増すばかりです。何に注意をしていけばいいのでしょうか?
「三密」状態を避ける必要はあるものの、コロナウィルスを見える化することなんてできません。でも、空気環境を測定するセンサは世の中に存在しています。空気環境を測定することにより、身近な場面での、コロナ感染拡大を抑えることができるのではないでしょうか?
ということで、ここでは、室内の二酸化炭素濃度について、簡単な測定方法と考察を行ってみます。

推奨される換気の方法

厚生労働省からは、3月30日『商業施設等における「換気の悪い密閉空間」を改善するための換気について』、良好な換気状態の基準として二酸化炭素濃度(CO2濃度)1,000ppm以下と提示されています。

technical0817_02.png

この換気方法の推奨に当たっては、いくつかの注意点もあるため、誤解を招かないようこちらの全文を読まれることをお奨めします。

CO2センサで測ってみる

ここでは、CO2センサ(MH-Z19)とSigfox Shield for Arduino(以降UnaShield)を使って、CO2濃度の計測とグラフ化までを試してみます。

準備するもの

項目イメージドキュメント
CO2センサ(MH-Z19) technical0817_02.png User’s Manual
Arduino Uno technical0817_02.png
Sigfox Shield for Arduino (UnaShield) technical0817_02.png Hardware Guide / 無償回線利用方法
ピンヘッダ x2 image.png
ジャンパワイヤ x4 image.png

UnaShieldとMH-Z19を接続

MH-Z19にピンソケットをはんだ付けしたら、Arduinoからの5V出力をMH-Z19のV+/V-ピンに、ArduinoのDigital I/OをMH-Z19のTx/Rxピンに接続します。

technical0817_08.png

サンプルコード

10分間隔でMH-Z19からCO2濃度を取得し、Sigfoxで送信するサンプルコードになります。

mhz19.ino.c
#include  
#define txPin_mhz 10
#define rxPin_mhz 11
#define REQUEST_SIZE 8 //Byte size for request to MH-Z19
#define RESPONSE_SIZE 9 //Byte size of response from MH-Z19

uint8_t
get_co2[REQUEST_SIZE] = {0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t abc_on[REQUEST_SIZE] = {0xff, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00}; uint8_t abc_off[REQUEST_SIZE] = {0xff, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00}; void setup() { Serial.begin(9600); // Arduinoハードウェアシリアルを起動(Arduino <-> PC) Serial.println("==========================="); Serial.println("Sigfox UnaShield CO2(MH-Z19B) Sample"); Serial.println("==========================="); //mhzCommand(abc_off, NULL); //Auto Caliburation: off } void loop() { int co2 = mhzGetCo2(); Serial.print("CO2: "); Serial.println(co2); if (co2 != 0) { sigSendMessage(String(co2, HEX)); } delay(600000); } int mhzGetCo2() { int co2 = 0; uint8_t buf[RESPONSE_SIZE]; mhzCommand(get_co2, buf); if (buf[0] == 0xff && buf[1] == 0x86 && mhzChecksum(buf) == buf[RESPONSE_SIZE-1]) { co2 = buf[2] * 256 + buf[3]; } return co2; } void mhzCommand(uint8_t cmd[], uint8_t *response) { SoftwareSerial ss_mhz(rxPin_mhz, txPin_mhz); ss_mhz.begin(9600); ss_mhz.write(cmd, REQUEST_SIZE); ss_mhz.write(mhzChecksum(cmd)); ss_mhz.flush(); if (response != NULL) { int i = 0; while (ss_mhz.available() <= span=""> 0) { if (++i > 100) { Serial.println("no response."); return; } yield(); delay(10); } ss_mhz.readBytes(response, RESPONSE_SIZE); } } uint8_t mhzChecksum(uint8_t com[]) { uint8_t sum = 0x00; for (int i = 1; i < REQUEST_SIZE; i++) { sum += com[i]; } sum = 0xff - sum + 0x01; return sum; } void sigSendMessage(String s) { String msg = sigSFMessage(s); SoftwareSerial ss_sig(5, 4); pinMode(5, INPUT); pinMode(4, OUTPUT); ss_sig.begin(9600); delay(100); ss_sig.print(msg); Serial.println(msg); String res = ""; char output; while(ss_sig.available()) { res += (char)ss_sig.read(); delay(10); } if (res.length()) Serial.println(res); } String sigSFMessage(String msg) { String sfMessage = ""; if(msg.length() % 2 == 1) { sfMessage = "0" + msg; } else { sfMessage = msg; } return "AT$SF=" + sfMessage + "\r"; }

MH-Z19のユーザーマニュアルを見ると、下記のように[0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79(CRC)]がCO2取得の要求コマンドとなっています。

technical0817_09.png

また、オートキャリブレーション機能があり、電源投入後から24時間毎に、その間の最低値を400ppmにみなしてくれる機能のようです。オフィスなどではONにすることが推奨されているようですので、利用環境に合わせて設定を変えた方が良さそうです。

technical0817_10.png

IoT Agency Platformでグラフ化

先程のサンプルスケッチではCO2濃度を2バイトでSigfoxクラウドに送信しています。SigfoxクラウドからCallback機能を使って、IoT Agency Platformに転送することにより、グラフ化が可能です。

IoT Agency Platformへのコールバック

IoT Agency Platform (https://try.iotagency.sigfox.com )にログイン後、APIメニューを開けます。

technical0817_11.png

  1. Manage Tokensをクリックして、まだトークンがない場合は、"Create New"ボタンをクリックし、新規にトークンを作成してください。既にトークンがある場合は、そのトークンを使えます。
  2. UPLINKをクリックし、表示されているUrl patternUse HTTP MethodHeadersContent TypeBodyをSigfoxクラウドのCallback設定に入力します。

Sigfoxクラウド上でのCallback設定方法は、こちらを参考にしてください。
これで、CO2計測後のメッセージが送信されると、そのデータがIoT Agency Platformまで転送されます。転送されると、自動的にDeviceが登録され、そのメッセージを確認することもできます。

データをパースする

Sigfoxメッセージ上のデータは、CO2濃度を16進表記で表した文字列です。グラフ化するためには、この文字列をパースする必要があります。
Parsersメニューを選択します。ここでパーサをJavascriptでコーディングするのですが、Parserは、このプラットフォーム上で全共有となるため、画面レスポンスが悪いことは覚悟しておいてください。

technical0817_12.png

ここで、下記コードを入力すれば、Sigfoxメッセージ上のCO2濃度をco2変数として扱えるようになります。

mhz19.java
var payload, co2, parsedData = [], obj = {};
co2 = parseInt(payload.slice(0, 4), 16);

// Store objects in parsedData array
obj = {};
obj.key = 'CO2';
obj.value = co2;
obj.type = 'number';
obj.unit = 'ppm';
parsedData.push(obj);

//console.log(parsedData);
return parsedData;

既にMHZ19という名前("parserId": "5f0082144ab1680018d4a525")のパーサーを作っていますので、それを使ってもらってもかまいません。

カテゴリを作る

Categoriesメニューを開いて、適当にカテゴリを作っておいてください。今回は、Enviromentというカテゴリを作りました。

technical0817_13.png

Deviceを編集する

Devicesメニューを開けると、認識済みのデバイスが一覧表示されています。対象のデバイスIDのEdit deviceボタンを選択し、デバイス名(Name)と使用するパーサ(Parser)、カテゴリ(Category)を選択してください。

technical0817_14.png

ダッシュボードでグラフ化

New dashboardメニューを開き、Add widgetボタンをクリックし、線(Line)グラフを作成します。CategoriesやDevicesは、先ほど設定したものを選択します。また、Parserが設定されていれば、Keys項目からCO2を選択できるようになります。

technical0817_15.png

1日のCO2濃度変化

自宅のリビングルームにCO2センサを設置し、その変化を見てみました。

technical0817_16.png

我が家は窓を開けていることが多いので、平均的には500~600ppm程度を推移していましたが、エアコンをつけた後、CO2濃度は上昇し、3時間程度で1,000ppmを超えてしまいました。その後の換気効果が目に見えてでていました。
「密閉空間」(換気の悪い密閉空間)であっても、空気環境を測定し、時間帯、場所による変化を把握し、人の密集を避ける手立てをとることによって、「密集場所」を作らないようにすることから始めてみてもいいかもしれませんね。