RaspberryPiを使って、インターホンを押すとLINEに通知が行く仕組みを1からつくってみた

#はじめに
ある日、まだコロナの影響が広がる前、通っているプログラミング教室を
受講生で埋め尽くすくらい人がいた時のこと。

「この状況で新規の受講生が来た時、講師の人は対応に困らないだろうか…」

ふと、そんなことを考えることがありました。

（ある日、YouTubeを見てた時・・・）
 :bust_in_silhouette:某YouTuber
「今日はこの**RaspberryPi**を使って、**IoT**に挑戦して見たいと思います。」

.
.

**心の声『これやん…』**

と言うことで、作ってみました。

##ざっくりとしたイメージ
![スライド2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/c5e92dd0-0ebb-8a59-3b58-6eada9f7a2df.png)
このような感じのものを作っていきます。

##環境

- 本体：[Raspberry Pi 4 4B-32GB](https://www.amazon.co.jp/gp/product/B082VVJCPT/ref=ppx_yo_dt_b_asin_title_o07_s00?ie=UTF8&psc=1)
- インターホン：[ドア出口スイッチ プッシュエンドリリースボタンスイッチパネル](https://www.amazon.co.jp/gp/product/B07L6BSCZH/ref=ppx_yo_dt_b_asin_title_o06_s00?ie=UTF8&psc=1)
- PC：MacBook Pro(OS:catalina 10.15.5)
- ついでに：[Electronic Fun キット](https://www.amazon.co.jp/gp/product/B06Y4LRHJK/ref=ppx_yo_dt_b_asin_title_o07_s01?ie=UTF8&psc=1)
:point_left: これはテスト用に使用した為、無くてもOK
- 私：RaspberryPi、Pythonを初めて触る。Ruby on RailsでWebアプリが作れる程度

##RaspberryPiの準備
RaspberryPiの初期設定の記事を漁っていたら、
有線のマウスとかキーボードがいるとかなんとかの情報が…

**いや、持ってないがな！**

何とか、出来ないものか…　
すぐにでも初期設定を完了させてRaspberryPiを触りたい…
マウス、キーボードレスで出来ないか…

.

適当に調べていたら、いい記事を発見！
[ラズパイ(raspberry pi)のセットアップをするよ(win/mac対応)](https://sukiburo.jp/setup-raspberry-pi/#OS):point_left:

これがすごく丁寧に解説してくれて、本当にわかりやすかったです。（感謝）
ただ、自分の知識不足のせいで、つまづいたことが何点かあったので、補足を加えます。

###SSH有効化
![スクリーンショット 0002-08-15 午後2.41.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/19de05bf-21e3-b805-d600-386ad2bb3932.png)


ただ、「ssh」という名前のファイルを作るだけなのに、
**ダウンロード出来んやん！　え、どうすると？**
と1人で1時間ほど格闘していました。

やることは単純で、SDカードのフォルダ（boot）の中に「ssh」という名前の
ファイルの作るだけです。

```terminal
touch /Volumes/boot/ssh
```
上記をターミナルに入力してreturn（enter）
もしくは、フォルダを開いて直接ファイルを作る方法もあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/cc7ec700-534e-ed2e-8b2e-d73ceb108d2f.png)

###Wi-Fiの有効化
これもまた、参考のサイト上でダウンロードがうまくいかなくて苦戦。

```
nano /Volumes/boot/wpa_supplicant.conf
```
ターミナルに同様、入力してreturn（enter）
つくったファイル（wpa_supplicant.conf）を開いて、中身を下記の通りにします。

```terminal
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
country=JP
update_config=1

network={
    ssid="aaa"
    psk="bbb"
}
```

**はい、そのままコピペしたものだからうまくいきませんでした！！**

####ssidとpskがわからなかった…

ssidって何それ、おいしいの？
調べてみたら、無線LAN（Wi-Fi）におけるアクセスポイントの識別名だと。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/ec91d0ac-4b3b-820b-bebd-333e66db7974.png)

こんなことも知らなかった自分が恥ずかしい…
しかし、いい勉強になりました。もう、これでssidは怖くないぞ！

要は、ご家庭のルーターに貼ってあるシールに「ssid」と「psk」が書いてあるので、
今回の例だとssidを"aaa"に、pskを"bbb"に代入してあげるだけです。

あとは、参考のサイトに記載してあることを手順通りに実行して初期設定を完了させます。

##ボタン:left_right_arrow:RaspberryPiの構成
回路図は下記の通りで、RaspberryPiはGPIO２番ピンとGNDを、
ボタンは一般的なA接点と呼ばれる、押したら繋がるものを使用しました。
回路内にコンデンサが付いているのは、「チャタリング防止」なので、
無くても問題はないですが、お気持ち程度に設置しました。

チャタリングとは、スイッチON/OFF直後に出力が短い時間 ON/OFFを繰り返す現象のことで
「ノイズ」というイメージですかね。
（この分野に関してはあまり詳しくないので、表現に間違いがあればご指摘願います。）
![スライド1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/a81da624-390b-a582-b609-99916f8f4e06.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/7fdfcf60-0bf3-f482-d82f-185797464989.png)
###RaspberryPiのピン出力を読み取る

####その前に、コードを書けるように準備しましょう

まず、RaspberryPiにログインしてコードを書いていきます。
ログイン完了後、左上のラズベリーマークをクリックし、
すぐ下の「プログラミング」をクリック。
格納してある「Thonny Python IDE」を起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/69c5d884-c598-f2c4-af5c-d470d6552ca6.png)
「New」をクリックして新規ファイルを作成して、コードを書ける準備をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/94e87c6e-200a-3819-659d-3f0b21716807.png)

