Opt Technologies Advent Calendar 2017の12日目です。
はじめに
オプトテクノロジーズの伊藤です。
QiitaはもっぱらROM専でしたが、Advent Calendarで投稿の機会をいただき、自社の会議室の最適利用の為にIoTした事例をご紹介します。
課題ときっかけ
「とにかく会議室の空きがない」「ミーティングしたいのに場所がない」なんて声を社内のあちらこちらで聞くようになりました。
この課題に向き合うと決めて、オフィスサービスEXPOなどの展示会を見に行ったり、IoTをリードする企業様と会って話を聞きましたが、何れもイニシャル/ランニングコストが高額だったり、大規模な工事や機器の設置が必要なソリューションばかりで、とても気軽に導入できるものではありませんでした。
一方で、個人的な趣味で人感(赤外線)センサーを使った行動モニタリングをやっていたこともあり、自作のデバイスを活用したソリューション開発を推進することになりました。
課題の考察
全社的に会議自体の頻度が多い&時間が長いのもありますが、実は会議室をおさえたものの開催されなかったケースが一定数あるような気がしました。
総務とも共同し、一週間程度の会議室の利用状況を計測したところ、71%程度の稼働率。つまり、約3割は使われていないことが判明しました。
ソリューション要件
- 会議室の利用状況をリアルタイムで可視化したい
- 会議室が未利用の場合はスケジュールを自動削除したい
- 会議室が未利用の場合はログを残したい(空予約者のあぶり出し)
- 会議終了の5分前にアラームを鳴らしたい
実現方式
どうすれば会議室の利用状況が把握できるか?
【試行1】 人感(赤外線)センサーによる計測数値をモニタリング

この方式で人が居る/居ないが判定できればよかったのですが 深夜や早朝に謎の計測があがる おーこわ…
そもそも、この人感センサーは、焦電型赤外線センサーと言うやつでして、デバイスメーカーさんの説明によると
センサー自身からLEDなどの光を発光するのではなく、周囲と温度差のある人(物)が動く際におこる赤外線の変化量を検出するセンサーです。 温度差を検出するため、体温を持つ人体の検出に適します。
とのことなので、つまり 温度差に弱い のです。
太陽光などの外的要因で温度差が生じ、誤検知も十分にありえます。
これはいけません。そこで次に試したのは。
【試行2】 音量センサーによる計測数値をモニタリング

箱の角に穴が空いてますが、その中に音量センサーを追加し、上記と同様に計測数値をBaaSに投げつけます。
結果。よくわからない。
生活音や屋外の音も拾ってしまい、居る/居ないの正確な判断ができませんでした。
(そもそも会議中に誰も喋らない時間があったらマズいのでは?とかも)
その後も、計測数値を1分間サマった平均値をとって比較したり、サーモグラフィーでも買ってみようかとか迷走しましたが、結局は↓に落ち着きました。
【試行3】 ボタン押下による計測数値をモニタリング

