Raspberry Piで温湿度を計測し、Alexaに喋ってもらおう

# Alexa対応の温湿度センサー、わざわざ買わなくても作れるんじゃない？
我が家のAlexaくんは温湿度センサーを未導入のため、外気温は教えてくれますが室温は教えてくれません。
そこで、物置に眠っていたRaspberry Pi 4とDHT11で温湿度を計測し、AlexaSkillsKitに飛ばして読み上げてもらえるようにします。

## 部材
### RaspberryPi 4 B(4GB)
言わずと知れたシングルボードコンピューターです。
品薄になる前に購入してほったらかしていたので6,000円くらいだったと思います。

https://akizukidenshi.com/catalog/g/gK-15038/

### DHT11
実績たっぷり、安心と信頼のDHT11温湿度センサーです。
温度センサーレスポンスが10秒以内、精度が±2℃と高価格センサーには敵わないものの、今回の用途には十分な性能です。
電源電圧は3.3V、サンプリング間隔は2秒以上です。

すでにプルアップ抵抗が付いているものもありますが、今回はDHT11単品の4pin仕様を使います。

https://akizukidenshi.com/catalog/g/gM-07003/

### 抵抗
こちらは10KΩの抵抗を使用します。

https://akizukidenshi.com/catalog/g/gR-25103/

### LCDモジュール
Alexaに読み上げさせるだけではつまらないので、キャラクターLCDを使って目視でも確認できるようにしたいと思います。
今回は8x2のバスリピーター付きラズパイ用LCDを使用します。ピンヘッダに刺すだけで使えます。
1番と9番で電源を、3番と5番でI2C信号を供給しています。
型番はAE-AQM0802+PCA9515です。

https://akizukidenshi.com/catalog/g/gK-11354/

## 制作
まずは回路図です。
LCDは適当なパーツ素材がなかったので、脳内で置き換えてください。
![Untitled Sketch_ブレッドボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/8bb8361c-1f4e-cc57-6e95-e4fd933fa00b.png)

5Vから電源を取り、GPIO14を通信に使っています。
LCDモジュールは、3.3Vのソケットがラズパイ側の1番3.3Vに刺さるようにしてください。
またI2Cを利用するので、設定から有効にしておいてください。

## LCD
### LCDのチェック
I2CでLCDが認識されているかチェックしてください。
```
$ i2cdetect -y 1

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- 
```
スレーブが3eで認識していればOKです。

### 初期化
初期化をします。

```
$ i2cset -y 1 0x3e 0x00 0x38
$ i2cset -y 1 0x3e 0x00 0x39
$ i2cset -y 1 0x3e 0x00 0x14
$ i2cset -y 1 0x3e 0x00 0x38
$ i2cset -y 1 0x3e 0x00 0x73
$ i2cset -y 1 0x3e 0x00 0x56
$ i2cset -y 1 0x3e 0x00 0x6c
$ i2cset -y 1 0x3e 0x00 0x38
$ i2cset -y 1 0x3e 0x00 0x01
$ i2cset -y 1 0x3e 0x00 0x0c
$ i2cset -y 1 0x3e 0x40 0x61
$ i2cset -y 1 0x3e 0x40 0x62
$ i2cset -y 1 0x3e 0x40 0x63
$ i2cset -y 1 0x3e 0x40 0x64
$ i2cset -y 1 0x3e 0x40 0x65
$ i2cset -y 1 0x3e 0x40 0x41
```


## プログラム
今回はPythonでflaskを利用します。
ついでにsqliteで保存できるようにしてみました。

```python:main.py
from flask import Flask, request, jsonify
import json
import sqlite3
from datetime import datetime
import RPi.GPIO as GPIO
import dht11
import smbus
import time

# LCD初期化
i2c = smbus.SMBus(1) # バスナンバー
addr02=0x3e #LCD
_command=0x00
_data=0x40
_clear=0x01
_home=0x02
display_On=0x0f
LCD_2ndline=0x40+0x80

# GPIO初期化
GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()

# GPIO14を指定
instance = dht11.DHT11(pin=14)

app = Flask(__name__)

# LCD AQM0802
def command( code ):
        i2c.write_byte_data(addr02, _command, code)
        time.sleep(0.1)

def writeLCD( message ):
        txt_list=[]
        for txt in message:
                txt_list.append(ord(txt)) 
        i2c.write_i2c_block_data(addr02, _data, txt_list)
        time.sleep(0.1)

def init ():
        command(0x38)
        command(0x39)
        command(0x14)
        command(0x73)
        command(0x56)
        command(0x6c)
        command(0x38)
        command(_clear)
        command(display_On)

# POSTリクエストに応答
@app.route("/", methods=["POST"])
def index():
    timestamp = datetime.now()
    conn = sqlite3.connect("temperature.db")
    cursor = conn.cursor()
    cursor.execute("""
    CREATE TABLE IF NOT EXISTS temperature (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        temp DECIMAL,
        hum DECIMAL,
        timestamp DATETIME
    """)
    conn.commit()
    conn.close()

    # GPIOから読んだデータをresultへ格納
    result = instance.read()
    temp = float(result.temperature)
    hum = float(result.humidity)

    # Alexaに喋らせる文を設定
    speak = f"現在の室温は{temp}度、湿度は{hum}％です。"

    # データベースへ格納
    cursor.execute("INSERT INTO temperature (temp, hum, timestamp) VALUES (?, ?, ?)", (temp, hum, timestamp))
    conn.commit()

    # HTTPレスポンスを設定
    resp = {
        "version": "1.2",
        "response": {
            "outputSpeech": {
                "ssml": "<speak>" + speak + "</speak>",
                "type": "SSML"
            },
            "shouldEndSession": True
        },
        "sessionAttributes": {}
    }
    
    # jsonifyでAlexaにJSONを返す
    return jsonify(resp)


# 表示スペースの都合上室温のみ
init ()
command(_clear)
writeLCD("Temp: ")
while 1:
        command(LCD_2ndline)
        res = str(float(instance.read().temperature))
        print(res)
        writeLCD(res)
        time.sleep(6)

if __name__ == "__main__":
    app.run(debug=True)
```

