大学生の初めての電子工作　スマートリモコン編（プログラム）

この記事では[スマートリモコン編](https://qiita.com/joru/items/b6385808289b62a40493)で作成したスマートリモコンのプログラムを記載していきます。回路は[スマートリモコン編（回路）](https://qiita.com/joru/items/e4d4e12e8800743cc0bf)に記載してあります。
今回作成するプログラムは以下の5つです。

1. LED点灯確認用プログラム
2. ボタンシャットダウンプログラム
3. 温度測定プログラム
4. 赤外線信号学習プログラム
5. スマートリモコンプログラム

　1. LED点灯確認用プログラムでは、作成した回路が正常に動作するかどうか確認したり、赤外線を使わずWi-Fi経由で家電を操作したりする場合([PS4を操作する](https://jorublog.site/alexa-googlehome-control-ps4/)など)に、正しく動作しているか確認するためにLED点灯確認用プログラムを作成します。
　2. ボタンシャットダウンプログラムでは、不具合などで正しく動作しなくなったときなどに、RaspberryPiに接続することなくボタンでシャットダウンや再起動ができるようにするプログラムを作成します。
　3. 温度測定プログラムでは、温度センサーが取得したデータを計算し、温度や湿度を出力するプログラムを作成します。
　4. 赤外線信号学習プログラムでは、家電のリモコンの赤外線信号を学習したり、その学習した赤外線信号を発信するための回路を作成します。
　5. スマートリモコンプログラムでは、MQTTという通信プロトコルを使って、beebotteに送ったデータを受信し、その受信したデータに応じた処理（エアコンオンなど）を実行するためのプログラムを作成します。


#はじめに
####初期設定
まず、プログラムを作成する上で必要になってくるpythonモジュールや、その他ソフトウェアなどをインストールします。まずはpythonのパッケージをインストールする際に apt-get ではなく apt を使用していくのでaptのバージョンを最新にします。

~~~bash
pi@raspberrypi:~ $ sudo apt update
pi@raspberrypi:~ $ sudo apt upgrade
~~~

　　
次に、プログラムを保存するディレクトリを作成します。今回はcodeという名前で作成します。

~~~bash
pi@raspberrypi:~ $ mkdir code
~~~

今回作成していくプログラムはこのcodeというディレクトリの中に作成してください。

　　
　　
####温度測定プログラムのダウンロード
今回使用する温度センサーであるDHT11のサンプルプログラムがgitにあがっているので、これを以下のコマンドでダウンロードします。

~~~bash
pi@raspberrypi:~ $ cd code
pi@raspberrypi:~/code $ sudo apt-get install git
pi@raspberrypi:~/code $ git clone https://github.com/szazo/DHT11_Python.git
~~~

　　
　　
####赤外線信号学習プログラムのダウンロード
まず、GPIOピンを制御するためにpigpioというパッケージをインストールします。 GPIOピンを制御するためのpythonパッケージとして、Raspbianに標準でインストールされているRPi.GPIOというパッケージや、WiringPiというパッケージがあるのですが、pigpioの方が精度や機能的な面で他の２つよりも優れているということなのでこちらを使っていきます。([こちらのサイト](https://karaage.hatenadiary.jp/entry/2017/02/10/073000)でこれら３つのパッケージについて比較されています。)

~~~bash
pi@raspberrypi:~ $ sudo apt install pigpio python3-pigpio
~~~

このpigpioというパッケージを使用するにはpigpiodというデーモン(バックグラウンドで動作する常駐プログラム)を起動する必要があります。この操作はRaspberryPiを起動するたびに行う必要があるので、このデーモンをRaspberryPiを起動するときに自動的に起動するようにします。

~~~bash
pi@raspberrypi:~ $ sudo systemctl enable pigpiod.service
pi@raspberrypi:~ $ sudo systemctl start pigpiod
~~~

正しく設定できたかどうかを以下のコマンドで確認します。

~~~bash
pi@raspberrypi:~ $ systemctl status pigpiod.service
~~~

![Untitled Diagram.png](https://qiita-image-store.s3.amazonaws.com/0/276761/b418fb03-276f-af26-c051-3deae5e6a856.png)

上記のようになっていれば、pigpioは使えるようになっています。

　　
続いて、赤外線学習用のプログラムを以下のコマンドでダウンロードします。

~~~bash
pi@raspberrypi:~ $ cd code　　
pi@raspberrypi:~/code $ curl http://abyz.me.uk/rpi/pigpio/code/irrp_py.zip | zcat > irrp.py
~~~

（すでにcodeディレクトリにいる場合は `cd code` は必要ありません。）
　　
　　
####スマートリモコンプログラムに必要なパッケージ等のダウンロード
まず、スマートリモコンプログラムで必要となる、BeeBotteにデータが送られてきたかどうかを監視するpaho-mqttというパッケージをインストールします。

~~~bash
pi@raspberrypi:~ $ sudo pip3 install paho-mqtt　　
~~~

　　
続いて、beebotteのサーバにアクセスするために必要となる証明書をダウンロードします。

~~~bash
pi@raspberrypi:~ $ cd code　　
pi@raspberrypi:~/code $ curl -O https://beebotte.com/certs/mqtt.beebotte.com.pem
~~~

（すでにcodeディレクトリにいる場合は `cd code` は必要ありません。）

　　
以上で必要なパッケージ等のインストールは完了です。
　　
　　
#1. LED点灯確認用プログラム

今回作成した回路は、FETによって電流が増幅されているので、よくある数秒間隔で点滅させるというようなLチカプログラムを実行した場合、接続するLEDによっては流れる電流に耐えきれず、壊れてしまう可能性があります。なので点滅間隔を短めに設定したLチカプログラムを作成します。

~~~python3:L_tika.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

import time
import pigpio 

pi = pigpio.pi() 

set_pin = 17
pi.set_mode(set_pin, pigpio.OUTPUT) 

for i in range(10):
    pi.write(set_pin, 1)
    time.sleep(0.001)
    pi.write(set_pin, 0)
    time.sleep(0.3)

~~~


#2. ボタンシャットダウンプログラム

ボタンを長押し(1秒間)することでシャットダウンや再起動ができるようになるプログラムを作成します。23pinにつながれたボタンを長押しするとシャットダウン、24pinにつながれたボタンを長押しすると再起動するというプログラムです。

まず、プログラムが正しく動作するか確認するために以下のプログラムを作成して実行してみてください。

~~~python3:shutdown_switch_sample.py
#!/usr/bin/python3
# coding:utf-8

import time
import RPi.GPIO as GPIO
import subprocess

p_off_command = "sudo shutdown -h now"
reboot_command = "sudo reboot"

GPIO.setmode(GPIO.BCM)

pin_p_off  = 23
pin_reboot = 24

GPIO.setup(pin_p_off,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(pin_reboot,GPIO.IN,pull_up_down=GPIO.PUD_UP)

def callback_shutdown(pin_number):
    counter = 0
    print("GPIO[%d]のコールバックが発生しました" % pin_number)
    while True:
        status = GPIO.input(pin_p_off)
        if status == 0:
            counter = counter + 1
            if counter >= 100:
                print("シャットダウンします")
                #subprocess.call(p_off_command.split())
                break
        else:
            print("シャットダウンを取り消します")
            break
        time.sleep(0.01)
    print(counter)

def callback_reboot(pin_number):
    counter = 0
    print("GPIO[%d]のコールバックが発生しました" % pin_number)
    while True:
        status = GPIO.input(pin_reboot)
        if status == 0:
            counter = counter + 1
            if counter >= 100:
                print("再起動します")
                #subprocess.call(reboot_command.split())
                break
        else:
            print("再起動を取り消します")
            break
        time.sleep(0.01)
    print(counter)

GPIO.add_event_detect(pin_p_off, GPIO.FALLING, callback = callback_shutdown, bouncetime = 500)
GPIO.add_event_detect(pin_reboot, GPIO.FALLING, callback = callback_reboot, bouncetime = 500)

while True:
  time.sleep(300)

~~~

上記のプログラムは動作確認用のため、ボタンを長押ししてもシャットダウンや再起動は実行されません。"シャットダウンします"や"再起動します"という文字が表示されたら正しく動作しているので、以下のプログラムで実際にシャットダウンや再起動が実行されるか確認してみてください。
　　

~~~python3:shutdown_switch.py
#!/usr/bin/python3
# coding:utf-8

import time
import RPi.GPIO as GPIO
import subprocess

p_off_command = "sudo shutdown -h now"
reboot_command = "sudo reboot"

GPIO.setmode(GPIO.BCM)

pin_p_off  = 23
pin_reboot = 24

GPIO.setup(pin_p_off,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(pin_reboot,GPIO.IN,pull_up_down=GPIO.PUD_UP)

def callback_shutdown(pin_number):
    counter = 0
    #print("GPIO[%d]のコールバックが発生しました" % pin_number)
    while True:
        status = GPIO.input(pin_p_off)
        if status == 0:
            counter = counter + 1
            if counter >= 100:
                print("シャットダウンします")
                subprocess.call(p_off_command.split())
                break
        else:
            #print("シャットダウンを取り消します")
            break
        time.sleep(0.01)
    print(counter)

def callback_reboot(pin_number):
    counter = 0
    #print("GPIO[%d]のコールバックが発生しました" % pin_number)
    while True:
        status = GPIO.input(pin_reboot)
        if status == 0:
            counter = counter + 1
            if counter >= 100:
                print("再起動します")
                subprocess.call(reboot_command.split())
                break
        else:
            #print("再起動を取り消します")
            break
        time.sleep(0.01)
    print(counter)

GPIO.add_event_detect(pin_p_off, GPIO.FALLING, callback = callback_shutdown, bouncetime = 500)
GPIO.add_event_detect(pin_reboot, GPIO.FALLING, callback = callback_reboot, bouncetime = 500)

while True:
  time.sleep(300)

~~~

　　
　　

最後に、RaspberryPiが起動したときにこのプログラムが自動で起動するようにデーモンを作成します。

~~~bash
pi@raspberrypi:~ $ cd code　　
pi@raspberrypi:~/code $ sudo chmod 755 shutdown_switch.py
pi@raspberrypi:~/code $ sudo nano /usr/lib/systemd/system/shutdown_switchd.service
~~~

（すでにcodeディレクトリにいる場合は `cd code` は必要ありません。）
上記のコマンドを実行するとnanoエディタが起動するので、以下のように記述し、「ctrl」+「O(ローマ字のオー）」を押した後に「enter」を押して保存し、「ctrl」+「X」を押して終了します。

~~~shell
[Unit]
Description=Shutdown Daemon

[Service]
ExecStart =/home/pi/code/shutdown_switch.py  #<--ここには、shutdown_switch.pyが存在する絶対パスを記載してください。
Restart=always
RestartSec=10s
Type=simple

[Install]
WantedBy=multi-user.target
~~~

　　
作成したデーモンを有効化します。

~~~bash
pi@raspberrypi:~ $ sudo systemctl enable shutdown_switchd.service
pi@raspberrypi:~ $ sudo systemctl start shutdown_switchd
pi@raspberrypi:~ $ sudo systemctl daemon-reload
~~~

　　
正しく設定できたかどうかを以下のコマンドで確認します。

~~~bash
pi@raspberrypi:~ $ sudo systemctl status shutdown_switchd.service
~~~

この記事の初めのほうに記載したpigpioのデーモンを有効化したときのようにenable、activeになっていれば設定完了です。実際にボタンを押して動作するかどうか確認してみてください。（もし、動作しなかったら再起動をしてから実行してみてください。）

　　
　　
#3. 温度測定プログラム
先ほどgitからダウンロードしたプログラムはDHT11_Pythonというフォルダの中に入っています。dht11.pyというプログラムが温度や湿度を計算してくれるプログラムで、dht11_example.pyというプログラムが温度や湿度を表示するプログラムで、1秒おきにデータを取得して良好なデータが取得された場合のみ表示されます。
　　

スマートリモコンと連動させたい場合は、一回温度が取得できれば十分なので、1回温度を取得したら終了する、または何かのプログラムを実行する、というほうが扱いやすいです。なので今回は1回温度を取得したら、温度に応じてエアコンをつけるプログラムを作成します。以下のプログラムをDHT11_Pythonというフォルダの中に作成してください。

~~~python3:dht11_for_homeauto.py
import RPi.GPIO as GPIO
import dht11
import time
import datetime
import sys
import subprocess

# 実行するコマンドを以下に書き込んでください(pythonプログラムを実行する場合はパスに注意！)
command = ("python3 ../irrp.py -p -g17 -f codes air_con:on")
# コマンドを実行する基準となる温度を以下に書き込んでください
set_temp = 26

# initialize GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()

# read data using pin 14
instance = dht11.DHT11(pin=14)

while True:
    result = instance.read()
    if result.is_valid():
        print("Last valid input: " + str(datetime.datetime.now()))
        print("Temperature: %d C" % result.temperature)
        print("Humidity: %d %%" % result.humidity)

        if set_temp < result.temperature:
            subprocess.call(command.split())
        else:
            print("設定した温度より室温が低かったため，コマンドは実行されませんでした")

        #プログラム終了
        sys.exit()
    else:
        #print("couldn't get good data")
    time.sleep(1)
~~~

上記のプログラムは、実行するコマンドを "エアコンをつける赤外線を発信するコマンド" 、基準となる温度を "26℃" に設定し、1度温度を取得したら、その室温と基準温度を比較し、基準温度よりも室温が高かったら "エアコンをつける赤外線を発信するコマンド" を実行するというプログラムです。
（注意：ここでは、DHT11_Pythonというフォルダが以下の 4. で説明するirrp.pyというプログラムと同じ階層にあることを想定しています。他の階層にある場合や他のプログラムを実行する場合にはパスに注意してください。）
　　

このプログラムのsubprocess.callのところを以下のように編集すると温度や湿度をjsonデータとして送ることができ、外出先から室温を確認できたりします。

~~~python
subprocess.call(["curl", "-X", "POST", "-H", "\"Content-Type: application/json\"", "-d", "'{\"value1\":\"%s\",\"value2\":\"%d\",\"value3\":\"%d\"}'" % (datetime.datetime.now(),result.temperature,result.humidity), "https://xxx/xxxxx/xxxx/xxxxx"])
~~~

　　
　　
#4. 赤外線学習プログラム
赤外線学習については[こちらのサイト](https://qiita.com/takjg/items/e6b8af53421be54b62c9)に詳しく記載されているので、ここでは簡単に説明します。
赤外線学習には先ほどダウンロードしたirrp.pyというプログラムを使うのですが、このプログラムを使用する際、このプログラムの中身を変更してGPIOの設定をするわけではないので、初めに使用するGPIOピンの設定をする必要があります。
(今回はスマートリモコンの回路設計編で作成した回路に基づいて記載していくので、ピン配置を変えた方はご自身の回路に合わせてピン番号を読みかえて行ってください。)

以下のコマンドで、17ピンを出力に、18ピンを入力に設定します。

~~~bash
pi@raspberrypi:~ $ echo 'm 17 w   w 17 0   m 18 r   pud 18 u' > /dev/pigpio
~~~
　　

####赤外線の学習

赤外線の学習をするには以下のようにコマンドを入力します。

~~~bash
pi@raspberrypi:~ $ python3 irrp.py -r -g18 -f codes air_con:on --post 130
~~~

この例では18ピンを赤外線信号を受信するピンに設定し、codesというファイルにair_con:onという名前で学習したデータを保存するというコマンドを実行しています。名前は 機器名:操作名 と記述するといろいろと便利です。
　　

![Untitled Diagram.png](https://qiita-image-store.s3.amazonaws.com/0/276761/93e93fa7-9f1f-2b86-82ed-bc672d1dde96.png)

このコマンドを実行すると上のような文字が表示されるので、表示されたら赤外線を赤外線受信モジュールに向けて発信します。すると、確認用にもう一度送信するように促されるので、もう一度赤外線を発信します。okayと表示されプログラムが終了したら成功です。

エアコンのリモコンなど、リモコンの種類によっては"温度"、"風量"、"風向"など、複数の信号を一度に送信したり、ONとOFFで信号が違ったりするため注意してください。
(例えば、私のエアコンのリモコンでは、赤外線送信時に温度情報ものせて送信しているらしく、リモコンの表示が26度のときに温度を上げるのと、リモコンの表示が27度のときに温度を上げるのとでは信号が違うため、1回目と2回目で信号が一致せずエラーとなります。)
　　

####赤外線の送信
学習した赤外線を送信するには以下のようにコマンドを入力します。

~~~bash
pi@raspberrypi:~ $ python3 irrp.py -p -g17 -f codes air_con:on
~~~

この例では17ピンを赤外線信号を送信するピンに設定し、codesというファイルに記述されているair_con:onという名前のデータを送信するというコマンドを実行しています。
　　

#5. スマートリモコンプログラム
最後に、メインとなるプログラムを作成します。ここでは、外部サービスで設定する情報が必要になるので、先に[外部サービスとの連携](https://qiita.com/joru/items/b5ceb56bf9a4fd31c015)を済ませておいたほうがいいかもしれません。

~~~python3:homeauto.py
# -*- coding: utf-8 -*-

import paho.mqtt.client as mqtt
import subprocess
import json
from time import sleep

HOST = 'mqtt.beebotte.com'
PORT = 8883
CA_CERTS = 'mqtt.beebotte.com.pem'
TOKEN = '[Beebotteのトークン　　　例：token_xxxxxxxxxxxxxxxx  ]'  #Beebotteで作成したチャンネルのトークンを入力
TOPIC = '[Beebotteのトピック名　　例：RaspberryPi/SmartRemocon]'  #Beebotteで作成したトピック名を入力

def on_connect(client, userdata, flags, respons_code):
    print('status {0}'.format(respons_code))

def on_message(client, userdata, msg):
    print(msg.topic + ' ' + str(msg.payload))
    data = json.loads(msg.payload.decode("utf-8"))["data"][0]
    print(msg.payload.decode("utf-8"))
    print(data)

    def default_command(data):
        subprocess.call(["python3", "irrp.py", "-p", "-g17", "-f", "codes", data["device"]+":"+data["action"]])
        print("excuted command: " + data["device"]+":"+data["action"])
        print(" ")

    # control ps4
    if (data["device"] == 'ps4'):
        subprocess.call(["python3","L_tika.py"])  #動作確認用
        if (data["action"] == 'on'):
            subprocess.call(["sudo", "/opt/nodejs/bin/ps4-waker"])
        elif (data["action"] == 'standby'):
            subprocess.call(["sudo", "/opt/nodejs/bin/ps4-waker", "standby"])
        elif (data["action"] == 'enter'):
            subprocess.call(["sudo", "/opt/nodejs/bin/ps4-waker", "remote", "enter"])
        elif (data["action"] == 'back'):
            subprocess.call(["sudo", "/opt/nodejs/bin/ps4-waker", "remote", "back"])
        elif (data["action"] == 'torne'):
            subprocess.call(["sudo", "/opt/nodejs/bin/ps4-waker", "start", "CUSA00442"])
        elif (data["action"] == 'primevideo'):
            subprocess.call(["sudo", "/opt/nodejs/bin/ps4-waker", "start", "CUSA03099"])
        elif (data["action"] == 'youtube'):
            subprocess.call(["sudo", "/opt/nodejs/bin/ps4-waker", "start", "CUSA01065"])
        print(" ")

    # control by temperature
    elif (data["device"] == 'temp'):
        subprocess.call(["cd", "DHT11_Python"])
        subprocess.call(["python3", "dht11_for_homeauto.py"])
        subprocess.call(["cd", ".."])
        print(" ")

    # control other
    else:
        default_command(data)
        print(" ")

if __name__ == '__main__':
    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
    client.username_pw_set('token:%s' % TOKEN)
    client.tls_set(CA_CERTS)
    client.connect(HOST, PORT)
    client.subscribe(TOPIC)
    client.loop_forever()
~~~

このプログラムは現在私が使用しているプログラムで、まだまだ改善途中なので随時更新していきたいと思います。ちなみに、ps4-wakerというpythonパッケージを使うと上記のプログラムのようにPS4をRaspberryPiで操作できるようになります。これについての記事も書いたので詳しく知りたい方は[こちら](https://jorublog.site/alexa-googlehome-control-ps4/)をご覧ください。

　　
　　
プログラムは以上となります。改善案や訂正箇所などがありましたら教えてくださると助かります。



#参考サイト
###格安スマートリモコンの作り方
https://qiita.com/takjg/items/e6b8af53421be54b62c9
こちらにスマートリモコンの作り方がとても分かりやすく記載されています。
しかし、このサイトに従い進めていったところ、hubotのインストールのところでつまずいてしまい(nodejsやnpmのversionの違い？)、hubotによる遠隔操作ではなく、下に記載させていただいた、Beebotteという外部サービスを用いるやり方により実現しました。

###IFTTTとBeebotteを使ってGoogleHomeからRaspberryPiを操作する
https://qiita.com/msquare33/items/9f0312585bb4707c686b
hubotを用いた方法ではうまくいかなかったので、こちらのBeebotteを介した方法を採用させていただきました。

###Raspberry PiとDHT11で温度・湿度を測る
https://qiita.com/mininobu/items/1ba0223af84be153b850
温度センサーの取り付けやプログラムのダウンロード方法などを参考にさせていただきました。

###ラズパイでシャットダウンボタンを付ける(ついでに起動ボタン)
https://qiita.com/clses/items/e701c1cb6490751a6040
　タンでシャットダウンや起動を行う方法について記載されています。