知的好奇心 for IoT

IoT関連の知的好奇心を探求するブログです

シェアハウスで洗濯機や乾燥機の終了を通知するサービスを稼働させた(センサーアプリ編)

センサーBOXの動作

洗濯機と乾燥機1台ずつに設置したセンサーBOXは、一定以上の電流が流れていればLow、以下ならHighの電圧を出力する単純な構造になっています。これは、使用しているセンサーが磁気を検知するとLowになるタイプなためで、信号用のGPIOをプルアップの設定にして使用しています。洗濯機や乾燥機は電源を入れた時から終わるまで常に一定以上の電流が流れ続けないため、機械が動作中のセンサーBOXの出力はLowとHighを繰り返しています。

 

洗濯機と乾燥機の開始と終了の判定

色々と試行錯誤をしてみましたが、結局最終的に落ち着いた方法は一番単純なタイムアウト方式でした。センサーBOXの出力がLow(ON)になったら開始と判定して、一定時間High(OFF)のままの状態が続いたら終了と判定するようにしています。

 

コマンドライン引数

プログラム構造を単純化させるため、センサーBOX毎に同一のセンサーアプリを動かすようにしました。固有の情報はコマンドライン引数で次のように与えます。

laundry.py ビルID フロアID マシンID 電源ピン番号 信号ピン番号 タイムアウト分数

ビルIDとフロアIDとマシンIDはクラウドへのリクエストに使用する情報です。

電源ピン番号と信号ピン番号は繋いだセンサーBOX用で、センサーは消費電流が少ないので電源ピン番号のGPIOをHighにして電源として利用しています。

タイムアウト分数は終了判定用で、このシェアハウスでは洗濯機の場合は8分、乾燥機の場合は1分に設定しています。

 

クラウドへのリクエス

センサーアプリが開始と判定したときと、タイムアウトして終了と判定したときにクラウドに開始か終了のリクエストを送信しています。

開始と判定したときのリクエス

https://HEROKU_URL/ビルID/フロアID/マシンID/1/

終了と判定したときのリクエス

https://HEROKU_URL/ビルID/フロアID/マシンID/0/

リクエストを送信する関数では、ネットワーク障害などでリクエストに失敗したときのためにリトライを行うようにしています。今はリクエストに失敗すると6秒待ってから再送信する動作を10回繰り返して、それでも送信できなかった場合はログにエラーを記録するようにしています。

 

センサーアプリ laundry.py

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Laundry Reminder Sensor Application Program
# Octor 7, 2017
# By Hiroyuki ITO
# http://intellectualcuriosity.hatenablog.com/
# MIT Licensed.
#
# Usage
# $ laundry.py 'building id' 'floor id' 'machine id' 'power pin no (BCM)' 'signal pin no (BCM)' 'stop timeout'
#

import RPi.GPIO as GPIO
import datetime
import time
import urllib2
import sys

#
# Define
#
HEROKU_URL = "Plese replace at your URL"
OFF = 0   # for Flags
ON  = 1   # for Flags

#
# Command Line
#
args = sys.argv
if len(args) < 7:
    print 'Usage: $ python %s building_id floor_id machine_id power_pin_no signal_pin_no stop_timeout' % args[0]
    sys.exit()

# Request Parameters
BUILDING_ID = args[1]
FLOOR_ID = args[2]
MACHINE_ID = args[3]
STOP_TIMEOUT = int(args[6]) * 60   # minutes

# Raspbery Pi PIN (BCM) Initialization
POWER_PIN = int(args[4])
SIGNAL_PIN = int(args[5])
GPIO.setmode(GPIO.BCM)
GPIO.setup(POWER_PIN, GPIO.OUT)
GPIO.output(POWER_PIN, GPIO.HIGH)
GPIO.setup(SIGNAL_PIN, GPIO.IN, pull_up_down = GPIO.PUD_UP)

# Valiables
power_flag = OFF
state_flag = OFF   # Not consider timeout
wait_time = 0
filename= MACHINE_ID + '.log'

#
# REST Request Function
#
def rest_request(state):
    url = HEROKU_URL + BUILDING_ID + "/" + FLOOR_ID + "/" + MACHINE_ID + "/" + state
    req = urllib2.Request(url)
    for _ in range(0, 10):
        try:
            tNow = datetime.datetime.now()
            stNow = tNow.strftime("%Y-%m-%d %H:%M:%S")
            response = urllib2.urlopen(req)
            response.close()
            with open(filename, 'a') as logfile:
                logfile.write(stNow + ' Request Sent\n')
            print stNow, 'Request Sent'
            break
        except Exception as e:
            with open(filename, 'a') as logfile:
                logfile.write(stNow + str(e) + ' Retry\n')
            print stNow, str(e), 'Retry'
            time.sleep(6)
    else:
        with open(filename, 'a') as logfile:
            logfile.write(stNow + ' Network Error!\n')
        print stNow, 'Network Error!'
    return

#
# Main loop
#
try:
    while True:
        lNow = long(time.time())
        tNow = datetime.datetime.now()
        stNow = tNow.strftime("%Y-%m-%d %H:%M:%S")

        # == Signal ON ==
        if GPIO.input(SIGNAL_PIN) == 0:
            if power_flag == OFF:
                # Power OFF -> ON
                power_flag = ON
                state_flag = ON
                with open(filename, 'a') as logfile:
                    logfile.write(stNow + ' Power ON\n')
                print stNow, 'Power ON'
                rest_request('1/')   # Send ON Request
            elif state_flag == OFF:
                state_flag = ON

        # == Signal OFF ==
        elif power_flag == ON:
            if state_flag == ON:
                state_flag = OFF
                wait_time = lNow
            elif lNow - wait_time > STOP_TIMEOUT:
                # Power ON -> OFF
                power_flag = OFF
                with open(filename, 'a') as logfile:
                    logfile.write(stNow + ' Power OFF\n')
                print stNow, 'Power OFF'
                rest_request('0/')   # Send OFF Request

        time.sleep(0.1)   # Load reduction

finally:
    GPIO.cleanup()

 

Laundry Reminderを導入してみたい!興味がある!という方は、プロフィール欄のメールアドレスにお問い合わせください。

 

おわり