【電子工作】Raspberry Piと光センサーでトイレの使用状況をSlack Botに通知してみた

はじめに:Raspberry Pi(ラズパイ)とは?

Raspberry Pi(ラズベリーパイ)とは、内蔵ハードディスクなどを搭載しない代わりに、電源やSDカードストレージを装着することによって使用できる、シングルボードコンピュータと呼ばれるハードウェアです。2012年に教育用として英国のラズベリーパイ財団によって開発されました。

通称「ラズパイ」と呼ばれるこの小さなボードには、たくさんの可能性が詰まっています。主な特徴は以下の通りです。

  • Linuxを動かすことのできる超小型PCです。
  • OSはmicroSDカードにインストールして使用します。
  • 小型・安価ながらグラフィック機能(GPU)も内蔵され、LANポート、HDMI、USBポートなど、接続できるデバイスは一般的なPCと変わりません。(今回使用するRaspberry Pi 3 Model BはWi-Fi接続、Bluetoothにも対応しています)
  • GPIO(General Purpose Input/Output: 汎用入出力)ピンが搭載されており、センサーや回路を接続して電子工作が可能です。
  • Linuxで動作するため、ApacheやPHPなどをインストールして、電力消費の少ない常時起動のホームサーバーとして利用できます。
  • GPIOを活用し、ホームキットなどのIoT(モノのインターネット)分野で広く活用されています。

