こんにちは!管理部インハウス課の河内です。
早いもので、ヌーラボに入社して既に半年以上が経ちました。フルリモートでの働き方にもすっかり慣れてきた今日この頃ですが、気になってきたのが仕事部屋の空調管理です。特にCO2濃度は身体で直に感じることができないだけに、知らない間に高くなってしまっているかもしれません。放置していると眠くなったり集中力が低下したりと業務にも影響が出てきそうですし、何より健康によくありません。定期的に窓を開け閉めするのも仕事に熱中しているとついうっかり・・・となりがちです。そこで、ヌーラバーらしくDIY精神を発揮して、CO2濃度を教えてくれるシステムを自作してみることにしました。
目次
はじめに
テレワークでの作業環境は、厚生労働省からガイドラインやチェックリストが公開されています。事務所衛生基準規則などの法規制は適用されないようですが、これらの基準と同等の作業環境になるよう改善を図ることが重要と書かれています。
テレワークの適切な導入及び実施の推進のためのガイドライン(厚生労働省)
|
(3) 自宅等でテレワークを行う際の作業環境整備の留意点 |
自宅等においてテレワークを行う際の作業環境を確認するためのチェックリスト【労働者用】(厚生労働省)
| 2 作業環境の明るさや温度等について (2) 作業の際に、窓の開閉や換気設備の活用により、空気の入れ換えを行っているか。 |
システム構成
Raspberry Pi にCO2センサーと温湿度センサーを接続し、取得したデータをGoogle Apps Script (GAS) で作成したWebアプリケーションに送ります。送られたデータはGoogleスプレッドシートに記録し、CO2濃度が閾値を超えた場合にTypetalkに通知します。
さらに、季節や時間帯による値の変化も確認したいのでデータをクラウド上に蓄積してダッシュボードで確認できるようにしてみました。
今回はデバイス購入にお金がかかるため、ソフトウェアはすべて無料で利用できるサービスを選ぶことにしました。
Raspberry Pi側
- Raspberry Pi OS
- Python 3.8.10
クラウド側
- Googleスプレッドシート(CO2濃度および温湿度の記録)
- Google Apps Script(センサーデータのリクエスト受け付け)
- Google データポータル(データ可視化のためのダッシュボード)
- Typetalk フリープラン(ボットによるユーザーへの通知)
システム要件
システム要件を検討します。
- 換気を促すため、CO2濃度が閾値を超えたらTypetalkに通知します。
- 窓を閉めるタイミングを掴みやすいように、CO2濃度が閾値を下回ったときもTypetalkに通知します。
- CO2濃度の閾値はスプレッドシートで設定できるようにします。
- 熱中症を防ぐため、部屋の温度と湿度も合わせて通知します。
- 温度、湿度、CO2濃度を10分おきに Googleスプレッドシートに記録します。
- 環境の変化を後で確認できるようにするため、スプレッドシートに記録したデータをグラフ化してダッシュボードに表示します。
ハードウェアの組み立て
作業環境を測定するための機器を組み立てます。以下のものを揃えました。Amazonなどで買えます。
- Raspberry Pi 4 Model B 8GB
- 温湿度センサー DHT22
- CO2センサーモジュール MH-Z19C
- ブレッドボードとジャンブワイヤ
CO2センサーにはいくつかの方式があるのですが、今回は外部環境の影響を受けにくいとされているNDIR方式のものを採用しました。
配線図はこのようになります。
実際に配線したものがこちらです。CO2センサーと温湿度センサーはブレッドボードに配置し、ジャンプワイヤで繋ぎます。CO2センサーとブレッドボードのピン間隔が微妙に合わなかったので、ブレッドボードを2枚用意しまたぐように配置しました。
ソフトウェアの開発(Raspberry Pi)
必要なライブラリ
温湿度センサーから値を取り出すために Adafruit_CircuitPython_DHT ライブラリを使用しました。
こちらのページを参考に、ダウンロード、インストールを行います。
https://circuitpython.readthedocs.io/projects/dht/en/latest/index.html#
$ pip install adafruit-circuitpython-dht
CO2センサーから値を取り出すために、mh-z19ライブラリを使用しました。
こちらのページを参考にインストールします。
https://github.com/UedaTakeyuki/mh-z19
$ pip install mh-z19
GASにPOSTリクエストを送るため、requests を使用します。
$ pip install requests
CO2濃度の測定
CO2濃度を測定するコードを書きます。
mh_z19.read()の戻り値から、CO2濃度を取り出します。
# co2.py
import mh_z19
def get_co2():
r = mh_z19.read()
return r['co2']
温度と湿度の測定
温度と湿度を測定するコードを書きます。
動作を確認したところ、センサーがうまく値を取得できずエラーになることがあったので、10回呼び出しを行い中央値を採用するようにしました。
今回使用した温湿度センサーDHT22はサンプリング周期が2秒以上のため、2秒間隔でループします。
# hygrothermo.py
import time
import board
import adafruit_dht
import statistics
import math
import sys
def get_hygrothermo():
dhtDevice = adafruit_dht.DHT22(board.D23)
temperature = []
humidity = []
INTERVAL = 2.0
for i in range(10):
try:
temperature.append(dhtDevice.temperature)
humidity.append(dhtDevice.humidity)
except RuntimeError as error:
print(error.args[0], file=sys.stderr)
time.sleep(2.0)
continue
except Exception as error:
dhtDevice.exit()
raise error
time.sleep(INTERVAL)
# 中央値を求める
temperature_median = statistics.median(temperature)
humidity_median = statistics.median(humidity)
return temperature_median, humidity_median
GASにPOSTリクエストを送る
取り出したCO2濃度と温湿度をJSON形式にして、GASにPOSTリクエストを送ります。GASのURLは、今回は環境変数 ECOMONITOR_URL から取得することにしました。
# ecomonitor.py
import json
import requests
import datetime
import os
from hygrothermo import get_hygrothermo
from co2 import get_co2
def make_json(temperature, humidity, co2):
dt = datetime.datetime.now()
date = dt.isoformat()
return json.dumps({
"date":date,
"temperature": temperature,
"humidity": humidity,
"co2": co2
})
def post_data():
# 環境変数 ECOMONITOR_URL からGAS WebアプリケーションのURLを取り出す。
url = os.getenv('ECOMONITOR_URL')
temperature, humidity = get_hygrothermo()
co2 = get_co2()
response = requests.post(
url,
make_json(temperature, humidity, co2),
headers={'Content-Type': 'application/json'}
)
if __name__=='__main__':
post_data()
cronの設定
定期実行するために crontab を設定します。
$ crontab -e
10分おきにスクリプトを実行するよう設定します(毎時0分、10分…50分)
ECOMONITOR_URL=https://script.google.com/macros/s/XXXXXXXXXXXXX/exec0,10,20,30,40,50 * * * * sudo -E python "ecomonitor.pyの格納されているパス" > /var/log/ecomonitor.log 2>&1
ソフトウェアの開発(Google Apps Script)
Typetalk ボットの作成
Typetalk ボットを作成します。ボットはTypetalkのトピック設定画面で簡単に作ることができます。Typetalk Token と、メッセージの取得と投稿のURLを後で使いますのでメモしておきます。
サーバー側のパラメータ設定
サーバー側の設定は、Google スプレッドシートの Setting シートで行えるようにします。
以下のようにパラメータを決めます。
| パラメータ | 設定値 |
| topic_url | メッセージを送るTypetalkトピックのURLを指定します。 |
| token | Typetalk Token を指定します。 |
| mention | 特定のユーザーにメッセージを送りたい場合、そのユーザーのユニークIDを指定します。 |
| send_message | Yesの場合、CO2濃度が閾値を超えた場合にメッセージを送ります。Yes以外の場合は送りません。 |
| co2_high | この値を超えるとCO2濃度を警告します。 |
| co2_low | この値を下回ったときに、お知らせします。 |
スプレッドシートにはこのように書きます。
スクリプトの作成
受け取ったセンサーデータを、Google SpreadSheet に記録し Typetalkへポストするスクリプトを書きます。
function doPost(e) {
var p = JSON.parse(e.postData.contents);
ecomonitor(p);
}
function ecomonitor(p) {
let conf = new Config();
let ss = SpreadsheetApp.getActiveSpreadsheet();
let sheet = ss.getSheetByName('monitor');
sheet.appendRow([p.date, p.temperature, p.humidity, p.co2]);
notifyTypetalk(conf, p)
}
function notifyTypetalk(conf, p) {
let topicURL = conf.getParam('topic_url');
let token = conf.getParam('token');
let mention = conf.getParam('mention');
let sendMessage = conf.getParam('send_message');
let co2_high = conf.getParam('co2_high');
let co2_low = conf.getParam('co2_low');
let last_co2 = conf.getParam('last_co2');
let message = null;
if (sendMessage !== 'Yes') {
return;
}
if (p.co2 >= co2_high && last_co2 < co2_high) {
// CO2濃度が上限を超え、前回のCO2濃度が上限を超えていなかった場合にメッセージを通知する
message = Utilities.formatString(
"こんにちは!衛生委員です。\n" +
"なんと!お部屋のCO2濃度が %s ppm になっちゃってます。ちょーまずい!激ヤバです!\n" +
"適正なCO2濃度の目安は1000ppm以下みたいなので、こまめに換気しましょうね!\n" +
"現在の気温は %s ℃、湿度は %s % です。",
p.co2,
p.temperature,
p.humidity
);
}
if (p.co2 <= co2_low && last_co2 > co2_low) {
// CO2濃度が下限を下回り、前回のCO2濃度が下限を超えていた場合にメッセージを通知する
message = Utilities.formatString(
"こんにちは!衛生委員です。\nわーい、お部屋のCO2濃度が %s ppm に戻ったみたいですよ。よかったですね!\n" +
"適正なCO2濃度の目安は1000ppm以下だそうです。これからもこまめな換気を心がけてくださいね!\n" +
"現在の気温は %s ℃、湿度は %s % です。",
p.co2,
p.temperature,
p.humidity
);
}
conf.setParam('last_co2', p.co2);
if (message) {
let data = {
'message' : mention + ' ' + message
};
let options = {
'method' : 'post',
'contentType': 'application/x-www-form-urlencoded',
'payload' : data
};
let url = topicURL + '?typetalkToken=' + token;
const result = UrlFetchApp.fetch(url, options);
if(result.getResponseCode() !== 200) {
throw new Error(result.getContentText());
}
}
}
// スプレッドシートから設定情報を読み書きする。
class Config {
constructor() {
let ss = SpreadsheetApp.getActiveSpreadsheet();
this.sheet = ss.getSheetByName('Settings');
this.colno_key = 1;
this.colno_value = 2;
this.rowno = 2
}
getParam(key) {
let value = ''
for (let i = this.rowno; i <= this.sheet.getLastRow(); i++) {
if (key == this.sheet.getRange(i, this.colno_key).getValue()) {
value = this.sheet.getRange(i, this.colno_value).getValue();
break;
}
}
return value;
}
setParam(key, value) {
for (let i = this.rowno; i <= this.sheet.getLastRow(); i++) {
if (key == this.sheet.getRange(i, this.colno_key).getValue()) {
value = this.sheet.getRange(i, this.colno_value).setValue(value);
break;
}
}
}
}
Google スプレッドシートに記録
受け取った値をスプレッドシートに記録します。
| カラム名 | 説明 |
| date | 値を取得した日時 |
| temperature | 気温(℃) |
| humidity | 湿度(%) |
| co2 | CO2濃度(ppm) |
このように記録されていきます。
ダッシュボードの作成
Googleデータポータルにログインしてダッシュボードを作成します。Googleデータポータルはクラウドベースの無料で使用できるBIツールで、様々なデータソースからデータをビジュアライズして表示することができます。
https://datastudio.google.com/
今回はデータを記録したGoogleスプレッドシートをデータソースとして選択します。グラフや表を選び、どのような軸で表示するか設定していきます。
完成したダッシュボードです。
上段に現在の気温、湿度、CO2濃度を表示し、下に値の推移をグラフ表示します。表示する期間は右上のドロップダウンリストで切り替えることができます。いまは湿度とCO2濃度は適正値ですが、換気中のせいか少し気温が高めになっていますね。
とある日のCO2濃度の推移です。この日はこまめに換気を心がけたせいか、適正値と言われる400ppm〜1000ppmの間をキープできたようです。仕事が終わって換気をやめたとたん、急激に濃度が上がっているのがわかります。
Typetalkに通知
警告値を超えたらTypetalkに通知します。現在の気温と湿度も合わせて通知するようにしています。換気を終了するタイミングを知るため、CO2濃度が下がってきたときも通知します。
おわりに
いかがでしょうか?
Typetalkではとても簡単にボットを作ることができます。今回は IoTデバイスと連携させたシステムを作ってみました。ちょっとした身の回りの環境も数値化して記録してみることで、色々な気づきがあります。電子工作もブレッドボードを使えばはんだづけは不要ですし、データの蓄積やビジュアル化も無料で便利なサービスが色々出ていますので、興味を持たれた方は試してみてはいかがでしょうか。
無料で使えるフリープランでもボットは作れますので、とりあえず試してみたい方はこちらから!
https://www.typetalk.com/
Typetalkのボットの作り方をさらに詳しく知りたい方は、こちらをご覧ください。
https://www.typetalk.com/ja/blog/post-a-message-to-typetalk-using-a-bot/
それでは、安全で快適なテレワークを楽しみましょう!










