はじめに
Amazon Echo買ったけど、使わずに結局放置している方多いんじゃないでしょうか??(私だけ?)
周りの人をみてみると、Alexaは家電の操作をメインの使い方にしている方は良く使っているみたいですね。
しかーし、わざわざAlexa対応家電なんか買ってるとお金が...
買う余裕がない⇨作ればいい
発想の転換というやつですかね。知らんけど。
ということでとりあえず電気のスイッチを押してもらうものを作りました。
せっかくなので作り方を共有します。
完成物
まずはじめに完成物をどうぞ
古いバージョンなのでマウンタがなく、完成のものとは少し見た目が違いますがこのような感じです。
このように1台のESP32で2台のサーボを操作するものを作成していきます。
1台ですが動いている様子はこちら 1
ちょっと品質悪いですが我慢してください。
動作デモ pic.twitter.com/39iSgwlGMV
— ふなし (@koni428) 2019年6月28日
#環境
###プログラミング環境
- Python 3.7
- Arduino v1.8.8(ESP32へ書き込めるようセットアップ済みとする)
###使用機器・部品
- ESP32
- ブレッドボード
- サーボモータ SG90 * 2個
- 強力両面テープ
概要
今回のシステムの大まかな構成図はこんな感じです。
#作り方
かなり詳しく書くつもりなので、わかるところはどんどん読み飛ばしてください。
一部はAmazon公式チュートリアルの手順を参考にしています。ぜひこちらも参照してみてください。
##AWS IoT
今回、AWSIoTで使用する中心の機能として、MQTTプロトコルでの通信とshadow機能です。
MQTTとは、IoT向けのPubSub型軽量プロトコルとなっており、ざっくり説明すると中央で全てを管理しているサーバが存在し、指定したトピックに対して書き込みができたり、購読しているトピックに変更があると通知が来るような仕組みになっています。あまり詳しくはないので調べてください。
Shadow機能はClassmethodsさんの解説記事がわかりやすいです。
では早速セットアップしていきましょう。
###リージョンの設定
重要
AWSのリージョンを米国西部(オレゴン)に変更しましょう
私はこれで時間を無駄にしました
AWS IoTでモノのポリシーを作成する
AWS IoT Coreを開いた後、左側のところから「安全性」⇨「ポリシー」と進み、「ポリシーの作成」をクリックしましょう。
ポリシーは以下の画像のように作成しましょう。
妥協した設定なので、詳しく設定可能な方は設定してください。
入力できると「作成」を押して作成しましょう。
AWS IoTにモノの登録をする
AWSIoTとESP32を紐付けるためにAWS IoTを使っていきます。
まずAWS IoT Coreを開き、左の「管理」⇨「モノの登録」⇨「単一のモノを作成する」の順に進みます。
この画面では名前のみ決め、次に進みます。
証明書を作成し、
証明書をダウンロードします。
また、必ず有効化を忘れないようにしましょう。
##ESP32
ESP32とAWS IoTを接続する
####Arduinoに必要ライブラリを導入する
必要となるライブラリは
- PubSubClient
- ArduinoJson(version5系を入れてください)
どちらもライブラリマネージャから導入可能です。
導入方法はググってください。(手抜き)
スケッチを書き換える
まず、ここから私が作成したスケッチをダウンロードしてください。
10, 11行目
にwifiの設定、14行目
のendpointの設定、40行目、61行目
の証明書の設定を書き換えましょう。
14行目のendpointの設定はAWS IoT Core⇨管理⇨先ほど作成したモノ⇨操作の中にあるRestAPIエンドポイントです。
40行目,61行目の証明書は先ほどダウンロードした中身をひたすらコピペしていきます。
書き換えが完了するとESP32に書き込んでみましょう。
このようにPublishedが表示されるとOKです!
Error=-2が出た場合は証明書が間違ってる可能性が高いです。
Error=-1はAWSIoTで指定したポリシー違反を起こしていると思います。(今回は何も拒否していないので大丈夫だと思います)
私はWiFiが不安定な事象が発生したのですが、ボードマネージャからESP32用のボードをv1.0.2からv1.0.0にしたところ改善しました。
AWS Lambda
Lambdaいいですよね。
Lambdaをざっくり解説すると、何か実行するプログラムを書いておき、何かのサービスをトリガーにしてそのプログラムを実行してもらえるサービスです。
しかも、そのトリガーが発火してからサーバーが勝手に立ち上がり、処理が終わると勝手に死亡するのでサーバ管理が全く必要ありません。しかも時間課金で無料枠いっぱい。神かよ。
今回はAlexaからの入力をトリガーにしてAWSIoTへデータを流すor現在の状態をAlexaに返答するといったことに使います。
###Lambda関数作成
超重要
**リージョンをオレゴンに変えましょう。**東京で作るとAlexaConsoleで弾かれます。
これからの画像は東京リージョンになってるとこもありますが、気にしないでください。
こんな感じで関数名だけ決めて作りましょう。言語はPython3.7を使用します。
###Roleへのポリシーの追加
AWS IAM⇨ロールに移動し、先ほど自動で作成されたロールを探します。
関数名-role-*****のような形になっているはずです。
それをクリックすると次のページで「ポリシーをアタッチします」という青いボタンが出現するのでためらわず押しましょう。
次に上の方にある「ポリシーの作成」というボタンを押します。
JSONタブを選び、下のJSONファイルをコピペしましょう。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"iot:GetThingShadow",
"iot:Publish"
],
"Resource": [
"*"
]
}
]
}
そのあとはポリシーの確認⇨名前の設定⇨ポリシーの作成の順に行うと大丈夫です。
これでポリシーの作成は完了したため現在のタブを閉じ、「ポリシーをアタッチします」のボタンを押した後の画面に移動しましょう。
その後はこの画像の通り作成したポリシーを選択し、右下のポリシーのアタッチを行います。
これでポリシーの設定は完了です。
Lambda関数の中身を作る
ここからファイルをダウンロードしてください。
このコードはAmazon公式チュートリアルで配布されているコードをベースに改変しています。ありがとうございます。
ここでは変更する必要のあるファイルはconfig.json
のみです。
{
"DEVICES_CAPABILITY": [
{
"endpointId": "lightUp",
"friendlyName": "上の電気(呼び出しに必要な名前)",
"description": "説明(AlexaAppに表示)",
"manufacturerName": "your name",
"displayCategories": [
"LIGHT"
],
"cookie": {
"extraDetail1": "optionalDetailForSkillAdapterToReferenceThisDevice"
},
"capabilities": [
{
"type": "AlexaInterface",
"interface": "Alexa.PowerController",
"version": "3",
"properties": {
"supported": [{
"name": "powerState"
}],
"proactivelyReported": true,
"retrievable": true
}
},
{
"type": "AlexaInterface",
"interface": "Alexa",
"version": "3"
}
]
},
{
"endpointId": "lightDown",
"friendlyName": "下の電気",
"description": "部屋の下の電気",
"manufacturerName": "your name",
"displayCategories": [
"LIGHT"
],
"cookie": {
"extraDetail1": "optionalDetailForSkillAdapterToReferenceThisDevice"
},
"capabilities": [
{
"type": "AlexaInterface",
"interface": "Alexa.PowerController",
"version": "3",
"properties": {
"supported": [{
"name": "powerState"
}],
"proactivelyReported": true,
"retrievable": true
}
},
{
"type": "AlexaInterface",
"interface": "Alexa",
"version": "3"
}
]
}
],
"thingsName": "powerPotchinThings"
}
DEVICE_CAPABILITY
で定義するデバイスを書いていきます。
今回は1台のESP32で2つのスイッチを操作する予定なのでlightUp,lightDownの2つを定義しています。
(注:lightUp,lightDownの名前を変更するとArduinoのスケッチを変更する必要があります)
(注:サーボ1台でいい場合はlightDownのブロックを消すといいと思います。)
ここで変更するのはfriendlyName
,description
,manifactureName
,thingsName
のところのみです。
frienlyName
はAlexaから呼び出す名前になるので、呼びやすい名前に変更してください。
thingsName
はAWSIoTで設定したモノの名前です。
discription
,manifactureName
は適当で大丈夫。
コードの説明
飛ばしても大丈夫です。
まず、このLambdaに飛んでくるリクエストとして、デバイスを発見するための通信と、デバイスの状態を確認orデバイスの状態を更新させる通信があります。
デバイスを発見する通信は以下に挙げるhandle_discover_v3で処理しています。
といってもconfigのDEVICE_CAPAVILITYを読み込んで返却しているだけですね。
# v3 handlers
def handle_discovery_v3(request):
with open('config.json') as f:
config = json.load(f)
devices_capability = config['DEVICES_CAPABILITY']
response = {
"event": {
"header": {
"namespace": "Alexa.Discovery",
"name": "Discover.Response",
"payloadVersion": "3",
"messageId": get_uuid()
},
"payload": {
"endpoints": devices_capability
}
}
}
return response
こちらは発見のための通信ではないものが全て流れてきます。
重要なのがrequest_namespace
であり、Alexa.PowerController
はデバイスの電源の状態を制御するために使われます。ここで詳しいドキュメントが見れます。
Alexa
の場合はデバイスの状態を確認するために使われており、ここでドキュメントが見れます。
返却値はそちらを参照してもらうとして、デバイスの制御に重要なのはどのデバイスをON、OFFどちらにするのかであり、その値はrequest_device
,value
に格納されています。それをAWSIoT側に渡していますね。
def handle_non_discovery_v3(request):
request_namespace = request["directive"]["header"]["namespace"]
request_name = request["directive"]["header"]["name"]
request_device = request["directive"]["endpoint"]["endpointId"]
if request_namespace == "Alexa.PowerController":
if request_name == "TurnOn":
value = "ON"
else:
value = "OFF"
iot.publish(request_device, value)
response = {
"context": {
"properties": [
{
"namespace": "Alexa.PowerController",
"name": "powerState",
"value": value,
"timeOfSample": get_utc_timestamp(),
"uncertaintyInMilliseconds": 500
}
]
},
"event": {
"header": {
"namespace": "Alexa",
"name": "Response",
"payloadVersion": "3",
"messageId": get_uuid(),
"correlationToken": request["directive"]["header"]["correlationToken"]
},
"endpoint": {
"scope": {
"type": "BearerToken",
"token": "access-token-from-Amazon"
},
"endpointId": request_device
},
"payload": {}
}
}
return response
デバイスの状態アップデートをAWSIoT側に渡すコードの中身はこのようになっています。
# -*- coding: utf-8 -*-
import json
import boto3
iot = boto3.client('iot-data')
with open('config.json') as f:
config = json.load(f)
things_name = config['thingsName']
def publish(device_name, state):
topic = f'$aws/things/{things_name}/shadow/update'
payload = {
"state": {
"desired": {
}
}
}
payload["state"]["desired"][device_name] = state
iot.publish(
topic=topic,
qos=0,
payload=json.dumps(payload, ensure_ascii=False)
)
return True
このようなpayloadを作り、AWSIoT側に渡しています。
{
"state": {
"desired": {
"lightUp": "ON"
}
}
}
AWS IoTではモノから報告された状態と、desiredで報告された値に差が出るとdeltaトピックに差分を報告します。
ESP32ではそのトピックを購読してあるので差分を解消するようにデバイスを操作してあげればOKですね。
Lambdaにデプロイする
先ほどダウンロードし、一部書き換えたものをzipに圧縮しlambdaにアップロードします。
圧縮する際は下の画像のようにファイル、フォルダを全部選択して圧縮し、解答したときに余分なフォルダが出来ないようにしましょう。
圧縮ができるとLambdaにアップロードします。
こんな感じでデプロイしましょう。
すぐにこの画面を使うのでそのままにしておいてください。
Alexaスキル
Alexaに自作のスキルを登録していきます。
公式参考ページ
###Alexaスキル登録
まずはじめにAlexa開発者登録を行います。
こちらのページからいつも使用しているAmazon.co.jpのid,passでログインしましょう。
amazon.comのアカウントも所持している方はpassが同一だと日本のアカウントにログインできない問題が起こる可能性があるので、違うものに変更しておくことをお勧めします。
ログインできるとスキルの作成と進み、スキル名を入力し、スマートホームを選択します。
選択したらスキルの作成を押して次の画面に移動しましょう。
この画面ではまず、スキルIDをLambda関数側にコピーする必要があります。
AlexaConsoleの画面の囲っているIDをのLambdaの画面に貼り付けましょう
この様にコピペしてから右下の追加を押してから右上の保存を押してLambda関数を保存してください。
最後に、Lambdaの右上にあるARNをコピーしておきましょう。
Lambdaの画面↓
そしてそれが終わると、コピーしたARNをAlexa側に登録します。
AlexaConsoleの画面↓
特にエラーなく完了すると大丈夫です!
###アカウントリンクの設定
次にアカウントリンクを設定します。
OAuth2.0を使ってアカウントとリンクさせる機能を提供する必要があるのですが、Login with AmazonというAmazonのアカウントを使ってログインするものがAmazonより提供されているのでそれを使う様にセットアップしていきます。
まず、Login with amazonのコンソールページに移動しましょう。
そこでセキュリティプロファイルの新規作成を行います。
- セキュリティプロファイル名
- セキュリティプロファイルの説明
- プライバシー規約同意書URL
の入力欄があると思うのですが、適当に埋めましょう。外部に公開するわけではないので、規約同意書URLはexample.comなどで大丈夫です。
登録が完了するとマークからWeb設定に移動します。
そこでは許可された返信URLをAlexaConsole側のリダイレクト先のURIを設定し、AlexaConsole側の設定は以下の様に入力しましょう
設定項目 | 値 |
---|---|
認証画面のURI | https://www.amazon.com/ap/oa |
アクセストークンのURI | https://api.amazon.com/auth/o2/token |
クライアント ID | login with amazonに表示 |
クライアントシークレット | login with amazonに表示 |
スコープ | profile |
Alexaアプリ
ここからはAlexaアプリでスキルを有効化してデバイスの検索をしていきます。
スマートフォンにAlexaアプリを入れてることを前提とします。
Alexaスキルの有効化
Alexaアプリでスキルの画面に移動し、有効なスキル⇨開発⇨先ほど作成したスキル名に移動します。
有効にして使用するを押して手順に従うと、最終的にデバイスの検索が始まって2台のシーンが検出されるとOKです。
ここでログイン画面がでない場合はアカウントリンクの設定が怪しいです。
デバイスが検出されない場合はLambdaが怪しいです。
名前の変更について
Alexaアプリの最初の画面に戻り、右下のデバイスを選ぶと、照明のボタンが上に存在すると思うので押すと、config.jsonで設定したfriendlyName
の値が反映されているはずです。
名前を変更したい場合は一度デバイスをアプリで削除してからLambdaのfriendlyName
を更新してから新しいデバイスの追加を行うと新しい名前で見つけることができると思います。
###ESP32までデータが流れるか確認する
Alexaアプリからモノの状態を変更すると、Alexa->Lambda->AWS IoT->ESP32という順にデータが流れていきます。ということでAlexaアプリでON,OFFを切り替えてESP32のシリアルモニタにその情報が流れてきているか確認しましょう。
ON,OFFを切り替えたときにこのような表示があればOKです。
Received. topic=$aws/things/powerPotchinThings/shadow/update/delta
lightDown -> OFF
ここまで完成するとあとはハードウェア側のセットアップです。
##ESP32
###サーボモータをつなぐ
#define LIGHT_UP_PIN 27
#define LIGHT_DOWN_PIN 25
#define BUTTON_PIN 0
こちらに定義したピンにpwm信号が流れるようになっています。なので、LIGHT_UP_PIN
,LIGHT_DOWN_PIN
に指定したピン番号にサーボモータのオレンジの線が繋がるようにつなぎましょう。
また、BUTTON_PIN
はその場でスイッチを切り替えたい場合に使うスイッチ用のピンとなっています。外付けでプルアップ抵抗を繋ぐことを想定しています。(書き換えることのできる方は内臓プルアップに変更も可能です)
接続が完了するとボタンを押したりAlexaアプリからON,OFFを切り替えたりしてサーボモータが動くか確かめましょう。
##モーターを固定するマウンタを作る。
3Dプリンタを使って作ります。
ない方は買いましょう。
なくても両面テープでくっつけるだけでもなんとかなります。力が逃げてスイッチが押せなくなったりするので、何かしらの方法でマウンタを作ることをお勧めします。
Fusion360でサクッと作ったモデルがこちらから閲覧・DL可能です。使えそうなら使ってください。
使用例はこんな感じ
##設置
ブレットボードをいい感じのところに貼り付けて、ESP32の電源を確保すると完成!! 2
##モーターの角度を調整する
ArduinoのスケッチのswitchLightをいじってください。
void switchLight(char* state, char* deviceName) {
int ch;
if(!strcmp(deviceName, "lightUp")) ch = 0;
else if(!strcmp(deviceName, "lightDown")) ch = 1;
if (!strcmp(state, "OFF")) {
if(ch == 1){
ledcWrite(ch, 80);<-これ
}else{
ledcWrite(ch, 80);<-これ
}
delay(150);
ledcWrite(ch, 0);
} else if (!strcmp(state, "ON")) {
if(ch==1){
ledcWrite(ch, 105);<-これ
}else{
ledcWrite(ch, 105);<-これ
}
delay(150);
ledcWrite(ch, 0);
}
}
#最後の独り言
やっと記事にできた。
どこまで詳しく書くべきなのか難しいですね。
ここ間違ってるよ!
こうする方がいいよ!といった指摘あれば大歓迎ですのでどしどしどうぞ。
そんなことより何かいいインターンないですかねぇー。
チーム開発を学びたい。