## ホスティング
ポート開けたりなんやかんやはめんどくさいので、ngrokを使ってフォワーディングします。
### インストール (ARM版)
```
$ wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-arm.zip
$ unzip ngrok-stable-linux-arm.zip
$ sudo mv ngrok /usr/local/bin/
```
### トークンの設定
ngrokに登録し、ダッシュボード > YourAuthTokenからTokenをコピーしてきてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/7f7cab83-96e9-3925-efb8-3f7b2f074af4.png)

```
$ ngrok authtoken YOUR_TOKEN_HERE
```

## 起動
ngrokとスクリプト、回路の構成が終わったら起動します。

```
$ ngrok http 5000
ngrok by @inconshreveable                                  (Ctrl+C to quit)

Session Status                online
Account                       YOUR_ACCOUNT_NAME (Plan: Free)
Version                       2.3.41
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://xxxxxxxx.ngrok-free.app -> localhost:5000
Forwarding                    https://xxxxxxxx.ngrok-free.app -> localhost:5000

Connections                   ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00
```

Forwarding https://xxxxxxxx.ngrok-free.app のURLを控えておいてください。
AlexaSkillへの登録に使用します。

# AlexaSkillKitへの登録
### Developmer Console
まずAlexa Developmer Consoleを開きます。

https://developer.amazon.com/ja-JP/alexa/alexa-skills-kit

### スキルの作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/5f07bdf8-4e49-6119-65c1-6fff8e432cb4.png)
スキルの作成をクリックし、続く画面でスキル名、ロケールを設定したら、

エクスペリエンスのタイプ -> その他
モデルを選択 -> カスタム
ホスティングサービス -> 独自のプロビジョニング

を設定して次へ進みます。
テンプレートは適当に選択しておいてください。

### エンドポイント
スキルを作成後、エンドポイントでHTTPSを選択し、先ほど控えておいたngrokのフォワーディングURLを入力します。
SSL証明書の種類はワイルドカード証明書のサブドメインにしておいてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/2d0ad047-75da-9dd7-2909-60f2e87051c0.png)

### ウェイクワード
呼び出し名を選択し、ウェイクワードを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/d5448804-8bf6-5514-4417-1c97687bdd27.png)


### テスト
ウェイクワードとURLの設定が終わったらスキルをビルドします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/4d1ab6f8-1e7a-236a-3b43-0ae145a24c47.png)

ビルドが終わったら、テストタブを開いてテストを開発中にし、Alexaシミュレータのテキストボックスにウェイクワードを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/6c762f1b-291b-f301-3073-2efd6361a339.png)

すべて正しく設定されていれば、室温と気温が返ってくるはずです。
スキルの起動に失敗する場合はFlaskなどのログやngrokのURLを確認してください。

### 公開
通常公開では審査に時間がかかり、ほかに必要としているユーザーもいないのでベータテスト扱いで公開します。
公開タブを開いたら、公開名・説明・詳細説明・サンプルフレーズを設定し、スキルアイコン2つを設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/03dcd155-5762-6a80-d82d-e6cf99c83573.png)
プライバシーとコンプライアンスは画像の通りに設定してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/4ff67ae9-91b1-5048-a0dd-ddd8af36c795.png)
ベータテスターのメールアドレス欄に自分のAlexaで登録しているメールアドレスを設定すれば、スキルが使えるようになります。

### おわり
以上で終了です。
あんまりテストしてないので動かなかったら直すなりしてください。


## おまけ
Switchbot温湿度センサー
1,980円

今回のキット
ラズパイスターターキット
10,000円
ブレッドボード
300円
DHT11
500円
LCD
800円
10,000 + 300 + 500 + 800=











=11,600円