(参考記事: ラズベリーパイで何ができる?初心者にやさしい活用法12選 | Workship MAGAZINE


この記事の目的:オフィスのトイレ空き状況をSlackで確認したい

さて、今回このラズパイを使って解決したい問題は、オフィスの「トイレ空き状況」問題です。

私たちのオフィスにはトイレが1つしかなく、各社員は利用のたびにトイレまで出向き、空き状況を調べています。しかし、行ってみたら空いていなかった、ということが頻繁に発生していました。

この「無駄足」をなくすため、自席のPCからトイレの空き状況を確認できる仕組みを開発します。

具体的なアプローチは以下の通りです。

  1. 洗面所にRaspberry Pi(ラズパイ)を設置します。
  2. 光センサーを接続し、トイレの「使用・未使用」を判定します。
    • 電気がついている → 使用中
    • 電気がついていない → 未使用
  3. Raspberry Pi 3は標準でWi-Fiが使用可能なため、社内ネットワークに無線で接続します。
  4. 取得した情報をSlackに通知し、PC上からステータスとして確認できるようにします。

準備するもの(ハードウェアと費用)

今回の開発で必要になった機材は以下の通りです。

  1. PC(セットアップ用)
  2. Raspberry Pi 3 Model B+
  3. microSDカード (8GB以上推奨)
  4. micro USBケーブル
  5. USB ACアダプタ (2.4A推奨。ラズパイ専用品が安心です)
  6. 光センサー (BH1750)
  7. ジャンピングワイヤー / ブレッドボード

Raspberry Pi 3とセンサー類

今回は、これらを準備しました。
最低限必要なもの(本体セット + SD16GB + ACアダプタ)で約10,000円、光センサーやワイヤー類で約2,000円、合計12,000円ほどでした。センサー類を含めると、思ったより費用がかかる印象かもしれません。


ステップ1:ラズパイのOSをセットアップ

まずはラズパイを起動させるため、OSをインストールします。
(今回の最終目的とは少し離れますが、学習も兼ねて一度GUIで起動してみます)

PCでRaspbian(現在はRaspberry Pi OS)というLinuxベースの専用OSをダウンロードします。

Raspbianのダウンロードページ

SDカードをフォーマットし、このOSイメージを書き込みます。
(参考記事: Raspberry Pi 3 Model B+ に Raspbian Stretch Lite をクリーンインストールする手順 – Qiita

microSDカードをラズパイに挿入し、HDMIケーブルと電源を繋ぐと、あっという間に起動できます。

Raspbianのデスクトップ画面

今回は色々入っているイメージをインストールしたため、初めからマインクラフトやOffice代替ソフトなどが入っていました。OSがSDカード駆動でも、意外とサクサク動くことに驚きます。

これで動作確認は完了です。


ステップ2:MacからSSHでラズパイにリモート接続

今回はGUI(デスクトップ画面)で操作することはしません。最終的にラズパイは電源だけを接続した「ヘッドレス」状態で運用するため、MacのターミナルからSSH接続で設定を進めます。普段の仮想サーバー(Vagrantなど)や社内サーバーの設定と特に変わりありません。

(参考記事: MacからRaspberry Pi 3にSSH接続(Wi-Fi) – Qiita

SSH接続設定のイメージ

手順の概要は以下の通りです。

ラズパイ側の設定

  1. SSH接続を許可します。
  2. 無線LAN(Wi-Fi)を接続してIPアドレスを確認します。
  3. IPアドレスが変更されないよう、固定化します(設定ファイルを編集)。

Mac側の設定

  1. ラズパイのIPアドレスへ、社内LAN経由でSSH接続します。
  2. 接続を簡単にするため、公開鍵認証を設定します。

MacのターミナルからSSH接続した画面

無線LAN経由でSSH接続が完了しました。
(今回は使用しませんが、VNC(リモートデスクトップ)も設定しておくと、いざという時にGUI操作ができて便利です)

これで、ラズパイに接続していたモニターやキーボードをすべて外しても問題ありません。ここからはリモート操作で進めていきます。


ステップ3:光センサー(BH1750)を接続しI2Cで制御

Raspberry Pi にあるGPIO(汎用入出力ピン)にジャンピングワイヤーを繋ぎ、ブレッドボード経由で光センサーBH1750を接続します。

(参考記事: Raspberry Pi + BH1750 (I2C接続の照度センサ) – Qiita

ラズパイ側でI2Cモジュールが使用できるよう設定を行います。

接続後、I2Cデバイスのアドレスを確認します。

$ sudo i2cdetect -y 1

i2cdetectの実行結果

アドレス(この場合は 23)が確認できました。
一旦、動作確認のためにシェルスクリプトを作成して実行してみます。

bh1750_sample.sh

シェルスクリプトのコード
シェルスクリプトの実行結果

無事に照度の値を取得できました。


ステップ4:PythonでSlackに通知する仕組みを構築

Pythonを使う理由

Raspberry Piでの開発はPythonで行う人が多いようです。必須ではありませんが、センサー用のモジュールがPythonで提供されていることが多いため、今回は勉強も兼ねてPythonで書いていきます。

まず、I2Cをツールで使えるようにします。

$ apt-get install i2c-tools

※I2C通信とは
I2C(Inter-Integrated Circuit)は、シリアル通信の方式の一つです。
(要約)
・電源・グランドを除き、わずか2線のみで利用可能
・シリアル通信
・通信内容にアドレス情報、確認信号を持つ
・マスタ(今回はRaspberry Pi)、スレーブ(今回は光センサー)の関係で動作する

(参考記事: I2C通信について – Qiita

PythonでI2Cバスをコントロールするためのライブラリ「Python-smbus」をインストールします。

smbusモジュールの設定

$ sudo apt-get install python3-smbus
$ pip3 install smbus

サンプルコードでテスト

Pythonで照度を取得する簡単なコードを書いてみます。

import smbus

def get_lux():
    bus = smbus.SMBus(1)
    addr = 0x23
    lux = bus.read_i2c_block_data(addr, 0x10)
    return (lux[0]*256+lux[1])/1.2

if __name__ == '__main__':
    print(get_lux())

→このテストで、暗いときは100未満、蛍光灯レベルでは200以上の値が出ることがわかりました。しきい値は「100」に設定するのが良さそうです。

Slackへの通知

Slackで使用できる公式API “Bots” をPythonから簡単に使うためのモジュールslackbotをインストールします。

$ pip3 install slackbot

ファイル構成は以下のようにします。

.
├── run.py                 # 実際に実行するスクリプト (後で作成)
├── plugins                # 話しかけられた際の応答処理を格納するディレクトリ
│   ├── __init__.py
│   └── toilet.py          # 会話の応答処理を定義するスクリプト
└── slackbot_settings.py   # Botの設定ファイル

Slack Tokenの取得

Botを作成するため、対象のワークスペースにログインし、Slack側でBotを登録してトークン(xoxb-から始まる文字列)を取得します。

Slack Botの作成画面

取得したトークンを slackbot_settings.py に設定します。

# botアカウントのトークンを指定
API_TOKEN = '取得したトークンをここに記述'

Botによる会話

slackbotライブラリは、話しかけられた際の応答を plugins 配下のスクリプトで定義します。
以下を plugins/toilet.py として配置します。

# coding: utf-8
import smbus
from slackbot.bot import respond_to     # @botname: で反応するデコーダ
from slackbot.bot import default_reply  # 該当する応答がない場合に反応するデコーダ

def get_lux():
    bus = smbus.SMBus(1)
    addr = 0x23
    lux = bus.read_i2c_block_data(addr,0x10)
    return (lux[0]*256+lux[1])/1.2


# 「といれ」と話しかけられたときの応答
@respond_to('といれ|toilet|トイレ')
def check_toilet(message):
    lux = get_lux()
    message.reply('現在の明るさ: {}'.format(round(lux)))
    if lux > 100:
        message.reply('使用中だよ')
    else:
        message.reply('空いてるよ')


# いずれのルールにも該当しない場合の応答
@default_reply
def default_func(message):
    message.reply('「といれ」「トイレ」「toilet」のどれかで話しかけてね')

この時点でBotを起動し、Slackから「トイレ」とメンションを送ると、照度と空き状況を返してくれます。

Slack Botの応答

Botの在籍状況(ステータス)の設定

このままでも動作しますが、毎回「トイレ」と入力するのは面倒です。そこで、Botのステータスアイコンで空き状況を判別できるように改良します。

  • トイレ使用中 → トイレbot ログイン中(緑丸)
  • トイレ未使用 → トイレbot 離席中(グレー丸)

今回利用している slackbot ライブラリには、Botアカウントの在籍・離席といったステータスを設定する機能がありません。そのため、SlackのWEB APIである users.setPresence を直接利用します。

また、定期実行の機能もないため、スレッド(threading)を使って照度を定期的にチェックするロジックも追加します。

run.py (これがメインの実行ファイルです)

#!/usr/bin/env python3
# coding: utf-8
import time
import datetime
import threading
import requests

from slackbot.bot import Bot
# slackbot_settings.py からトークンを読み込む
from slackbot_settings import API_TOKEN

# plugins/toilet.py から照度取得関数を読み込んでおく
from plugins.toilet import get_lux

API_URL = 'https://slack.com/api/users.setPresence?token={token}&presence={status}'
LIGHT_THRESHOLD = 100 # 照度のしきい値


def set_away():
    """ Botのステータスを離席にする """
    res = requests.get(API_URL.format(token=API_TOKEN, status='away'))
    print(res.content)


def set_active():
    """ Botのステータスを在籍にする """
    res = requests.get(API_URL.format(token=API_TOKEN, status='auto'))
    print(res.content)


def check_lux():
    """
    Threadで定期的に明るさをチェックして
    しきい値を基にステータスを変更する
    """

    is_using = False # 現在の使用状況
    # 初期化(起動時は「離席」にする)
    set_away()

    while True:
        lux = get_lux()
        print(datetime.datetime.now(), lux)

        # ステータスが変化したら通知する
        # (未使用) で (明るくなった) -> 利用開始
        if lux > LIGHT_THRESHOLD and not is_using:
            is_using = True
            print('ON: トイレ使用開始')
            set_active()

        # (使用中) で (暗くなった) -> 利用終了
        elif lux <= LIGHT_THRESHOLD and is_using:
            is_using = False
            print('OFF: トイレ利用終了')
            set_away()

        # 3秒ごとにチェック
        time.sleep(3)
        

def main():
    bot = Bot()
    # 照度監視用のスレッドを起動する
    th_me = threading.Thread(target=check_lux, name="th_check_lux")
    th_me.setDaemon(True)
    th_me.start()

    try:
        # bot(メンション応答機能)を起動する
        bot.run()
    except Exception as e:
        print(e)


if __name__ == "__main__":
    print('start slackbot')
    main()

これで、以下のコマンドでBotを起動すれば、照度の監視が開始されます。

pi@raspberrypi:~/work/script/iot_toilet $ python3 run.py
start slackbot
b'{"ok":true}'
2019-01-21 14:35:29.918734 0.0
2019-01-21 14:35:32.927363 0.0
2019-01-21 14:35:35.936223 0.0
...

完成:Slack Botの動作確認

これで、一通りの設定が完了しました。
Slackのユーザー一覧を見ると、トイレ(電気)が消えている時はBotが「離席中」になっています。

SlackでBotが離席中の状態

トイレの電気がつくと(使用中になると)、Botが「在籍中」に変わります。

SlackでBotが在席中の状態

これで、自席からトイレの空き状況が一目でわかるようになりました。

(今回の主な参考サイト: Raspberry Pi + Python + Slack Bot でトイレの空き状況を可視化する – Denzow System


まとめと今後の展望

今回はRaspberry Piと光センサー、Pythonを使って、オフィスのトイレ空き状況をSlackで可視化する仕組みを構築しました。電子工作とWeb APIの連携は相性が良く、意外と簡単に実装できました。

今後の展望としては、Slack Botを使う代わりに、Raspberry Pi自体を「POSTリクエストを投げるとJSON(空き状況)を返却してくれる簡易サーバー」にして、Chromeの拡張機能ボタンでステータスを確認できると、より多くの人が手軽に使えるようになって便利だと考えています。

最後までお読みいただき、ありがとうございました。


よくある質問(FAQ)

Q1. 光センサーを使っていますが、誰かが電気を消し忘れたらどうなりますか?
A1. ご指摘の通り、この仕組みでは「電気がついている=使用中」と判定するため、消し忘れには対応できません。あくまで簡易的な実験(PoC)として安価な光センサーを採用しました。より正確性を高めるなら、人の動きを検知する「人感センサー(PIR)」や、対象物までの距離を測る「超音波センサー」などを組み合わせるのが良いでしょう。(※ドアの開閉センサーだけでは、無人の「閉め切り」を区別できないため注意が必要です)
Q2. Raspberry Piを持っていないのですが、もっと簡単な方法はありますか?
A2. 「自作」にこだわらなければ、市販のIoT製品が最も簡単です。例えば「SwitchBot」の人感センサーなどを使い、IFTTTなどの連携サービスを経由してスマホやSlackに通知を送ることで、ハードウェアの知識がなくても同様の仕組みを実現できます。
Q3. この仕組みを自宅のトイレで使えますか?
A3. はい、ご自宅でも応用可能です。今回のスクリプトをそのまま動かし、ご自身のプライベートなSlackワークスペースに通知する設定にすれば使えます。また、、工夫次第でスマートスピーカー(Google Home/Alexa)に喋らせたりすることも可能です。

CONTACT

webサイト制作、デザインに関するご相談、御見積のご依頼など、弊社へのお問い合わせはこちら