RaspberryPiで人感センサーを作る

# はじめに

「部屋に突然家族が入ってくるのをなんとかできないかな？」と常日頃悩んでいたのだが
ある日部屋に転がっていたRaspberryPiを見てはたとひらめいた。
RaspberryPiは外部入出力を使えるので、センサーを付けて廊下に設置、人通りを感知
したら私の部屋のPCにポップアップを出すことによって、家族の突入を事前に察知できる
のではないかと。  
思春期的な発想ではあるが、思い立ったので作ってみた。  

# 環境

- 本体：[RaspberryPi Type B](http://www.amazon.co.jp/Raspberry-Pi-Type-B-512MB/dp/B00CBWMXVE)
- 人感センサ：[Parallax PIRセンサー Rev.B](http://akizukidenshi.com/catalog/g/gM-06835/)
- 無線LANアダプタ：[Buffalo WLI-UC-GN](http://buffalo.jp/products/catalog/network/wli-uc-gn/)

# RaspberryPiの事前準備

RaspberryPiは箱から出した状態ではOSが入っておらず動かない。
SDカードにOSを準備してRaspberryPiに指すことによって初めて起動する。
そのほかにも色々と初期設定が必要なので、順を追って説明する。

## Raspbianインストール

PCで任意のOSをダウンロードし、SDカードにイメージを展開してそれをRaspberryPiに
マウントするのだが、この手順は他のHPに詳しく記載されているのでそちらを。  
参考：[Raspberry Pi初心者のためのOS別セットアップガイド](http://www.lifehacker.jp/2013/02/130226raspberry_pi_guide.html)
　　：[Raspberry Pi（ラズベリーパイ）にOSをインストール。](http://python-financial-engineering.com/entry/20140904_Raspberry_Pi_OS_Install.html)  

忘れやすいので初期のUserIDとPasswdを書いておく。

- UserID：pi
- Passwd：raspberry  

## Raspbian設定

インストール後の初期設定まで済んだとして、その他の設定をしていく。
ちなみに私の場合は、RaspberryPi Type B なのでUSBポートが2つしかなく、無線LANアダプタを挿したらキーボートかマウスが挿せなくなるので、以降の操作はすべてSSH接続で外部PCから設定した。
RaspberryPi Type B+ならUSBポートが4つなのでそのままいけるが、やはり毎回準備するのが面倒なのでSSHのが便利だと思う。

### システムアップデート

[ここ](http://baticadila.dip.jp/rpi_004.html)を参考にアップデートとアップグレードを実施。
何のために何をどうしているのかはあまり分かっていないが、やった方がいいと思う。

```console
pi@raspberrypi:~$ sudo apt-get update
pi@raspberrypi:~$ sudo apt-get upgrade
```

updateは数分、upgradeは30分以上かかった。

## rootパスワード設定

これも[ここ](http://baticadila.dip.jp/rpi_004.html)を参考に実施。
GPIOを使う際、root権限が必要なので"su"コマンドが使えるようにしておくと楽。

```console
pi@raspberrypi:~$ sudo passwd root
```

## 無線LAN設定

廊下に設置するので無線LAN化は必須。たまたま家に[これ](http://buffalo.jp/products/catalog/network/wli-uc-gn/)があったので設定してみた。
[このページ](http://momijiame.tumblr.com/post/91958442191/raspberry-pi-lan)を参考にした。  

まず、用意した無線LANアダプタを差し込み、/etc/network/interfacesを確認する。

```console
pi@raspberrypi:~$ cat /etc/network/interfaces 
auto lo

iface lo inet loopback
iface eth0 inet dhcp

allow-hotplug wlan0
iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
iface default inet dhcp
```

wlan0が無線LANデバイスらしいので、とりあえず起動時に自動で有効かするよう設定。

```console
pi@raspberrypi:~$  sudo sed -i -e "/^allow-hotplug wlan0$/i auto wlan0" /etc/network/interfaces 
```

実行後、"auto wlan0"が追記されているのが確認できる。

```console
pi@raspberrypi:~$ cat /etc/network/interfaces 
auto lo

iface lo inet loopback
iface eth0 inet dhcp

auto wlan0
allow-hotplug wlan0
iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
iface default inet dhcp
```
ここからは[参考ページ](http://momijiame.tumblr.com/post/91958442191/raspberry-pi-lan)を引用

> 次に無線 LAN の認証・暗号化周りを設定する。 無線 LAN の SSID とパスフレーズの設定に> は wpa_passphrase コマンドを使うと良い。 コマンドから設定内容が生成されるので設定ファイルに追記する。

```console
pi@raspberrypi:~$ wpa_passphrase <ssid> <passphrase> | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf
```

ssidには無線親機のSSIDを、passphraseには暗号化キーを入れる。

> 実行後の設定ファイルは以下のようになっているはず。 パスフレーズを示す psk の内容は平文ではなくなっている。 # でコメントアウトされている平文のパスフレーズの方は削除してしまって構わない。

```console
pi@raspberrypi:~$ sudo cat /etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
        ssid="<ssid>"
        #psk="<passphrase>"
        psk=********
}
```

[参考ページ](http://momijiame.tumblr.com/post/91958442191/raspberry-pi-lan)では認証方式等設定しているが、今回は設定せず進めた。
これで設定完了なので、RaspberryPiを再起動する。再起動時、有線LANは抜いておくこと。

```console
pi@raspberrypi:~$ sudo shutdown -r now
```

## Samba設定

前述のように、SSH接続で外部PCから作業するため、ファイル共有のための共有フォルダを設定する。
[このページ](http://www.hiramine.com/physicalcomputing/raspberrypi/samba_installconfigconnect.html)を参考に設定した。  

```console
pi@raspberrypi:~$ sudo apt-get install samba
```

参考ページでは編集は"nano"でしているが、慣れている"vi"でやる。

```console
pi@raspberrypi:~$ sudo vi /etc/samba/smb.conf
```

Sambaの設定ファイルを開き、設定ファイルの末尾に以下を追加。

```
[pi]
path = /home/pi
read only = No
guest ok = Yes
force user = pi
```

Sambaのサービスを再起動して設定完了。

```console
pi@raspberrypi:~$ sudo service samba restart
```

再起動完了すると、RspberryPiの/home/piフォルダは外部PCと共有フォルダとなる。
外部PCのエクスプローラから"\\192.168.##.##"(RaspberryPiのIP)を入力すると、通常通りファイルを扱うことができる。

# センサー取り付け

ここまででソフト側の準備が整ったので、いよいよRaspberryPiにセンサーを取り付けていく。
この[書籍](http://www.oreilly.co.jp/books/9784873116372/)を参考に実施した。

## GPIOピンへ取り付け

今回使用したRIPセンサーはたまたま動作電源が3～5Vだったので、GPIOピンとセンサーをジャンパピン(ﾒｽ-ﾒｽ)で直接繋いだ。ブレッドボードを用意しなくていいのはとてもありがたい。  
繋ぐ場所は下記の通り。

|センサー|GPIOピン|
|:-:|:-:|
|GND|－|
|VCC|3.3v|
|OUT|25|

![rpigpiopinoutsx600px.png](https://qiita-image-store.s3.amazonaws.com/0/59636/b8d55099-4333-af50-f9d2-95da43f56b77.png)

正しく接続できていると、この時点でセンサーが反応して赤く光ることが確認できる。

## 動作確認

コマンドラインからGPIOにアクセスするためには、root権限がいるためsuコマンドでrootになっておく。

```console
pi@raspberrypi:~$ sudo su
root@raspberrypi:/home/pi# 
```

GPIOで25ピンを使うので、GPIO25をユーザー空間にエクスポートする。
これをしないとGPIOにアクセスすることができない。

```console
root@raspberrypi:/home/pi# echo 25 > /sys/class/gpio/export
```

echoコマンドで/sys/class/gpioディレクトリにあるexportファイルへ書き込むと/sys/class/gpio/gpio25という新しいディレクトリが作成され、GPIOをコントロールするために必要な仮想ファイルが用意される。
今回はセンサーからの入力を判定するので、directionファイルにinを設定する。

```console
root@raspberrypi:/home/pi# cd /sys/class/gpio/gpio25
root@raspberrypi:/sys/class/gpio/gpio25# echo in > direction
```

これで、センサーに反応があるとvalueファイルに1、反応がなくなると0が書き込まれる。
センサーを反応させて下記コマンドを実施すると、valueに1が書き込まれ動作確認ができる。

```console
root@raspberrypi:/sys/class/gpio/gpio25# cat value
1
```

## スクリプト作成

ここまででやっと環境が整ったので、いよいよスクリプトを書いていく。
今回の目的を簡単にまとめると・・・  
1. 人が通るとセンサーから入力がありRaspberryPiが検知
2. RaspberryPiから指定したIPに文字列を送信
3. HostPC側が文字列を受け通知を出す

Raspbianには元々Pythonが入っており、また、GPIOを制御するパッケージも用意されているためPythonで書いていく。

色々と検討したが、とりあえず下記のコードで動いた。  
RaspberryPiで実行するスクリプト

```py:senser.py(RaspberryPi側)
from __future__ import print_function
import socket
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(25, GPIO.IN)
from contextlib import closing

def main():

  while True:
    inputValue = GPIO.input(25)
    if (inputValue == True):
      send()
    time.sleep(1)

def send(): #送信処理

  host = '192.168.1.0' #送信先IP
  port = 8080 '送信先Port
  bufsize = 4096

  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  sock.settimeout(0.5) #接続タイムアウト値設定
  
  with closing(sock):
    try:
      sock.connect((host, port))
      sock.send(b'raspberry')
    except socket.timeout:
      print(b'socketTimeout')
    except socket.error:
      print(b'socketError')
    time.sleep(5)
  return

if __name__ == '__main__':
  main()
```

ホストPCで実行するスクリプト

```py:host.py
from __future__ import print_function
import socket
import time
from contextlib import closing

from ctypes import *

def main():

  while True: #ずっと繰り返し
    recv()
    time.sleep(1) #一秒止める

def recv(): #受信処理
  host = '192.168.1.0' #自分のIP
  port = 8080 #任意のIP
  backlog = 10
  bufsize = 4096
  socket.timeout(1)
  
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  try:
    with closing(sock):
      sock.bind((host, port))
      sock.listen(backlog)
      conn, address = sock.accept() 
      msg = conn.recv(bufsize)
      if (msg == 'raspberry'): #識別文字列が一致したら表示処理
        popup()
        
  except socket.timeout:
    print(b'socketTimeout')
  except socket.error:
    print(b'socketError')

def popup(): #Windows画面にメッセージを出す
  user32 = windll.user32
  user32.MessageBoxA( 0, "Enemy Will Come Here!!", "Caution!!", 0x00000030)
  
if __name__ == '__main__':
  main()
```

上記を同時に実行しておくと、センサーが反応した際、メッセージボックスが表示される

# スクリプト自動実行設定

動作確認ができたので、実用時に面倒くさくないよう自動実行できるようにする。HostPC側は適当にバッチファイルを作ってスタートアップに入れればできたがRaspberryPi側がよくわからなかったのでメモっておく  

[このページ](http://tokunn.hateblo.jp/entry/2014/05/31/105531)を参考に設定した  

まず、/etc/init.dにスクリプトを置いておくと、RaspberryPi起動時に自動実行されるらしい
Windowsのスタートアップと同じようなイメージなのかな
なので、そこに"autorun_script"ファイルを作って、sencer.pyを起動させる内容を書いておく

```console
pi@raspberrypi:~$ cd /etc/init.d/
pi@raspberrypi:/etc/init.d$ sudo vi autorun_script
```

autorun_scriptに記載する内容は下記

```sh
        #!/bin/sh


        sudo python /home/pi/Python/sencer.py


        while true
        do
            :
        done
```

"/home/pi/Python/sencer.py"の箇所にはスクリプトのパスを入れる  
あとは実行権限を付与して完了

```console
pi@raspberrypi:/etc/init.d$ sudo chmod 755 autorun_script
pi@raspberrypi:/etc/init.d$ sudo update-rc.d autorun_script defaults
```

# 所感

この後、WinAPIではださいので他のツールを使ってポップアップをおしゃれなものに変えたりしたがまた後日書き足すことにする。
また、TCP/IPでの送受信で実装したが、HerokuとかGoogle app Engineとかのクラウドでやっている人がいるみたいなので、今後そちらも検討したい。

--------------------------------------------
### 追記

順調に運用していたセンサーが先日突如として動かなくなり、家族の部屋への事前察知ができず、とんでもない事態に発展してしまった

原因を調べたところ、RaspberryPiもクライアントPCもIPアドレスを固定していなかったため、ソケット通信が失敗していたためであった  

DHCPでの自動割り当てでもIPなんてそうそう変わらないでしょwwとタカをくくっていた自分を呪いたくなったが、とりあえずRaspberryPiのIP固定の方法を追記する  

[このページ](http://www.hiramine.com/physicalcomputing/raspberrypi/wlan_howto.html)を参考に（というかそのまんま）下記変更を入れた  

```console
pi@raspberrypi:~$ sudo nano /etc/network/interfaces 
auto lo

iface lo inet loopback

iface eth0 inet static
address 192.168.1.111
netmask 255.255.255.0
gateway 192.168.1.1

allow-hotplug wlan0
iface wlan0 inet static
address 192.168.1.121
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

iface default inet dhcp
```

それぞれの"address"を任意に変更し、RaspberryPiを再起動すれば完了
たったこれだけのことを疎かにしたがために、大きな代償を払うことがある
今回得た教訓だが、代償が大きすぎた