スマートじゃないDIYスマートホーム

[FUJITSU Advent Calendar 2017](https://qiita.com/advent-calendar/2017/fujitsu) 8日目の記事です。
昨年は[SI現場でいろいろ作った話](http://yamaguchi-39.hatenablog.com/entry/demo-or-die)を書きました。今回は仕事とは無関係。

### こんなものが出来上がります

（動画は音が鳴ります）

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">DIYスマートホーム デモ動画 <a href="https://t.co/tZt7Csqpz6">pic.twitter.com/tZt7Csqpz6</a></p>&mdash; Yamaguchi Takuya (@yamaguchi_39_jp) <a href="https://twitter.com/yamaguchi_39_jp/status/937179342303457281?ref_src=twsrc%5Etfw">2017年12月3日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



人間はぼけーっと起きただけで何もしていません。

### とある未来の家のお話…

> 朝、起床時間。
> 
> 照明が徐々に明るくなり、カーテンがゆっくりと開く。
> すべて機械が自動的にやってくれる。そろそろテレビの電源が入るだろう。タイミングはバッチリ。
> 
> 柔らかな日射しに、自然と目が覚める。爽やかな朝だ。

#### …という夢を見たんだ。

10年以上前、IT系の雑誌でこんな話を読んだ記憶があります。
しかし残念なことに、一人暮らしの私の部屋にその未来は訪れていません。

でも、ちょっとでもそういう未来に近づいてみたい。
というわけで、Raspberry Piを使って手作り感あふれる「DIY未来」を作ってみました。

### 材料

|品名|価格|備考 (私が買ったもの)|
|:--|--:|:--|
|Raspberry Pi 3 Model B|\5,000|ネットワーク接続できれば別モデルでもOK|
|ブレッドボード|\200|[BB-801 (秋月 P-05294)](http://akizukidenshi.com/catalog/g/gP-05294/)|
|ジャンパーワイヤ (オス-オス)|\180|[秋月 C-05371](http://akizukidenshi.com/catalog/g/gC-05371/)|
|ジャンパーワイヤ (オス-メス)|\220|[秋月 C-08934](http://akizukidenshi.com/catalog/g/gC-08934/)|
|人感センサ(焦電型赤外線センサ)|\500|[SB412A (秋月 M-09002)](http://akizukidenshi.com/catalog/g/gM-09002/)|
|ACアダプタ 5V (0.5A～)|\650|[GF12-US0520 (秋月 M-01801)](http://akizukidenshi.com/catalog/g/gM-01801/)|
|DCジャック DIP化モジュール|\313|[マルツエレック DC-POWER-JACK基板(MDK024)](https://www.marutsu.co.jp/pc/i/833005/)|
|電解コンデンサ 100μF|\10|[ルビコンPX 100μF/16V/105℃ (秋月 P-10271)](http://akizukidenshi.com/catalog/g/gP-10271/)|
|サーボモータ|\400|[SG-90 (秋月 M-08761)](http://akizukidenshi.com/catalog/g/gM-08761/)|
|USB オーディオ変換アダプタ(DAC)|\1,220|[BUFFALO BSHSAU01BK](http://www.yodobashi.com/product/100000001001525009/)|
|スピーカ|-|(PC用スピーカを流用)|
|オーディオケーブル|-|(余ってたものを流用)|

#### 各材料について

<dl>
  <dt>ブレッドボード、ジャンパーワイヤ</dt>
  <dd>Raspberry Piと各電子部品との接続に用います。</dd>
  <dt>人感センサ</dt>
  <dd>人(熱源)が動くと反応します。</dd>
  <dt>サーボモータ</dt>
  <dd>普通のモータと違い、"指示した位置(角度)まで動いてくれる"モータです。照明をつけるのに使います。</dd>
  <dt>ACアダプタ、DCジャック、電解コンデンサ</dt>
  <dd>サーボモータへの電源供給用です（コンデンサは動作安定化のため）。照明をつけるのに使います。なぜDCジャックだけマルツが登場するのかというと、ハンダ付け済みだからです（手抜き）。<br>※RasPiの+5Vで事足りるかもしれませんが未検証です。</dd>
  <dt>USB オーディオ変換アダプタ(DAC)、スピーカ、オーディオケーブル</dt>
  <dd>音楽や音声を流すのに使います。<br>USB DACを用意するのは、サーボモータでPWM機能を使うとRasPiのアナログ音声出力が使えなくなるためです。</dd>
</dl>

### 1. 起きたらクラシックを流し、帰ったら「おかえり」と喋ってもらう

最初に簡単なところから。人感センサに反応して音声再生をさせます。

#### 人感センサとスピーカを繋ぐ

![SB412A](https://qiita-image-store.s3.amazonaws.com/0/152552/a716e88d-5f80-a752-e70a-117891413bcf.jpeg)

まずは人感センサを繋ぎます。SB412Aのピンそばに表示があるので、確認しつつ接続します。

|SB412Aのピン表示|Raspberry Piのピン|補足|
|:--|:--|:--|
|"+"|+5V|+5Vは2箇所あるがどちらでもOK。+3.3Vでも動作|
|"VO" (Vout)|GPIO4|任意のGPIOでもOK|
|"-"|GND|GNDは複数箇所あるがどこでもOK|

![ブレッドボード図](https://qiita-image-store.s3.amazonaws.com/0/152552/0aab9167-919e-dd65-b9ef-323eec9f03eb.jpeg)

そして、Raspberry PiにUSB オーディオ変換アダプタ(DAC)を接続し、さらにスピーカの入力端子とをオーディオケーブルで繋ぎます。ちなみにDACへ出力するためにはちょっと工夫がいります(参考:[Rapberry Pi に USB DAC をつないでオーディオサーバーにする (接続編) - Qiita](https://qiita.com/henri_t/items/33a514ae4ac0fda89ee0))。

#### スクリプトを書く

人感センサの出力(Vout)は通常LOWで、熱源が動くとしばらくHIGHになります。
これを利用して、長時間センサがLOWのままだと就寝・外出とみなし、そこからHIGHになったタイミングで起床・帰宅と判断します。

スクリプトはPythonで。
GPIOの入力はRPi.GPIOモジュールを、音声再生には[omxplayer](https://elinux.org/Omxplayer)をsubprocessモジュールで実行しています。omxplayerではなくmpg321等でもかまいません。

```
#!/usr/bin/python3
# -*- coding: utf-8 -*-

# -- 設定
pin = 4                           # センサのGPIOピン
welcome_sound_seconds = 180 * 60  # 外出・就寝したとみなす無反応時間(秒)
watch_interval_seconds = 5        # 監視間隔(秒)

# -- スクリプト
import RPi.GPIO as GPIO
from time import sleep
import subprocess
import datetime

GPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.IN)

off_seconds = 0
away_from_room = False

try:
	while True:
		# 経過時間を測定
		value = GPIO.input(pin)
		if value == 0:
			off_seconds += watch_interval_seconds
		else:
			off_seconds = 0
		
		if away_from_room == False and off_seconds >= welcome_sound_seconds:
			# 外出モードオン
			away_from_room = True

			print(datetime.datetime.now())
			print("value:", value, ", off_seconds:", off_seconds)
			print("away:", away_from_room)

		if away_from_room == True and off_seconds == 0:
			# 外出モードオフ
			away_from_room = False

			hour = datetime.datetime.now().hour
			if hour > 4 and hour < 12:
				subprocess.Popen(['omxplayer', '-o', 'alsa', '/home/pi/Music/peergynt_morning.mp3'])
			else:
				subprocess.Popen(['omxplayer', '-o', 'alsa', '/home/pi/Music/welcome.mp3'])

			print(datetime.datetime.now())
			print("value:", value, ", off_seconds:", off_seconds)
			print("away:", away_from_room)

		sleep(watch_interval_seconds)

except KeyboardInterrupt:
	pass
finally:
	GPIO.cleanup()
```

音声ファイルはscp(WinSCP)などでRaspberry Piに転送しておきます。
また、omxplayerが入っていなければインストールしておきます。

```
$ sudo apt-get install omxplayer
```

#### 人感センサの設置場所を決める

下記を満たすような、絶妙な場所を探して配置します。

- 就寝時には反応しない
- 起床時、帰宅時には反応する
- 在宅中にも「外出・就寝とみなす無反応時間（3時間）」以内にちょくちょく反応する

データシートでは、検出角度は100度、最大検出距離は3～5mとなっています。この辺も加味します。

#### デモンストレーション

無反応時間を10秒に設定して確かめてみます。よい感じです。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">DIYスマートホーム 音声再生サンプル <a href="https://t.co/xncQEKlPNH">pic.twitter.com/xncQEKlPNH</a></p>&mdash; Yamaguchi Takuya (@yamaguchi_39_jp) <a href="https://twitter.com/yamaguchi_39_jp/status/937179931183681537?ref_src=twsrc%5Etfw">2017年12月3日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

※動画では権利的に微妙なのでゆっくりに喋ってもらいましたが、実際はトールとカンナちゃん(小林さんちのメイドラゴン #11)が「おかえり(なさい)」と言ってくれます。最高です。

### 2. テレビじゃなくてPCの電源を入れてもらう

PCがないと落ち着かない人間なので、人感センサに反応したらPCの電源を入れてもらいます。

ここでは[Wake on LAN](https://ja.wikipedia.org/wiki/Wake-on-LAN)機能を使います。
ネットワーク越しに特殊なパケットを送りつけることで電源が入ります。

#### PCのWake on LAN(WoL)を有効にし、MACアドレスを確認する

BIOS・OS・NIC(ドライバ)によって設定方法が異なるので、手順は端折ります。
MACアドレスは ipconfig /all (Windows) とかで。

※Windows 10+Realtek(RTL8111F) NICの組み合わせだと、設定を有効にしても機能せず、[公式サイト](http://www.realtek.com.tw/default.aspx)から最新版ドライバをインストールしたらやっと機能した、ということがありました。

#### スクリプトを書く

まずは wakeonlan をインストールしておきます。

```
$ sudo apt-get install wakeonlan
```

さっきのスクリプトの「外出モードオフ」のところに、wakeonlanコマンドを実行するようにコードを追加します。

```
		if away_from_room == True and off_seconds == 0:
			# 外出モードオフ
			away_from_room = False

			hour = datetime.datetime.now().hour
			if hour > 4 and hour < 12:
				subprocess.Popen(['omxplayer', '-o', 'alsa', '/home/pi/Music/peergynt_morning.mp3'])
			else:
				subprocess.Popen(['omxplayer', '-o', 'alsa', '/home/pi/Music/welcome.mp3'])

			print(datetime.datetime.now())
			print("value:", value, ", off_seconds:", off_seconds)
			print("away:", away_from_room)
			
			# PCの電源を入れる
			subprocess.Popen(['wakeonlan', '00:00:5E:00:53:01'])
```

### 3. 照明をつけてもらう

最後に、照明をつけてもらうようにします。

といっても、電気工事とかは怖くてできません。
そこで、サーボモータに照明のスイッチを押してもらいます。

#### サーボモータを繋ぐ

サーボモータ、ACアダプタ、DCジャック、コンデンサを繋ぎます。
※電解コンデンサは極性があるので+-逆に繋がないよう注意します。

|SG-90のワイヤ色|接続先|補足|
|:--|:--|:--|
|橙 (PWM)|RasPi GPIO18|GPIO18限定。PWM機能を使用|
|赤 (+)|DCジャック +||
|茶 (-)|DCジャック - と RasPi GND|RasPi GNDは複数箇所あるがどこでもOK|

![qiita_2_ブレッドボード_.png](https://qiita-image-store.s3.amazonaws.com/0/152552/16eab979-6d43-6315-2d17-b41ea5309e54.png)

#### サーボモータの動かし方

サーボモータを動かすには"PWM"を使います。
一定周期で信号のON/OFFを繰り返すのですが、そのON/OFFの比率(デューティ比)によって、サーボモータの位置を指示できます。

#### サーボモータの設置場所とスクリプトを試行錯誤する

「スイッチを押す」という動きを実現するには、試行錯誤するしかありません。
[Raspberry Piでサーボモータ（SG90）を制御する|Buffer Overruns](http://bufferoverruns.blogspot.jp/2016/08/raspberry-pisg-90.html) を参考にしつつ、サーボモータの設置場所と指示角度・指示タイミングを決めます。

![20171202_PC020950.jpg](https://qiita-image-store.s3.amazonaws.com/0/152552/af157cf0-4172-ca7b-d6bd-21886f221fc1.jpeg)

※サーボモータには、付属する白い部品（ホーン[Horn]:角）をつけておきます。

余っているダンボールとセロハンテープで固定。急に手作り感が出てきました。
そしてPythonスクリプトを書いては調整を繰り返し、安定してスイッチが押せるようになりました。

```
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

gp_out = 18
GPIO.setup(gp_out, GPIO.OUT)
servo = GPIO.PWM(gp_out, 50) 

servo.start(0.0)

time.sleep(0.2)

servo.ChangeDutyCycle(9)
time.sleep(0.2)

servo.ChangeDutyCycle(11.5)
time.sleep(0.2)

servo.ChangeDutyCycle(9)
time.sleep(0.2)

servo.stop()
```

#### サーボモータのようす

![サーボモータ動作](https://qiita-image-store.s3.amazonaws.com/0/152552/8440eef0-1393-5ed6-9556-e56627d479c5.gif)

がんばってスイッチを押してくれています。

#### スクリプトを書く

GPIO18をPWM出力として初期化し、サーボモータを動かす処理を組み込みました。

```
#!/usr/bin/python3
# -*- coding: utf-8 -*-

# -- 設定
pin = 4                           # センサのGPIOピン
pwm_pin = 18                      # サーボモータのPWM制御ピン
welcome_sound_seconds = 180 * 60  # 外出・就寝したとみなす無反応時間(秒)
watch_interval_seconds = 5        # 監視間隔(秒)

# -- スクリプト
import RPi.GPIO as GPIO
from time import sleep
import subprocess
import datetime

GPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.IN)
GPIO.setup(pwn_pin, GPIO.OUT)

servo = GPIO.PWM(pwn_pin, 50) 

off_seconds = 0
away_from_room = False

try:
	while True:
		# 経過時間を測定
		value = GPIO.input(pin)
		if value == 0:
			off_seconds += watch_interval_seconds
		else:
			off_seconds = 0
		
		if away_from_room == False and off_seconds >= welcome_sound_seconds:
			# 外出モードオン
			away_from_room = True

			print(datetime.datetime.now())
			print("value:", value, ", off_seconds:", off_seconds)
			print("away:", away_from_room)

		if away_from_room == True and off_seconds == 0:
			# 外出モードオフ
			away_from_room = False

			hour = datetime.datetime.now().hour
			if hour > 4 and hour < 12:
				subprocess.Popen(['omxplayer', '-o', 'alsa', '/home/pi/Music/peergynt_morning.mp3'])
			else:
				subprocess.Popen(['omxplayer', '-o', 'alsa', '/home/pi/Music/welcome.mp3'])

			print(datetime.datetime.now())
			print("value:", value, ", off_seconds:", off_seconds)
			print("away:", away_from_room)
			
			# PCの電源を入れる
			subprocess.Popen(['wakeonlan', '00:00:5E:00:53:01'])
			
			# サーボモータを動かす
			servo.start(0.0)
			sleep(0.2)
			servo.ChangeDutyCycle(9)
			sleep(0.2)
			servo.ChangeDutyCycle(11.5)
			sleep(0.2)
			servo.ChangeDutyCycle(9)
			sleep(0.2)
			servo.stop()


		sleep(watch_interval_seconds)

except KeyboardInterrupt:
	pass
finally:
	GPIO.cleanup()
```

### 完成: 未来がやってきた！

出来上がりました。動画は冒頭に載せたので省略。
既に1ヶ月ほど運用していますが、安定して動いています。照明スイッチの押し損ねもありません。

見た目は不格好でスマートさのかけらもありませんが、DIYで未来がやってきました！

### おわりに

#### 意外と便利、何より楽しい

「勢いで買ったけどどうしよ…」と思っていましたが、割と便利なものが出来ました。
既製品では出来ないヘンテコな動きも自由にできるのが良いです。

また、ソフトウェアだけでは出来ない「物理的なフィードバック」は非常に楽しいです。

#### 代償: お金と時間

あんまり楽しいので、2ヶ月くらい毎週秋月に通っていました。
ここに載せていない電子部品も色々買っていて、合算すると結構な額に…

#### もっと未来へ

まだまだ何かできそうです。
最近スマートスピーカが流行りだしてきたので、ラズパイさんに音声認識で電気を消してもらいました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">『ラズパイさん、電気消して』 <a href="https://t.co/yZyt0U2XCR">pic.twitter.com/yZyt0U2XCR</a></p>&mdash; Yamaguchi Takuya (@yamaguchi_39_jp) <a href="https://twitter.com/yamaguchi_39_jp/status/937203404949962754?ref_src=twsrc%5Etfw">2017年12月3日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

完璧…！　と思ったのですが、実際に運用すると3回に1回しか反応しませんでした。悲しくなったので使っていません。

うーん、次は何をしようかな。

### 参考

- 秋月電子通商のWebサイトから参照できるデータシート各種 (材料のリンクから参照ください)
- [Raspberry Piに焦電型赤外線（人感）センサーを接続してみた - Qiita](https://qiita.com/aryoa/items/72c40f241f868e25eb71)
- [家に帰ると小林さんちのメイドラゴンのカンナちゃんが「おかえりー」って言ってくれる装置を作った - Qiita](https://qiita.com/tsumugu/items/cba104dbf1c4f86276cf)
- [Raspberry Piでサーボモータ（SG90）を制御する|Buffer Overruns](http://bufferoverruns.blogspot.jp/2016/08/raspberry-pisg-90.html)
- [Raspberry PiのPWMはアナログ音声出力と共用 - Qiita](https://qiita.com/no_clock/items/ee216bb64b7fce1553f9)
- [Rapberry Pi に USB DAC をつないでオーディオサーバーにする (接続編) - Qiita](https://qiita.com/henri_t/items/33a514ae4ac0fda89ee0)
- [Julius 独自辞書作成方法 - Qiita](https://qiita.com/satosystems/items/4d379e57d2370177ead1)
- [文書に記述する際の説明用/例示用のドメイン名、IPアドレス、MACアドレス RFC6890,RFC5737,RFC3849,RFC2606,RFC6761,RFC7042 IANAなどなど – Abacus Technologies Blog – kana.me 要](https://kana.me/entry/reserved-for-documentation)

----

これは個人の見解であり、所属する組織の公式な見解ではありません。