🪟

リモートワークを快適にする換気システムを作ってみた

2022/04/15に公開

はじめに

リモートワーク中、頭がぼーっとするときはありませんか?
そんなときはもしかすると、部屋の二酸化炭素濃度が高いかもしれません。

この記事では、Raspberry Pi(以降、RPi)、CO2センサ、Nature Remo 3、および Amazon Echo(以降、Echo)を用いて、リモートワークを快適にする換気システムを作る方法について、解説します。

この換気システムを使用すれば、換気が必要なタイミング・換気が完了したタイミングを Echo に教えてもらうことができます。

換気システムの全体像

換気システムの全体像

換気システムの機能

  • CO2センサから取得した二酸化炭素濃度の値、Nature Remo 3 から取得した温度・湿度・照度の値(以降、各センサの値)をスプレッドシートに記録
  • スプレッドシートに記録した各センサの値をグラフ化し、Web に公開
  • 現在の各センサの値を Echo に教えてもらう
  • 換気が必要なタイミングを Echo に教えてもらう
  • 換気が完了したタイミングを Echo に教えてもらう

使用機材

  • RPi:Raspberry Pi 3 Model B
  • CO2センサ:MH-Z19B
  • 温度・湿度・照度センサ:Nature Remo 3

各機能の実装

RPi に接続した CO2センサから二酸化炭素濃度を取得

RPi と CO2センサをジャンパワイヤで接続

シリアル通信を有効化

  • $ sudo raspi-config を実行して下記の順番で画面を操作し、設定を変更
    1. Interfacing Options
    2. Serial Port
    3. Would you like a login shell to be accessible over serial ? → <No>
    4. Would you like the serial port hardware to be enabled ? → <Yes>
    5. The serial login shell is disabled The serial interface is enabled → <Ok>
  • $ sudo reboot を実行し、設定を反映

必要なライブラリをインストール

  • $ pip install mh-z19 を実行
  • 参考:mh-z19

動作確認

  • 下記の get_co2.py を実行
    • /home/pi/.virtualenvs/py3/bin/python は適宜変更(例:$ which python の実行結果など)
    get_co2.py
      import subprocess
      import json
    
    
      def get_co2():
          cmd = ['sudo', '/home/pi/.virtualenvs/py3/bin/python', '-m', 'mh_z19']
          res = subprocess.check_output(cmd).decode()
          co2 = json.loads(res)['co2']
    
          return co2
    
    
      if __name__ == '__main__':
          co2 = get_co2()
          print(co2)
    

CO2センサから取得される二酸化炭素濃度のキャリブレーション

  • 下記を実行し、CO2センサを屋外(400ppm)に20分間放置
      $ python
      >>> import mh_z19
      >>> mh_z19.zero_point_calibration()
    

NatureRemoAPI を叩いて Nature Remo 3 から温度・湿度・照度を取得

必要なライブラリをインストール

NatureRemoAPI を利用するためのアクセストークンを発行

  • Nature にアクセスし、アクセストークンを発行

動作確認

  • 下記の get_natureremo_data.py を実行
    get_natureremo_data.py
      from remo import NatureRemoAPI
      import json
    
    
      def get_natureremo_data():
          api = NatureRemoAPI(json.load(open('./config/setting.json'))['natureremo_token'])
          device = api.get_devices()[0]
          te = device.newest_events['te'].val  # 温度
          hu = device.newest_events['hu'].val  # 湿度
          il = device.newest_events['il'].val  # 照度
    
          return device, te, hu, il
    
      if __name__ == '__main__':
          device, te, hu, il = get_natureremo_data()
          print(device)
          print(te, hu, il)
    
    setting.json
      {
          "natureremo_token": "**********"
      }
    

各センサの値を Google Apps Script を用いてスプレッドシートに記録

各センサの値を記録・グラフ化するためのスプレッドシートを作成

  • 参考:Raspberry PiからGoogleスプレッドシートにCO2濃度データを保存する
  • シートの追加
    • log
      • センサデータの記録用

      • A列:時刻、B列:二酸化炭素濃度、C列:温度、D列:湿度、E列:照度

      • 完成後の表示例:

        A B C D E
        1 2022/04/12 8:51:59 559 26.6 51 137
    • filter
      • log から最新900件(5時間分)の各センサの値を取得

      • A1 に下記を記入

        =query(
          log!A:E,
          "SELECT A, B, C, D, E ORDER BY A DESC LIMIT 900 FORMAT A 'hh:mm'"
        )
        
      • 完成後の表示例:

        A B C D E
        1 08:51 559 26.6 51 137
    • graph
      • filter を用いて各センサの値をグラフ化
      • A1 に下記を記入
        =CONCATENATE(
          TO_TEXT(filter!A:A), ", ",
          TO_TEXT(filter!B:B), "ppm, ",
          TO_TEXT(filter!C:C), "℃, ",
          TO_TEXT(filter!D:D), "%, ",
          TO_TEXT(filter!E:E), "lx "
        )
        
      • A2 にグラフを挿入
        • グラフをクリックして、グラフエディタを編集
          • 設定
            • 以下の画像を参考に各項目を記入
            • データ範囲:filter!A1:A900,filter!B1:B900,filter!C1:C900,filter!D1:D900,filter!E1:E900
              グラフエディタ_設定
          • カスタマイズ
            • 横軸の軸ラベルの順序を逆にするにチェック
            • 好みのデザインにカスタマイズ
      • 完成後の表示例:
        グラフ

