窓を遠隔操作で開けれるようにする

私の家にはLOFTがあるのですが、LOFTに上がろうと思うと部屋を片付けてハシゴを出さないといけないので、とてもLOFTに上がるのが億劫です。なのですが、家の酸素濃度とかをwatchしているとLOFTの窓を開けたくなってきたので、遠隔でLOFTの窓を開けれるように頑張ってみました。

ひとりでは到底できなかったと思うのですが、ココナラで何人ものその道のプロの方々にサポートいただいたおかげで、構想から約3か月で完成しました。気軽にプロにアドバイスを求めることができる、素晴らしい時代になったなあと思います。

ちなみに、窓はクルクル回す取手があるタイプの窓です。
左がもとの窓、右が遠隔で開けれるようにした窓の画像です。
取手を外してモーターを取り付けています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849198/d436f286-baa4-5a60-3b82-c6aa7295b747.jpeg" width=30% height=30%>     <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849198/3047e591-d7b1-7add-6d17-df561bd38450.jpeg" width=30% height=30%>

## 前提/設計

### 動作イメージ

1. laptopからラズパイにログイン
1. 窓の開閉センサーの状態を取得するスクリプト`switchtest.py`で開閉状態を1秒毎に出力（ここではclose状態）
1. 窓を開けるスクリプト `motor_open.py`を実行
1. モータードライバに信号が伝わり、7.2Vバッテリーの電圧がモーターにかかる
1. モーターが回り、ギアヘッド、カップリングを伝って、窓の取手が取り付けられている軸が回る
1. 窓が開く
1. 窓の開閉センサーの開閉状態がopenに変わる
1. 窓を開けるスクリプトを止める

※ 窓を閉めるときは同様に窓を閉めるスクリプト  `motor_close.py` を実行
※ 将来的にはswitchbotのリモートボタンを使って開閉させたいと思っています

![mado_gaiyou.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849198/beb05971-b7af-7019-dd98-f475c7be29c3.png)

### 部品

