suzukiotのアイコン画像
suzukiot 2021年05月10日作成 (2021年05月16日更新)
製作品 製作品 閲覧数 1500
suzukiot 2021年05月10日作成 (2021年05月16日更新) 製作品 製作品 閲覧数 1500

!!在宅ワークの頼もしい味方!! 作業部屋の照明消し忘れを止める装置『Light番』

!!在宅ワークの頼もしい味方!! 作業部屋の照明消し忘れを止める装置『Light番』

はじめに

最近は在宅ワークとなって自分の部屋で仕事をされている方も多いのではないかと思います。会社にいる時はあまり気にしなかったのですが、気分転換や休憩などで席を外した際に部屋の照明を点けたままにしているとその分の電気代が無駄になって家計に響いてしまいます。仕事で部屋を使うだけでも増えてしまっている電気代を少しでも節約したいと思って、今回のシステムを作りました

デモ動画

ここに動画が表示されます

システム概要

消し忘れを検知するためにCDSセルで照明が点いていること、PIRセンサーで部屋に人が居ることを検知します。人が居ないのに照明が点いていると消し忘れと判断して照明リモコンの消灯信号を赤外線LEDで送って照明を消します

構成図

キャプションを入力できます

前提条件

  • 本システムの前提として、作業中はPCが立ち上がっていているかつ、ブラウザでObniz Cloudの画面を開いてObnizのプログラムを実行し続けることとしています。
  • 作業部屋は電気を点けていないと1日中暗い状態なので、作業中は照明を点けていることとしています。つまり、日中は日差しが入って照明がなくても十分に明るい状態は考慮していません
  • 本システムでは消し忘れの時に照明を消すだけで、自動的にONにすることはしません

材料

ここで使用した部品を表にしました。抵抗は参考までとして使用する部品やLEDの明るさ等で適宜変更してください

名称 数量 URL メモ
obniz Board 1 https://obniz.com/ja/products/obnizboard 本記事では obniz Board 1Y を使っています
LED (赤x1、黄x1) 2 必要に応じて、色を変えたり3色LEDにしてもいいかもしれません
CDSセル (光センサー) 1 https://akizukidenshi.com/catalog/g/gI-00110/
赤外線LED 940nm 1 https://akizukidenshi.com/catalog/g/gI-03261/
赤外線リモコン受信モジュール 1 https://akizukidenshi.com/catalog/g/gI-04659/
PIRセンサー (人感センサー) 1 https://akizukidenshi.com/catalog/g/gM-02471/ obnizのIOに合わせて5V駆動の物を使います
抵抗 4 LED用470Ω x2、赤外線LED用20Ω、PIRセンサー用10kΩ、CDSセル用100kΩ

制作過程

部品の接続

obnizと各部品を次のように繋ぎます

配線図

すべて繋いだ状態です。ツールの都合上PIRセンサーの黄色は実際は白色になります

キャプションを入力できます

事前準備

動作のプログラムを実行する前に、以下の準備用のプログラムを使ってリモコン信号の保存とセンサーの調整を行います

キャプションを入力できます

動作確認用のブロックを実行すると次の画面になります

キャプションを入力できます

リモコン信号の学習

照明を消す時のリモコンの信号を学習させます。[OFFを学習] のボタンをクリックして、赤外線リモコン受信モジュールに照明リモコンの消灯ボタンを押した時の赤外線を当てます

リモコンの信号を受信モジュールに当てる

必須ではありませんが、ONにした時も同様にして学習させるとobnizから照明を点けることもできます。

信号の動作確認

学習した赤外線信号を送信して、照明が反応する位置にLEDを設置します。[OFF] のボタンをクリックしながら、照明が反応する位置を確認して設置位置を決めます。

光センサーの感度確認

確認用プログラムを実行するとobnizのLEDにCDSセルの電圧が表示されます。照明が点いている時と消えている時の値を確認して、判定の閾値を決めます。確認した閾値はプログラム内の [cds] 変数の判定箇所にセットします。

obnizのLCDでCDRセルの電圧を表示

Webhookの準備