各センサの値をスプレッドシートへ記録する方法

  • GAS(Google Apps Script)を利用
    • GAS は、作成したプログラムを Webアプリとして公開可能
  • 記録方法の流れ
    • RPi が GAS にセンサデータを POST
    • GAS が各センサの値をスプレッドシートに記録

RPi から POSTされた各センサの値を受け取り、スプレッドシートへ記録する GAS のプログラムを作成

  • 参考:Raspberry PiからGoogleスプレッドシートにCO2濃度データを保存する
  • GAS のプログラムを書くためのエディタの開き方
    • スプレッドシートを開く
    • 拡張機能、Apps Script の順にクリック
  • GAS のプログラム(sensor_data.gs)を作成
    sensor_data.gs
    function doPost(e) {
        // パラメータの値を取得
        var co2 = e.parameter.co2;  // co2
        var te = e.parameter.te;  // 温度
        var hu = e.parameter.hu;  // 湿度
        var il = e.parameter.il;  // 照度
    
        // 保存先のシートを取得
        var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('log');
    
        // シートに各センサの値を追記
        sheet.appendRow([new Date(), co2, te, hu, il]);
    }
    

GAS のプログラムを Webアプリとして公開

  • デプロイ、新しいデプロイの順にクリック
    • 設定する項目
      • 新しい説明文:任意
      • 次のユーザとして実行:自分
      • アクセスできるユーザ:全員
    • 許可を確認を選択し、自分のアカウントをクリック
  • 「このアプリは確認されていません」の画面における操作
    • 左下の詳細をクリック
    • (安全ではないページ)に移動をクリック
    • 許可をクリック
  • Webアプリの URL をコピー

RPi が GAS にセンサデータを POSTするプログラムの作成

import schedule
import time
from get_co2 import get_co2
from get_natureremo_data import get_natureremo_data
import requests
import json


def logging_sensor_data():
    co2 = get_co2()
    device, te, hu, il = get_natureremo_data()

    response = requests.post(
        url=json.load(open('./config/setting.json'))['apps_script_url'],
        data={
            'co2': f'{co2}',
            'te': f'{te}',
            'hu': f'{hu}',
            'il': f'{il}'
        }
    )


def main():
    schedule.every(1).minutes.do(logging_sensor_data)

    while True:
        schedule.run_pending()
        time.sleep(1)


if __name__ == '__main__':
    main()
setting.json
{
    "apps_script_url": "**********"
}

動作確認

  • 上記の logging_sensor_data.py を実行
  • スプレッドシートが更新されるか確認

スプレッドシートの graph を Web に公開

  • ファイル、Web に公開の順にクリック

Voiceflow で Alexaスキルを作成し、現在の各センサの値を Echo に教えてもらう

プロジェクトを作成

Alexaスキルを作成

  • 以下の画像を参考に、Steps から必要なカードを選択して配置
    Voiceflow_steps
    • Speak カード
      • Speak sensor data
      • Speak err msg
    • Google Sheets カード
      • Get sensor data
  • 現在の各センサの値を取得するためのカード(Get sensor data)を編集
    • Retrieve Data を選択
    • ユーザ、スプレッドシート、シート(filter)を選択
    • 以下の画像を参考に、With Settings と Mapping Output を編集
      With_Settings
      Mapping_Output
  • Start カードをクリックしてテストを実行し、動作を確認

Alexaスキルをデプロイ

  • 右上の Upload to Alexa をクリックし、その後、Connect Amazon をクリック
  • Alexa開発者コンソールにログインし、Alexaシミュレータで Alexaスキルの動作を確認
    • Alexaシミュレータにセンサーと入力

動作確認

  • Echo に「エコー、センサー」と話しかけてみる
    • 応答例:現在の時刻は14時25分、二酸化炭素濃度は583ppm、温度は26℃、湿度は39%、光量は28lxです。

おまけ

  • 私の場合、この機能は定型アクションに組み込んで使っていたりします。
    定型アクション

alexa-remote-control を用い、換気が必要なタイミングを Echo に教えてもらう

換気が必要なタイミング

alexa-remote-control の設定

  • 必要なライブラリをインストール

    • $ sudo apt install jq を実行
  • alexa-remote-control をダウンロード

    • $ wget https://raw.githubusercontent.com/thorsten-gehrig/alexa-remote-control/master/alexa_remote_control.sh を実行
  • alexa_remote_control.sh を編集

    • 編集項目
      SET_EMAIL='**********'
      SET_PASSWORD='**********'
      SET_LANGUAGE='ja-JP'
      SET_TTS_LOCALE='ja-JP'
      SET_AMAZON='amazon.co.jp'
      SET_ALEXA='alexa.amazon.co.jp'
      
  • alexa_remote_control.sh の権限を変更

    • $ sudo chmod 777 alexa_remote_control.sh を実行