| No. | item | 型番  | メーカー | 費用 | メモ |
|:-:|:-:|:-:|:-:|:-:|:-:|
| a  | 窓 | D-05705-ARJV  | LIXIL | - |  - | 
| b  | カップリング  | MCOC26-6-RDC9  | Misumi  | 2,480円  | 穴のサイズ指定でオーダーできるものにしました。[商品ページ](https://jp.misumi-ec.com/vona2/detail/110300123930/?HissuCode=MCOC26-6-RDC9) |
| c  | ギアヘッド | AO-8052 タミヤギヤヘッドK150  | タミヤ  | 3,300円 |  [商品ページ](https://tamiyashop.jp/shop/g/g89895/)  |
| d  | モーター | AO-8042 ギヤヘッド用 540モーター | タミヤ  |  2,200円 | [商品ページ](https://tamiyashop.jp/shop/g/g89885/)   |
| e  | モータードライバ | TB6612FNG搭載 デュアルモータードライバ（ピンヘッダ付き） | Switch science(?)  | 708円  |  [商品ページ](https://www.switch-science.com/catalog/3586/) |
| f  | バッテリー |  タミヤ 7.2Vレーシングパック1600SPと充電器セット 55096 | タミヤ  | 3,355円  |  [商品ページ](https://www.amazon.co.jp/gp/product/B0019EF1PU) |
| g  | ラズパイ | Raspberry pi 4 model B  | amazonで買いました  | 6,875円  |   |
|   | ラズパイのケース |   | amazonで買いました  | 1,899円  | [商品ページ](https://www.amazon.co.jp/gp/product/B07H2WKYBF/) |
| h  | 開閉センサー |  マグネットスイッチAMS-10C | 竹中エンジニアリング  | 1,700円  | [商品ページ](https://www.amazon.co.jp/gp/product/B00NOUSVGS/) |
|   | 窓開閉機能の固定用金具 | CADを作成し発注 | ココナラで作ってもらったCADをMisumiへ依頼  | 4,000円  |　 |
|  | その他 | ブレッドボード、ジャンパワイヤー、ワニ口クリップワイヤー、SDカード、ネジ |  | 省略 |　|
| 合計  |   |   |   |22,517円    | |

### 回路図

* 簡単な回路図を書いたので置いておきます

![mado_kairozu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849198/b3d57541-9a8d-7aea-c203-243b023a2943.png)


## 作り方

#### 事前準備
* 部品を購入
    * 私が使用した部品の一覧は、上部に記載の通りです
    * 窓によって大きさ等が異なると思うので、部品の選定はプロに相談された方がよいと思います。私はココナラで相談(公開依頼)しました。
    * クルクル回す窓の場合、取手の代わりにカップリングをつけますが、そのカップリングの大きさに合わせてカップリングを選ぶ必要があります
    * モーターを窓枠に固定するための金具もCADで設計してもらいました
    * 
* Raspberry PIの初期設定を実施

https://qiita.com/tosshie/items/d06c9f57ceb340f2d589

#### 開閉センサーの制作
* 開閉センサーをブレッドボード経由でRaspberry PIに接続（回路図参照）
* Raspberry PIに開閉状態を検知するスクリプト `switchtest.py` を作成　（後述）

#### 窓開閉機能の制作

1. 窓の取手を、取手の根本のビスを緩めて外す。
1. 取手をとった場所にある軸の径をノギスで測り、カップリングを注文
1. 届いたカップリング、ギアヘッド、モーターが接続できることを確認
    * カップリングは六角で締めました（サイズがわからなかったので10種類ぐらいの六角がついているやつを買いました）
    * ネジも必要です
1. モーターに充電器から電圧をかけて回ることを確認
1. ラズベリーパイ、モーター、モータードライバをブレッドボード経由で接続する（回路図参照）
1. ラズベリーパイ上にスクリプト `motor_open.py` `motor_close.py` を作成 (後述)
1. スクリプトを動かしてモーターが回ることを確認
1. 窓開閉機能の固定用金具にギアヘッドを固定


#### 設置
1. 開閉センサーを窓に設置
1. スクリプト`switchtest.py` を実行して開閉センサーで開閉状態が検知できることを確認
1. 窓開閉機能を設置場所に置いてみて、軸~カップリング~ギアヘッド~モーターが一直線になることを確認
1. 固定用金具を取り付けるための穴を窓枠にキリで開ける
1. 固定用金具をボルトで固定する
1. カップリングを窓の軸に六角で固定
1. スクリプト`motor_close.py` or `motor_open.py` を実行して窓が閉まる（開く）ことを確認 
1. 完成です！

* こんな感じで設置しています

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849198/52c32444-9be6-b83b-b8e7-829470010669.jpeg" width=30% height=30%>     <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849198/3c39c075-a3d0-09d1-c9db-85c4fab05dbd.jpeg" width=30% height=30%>     <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849198/ff78ece2-4358-678e-4423-fee285954ec4.jpeg" width=30% height=30%>

## スクリプト

#### 窓開閉センサー用スクリプト `switchtest.py` 
* Raspberry PIに開閉状態を検知するスクリプトを作成
    * このページを参照しながらやりました

https://jellyware.jp/kurage/raspi/daiso_sensorlight.html

* ↑のページに記載されているプログラムをコピペして、switchtest.py で保存

※ ↓のプログラムではsleepを1秒に長くしています

``` switchtest.py
import time
import RPi.GPIO as GPIO
 
GPIO.setmode(GPIO.BCM)
 
GPIO.setup(18,GPIO.IN,pull_up_down=GPIO.PUD_UP)
 
sw_status = 1
 
while True:
    try:
        sw_status = GPIO.input(18)
        if sw_status == 0:
            print("Close")
        else:
            print("Open!")
 
        time.sleep(1)
    except:
        break
 
GPIO.cleanup()
print("end")
```

- センサーが近づいているとClose、離れているとopen!が出力されるはずです

```
$ sudo python switchtest.py
Close
Close
Close
Close
Close
Open!
Open!
Open!
Open!
Open!
Open!
Open!
```

定期的に窓の開閉状態を取得するスクリプトも作りました。こちらの記事を参照ください。

https://qiita.com/tosshie/items/0e4a7cb3f8171984dc39

#### 窓を開けるスクリプト `motor_open.py`
* このスクリプトを実行すると、2秒回り2秒スリープを繰り返します
* これがopenなのかcloseなのかは実際に動かしてみて決めました
* ctrl + Cでスクリプトを止めます

```motor_open.py
import RPi.GPIO as GPIO
import time

#a couple of delay constants
leg = 2

#set up control pins for motor driver
STBY = 31
AIN1 = 33
AIN2 = 35
PWMA = 37
BIN1 = 32
BIN2 = 36
PWMB = 38

GPIO.setmode(GPIO.BOARD) #use board pin numbers

#set the GPIO's to outputs
GPIO.setup(STBY, GPIO.OUT)
GPIO.setup(AIN1, GPIO.OUT)
GPIO.setup(AIN2, GPIO.OUT)
GPIO.setup(PWMA, GPIO.OUT)
GPIO.setup(BIN1, GPIO.OUT)
GPIO.setup(BIN2, GPIO.OUT)
GPIO.setup(PWMB, GPIO.OUT)

#set initial condiions, STBY
#is low, so no motors running
GPIO.output(STBY, GPIO.LOW)

GPIO.output(AIN1, GPIO.HIGH)
GPIO.output(AIN2, GPIO.LOW)
GPIO.output(PWMA, GPIO.HIGH)
GPIO.output(BIN1, GPIO.HIGH)
GPIO.output(BIN2, GPIO.LOW)
GPIO.output(PWMB, GPIO.HIGH)

def go_forward(run_time):
    GPIO.output(AIN1, GPIO.LOW)
    GPIO.output(AIN2, GPIO.HIGH)
    GPIO.output(BIN1, GPIO.LOW)
    GPIO.output(BIN2, GPIO.HIGH)

    GPIO.output(STBY, GPIO.HIGH) #start
    time.sleep(run_time)
    GPIO.output(STBY, GPIO.LOW) #stop

def reverse(run_time):
    GPIO.output(AIN1, GPIO.HIGH)
    GPIO.output(AIN2, GPIO.LOW)
    GPIO.output(BIN1, GPIO.HIGH)
    GPIO.output(BIN2, GPIO.LOW)

    GPIO.output(STBY, GPIO.HIGH) #start
    time.sleep(run_time)
    GPIO.output(STBY, GPIO.LOW) #stop

try:
    while True:

#        go_forward(leg)
        time.sleep(leg)

        reverse(leg)

except KeyboardInterrupt:

    GPIO.cleanup()

```

#### 窓を閉めるスクリプト ​`motor_close.py`
* このスクリプトを実行すると、2秒回り2秒スリープを繰り返します
* これがopenなのかcloseなのかは実際に動かしてみて決めました
* ctrl + Cでスクリプトを止めます

```motor_close.py
import RPi.GPIO as GPIO
import time

#a couple of delay constants
leg = 2

#set up control pins for motor driver
STBY = 31
AIN1 = 33
AIN2 = 35
PWMA = 37
BIN1 = 32
BIN2 = 36
PWMB = 38

GPIO.setmode(GPIO.BOARD) #use board pin numbers

#set the GPIO's to outputs
GPIO.setup(STBY, GPIO.OUT)
GPIO.setup(AIN1, GPIO.OUT)
GPIO.setup(AIN2, GPIO.OUT)
GPIO.setup(PWMA, GPIO.OUT)
GPIO.setup(BIN1, GPIO.OUT)
GPIO.setup(BIN2, GPIO.OUT)
GPIO.setup(PWMB, GPIO.OUT)

#set initial condiions, STBY
#is low, so no motors running
GPIO.output(STBY, GPIO.LOW)

GPIO.output(AIN1, GPIO.HIGH)
GPIO.output(AIN2, GPIO.LOW)
GPIO.output(PWMA, GPIO.HIGH)
GPIO.output(BIN1, GPIO.HIGH)
GPIO.output(BIN2, GPIO.LOW)
GPIO.output(PWMB, GPIO.HIGH)

def go_forward(run_time):
    GPIO.output(AIN1, GPIO.LOW)
    GPIO.output(AIN2, GPIO.HIGH)
    GPIO.output(BIN1, GPIO.LOW)
    GPIO.output(BIN2, GPIO.HIGH)

    GPIO.output(STBY, GPIO.HIGH) #start
    time.sleep(run_time)
    GPIO.output(STBY, GPIO.LOW) #stop

def reverse(run_time):
    GPIO.output(AIN1, GPIO.HIGH)
    GPIO.output(AIN2, GPIO.LOW)
    GPIO.output(BIN1, GPIO.HIGH)
    GPIO.output(BIN2, GPIO.LOW)

    GPIO.output(STBY, GPIO.HIGH) #start
    time.sleep(run_time)
    GPIO.output(STBY, GPIO.LOW) #stop

try:
    while True:

        go_forward(leg)
        time.sleep(leg)

#        reverse(leg)

except KeyboardInterrupt:

    GPIO.cleanup()

```