###では、コードを実際に書いていきます

```python:test.py
# coding: UTF-8
import RPi.GPIO as GPIO                                         
import time
import requests

GPIO_INPUT = 2

GPIO.setmode(GPIO.BCM) 
GPIO.setup(GPIO_INPUT, GPIO.IN) 
        
try:
    while True:
        if GPIO.input(GPIO_INPUT) == GPIO.LOW:
            print('Customer is here')
            time.sleep(0.1)
        else:
            print("runnning")
            time.sleep(0.1)
except KeyboardInterrupt:
    print("stopped")
  GPIO.cleanup()
```

###そんな、呪文のようなもの書けと言われても…

.

**では、中身を区切って、上から順番に確認してみましょう。**

.

###Pythonさんに取り込んで欲しいこと（インポート）を教えます

```
import RPi.GPIO as GPIO                                         
import time
import requests
```
|コード|意味|
|---|---|---|
|import RPi.GPIO as GPIO |GPIOにアクセスするライブラリをインポートします。|
|import time |タイマーをインポートします。今回はtimeのsleepをインポートしています。|
|import requests |HTTP通信用のライブラリをインポートします。今回はLineNotifyに通知を送るのでこちらを記述します。|

###「このピンを使うよ」と宣言します

```
GPIO_INPUT = 2

GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_INPUT, GPIO.IN)
```
|コード|意味|
|---|---|---|
|GPIO_INPUT = 2|GPIO_INPUTに2番を設定します|
|GPIO.setmode(GPIO.BCM)|GPIOへアクセスする番号をBCMの番号で指定します|
|GPIO.setup(GPIO_INPUT, GPIO.IN)|BCM 2番ピンを入力に設定します|
####GPIOとは
GPIOは「General Purpose Input/Output」の略で、
汎用のインプット・アウトプットのことです。