おまけ(LINEへ通知するための設定)

  • LINE Notify にアクセス
  • トークンを発行する、1:1でLINE Notifyから通知を受け取るの順にクリック

動作確認

  • 下記の notify.py を実行
    notify.py
    import subprocess
    import json
    import requests
    
    
    def let_echo_speak(msg):
        cmd = ['./sh/alexa_remote_control.sh', '-e', f'speak:{msg}']
        res = subprocess.check_output(cmd).decode()
        send_line_notify(msg=msg)
    
    
    def send_line_notify(msg):
        line_notify_token = json.load(open('./config/setting.json'))['line_notify_token']
        line_notify_api = 'https://notify-api.line.me/api/notify'
        headers = {'Authorization': f'Bearer {line_notify_token}'}
        data = {'message': msg}
        requests.post(line_notify_api, headers=headers, data=data)
    
    
    if __name__ == '__main__':
        let_echo_speak(msg='テスト')
    
    setting.json
    {
        "line_notify_token": "**********"
    }
    

Node-RED を用いて RPi と Echo を連携し、換気が完了したタイミングを Echo に教えてもらう

換気が完了したタイミング

  • Echo に「換気 ON」と伝えてから、初めてCO2センサから取得される二酸化炭素濃度の値が500を下回った場合

Node-RED の起動

  • 参考:Raspberry Piで実行する
  • Node-RED のスクリプトをダウンロード
    • $ bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered) を実行
  • Node-RED のサービスの起動時実行を有効化
    • $ sudo systemctl enable nodered.service を実行
  • Node-RED のサービスを起動
    • $ node-red-start を実行
  • ログに表示される URL(例:http://192.168.10.108:1880/)に PC からアクセス

Node-RED を用いて RPi と Echo を連携

  • Node-RED Alexa Home Skill Bridge の設定
    • Node-RED Alexa Home Skill Bridge にアクセスし、Node-RED Alexa Home Skill Bridge のアカウントを作成
    • Devices、Add Device の順にクリックし、下記の項目を設定
      Name:換気
      Description:換気システム
      Actions:On, Off
      Application Type:SWITCH
      
  • Node-RED に Node-RED Alexa Home Skill Bridge を追加
  • alexa-home ノードの設定
    • alexa-home ノードの設定を配置し、ダブルクリック
    • Acount の右の編集ボタンをクリックし、Node-RED Alexa Home Skill Bredge のサイトのユーザー名とパスワードを入力して add をクリック
  • Node-RED の Alexaスキルを有効化
    • Node-RED にアクセス
    • 端末を検出をクリック

Node-RED のフローを作成

動作確認

  • 下記の server.py を実行
server.py
from flask import Flask, Response
import json
from notify import let_echo_speak
from get_co2 import get_co2
import time

app = Flask(__name__)
ventilation = False


@app.route('/ventilate')
def ventilate():
    global ventilation
    ventilation = True
    check_ventilation_is_complete()
    return Response(response=json.dumps({'message': 'Start Ventilation!'}), status=200)


@app.route('/stop')
def stop():
    global ventilation
    ventilation = False
    return Response(response=json.dumps({'message': 'Stop Ventilation!'}), status=200)


def check_ventilation_is_complete():
    let_echo_speak(msg='換気を開始します。')

    while True:
        co2 = get_co2()
        if int(co2) <= 500:
            let_echo_speak(msg='換気が完了しました。')
            break
        elif not ventilation:
            let_echo_speak(msg='換気を終了します。')
            break
        else:
            time.sleep(60)


def main():
    app.run(host='0.0.0.0', port=50080)


if __name__ == '__main__':
    main()
  • Echo に「換気ON」と話しかけてみて、Echo から「換気を開始します」と応答があるか確認

RPi の低消費電力化(Wi-Fi 以外の不要な機能を停止)

オンボードLED の無効化 (RPi Model 3B の場合)

  • /boot/config.txt に下記を追記後、再起動
    • Act LED:データアクセス時に緑色に点滅
    • Pwr LED:起動後常に赤色に点灯
      config.txt
      # Disable the Activity LED
      dtparam=act_led_trigger=none,act_led_activelow=on
      # Disable the PWR LED
      dtparam=pwr_led_trigger=none,pwr_led_activelow=on
      

HDMI の無効化

  • /etc/rc.local に下記を追記後、再起動
    rc.loal
    # Disable HDMI
    tvservice --off
    

Bluetooth の無効化 (RPi Model 3B の場合)

  • /boot/config.txt に下記を追記後、再起動
    config.txt
    # Disable Bluetooth
    toverlay=disable-bt
    

USBコントローラの無効化

  • /etc/rc.local に下記を追記後、再起動
    rc.loal
    # Disable USB
    echo '1-1' |sudo tee /sys/bus/usb/drivers/usb/unbind
    

おわりに

この記事では、RPi、CO2センサ、Nature Remo 3、および Echo を用いて、リモートワークを快適にする換気システムを作る方法について解説しました。

皆さんのリモートワークが、少しでも快適になればと思います。

Discussion