換気の要否を判断するスマートフォン用二酸化炭素測定機を Flutter と Raspberry PI による iBeacon で自作する。


 ここ一年ほど、新型コロナウィルス感染拡大防止の為に、3密回避が求められておりますが、飛沫は肉眼では見えない(見えるほどの飛沫は、嫌)ので、間接的に判断する手段として、局所的な二酸化炭素濃度計を用いる方法が、一部で利用されています。飛沫は測定できませんが、口や鼻から排出された空気は、飛沫を含んでいる可能性は高いはずです。ご存知の通り、人の息は、二酸化炭素濃度が高くなっているので、これを測定することで換気の要否を間接的に判断し、換気を行うことで飛沫を吸い込むことを回避したいという意図です。

秋葉原を歩いていても、Amazon などでも、最近よく見かけるようになっているので、お気づきの方も多い事と思います。安価なものは、数千円で購入できるので、こんなご時世ですから、一つくらい持っていても無駄にはならないのではないかと思います。

ネットを検索してみても、COVID-19 以前から、二酸化炭素濃度計の記事はたくさん執筆されており、今に始まった事柄ではないのですが、私もこの機会に一つ、二酸化炭素濃度計を持っておこうと、自作してみました。何も難しいことはなく、電源に接続した二酸化炭素センサーをシリアル(UART)でコンピュータに接続して、値を読み取るだけです。Raspberry IP や、M5Stack、Arduino、ESP32 を使用した例が既に多数公開されており、SwitchScience などではキットもあります。私もこれらの記事を元に作成しました。

前置きはこれくらいにしておいて、早々に解説をしていきたいと思います。




1. 構成

 今回私が使用したのは、MH-Z19C という二酸化炭素濃度センサーです。

(1). MH-Z19C : 非分散型赤外線吸収法による空調用・室内空気質用 二酸化炭素センサー

Amazon などでも普通に購入できます。このモジュールは、2021年5月13日現在、MH-Z19、Z19B、Z19C とバージョンが幾つかあるようです。私は最新と思われる Z19C を購入しましたが、Z19B では、非公式に気圧も取得できるようです。 ※Z19C では、該当する値は、気圧とは程遠い値でしたので、仕様変更になっているようです。詳細は、下記ウェブサイトをご参照下さい。


このセンサーのレンジは、400[ppm]からとなっていますが、空気中の二酸化炭素濃度が 400[ppm] を下回る事はないので、実際、これより低い値は必要とされないはずです。


そして、このセンサーを動かすプログラムも既に有志によって開発されており、GitHub に公開されています。

この Python モジュールは pip で取得できるので、もうほとんど標準のような扱いやすさです。今回は、この組み合わせで作成することにしましたので、SBC(Single Board Computer)も、Raspberry PI を使用しました。Raspberry PI についての説明は必要ないと思われますが、iBeacon として作成しますので、Bluetooth が利用できるモデルをご使用下さい。私は、Raspbery PI Zero WH を使いました。




(2). Raspberry PI Zero WH : Raspberry PI Zero W (ヘッダー付き)

はんだ付けした方が簡潔で、省スペースの気もします。 ※図は、上記 GitHub より外部リンクによる参照

基本的に、MH-Z19C と Raspberry PI との接続に必要となる配線は、4本のみです。電源の(+),(-) と、シリアル(UART)の TX,RX です。




(3). Android Smart Phone

iBeacon を受信するスマートフォンは、今回は Android を前提に説明します。スマートフォン側のアプリケーションは、Flutter を使用して開発しますので、iOS でも大丈夫です。その際は、Android と iOS での開発方法の違いを適ほど調べて読み進めて下さい。ちなみに私は、Google Pixel3 を使用しました。

開発の際は、Bluetooth を使用しているので、AVD によるエミュレータでは、実行できません。スマートフォン実機を接続しての開発・デバッグが必要となります。また、flutter_blue_beacon パッケージを使用しているので、古すぎる Android では利用できないかも知れません。実は Flutter は、新しすぎても問題が発生するので、難しいところですが、今回は、targetSdkVersion:28、minSdkVersion:19 としています。


その他、結線をする為に必要なケーブル、はんだごてや半田、ニッパーやラジオペンチ、Raspberry PI を動かすために必要な電源やUSB ケーブル、Android 開発に必要なケーブルやコンピュータなど、適ほどご用意下さい。




2. Raspberry PI 側の作成

(1). 結線

 センサーと Raspberry PI の結線を作成します。とは言っても、上述の通り、たった4本です。

 (a). Raspberry Pi 側ピンアサイン

  No.4: (+) 5V 出力
  No.6: (-) GND 接地
  No.8: TX RPI側送信
  No.10: RX RPI側受信

 (b). MH-Z19C 側ピンアサイン

  Vin: (+) 5V 電源
  GND: (-) GND 接地
  Rx: センサー側受信
  Tx: センサー側送信

上記 GitHub や、下記 Raspberry PI 財団の公式ウェブサイトなどを参照しながら、結線して下さい。たった四本ですからね、間違えることはないと思います。