1. slackで通知用のワークスペースとチャンネルを作成しておきます

  • slack(https://slack.com/intl/ja-jp/) へアクセスしてチャンネルを追加します
    キャプションを入力できます
  • slackを使ったことがない方は [無料で試してみる] から初めて、ワークスペースを作成しておきます

2. IFTTTでwebhookのURL取得と、slackへメッセージを送るように設定しておきます

  • IFTTT(https://ifttt.com/) へアクセスして [Create] をクリックします
    キャプションを入力できます
  • If This の [Add] をクリックします
    キャプションを入力できます
  • Webhooksをクリックし、Connect service で [Connect] をクリックします
    キャプションを入力できます
    キャプションを入力できます
  • Event Nameを入力して、 [Create trigger] をクリックします
    キャプションを入力できます
  • 続いて Then That の [Add] をクリックします
    キャプションを入力できます
  • Slackを選びます
    キャプションを入力できます
  • Post to channelをクリックして、Connect service の画面で [Connect] をクリックします
    キャプションを入力できます
  • 送信するチャンネルやメッセージを設定します
    キャプションを入力できます
  • Continue をクリックして、Finish をクリックして完了です
    キャプションを入力できます
    キャプションを入力できます

3. Webhook URLの確認

  • Webhooksのページ (https://ifttt.com/maker_webhooks) を開き、Documentation を開きます
    キャプションを入力できます
  • Your key is:.. が表示されて、その下に送信先のURLが書いてあります。
    キャプションを入力できます
  • このURLをメモして後述のプログラミングのWebhookに設定します。これが叩かれるとslackに通知のメッセージが届きます

プログラミング

普段の開発でソースコードを打ち込むことには慣れているので、今回は敢えてブロックプログラミングだけで実現してみました。この通りにブロックを組み上げていけばデモ動画のような仕組みが出来上がります

ブロックプログラミング

キャプションを入力できます

プログラムの概要

  • 0.5秒間隔でセンサーの値をチェックするようにしています
  • センサーの値をアナログで取得して人が居ないかつ部屋が明るい場合かを判定します。
    • 以下の2つを同時に満たした場合
      • PIRセンサーで電圧が掛かっている場合(今回使用したセンサーは人を検知した時に電圧が下がる。センサーにより逆のパターンもある)
      • CDSセンサーの抵抗が低い(電圧が高い)
    • 変数leave_countの値をインクリメントして 30 (15秒相当) になったら消し忘れと判断し、照明リモコンのOFF信号を赤外線で送信します
    • 送信すると同時にIFTTTのWebhookを叩きます
    • leave_countの値によって光らせるLEDを変えます
      • 消し忘れを検知した時は、まず黄色が光ります
      • OFF信号を送る直前になると赤色が光ります
  • PIRセンサーは誤反応することがあるため、人が離れている時に検知してしまうことを想定してleave_countは一度にリセットしないで、6ずつ減らすようにしています
  • 赤外線送信は、1回だと照明が受け取れない可能性があるため5回繰り返し送るようにしています

ソースコード

参考までに、ブロックプログラミングから出力したソースコードを載せます

<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous" ></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous" ></script> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" /> </head> <body> <h3 id="bploading" style="text-align:center;">LOADING...</h3> <div id="OBNIZ_OUTPUT"></div> <br /> <script src="https://unpkg.com/obniz@latest/obniz.js" crossorigin="anonymous" ></script> <script src="https://unpkg.com/obniz-parts-kits@0.16.0/iothome/index.js" crossorigin="anonymous" ></script> <script src="https://unpkg.com/obniz-parts-kits@0.16.0/airobot/index.js" crossorigin="anonymous" ></script> <script src="https://unpkg.com/obniz-parts-kits@0.16.0/ui/index.js" crossorigin="anonymous" ></script> <script src="https://unpkg.com/obniz-parts-kits@0.16.0/ai/howler2.1.2/howler.js" crossorigin="anonymous" ></script> <script src="https://unpkg.com/obniz-parts-kits@0.16.0/ai/opencv3.4/opencv.js" crossorigin="anonymous" ></script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.3.0"></script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@2.1.0"></script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/posenet@2.2.2"></script> <script src="https://unpkg.com/obniz-parts-kits@0.16.0/ai/clmtrackr/clmtrackr.js" crossorigin="anonymous" ></script> <script src="https://unpkg.com/obniz-parts-kits@0.16.0/ai/clmtrackr/emotion_classifier.js" crossorigin="anonymous" ></script> <script src="https://unpkg.com/obniz-parts-kits@0.16.0/ai/clmtrackr/emotionmodel.js" crossorigin="anonymous" ></script> <script src="https://unpkg.com/obniz-parts-kits@0.16.0/ai/clmtrackr/model_pca_20_svm.js" crossorigin="anonymous" ></script> <script src="https://unpkg.com/obniz-parts-kits@0.16.0/ai/index.js" crossorigin="anonymous" ></script> <script src="https://unpkg.com/obniz-parts-kits@0.16.0/storage/index.js" crossorigin="anonymous" ></script> <script> $("#bploading").text("RUNNING..."); (async function(){ var obniz, leave_count, label, signal, pir, OFF, irmodule, cds, ON, learn_OFF, learn_ON; /** * 赤外線LEDから信号を送出します */ async function _E8_B5_A4_E5_A4_96_E7_B7_9A_E9_80_81_E4_BF_A1() { obniz.display.clear(); obniz.display.print(signal) for (var count = 0; count < 5; count++) { await ObnizUI.Util.wait(0); irmodule.send((await ObnizUI.Util.loadFromStorage(signal))); await ObnizUI.Util.wait(100); } } /** * 赤外線リモコンの信号を学習してクラウドに保存します */ async function _E8_B5_A4_E5_A4_96_E7_B7_9A_E3_82_92_E5_AD_A6_E7_BF_92_E3_81_99_E3_82_8B() { obniz.display.clear(); obniz.display.print('待受け中') await ObnizUI.Util.saveToStorage(signal,((await new Promise(resolve=>{ irmodule.ondetect = resolve; })))); obniz.display.clear(); obniz.display.print('受信しました') } /** * UIのボタンが押された時の処理をまとめています */ async function UI_E5_87_A6_E7_90_86() { obniz.display.clear(); obniz.display.print(pir) obniz.display.print(cds) obniz.display.print(leave_count) if (OFF.isClicked()) { label.setText('照明をOFFにしました'); signal = 'OFF'; await _E8_B5_A4_E5_A4_96_E7_B7_9A_E9_80_81_E4_BF_A1(); } if (ON.isClicked()) { label.setText('照明をONにしました'); signal = 'ON'; await _E8_B5_A4_E5_A4_96_E7_B7_9A_E9_80_81_E4_BF_A1(); } if (learn_OFF.isClicked()) { signal = 'OFF'; await _E8_B5_A4_E5_A4_96_E7_B7_9A_E3_82_92_E5_AD_A6_E7_BF_92_E3_81_99_E3_82_8B(); } if (learn_ON.isClicked()) { signal = 'ON'; await _E8_B5_A4_E5_A4_96_E7_B7_9A_E3_82_92_E5_AD_A6_E7_BF_92_E3_81_99_E3_82_8B(); } } /** * 消し忘れを示すLEDの点灯処理です */ async function LED_E3_82_92_E7_82_B9_E3_81_91_E3_82_8B() { if (leave_count < 20) { obniz.io7.output(true); obniz.io8.output(false); } else { obniz.io7.output(false); obniz.io8.output(true); } } /** * LEDを消灯します */ async function LED_E3_82_92_E6_B6_88_E3_81_99() { obniz.io7.output(false); obniz.io8.output(false); } obniz = new Obniz('8724-7032'); await obniz.connectWait(); leave_count = 0; label = new ObnizUI.Label('label'); label.setText('照明の消し忘れをチェックしています'); OFF = new ObnizUI.Button('OFF'); ON = new ObnizUI.Button('ON'); learn_OFF = new ObnizUI.Button('OFFを学習'); learn_ON = new ObnizUI.Button('ONを学習'); irmodule = obniz.wired("IRModule",{"vcc":0, "send":2, "recv":3, "gnd":1}); irmodule.start(); while (true) { await ObnizUI.Util.wait(0); pir = (await obniz.ad10.getWait()); cds = (await obniz.ad9.getWait()); if (pir > 3 && cds > 4) { leave_count = (typeof leave_count == 'number' ? leave_count : 0) + 1; await LED_E3_82_92_E7_82_B9_E3_81_91_E3_82_8B(); if (leave_count == 30) { signal = 'OFF'; await _E8_B5_A4_E5_A4_96_E7_B7_9A_E9_80_81_E4_BF_A1(); label.setText('消し忘れを検知したため、照明をOFFにしました'); await fetch('https://maker.ifttt.com/... {ご自身で発行したendpointに置き換えてください} ...', {method:"GET", mode: "cors", cache:"no-cache"}).catch(e=>{ /* nothing */ }); leave_count = 0; } } else { await LED_E3_82_92_E6_B6_88_E3_81_99(); if (leave_count > 6) { leave_count = leave_count - 6; } else { leave_count = 0; } } await UI_E5_87_A6_E7_90_86(); await ObnizUI.Util.wait(500); } })(); </script> </body> </html>

おわりに

今回は作業部屋に焦点を置いて作ってみましたが、他でも赤外線リモコンを使った機器があれば様々な所で応用が利くと思います。リビングの照明やテレビなどうっかり点けっ放しにしている所があれば、家の中全体で無駄が積もり積もって大きな無駄に繋がりますので、これからも改善を重ねて作っていきたいと思います

  • suzukiot さんが 2021/05/10 に 編集 をしました。 (メッセージ: 初版)
  • suzukiot さんが 2021/05/16 に 編集 をしました。 (メッセージ: IFTTTとslack通知のスクショ追加)
  • suzukiot さんが 2021/05/16 に 編集 をしました。 (メッセージ: 文言修正)
ログインしてコメントを投稿する