余談ですが、先述の展示会で拝見したソリューションのほとんどはこの方式です。
もちろん、計測デバイスはこんなお粗末なものではなく、タブレットを採用していてリッチで現代的ですが、計測する為の原理原則は同じです。
とにもかくにも、これで計測はクリアです。
会議室の利用状況をリアルタイムで可視化する
方法は色々とあるのですが、バックエンドでかつリアルタイム通知ができるサービスでパッと思いついたのが Firebase でした(使ったことがなかったので学習も含め)。
特にアプリ開発のシーンでは度々利用されていますね。リアルタイムデータベースとかpush通知とか。他にも機能はたくさんあるはず。
そして、先の章では触れませんでしたが、計測結果をWi-FiでBaaSに飛ばしているモジュールはESP-WROOM-02というもので、このモジュールとArduinoを組み合わせ、Arduinoスケッチで書いたC++をフラッシュメモリに書き込んで動かしています。
つまり、ArduinoでFirebaseのAPIを実行する必要があります。
これを一から書くのは骨が折れるなぁ…と思っていたら、ありましたよライブラリが。
Firebase-Arduino
OSS最高ですね。Contributorの方々にはホント頭が下がります。
使い方は、masterブランチをダウンロードし、Aruduino IDEでライブラリとして登録。
サンプルのスケッチも参考にして欲しいのですが、以下のようなコードでいけるはずです。
#include <ESP8266WiFi.h>
#include <FirebaseArduino.h>
#define FIREBASE_HOST "xxx.firebaseio.com"
#define FIREBASE_AUTH "Firebaseのデータベースシークレット"
#define WIFI_SSID "Wi-FiのSSID"
#define WIFI_PASSWORD "Wi-Fiのパスワード"
// ボタンの接続ピン番号
const int buttonBlue = 12;
const int buttonRed = 13;
// LEDの接続ピン番号
const int ledBlue = 14;
const int ledRed = 15;
void setup() {
// Aruduinoに結線しているIN/OUTのPINを定義
pinMode(buttonBlue, INPUT);
pinMode(buttonRed, INPUT);
pinMode(ledBlue, OUTPUT);
pinMode(ledRed, OUTPUT);
Serial.begin(115200);
// Wi-Fi接続
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("connecting");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println();
Serial.print("connected: ");
Serial.println(WiFi.localIP());
// Firebaseに接続
Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
}
void loop() {
// スイッチの状態を取得
int buttonBlueState = digitalRead(buttonBlue);
int buttonRedState = digitalRead(buttonRed);
// 青ボタンが押された場合
if (buttonBlueState) {
// 青のLEDを点灯し、赤のLEDを消灯
digitalWrite(ledBlue, HIGH);
digitalWrite(ledRed, LOW);
// Firebaseにデータをセット
Firebase.setString("Firebaseデータキー", "0");
}
// 赤ボタンが押された場合
if (buttonRedState) {
// 赤のLEDを点灯し、青のLEDを消灯
digitalWrite(ledBlue, LOW);
digitalWrite(ledRed, HIGH);
// Firebaseにデータをセット
Firebase.setString("Firebaseデータキー", "1");
}
// Firebaseからデータを取得
String strStatus = Firebase.getString("Firebaseデータキー");
if (strStatus == "0") {
// 青のLEDを点灯し、赤のLEDを消灯
digitalWrite(ledBlue, HIGH);
digitalWrite(ledRed, LOW);
}
if (strStatus == "1") {
// 赤のLEDを点灯し、青のLEDを消灯
digitalWrite(ledBlue, LOW);
digitalWrite(ledRed, HIGH);
}
}
上記でバックエンドに投げる側の実装はOKです。
次にユーザーインターフェイス側です。
これも非常に容易でして、Firebaseの管理画面に ウェブアプリに Firebase を追加 というリンクがあり、 Get Started with Firebase for Web Appsに使い方が載ってますので、そちらを参考にしていただき、キチンとデザインされたUIに導入していただければ良いと思います。
ちなみに、デザインスキルに乏しい私がやるとこんな感じにチープな仕上がりになります。一応レスポンシブ対応なのでスマホブラウザだと縦表示になったりはします。
ソースコードを一部抜粋するとこんな感じです(Configや必要なコンポーネントのインクルード部分は省略)。
<script>
var db = firebase.database();
var key = db.ref("Firebaseデータキー");
key.on("value", function(snapshot) {
document.getElementById("id").innerText = (snapshot.val().status == "1") ? "使用中" : "未使用";
document.getElementById("id").style.backgroundColor = (snapshot.val().status == "1") ? "Red" : "Blue";
});
</script>
会議室が未利用の場合はスケジュールを自動削除する
前章で計測デバイス、及びその計測。そして状態の可視化までできました。
続いてはスケジュールの操作です。
当社では、Googleカレンダー上でスケジュールや会議室の予約を管理しており、必然的に Google Apps Script を採用するのが最良と判断しました。
当然ですが、Google Apps ScriptからFirebase APIも実行可能です。そしてやはりライブラリです。
こちらを参考に、GASのスクリプトエディタからライブラリを追加してください。
なお、ライブラリのバージョンは適宜ご指定ください。投稿時期の兼ね合いもあるのであえて明示はしません。
Firebaseとのインターフェースのコードを抜粋するとこんな感じでシンプルな実装になっています。
var firebaseUrl = "FirebaseのURL";
var secret = "Firebaseのデータベースシークレット";
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl, secret);
// Firebaseからデータを取得する
base.getData("Firebaseデータキー");
// Firebaseにデータをセットする
base.setData("Firebaseデータキー", "セットしたい値");
続いて、カレンダーの操作です。
GASからGoogleカレンダーにアクセスする為には CalendarApp というクラスオブジェクトを使います。
リファレンスにもありますが Calendar
という親オブジェクトの下に CalendarEvent
という子オブジェクトがあり、この子オブジェクトであるカレンダーイベントが、カレンダーに登録している予定そのものです。
こんな感じで、オブジェクトをブレイクダウンし、カレンダーイベントのオブジェクトを操作してください。
// 全てのカレンダーオブジェクト取得
var cal = CalendarApp.getAllCalendars();
// カレンダーオブジェクト
for (var i = 0; i < cal.length; i++) {
// 日時指定でカレンダーイベントを取得
var ev = cal[i].getEvents(new Date('yyyy/MM/dd HH:mm:ss'), new Date('yyyy/MM/dd HH:mm:ss'));
// カレンダーイベントオブジェクト
for (var j = 0; j < ev.length; j++) {
ev[j].getTitle(); // カレンダータイトルを取得
ev[j].getDescription(); // カレンダー詳細を取得
ev[j].getLocation(); // 場所を取得
ev[j].getStartTime(); // 開始日時を取得
ev[j].getEndTime(); // 終了日時を取得
ev[j].deleteEvent(); // イベントを削除
}
}
Firebaseとのインターフェースとカレンダー操作を組み合わせたものを、数分おきに実行されるようにトリガーを設定してください。
そして、開始日時をN分経過した時点でfirebaseの値を取得し、未使用値ならイベントを削除すれば良いです。
ちなみに、GASのトリガーに登録可能な数は最大で20個なので、以前に投稿した記事を参考にトリガーが最適化されるような工夫が必要です。
会議室が未利用の場合はログを残す(空予約者のあぶり出し)
前章の deleteEvent
でイベントを削除する直前にスプレッドシートにイベント情報を書き出せば良いです。
var ss = SpreadsheetApp.openById("スプレッドシートID");
var sheet = ss.getSheetByName("シート名");
var newRow = sheet.getLastRow() + 1;
sheet.getRange(newRow, 1).setValue("書き出すイベントの内容");
会議終了の5分前にアラームを鳴らす
まずは計測デバイスにアラームを鳴らす為のモジュールを追加する必要があります。
秋葉原に圧電ブザーがありました。「ピーピーピー」と連続音を鳴らすものです。
これを計測デバイスに組み込んだものがこちら。相変わらずイケてない。
そして、Arduinoから圧電ブザーを鳴らしてみましょう。
Arduinoでは手軽にブザーを鳴らす為のtone関数というものが用意されていますのでそれを実装します。
// ブザーの接続ピン番号
const int buzzerPin = 16;
void loop() {
// firebaseからアラートフラグを取得
String strAlert = Firebase.getString("Firebaseデータキー");
if (strAlert == "1") {
// ピン番号, 出力周波数, 長さ(3秒)
tone(buzzerPin, 1, 3000);
// firebaseのアラート値を初期化
Firebase.setString("Firebaseデータキー", "0");
}
}
前章のGASによって、イベントの終了時間は取得できているので、会議終了の5分前にfirebaseにアラートフラグを立ててやればデバイス側でそれを検知し、アラームを鳴らしてフラグを初期化します。
導入後
2017年12月現在、一部の会議室でパイロット運用し、課題を洗い出しています。
ですので、これが完成形ではありません。デバイスはチープだし、UIも改善の余地があるので、全社展開の際には改良を予定しています。
これまでに判明していることとしては、Google Cloudが不安定なのか サーバーエラーが発生しました。しばらくしてからもう一度試してください。
的なエラーが定期的に発生するので、Try Catchによるエラーハンドリングは必須です(バッチですから)。
一方で利用者側からの要望は下記のようなものが挙がっており、随時対応中です。
- 空予約をしてしまうと(ボタンを押し忘れると)カレンダーが消えるのは困る
- 退出時に青ボタンを押し忘れる
- 部屋の予約だけ削除できないの?
部品類の情報共有
これまでの章では方式中心の説明でしたが、本デバイスに使用した部品に関する情報を列挙します。













工作関連の情報共有





参考情報
https://www.mgo-tec.com/blog-entry-ss-wroom-howto01.html
https://www3.panasonic.biz/ac/j/control/sensor/human/index.jsp
https://sites.google.com/site/scriptsexamples/new-connectors-to-google-services/firebase
http://jkoba.net/prototyping/arduino/led_switch.html