メーカーの PDF には、外形図やピン配列図も記載されています。結線が終わったら、間違えていないかよく確認します。


(2). 動作確認

 そうしたら、Raspberry PI 側は、Raspberry Pi OS や Python のインストール、基本設定まで一通り終わらせて下さい。

これらについては、たくさんのウェブサイトで解説されておりますので、解り易いウェブサイトを見つけて、ご参照下さい。公式ウェブサイトは、こちらです。

センサーとの通信に、シリアル(UART) を用いているので、Raspberry PI の No.8,10 pin を UART に使用する設定をします。丁度、こちらの記事が同じ設定を分かり易く説明しています。

また、公式ウェブサイトでは、こちらで説明されています。

設定が終わったら、MH-Z19C 用の Python module を pip を用いてインストールします。ここら辺についても、上記 GitHub に詳しく書かれてありますので、参照しながら作業を進めます。

sudo pip install mh-z19
sudo pip3 install mh-z19

正常にインストールできたら、実行してみます。

pi@raspberrypi:~ $ sudo python -m mh_z19
{'co2': 456}

上手く行けば、動作確認は完了です。 もうこれで、二酸化炭素濃度は計れるようになりました。周囲の二酸化炭素濃度(単位[ppm])は、どれくらいでしょうか。一般に、1000[ppm] を越えると、換気が必要なようです。3密回避の観点からも、空気は滞留していると考えられます。

MH-Z19C センサーは、通電されると白いテープの部分(決して剥がしてはいけません)が、ぴこぴこと点滅します。




3. Android 側の作成

 Android 側では、Flutter によるアプリケーション開発の準備が必要です。下記ウェブサイト(私が書いています)や、他にも優良なウェブサイトが多数、存在しますので、それらを参照して Flutter の開発環境の準備をしてください。

Flutter の開発環境が整ったら、アプリケーションを作成していきます。

Flutter アプリケーションの作成についての説明は、上記「わんぱく Flutter! 第六回 iBeacon で二酸化炭素測定っ!(こちらも私が書いており、こちらのウェブサイトと、このウェブサイトは連携しています。)に記載しています。

また、表示にゲージを使用したバージョン(表題画像参照)も解説しています。「わんぱく Flutter! 第七回 syncfusion_flutter_gauges で二酸化炭素濃度測定機を Modify するっ!

上記ウェブサイトを参照して、アプリケーションの作成が一通り終わったら、今度は、iBeacon の設定を行っていきます。




4. iBeacon の設定

 Android 側の Flutter アプリケーションの準備ができたので、iBeacon の設定を行って、測定値を送信できるようにします。MH-Z19C センサーの動作確認は、確実に終わっているでしょうか。そうしたら、iBeacon に載せて送信する為の Major と Minor 値を作成する Python プログラムを用意します。

export_mhz19.py
import mh_z19

ret=mh_z19.read_all()

CO2="%04x" % ret['co2']
TEMPERATURE="%02x" % ret['temperature']

print("00 %s %s %s" % (TEMPERATURE,CO2[0:2],CO2[2:4]))

これは、測定値を10進数から16進数に変換して、iBeacon 用の Major、Minor 値を出力するものです。トータル32[bit] = 4[byte]出力していますが、上位16[bit]は Major(温度[℃])、下位16[bit]がMinor(二酸化炭素濃度[ppm])です。実行してみましょう。

pi@raspberrypi:~ $ sudo python3 ./export_mhz19.py 
00 1e 03 8d
pi@raspberrypi:~ $ sudo python3 ./export_mhz19.py 
00 1e 03 9f

何度か実行してみて、違う値が返ってきていたら、ひとまず大丈夫でしょう。気になる方は、16進数を読んで正しい温度と二酸化炭素濃度になっているか確認してみて下さい。

今度は、Raspberry PI から iBeacon を送信する為に必要なライブラリと、BlueZ をインストールします。

$ sudo aptitude install libglib2.0-dev libdbus-1-dev libudev-dev libical-dev libreadline6-dev
$ wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.58.tar.xz
$ tar xvJf bluez-5.58.tar.xz
$ cd bluez-5.30
$ ./configure –disable-systemd –enable-library
$ make
$ sudo make install

インストールが終わったら、hciconfig コマンドを実行して、確認します。

pi@raspberrypi:~ $ hciconfig
hci0:   Type: BR/EDR  Bus: UART
    BD Address: B8:27:EB:55:85:CE  ACL MTU: 1021:8  SCO MTU: 64:1
    UP RUNNING 
    RX bytes:5429 acl:0 sco:0 events:543 errors:0
    TX bytes:17741 acl:0 sco:0 commands:533 errors:0

pi@raspberrypi:~ $ 

上記のようなステータスが返ってきていれば、インストールされています。
次は、iBeacon に測定値を載せる shell を作成します。

iBeacon.sh
#!/bin/bash

