Raspberry PiでPIRセンサの値を取って部屋に人がいるかいないかをTweetする
こんにちは.
前回はラズパイを使用してPIRセンサの値をTweetするシェルスクリプトを作成しました.ただ前回は単に定期的につぶやくので,Twitterのタイムラインが埋まってしまうという問題がありました.また,人がいると常に反応するわけではなく,人が動いている時だけ反応するため,人がいても値が0になってしまうという問題もありました.
今回はそのあたりの課題を解決すために,シェルスクリプトを改良したいと思います.
改良したものを以下に掲載します.ファイル名も前回から変更してみましょう.
nano watchroom.sh
#!/bin/sh
##GPIOポートの設定
LED=4
PIR=25
##GPIOの値に応じてつぶやく内容を設定
message1="入室しました"
message2="退室しました"
##GPIOのモード設定
gpio -g mode $LED out
gpio -g mode $PIR in
##メインの部分
status1="0"
while true ; do
##センサの値を取得
status2=`gpio -g read $PIR`
##センサが0から1になった場合
if [ $status2 -eq 1 ] ; then
if [ $status1 -eq 0 ] ; then
status1="1"
echo "入室したことをTweetしています..."
gpio -g write $LED 1
ttytter -ssl -status="$message1 `date +%H:%M`"
echo "Tweetしました!"
##センサの値1から1になった場合
elif [ $status1 -eq 1 ] ; then
sleep 10
fi
##センサが1から0になった場合
elif [ $status2 -eq 0 ] ; then
if [ $status1 -eq 1 ] ; then
i=0
while [ $i -ne 30 ] ; do
if [ `gpio -g read $PIR` -eq 0 ] ; then
i=`expr $i + 1`
echo "退室カウント $i /30"
sleep 1
elif [ `gpio -g read $PIR` -eq 1 ] ; then
i=0
fi
done
status1="0"
echo "退室したことをTweetしています..."
gpio -g write $LED 0
ttytter -ssl -status="$message2 `date +%H:%M`"
echo "Tweetしました!"
elif [ $status1 -eq 0 ] ; then
sleep 1
fi
fi
done
次に順に何をしているのか解説します.
#!/bin/sh
##GPIOポートの設定
LED=4
PIR=25
##GPIOの値に応じてつぶやく内容を設定
message1="入室しました"
message2="退室しました"
##GPIOのモード設定
gpio -g mode $LED out
gpio -g mode $PIR in
ここまでは前回とほとんど同じですね.
##メインの部分
status1=0
前回はセンサの状態を入れる変数が1個しかありませんでしたが,今回はstatus1とstatus2を作成します.これは,今のセンサの状態を取るだけでなく,直前に1だったか0だったかを把握するためです.status1が直前の値,status2が今の値です.ここではstatus1に初期値として0を代入しています.
while true ; do
##センサの値を取得
status2=`gpio -g read $PIR`
今のセンサの値を取得し,status2に代入します.これとstatus1の値を元にどの処理を行うかを判断していきます.これを永久に繰り返すため,while文を使用しています.
##センサが0から1になった場合
if [ $status2 -eq 1 ] ; then
if [ $status1 -eq 0 ] ; then
status1="1"
echo "入室したことをTweetしています..."
gpio -g write $LED 1
ttytter -ssl -status="$message1 `date +%H:%M`"
echo "Tweetしました!"
センサが0から1に変わった場合です.すなわち,status2が1でstatus1が0の場合です.この時ツイッターに入室しましたとつぶやきます.また,回路側でどちらの入室中/退室中を把握できるよう,LEDを点灯させます.
##センサの値1から1になった場合
elif [ $status1 -eq 1 ] ; then
sleep 10
fi
次に1から1に変化(つまり変化していない)場合です.この場合は特に何もする必要がありません.また,この場合おそらく人がずっと入室している状態を表していることが多いため,ラズパイへの負荷軽減のため10秒間何もしないようsleepを入れています.
##センサが1から0になった場合
elif [ $status2 -eq 0 ] ; then
if [ $status1 -eq 1 ] ; then
i=0
while [ $i -ne 30 ] ; do
if [ `gpio -g read $PIR` -eq 0 ] ; then
i=`expr $i + 1`
echo "退室カウント $i /30"
sleep 1
elif [ `gpio -g read $PIR` -eq 1 ] ; then
i=0
fi
done
入室したことを感知することは容易なのですが,人がいても少し動きがなくなるとセンサが反応しないことがあるため,退室を正確に感知することは難しいという問題があります.そこで今回は30秒間連続でセンサが0の時,人がいないと感知することにします.このあたりは実際の使用状況などを元に調整していく必要があると思いますが,今回はかりに30秒としています.
具体的にiという変数を定義し,それを使うことでC言語のfor分のような使い方をしています.sleep 1を使うことで1秒に1ずつiの値を増加させ,途中でセンサが1になるとその数値をリセットすることで30秒連続でセンサが0だった時のみ以下の動作を行うようにしています.今思ったのですが,30秒は後で調整する可能性があるので,最初に変数を作成して代入しておいたほうが便利ですね.
ちなみに「echo "退室カウント $i /30"」の箇所はiの値を増やしていく処理が狙い通り動いているかを見るための行ですので,省いてしまっても動作には影響ありません.不要であれば#でコメントにしてしまうのも良いと思います.
status1="0"
echo "退室したことをTweetしています..."
gpio -g write $LED 0
ttytter -ssl -status="$message2 `date +%H:%M`"
echo "Tweetしました!"
30秒間センサが反応しなかった時,退室したことをツイートします.こちらも回路側で状態を把握できるよう,LEDを消灯させます.
elif [ $status1 -eq 0 ] ; then
sleep 1
fi
センサが0から0に変化した(変化しなかった)とき,何も行わない,という命令です.ただしこの状態はとても多いので,sleepを入れないとものすごいスピードで処理を繰り返してしまいます.このため1にしています.センサが1から1になるときに10だったのに1にしているのは,入室時の反応をよくするためです.誤作動で0になることは多くとも誤作動で1になることはめったにないので,そういったセンサの特性を考慮した秒数の調整です.
fi
done
最後にifやwhileを閉じて終了です.
シェルスクリプトを書き終わったら前回と同様に実行許可を付与して,実行しましょう.
chmod 755 watchroom.sh
./watchroom.sh
想定通り動いたら自分の家のキッチンや,オフィスの会議室などにおいてみて動作テストを実施してみたいのですが,今の実行の仕方ではssh接続が切れるとシェルスクリプトも止まってしまいます.これはssh接続が切れる際にハングアップシグナルというものがクライアント(Mac)からサーバ(ラズパイ)に送信されるためです[1].これはLinuxの仕様なので仕方がないのですが,今回のようなケースではハングアップシグナルを無視させて,動作させ続ける必要があります.そこで,実行する際にハングアップを無視させるため,頭にnohupをつけてコマンドを実行します.
nohup ./watchroom.sh &
最後の&はこのシェルスクリプトをバックグラウンドで走らせることを表しています.これにより,シェルスクリプト実行中も他のコマンドを実行できるようになります.
これで例えば1日くらい走らせて,想定と違う動作をしてしまうようであれば,退室時のカウントを30から変更してみたり,色々と改良を加えてみてください.
参考文献
[1]ソースコード探検隊「ログアウトしてもバックグラウンド ジョブを継続する方法」(http://www.codereading.com/nb/ignore-the-hangup-signal.html)