私はパッシブ赤外線センサーを持っていて、動きに基づいてディスプレイのオンとオフを切り替えたいと思っていました。たとえば、5分間動きがない場合は、電力を節約するためにディスプレイをオフにする必要があります。ただし、動きがある場合は、ディスプレイをオフにしたり、オンに戻したりしないでください。(スクリーンセーバーがこれに適していない理由を聞かないでください。私が作成しているデバイスにはキーボードやマウスがありません。スタンドアロンディスプレイのみになります。)
私のアイデアは、プロデューサーとコンシューマーの2つのスレッドを作成することでした。プロデューサースレッド(PIRセンサー)はメッセージをキューに入れ、コンシューマー(ディスプレイを制御する)が読み取ります。このようにして、ある信号から別の信号を送信できます。
以下に完全に機能するコードがあります(説明付き)。これで前述の内容が完成します。私の質問は、これをよりエレガントな方法で達成する方法はあるのでしょうか?私のアプローチについてどう思いますか、大丈夫ですか、ハックですか?
#!/usr/bin/env python
import Queue
from threading import Thread
import RPi.GPIO as gpio
import time
import os
import sys
class PIRSensor:
# PIR sensor's states
current_state = 0
previous_state = 0
def __init__(self, pir_pin, timeout):
# PIR GPIO pin
self.pir_pin = pir_pin
# Timeout between motion detections
self.timeout = timeout
def setup(self):
gpio.setmode(gpio.BCM)
gpio.setup(self.pir_pin, gpio.IN)
# Wait for the PIR sensor to settle
# (loop until PIR output is 0)
while gpio.input(self.pir_pin) == 1:
self.current_state = 0
def report_motion(self, queue):
try:
self.setup()
while True:
self.current_state = gpio.input(self.pir_pin)
if self.current_state == 1 and self.previous_state == 0:
# PIR sensor is triggered
queue.put(True)
# Record previous state
self.previous_state = 1
elif self.current_state == 1 and self.previous_state == 1:
# Feed the queue since there is still motion
queue.put(True)
elif self.current_state == 0 and self.previous_state == 1:
# PIR sensor has returned to ready state
self.previous_state = 0
time.sleep(self.timeout)
except KeyboardInterrupt:
raise
class DisplayControl:
# Display's status
display_on = True
def __init__(self, timeout):
self.timeout = timeout
def turn_off(self):
# Turn off the display
if self.display_on:
os.system("/opt/vc/bin/tvservice -o > /dev/null 2>&1")
self.display_on = False
def turn_on(self):
# Turn on the display
if not self.display_on:
os.system("{ /opt/vc/bin/tvservice -p && chvt 9 && chvt 7 ; } > /dev/null 2>&1")
self.display_on = True
def check_motion(self, queue):
try:
while True:
try:
motion = queue.get(True, self.timeout)
if motion:
self.turn_on()
except Queue.Empty:
self.turn_off()
except KeyboardInterrupt:
raise
if __name__ == "__main__":
try:
pir_sensor = PIRSensor(7, 0.25)
display_control = DisplayControl(300)
queue = Queue.Queue()
producer = Thread(target=pir_sensor.report_motion, args=(queue,))
consumer = Thread(target=display_control.check_motion, args=(queue,))
producer.daemon = True
consumer.daemon = True
producer.start()
consumer.start()
while True:
time.sleep(0.1)
except KeyboardInterrupt:
display_control.turn_on()
# Reset GPIO settings
gpio.cleanup()
sys.exit(0)
プロデューサースレッドreport_motion
は、PIRSensor
クラスインスタンスの関数()を実行します。PIRSensorクラスは、パッシブ赤外線センサーの状態を1秒間に4回読み取り、動きを感知するとメッセージをキューに入れます。
コンシューマースレッドcheck_motion
は、DisplayControl
クラスインスタンスの()の関数を実行します。所定のタイムアウトでブロッキングモードで前述のキューを読み取ります。次のことが発生する可能性があります。
それは良い解決策だと思います。その理由は、異なるクラスの関心を分離したためです。1つのクラスがPIRセンサーを処理します。1つはディスプレイを処理します。あなたは今日、それらをキューで接着します。それが1つのアプローチです。
これを行うことにより、さまざまなクラスを簡単にテストできます。
これを拡張するには(読んで拡張可能にする)、コントローラーを導入することができます。コントローラは(キューからなど)イベントを取得し、イベントに基づいて動作します(たとえば、ディスプレイコントローラにディスプレイをオフにするように指示します)。コントローラーはセンサーについて知っており、ディスプレイについて知っています。ただし、センサーはディスプレイについて認識してはなりません。その逆も同様です。(これはMVCと非常によく似ており、この場合、データはモデル(センサー)であり、ディスプレイはビューであり、コントローラーはその間にあります。
このアプローチにより、設計はテスト可能、拡張可能、保守可能になります。そして、それによってあなたはハックではなく、実際のコードを書いています。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加