#New generated UUID: df19e9a4-6f0d-4c4a-a104-e7ddd31a4ab5
UUID="df 19 e9 a4 6f 0d 4c 4a a1 04 e7 dd d3 1a 4a b5"

RSSI="C8 00"

/usr/local/bin/hciconfig hci0 up
/usr/local/bin/hciconfig hci0 noscan
/usr/local/bin/hciconfig hci0 leadv 3

while true
do
        MAJOR_MINOR=`/usr/bin/python3 /home/pi/export_mhz19.py`
        /usr/local/bin/hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 1A 1A FF 4C 00 02 15 ${UUID} ${MAJOR_MINOR} ${RSSI}
        sleep 1
done

UUID は、この二酸化炭素濃度測定機固有の ID で、Flutter のアプリケーション側で設定してある UUID と一致している必要があります。RSSIとは、この Device から 1.0[m] の位置における信号強度[dB]です。hciconfig コマンドで iBeacon の送信準備をし、一秒間隔で先程の "export_mhz19.py" を起動して測定を行い、Major と Minor 値を更新しています。shell プログラムが準備できたら実行します。

pi@raspberrypi:~ $ sudo ./iBeacon.sh
< HCI Command: ogf 0x08, ocf 0x0008, plen 32
  1E 02 01 1A 1A FF 4C 00 02 15 DF 19 E9 A4 6F 0D 4C 4A A1 04 
  E7 DD D3 1A 4A B5 00 21 02 68 C8 00 
> HCI Event: 0x0e plen 4
  01 08 20 00 
< HCI Command: ogf 0x08, ocf 0x0008, plen 32
  1E 02 01 1A 1A FF 4C 00 02 15 DF 19 E9 A4 6F 0D 4C 4A A1 04 
  E7 DD D3 1A 4A B5 00 21 02 68 C8 00 
> HCI Event: 0x0e plen 4
  01 08 20 00 
< HCI Command: ogf 0x08, ocf 0x0008, plen 32
  1E 02 01 1A 1A FF 4C 00 02 15 DF 19 E9 A4 6F 0D 4C 4A A1 04 
  E7 DD D3 1A 4A B5 00 21 02 69 C8 00 
> HCI Event: 0x0e plen 4
  01 08 20 00

と、このように送信値が出力されてくれば、大丈夫です。




5. Android アプリケーションとの連携確認

 「3.Android 側の作成」で作成しておいた Flutter アプリケーションを実行します。問題がなければ、直ぐに気温とCO2濃度、距離が表示されます。

また、上述の表示にゲージを使用したバージョンでは、次のように表示されます。

こちらは、Landscape mode(横向き)にも対応しています。




上手く動いたら、自動起動できるように設定しておきましょう。下記ウェブサイトを参考に設定してみて下さい。

幾つか方法が紹介されていますが、一番簡単な方法は、autostart を利用して、~/.config/lxsession/LXDE-pi/autostart に下記の一行を加えることです。

@sudo /home/pi/iBeacon.sh




6. さいごに

 いかがだったでしょうか。上手く動いたでしょうか。Flutter などは、常に更新されているので、以前の情報を元に作業を進めると、エラーが発生するかも知れません。最新情報を検索して、問題を解決するようにしてみて下さい。ご覧のように、小規模なアプリケーションですが、少し込み入っているのは確かです。

ケーシングまでは間に合わなかったので、即席で持ち歩けるようにしてみました。

実際に使ってみた感じでは、ヒトの息による二酸化炭素濃度は局所的に増加するので、机の上やテーブルの上のような、人が滞在するところに設置して、数値が上がってきたら空気が滞留している証拠、つまり、周辺の飛沫も滞留している可能性が高いので、その箇所の換気をするという使い方が良さそうです。人が滞在する箇所の二酸化炭素濃度が上がらなければ、それは適切に換気されている証拠であり、飛沫の滞留も少ないだろうという予測です。

作業をしているデスクの上で測定しながら、こうして記事を書いていたり、コーディングをしていると、ひと仕事終えた頃には数値が 1000[ppm] を超えていました。また、お昼にうどんを食べているテーブルでは、食べ終わって一息ついていると急激に数値が上昇して、2000[ppm] を超えていました。確かに「ふぅっ」という一息によって、たくさん息が(当然、飛沫も)吐き出されているのです。

電車の中でも測定してみました。私も、非常事態宣言と、まん延防止等重点処置に協力しているので、満員電車には乗りませんでしたが、シートがすべて埋まっていて、立ち乗り客もいる位の車内でも、二酸化炭素濃度の数値は、600、700[ppm] 前後でした。つまり、換気が適切に行われてると言うことでしょう。

部屋全体の二酸化炭素濃度を測定しても、それは今回の飛沫の間接的な検出には向いていないかも知れません。二酸化炭素濃度の測定では、飛沫を検出しているのではないので、あくまでも換気が適切になされているかを判断する指標 と理解しておくことが大切です。

皆さんが、3密を避け、新型コロナウィルスへの感染を回避できることを願っています。

以上