Raspberry pi zero wとhomebridgeとHomeKitの連携その2

先日、Raspberry Pi Zero Wで、リビングのライトのスイッチを、壁のプッシュスイッチからも、iOSのHomeKitからもON/OFFできるように、まずはRaspberry Piに接続したプッシュスイッチでリレーを、とりあえずON/OFF出来るようにしました。

gentoolinux.hatenablog.com

しかし、このときはadd_event_detectをうまく使えず、while:ループの中で細かなsleepでごまかしながら力業で、プルアップされたGPIOインプットがGNDに落ちたかどうかを検出していました。

sleepを挟んでいるとはいえ、かなりCPUリソースを消費していたようです。

今は良いですが、夏は熱でスタックしそう・・・。

f:id:naoyukinagano:20171121223443j:plain

 

で、add_event_detectで立ち上がりエッジを検出後にだけ、該当のGPIOインプットがGNDに落ちているかを判定し、該当のインプットなら処理続行、該当のインプットでなければ処理中断をしてあげれば、問題なく動かせることに気づきました。

そこで、スクリプトの書き直しです。

また、killコマンドで終了したときにもしっかりとGPIOのクリーンナップが出来るように、シグナルハンドラも入れてみました。

こんな感じになりました。

#! /usr/bin/python

 

#必要なモジュールをインポートします。requestsはその1の記事を参照してインストールしてください。

import signal
import sys
import RPi.GPIO as GPIO
import time
import requests

 

#スイッチのGPIOとリレーのGPIOを指定します。

switch = 23
relay = 26

 

#リレーはwebiopiのほうからコントロールします。

#なぜならRPiライブラリでGPIO.setupすると、webiopiからはコントロール出来なくなるからです。

#リレーのURLを生成するための変数です。

url_front = 'http://localhost:8000/GPIO/'
url_back = '/value'
url_function_out = '/function/out'
url = url_front + str(relay) + url_back
url_function = url_front + str(relay) + url_function_out
url0 = url + '/0'
url1 = url + '/1'

 

#スイッチにつながったGPIOをプルアップされたインプットとして設定します。

GPIO.setmode(GPIO.BCM)
GPIO.setup(switch, GPIO.IN, pull_up_down=GPIO.PUD_UP)

 

#webiopiからリレーに接続されているGPIOをアウトプットとして設定します。

#設定にはPOSTメソッドを使います。
gpio_function = requests.post(url_function)

 

#エッジ検出後の動作をコールバック関数として記述します。

def switch_callback(switch):

    if GPIO.input(switch)==0:    #スイッチのGPIOがGNDに落ちているかどうかを判定。落ちていたら次の処理へ
         status = requests.get(url)    #リレーのGPIOの状態をwebiopi経由で取得するためにurlをgetで叩く
         if status.text=='1':    #getでリクエストしたBODY部分は.testに入るので、その値が(文字の)1かどうか判定
             requests.post(url0)    #1ならwebiopi経由で0にするためにPOST
             time.sleep(0.5)    #誤動作防止のため0.5秒スリープ
         else:
             requests.post(url1)    #getでリクエストしたBODYが1以外(つまり文字の0)だったら、webiopi経由で1に
             time.sleep(0.5)    #誤動作防止のため0.5秒スリープ

 

#killを受け取ったときの動作を関数として記述

def handler(signal, frame):   
     GPIO.cleanup()    #設定したGPIOを解放(解放しないと次に使えない)
     print "switch.py terminated for GPIO",switch
     sys.exit(0)    #終了

 

#スイッチとなるGPIOのエッジ(電位差の立ち上がりか立ち下がり)を検出したらswitch_callback関数を呼び出す。

GPIO.add_event_detect(switch, GPIO.RISING, callback=switch_callback, bouncetime=300)

#killされたらhandler関数を呼び出す。

signal.signal(signal.SIGTERM, handler)

 

#エラーが検出されても続行させる。エラー検出後の動作はexceptに記載

try:

     while True:    #永遠とループ
     time.sleep(0.1)    #イベントかエラーが出るまで0.1秒スリープの繰り返し

 

#ctrl+Cで中止が発生した場合の処理

except KeyboardInterrupt:
     print '\nswitch.py Stopped'
     GPIO.cleanup()    #設定したGPIOを解放(解放しないと次に使えない

 できるだけ細かくコメントを書いてみました。

実際にはコメントははずしています。

30分で0.5℃、CPU温度が下がりました。誤差かな?

 

さて、homebridgeの記載をwebiopiに合うように直します。

config.jsonの該当部分のみを出してみましょう。

{
"accessory": "Http",
"name": "Spot and Bracket Light",
"switchHandling": "realtime",
"http_method": "POST",
"on_url": "http://192.168.x.y:8000/GPIO/26/value/0",
"off_url": "http://192.168.x.y:8000/GPIO/26/value/1",
"status_url": "http://192.168.x.y:8000/GPIO/26/value",
"status_on": "0",
"status_off": "1",
"service": "Switch",
"brightnessHandling": "no",
"brightness_url": "",
"brightnesslvl_url": "",
"sendimmediately": "",
"username" : "",
"password" : ""
},

IPアドレスのx.yはご自身の環境に合わせて読み替えてください。

http_methodですが、EspEasyの場合はgetメソッドでコントロールしていたのに対し、webiopiはPOSTメソッドでコントロールします。

URLの中にある26は、対象のリレーが接続されているGPIOです。

サインスマートのリレーは0でON、1でOFFなので、"status_on": "0","status_off": "1"を明確に定義しています。

ServiceはLightにした方がよかったかも・・・。

 

これでhomebridgeを再起動すると、壁スイッチからもHomekitからも使えるようになりました。

しかも、EspEasyと違い、webiopiの動作が速いため、同時に複数のリクエストを捌くことができるようで、HomeKitのシーンで、3つのスイッチを同時に消灯することが可能になりました。