####BCMとは
BCMは「Broadcomモード」の略です。
ちなみに、RaspberryPiのボード(SoC)は米国のBroadcom社によって設計されているそうです。
RaspberryPi配置に注目すると、不連続なGPIOの割り当てがありますよね。 
それが「BCMモード」なのです。
[参考：ラズベリーpi bcm vsボード](https://ja.bccrwp.org/solution/raspberry-pi-bcm-vs-board/)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/ae268454-be09-e1d6-b5c1-5176df650220.png)


###「ボタンが押されたらこうしてよ」と指示します

```
try:
    while True:
        if GPIO.input(GPIO_INPUT) == GPIO.LOW:
            print('Customer is here')
            time.sleep(0.1)
        else:
            print("runnning")
            time.sleep(0.1)
except KeyboardInterrupt:
    print("stopped")
  GPIO.cleanup()
```
if文でピンの電圧状態読み取る(ボタンが押されたか)判定を行っています。
今回はGPIO_INPUT(GPIO2番ピン)の状態を読み取って、状態に応じた処理を実行します。

ボタンが押された場合、GPIO２番ピン、ボタン、GND間で回路が繋がります。
このボタンを通る、GPIO２番ピン〜GND間の電圧は０Vになります。
詳しい話をすると、電子回路について補足が必要になってくるので、そうなるんだでOKです。
詳しく知りたい方は私が過去にteratailで質問した記事が参考になると思います。
[リンク：RaspberryPiで回路が導通すると0Vになる理由が知りたいです。](https://teratail.com/questions/286408)

スイッチが押されては回路が０Vになった時、
if GPIO.input(GPIO_INPUT) == GPIO.LOW:の条件が通ります。
GPIO.LOW:は「false（0）」を意味しており、信号無しの「０」であるか？と左側に問いかけます。

信号無し「０」と判定した場合、
print('Customer is here')を出力、それを0.1秒周期で実行します。

:point_down:下記の写真は回路内の導通状態をLEDを使って
視覚的にわかるように設置しているので参考程度に見てください。
![49933939-AE6F-465E-8CF4-95A2E7D9FCFA.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/f97e559e-d117-2216-3326-1f6dcafcb3e6.png)

逆にスイッチが押されていない時、
回路内は3.3V(GPIO2の場合)の電圧が掛かっているため
信号有り「１」と判定され、else:以下の処理が実行されます。

print('runnning')を出力、それを0.1秒周期で実行します。
![F6B665DA-28F5-4D34-B4FE-BFA53BAACD82.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/ae7349a5-c6ee-a46d-847d-85e1f31ac36a.png)

###コードを書けたら保存しましょう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/c26ff97f-2135-991f-58ed-8a2ede17e5e7.png)

###では、動作するか確認しましょう
ここまでの工程が完了すると「run（プログラム実行）」ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/cae59c95-ae18-c922-72d9-7c6e33e48505.png)
プログラムが正常であれば、ボタンがOFFのときは「running」、
ONのときは「Customer is here」と出力されていればOKです。
##RaspberryPi:left_right_arrow:PC(LineNotify)の構成
このままだと、RaspberryPiの中でやりとりが完結しているため、
ボタンが押されたら、LineNotifyに通知が行くプログラムを追加します。

###LineNotifyのセットアップ
[【Python】LINE Notifyを使ってPythonから通知を送ってみる](http://s4t.hatenablog.com/entry/2018/01/10/005547)
↑この記事を参考にLineNotifyのセットアップを行いました。
（いやー、本当にわかりやすい記事、ありがとうございます。）

記事にも書いてある通り、「アクセストークン」が必要になるので、お忘れのないように！
→忘れた場合、再発行が出来ませんが、別のアクセストークンを発行することは可能です。
###プログラムを追加
先ほどの「test.py」を再度開いて、下記の通りに編集します。

```python:test.py
# coding: UTF-8
import RPi.GPIO as GPIO                                      
import time
import requests

GPIO_INPUT = 2

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

def line_notify():
    payload = {'message':'\n'+'お客様がお見えです'}  
    headers = {'Authorization': 'Bearer ' + '発行したアクセストークンをここに書きます' }
    requests.post('https://notify-api.line.me/api/notify', data=payload, headers=headers)
        
try:
    while True:
        if GPIO.input(GPIO_INPUT) == GPIO.LOW:
            line_notify()
            print('Customer is here')
            time.sleep(0.1)
        else:
            print("runnning")
            time.sleep(0.1)
except KeyboardInterrupt:
    print("stopped")
    GPIO.cleanup()
```
ボタンが押されると下記のif文が実行されます。

```
if GPIO.input(GPIO_INPUT) == GPIO.LOW:
            line_notify()
            print('Customer is here')
            time.sleep(0.1)
```
ine_notify()はline_notify関数を呼び出す記述で、
関数内で定義した処理を下記のコードで実行します。


```
def line_notify():
    payload = {'message':'\n'+'お客様がお見えです'}  
    headers = {'Authorization': 'Bearer ' + '発行したアクセストークンをここに書きます' }
    requests.post('https://notify-api.line.me/api/notify', data=payload, headers=headers)

```
payload = {'message':'\n'+'**ここに通知で送るメッセージを書きます**'}
ちなみに　'\n'　は「改行」を命令しています。

headers = {'Authorization': 'Bearer ' + '発行したアクセストークンをここに書きます' }
これは通知の送信先を指定しています。

requests.post('https://notify-api.line.me/api/notify', data=payload, headers=headers)
（）内でまとめた文字列は『どこに、何を送信するか』まとめたもの（URLパラメータ）を
requests.post（更新）でメッセージを送ります。

###動作確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/8bd29673-1679-df58-716d-22ea11bd55ab.png)

問題がなければ、上記の写真のようにボタンを押すと通知が来ます。

私は、LimeNotifyのアクセストークンの記述ミスで何度も失敗しました。:sweat_drops:
通知がいかない場合は、アクセストークンの記述ミス、もしくは、
定義している変数の記述ミス等が考えられます。

##課題

今回、初めてRaspberryPi、PythonでLINEに通知を送ることに挑戦をしたのですが、
改良したいなと思ったので、次回のために。

####RaspberryPiを起動してもプログラムを開いて実行させないと機能しない
これも、RaspberryPiが起動したタイミングで自動的にプログラムを実行するようにしたい

:up:(2020/8/23) プログラムの自動起動ができたので、続きはこちらからどうぞ！
[Raspberry Piでプログラムの自動起動をsystemdでやってみた](https://qiita.com/G-san/private/b0f9a340601cdb4a068f)
##参考記事、資料
###RaspberryPi
- [ラズパイ(raspberry pi)のセットアップをするよ(win/mac対応)](https://sukiburo.jp/setup-raspberry-pi/#OS)
- [Raspberry Pi(ラズパイ)のIPアドレスを見つける4つの方法まとめ](https://tkrel.com/8759)
- [【ラズベリーパイ（ラズパイ）】シャットダウン方法３つ](https://sakayasu.net/raspberrypi-shutdown/)

###Python
- [【電子工作】LINE連携IoT火災報知器を作ってみた【Raspberry Pi, Python, LINE notify】火災警報器, ラズベリーパイ](https://www.youtube.com/watch?v=sRb9POFZOAg)
- [Raspberry Pi のGPIOをPythonから利用する](https://qiita.com/maoutokagura/items/9aef5e23167ce2bc1d10)

###LineNotify
- [【Python】LINE Notifyを使ってPythonから通知を送ってみる](http://s4t.hatenablog.com/entry/2018/01/10/005547)