##もっと便利に・・
https://qiita.com/ABK28/items/1672fe26837c364a8de3
の続きです。
前回は、ESP8266とGoogle-Home-Notiferで、GoogleHomeを自由に喋らせるマシンを作りました。
でも、ローカル環境(LAN内)でしか使えません。外にいるときに家にいる人に呼びかけられるように、外部インターネットからも繋げられるようにしてみます。
(電話すればイイじゃん。ってのは おいといて・・)
##Serveo.net
最初、Serveo.netってを使えば簡単にできそう、って思ったんですが、最近ずっと、
ーーー
Serveo is temporarily disabled due to phishing.
Serveo will return in a few days with a few new restrictions to help dissuade abuse. Thanks for your patience!
ーーー
な状態で試すこともできません。なので、別のことを考えました↓。
##SSL/TLS化
8080でつながるようなWEBサーバにしていたので、家のルータに穴を開けただけだと、データ丸見えです。なのでSSL化しようとしました。
でも、高価な証明書は買えないし、定期的に更新が必要な無料の「Let's Encrypt」ですと、証明書を埋め込むArduinoでは不便すぎます。
そこで・・・
##GCP Always Free でリバースプロキシ構築
NGINXでリバプロを作って、SSL化しようと考えました。
でも、お金をかけなくないので、調べていたら、GCPに永久無料枠があるとのこと。
f1.microで、アメリカのバージニア州以外のリージョンで、とか条件がありますが、個人で使う分には、「無問題」ですので、それで作ることに・・。
早速、Google Cloudで、環境を作ります。
(作り方は、ここで書くと長くなるので、ググってください。)
###VM構築とNGINXインストール
・ubintu18.04LTSでVMを作りました。尚、固定IPにしています。無料VMで使う分には、固定IPも無料枠に入ってるみたいです。構築手順は省略します・・。
・NGINXとcertbotをインストールしました。
・ついでに、timezoneがMSTだったので、JSTにもしました。
$sudo su
#timedatectl set-timezone Asia/Tokyo
#apt update
#apt upgrade
#apt install nginx
#apt install certbot
#apt install python-certbot-nginx
###mydns.jpでドメイン設定
名前でアクセスしたいので、いつも使ってるmydns.jpで、ドメインを設定しました。IPアドレスでなくてもドメイン名でアクセスできます。
mydns.jpの設定は省略します・・
そして、cronに下記をセットしました。こんな感じ・・
0 1 * * * curl -u mydnsのID:mydnsパスワード https://www.mydns.jp/login.html
これ以降では「yourhogehohe.com」でアクセスできるようになったとします。
尚、自分は、家のインターネットもmydns.jpを使っています。以降は、仮に「househogehohe.com」となってる前提で記載しますね。
###certbotでSSLサーバ構築
certbotを使うと、SSL設定とか自動でやってくれます。楽ちんです。
#certbot --nginx -d yourhogehoge.com
途中で、httpをhttpsにリダイレクトするか?って聞いてきたので、そうしました。
完了したら、
ーーー
You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=yourhogehoge.com
ーーー
って出てきたので、そのサイトでSSLになってるか確認できます。
Let's Encryptは、定期的に証明書更新が必要なのですが、その設定も自動でやってくれてます。
念のため、下記に「certbot」があるか確認しました。
#ls /etc/cron.d
###リバーシプロキシの設定
/etc/nginx/site-available/default
を直接メンテしちゃいます。
server の中のlocationの上あたりに下記を埋め込みました。
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
locationの中には、
proxy_pass http://家のドメイン名:8080/;
を入れました。↓結果、このあたりは、こんな感じ。
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location / {
proxy_pass http://家のドメイン名:8080/;
try_files $uri $uri/ =404;
}
###家のルータの設定
ここは、それぞれのルータで設定が違うと思いますが、家のルータは、F660Aでしたので、下記のようにしました。あ、そういえば、ESP8266マシンは、ルータのMACアドレス制御で、固定IP化にしています。
###接続試験
nginxを起動して、さぁ、動くかなぁ・・
#nginx
##自由発声マシンの改造
以前のプログラムだとリバプロがうまく動きません。
どうも、「/speech」で発声するようにしているところが、うまくプロキシーしてくれないみたいなんです。
そこで全部「/」で動くように修正しました。
また、この状態ですと、URLを知っていれば誰でも、家のGoogleHomeを喋らせられちゃいます。
それはよろしくないので、BASIC認証をつけました。
また、起動時にIPアドレスを発声するとか、うるさくなってきたのでコメントアウトしました。
その他、マルチWiFi化とか、こまこま改良しました。
詳しくは、前回の記事+改良版のソースで・・・
なんか間違ってる気もしますが・・。
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiClient.h>
#include <esp8266-google-home-notifier.h>
#include <ESP8266WiFiMulti.h>
// Google Home
GoogleHomeNotifier ghn;
const char displayName[] = "あなたのデバイスネーム";
String mes;
// WiFi
ESP8266WiFiMulti wifiMulti;
const char* ssid1="あなたのSSIDその1";
const char* ssid2="あなたのSSIDその2";
const char* ssid3="あなたのSSIDその3";
const char* password1="あなたのパスワードその1";
const char* password2="あなたのパスワードその2";
const char* password3="あなたのパスワードその3";
// Web Server
ESP8266WebServer server(8080);
const char* www_username = "BASIC認証のログインID";
const char* www_password = "BASIC認証のパスワード";
static const char* htmlroot = "<html>"
"<head><meta charset=\"utf-8\"><title>free speaking machine</title></head>"
"<body style='background-color:#ffffde;font-family:sans-serif;font-size:32px;'>"
"<h1>Free Speaking Machine</h1>"
"<form action='/' method='get' name='phrase'>"
"<input type='radio' name='lang' value='ja' checked style='font-family:sans-serif;font-size:30px;height:30px;'> 日本語<br>"
"<input type='radio' name='lang' value='en' style='font-family:sans-serif;font-size:30px;height:30px;'> 英語<br>"
"<input type='text' name='phrase' style='font-family:sans-serif;font-size:30px;width:400px;height:80px;'>"
"  "
"<input type=submit value=' Speech ' name='button' style='font-family:sans-serif;font-size:30px;height:40px;'>"
"</form>"
"</body>"
"</html>";
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("");
//WiFi
Serial.println("");
Serial.println("connecting to Wi-Fi");
WiFi.mode(WIFI_STA);
wifiMulti.addAP(ssid1, password1);
wifiMulti.addAP(ssid2, password2);
wifiMulti.addAP(ssid3, password3);
while (wifiMulti.run() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected.");
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
Serial.print("IP address: ");
Serial.println(WiFi.localIP()); //Print the local IP
Serial.println("");
/*
//Google Home speak SSID.
mes = "I am Free Speaking Machine.";
mes += "SSID is ";
mes += WiFi.SSID();
mes += ".";
googleHomeConnection( "en", mes.c_str() );
*/
if (!MDNS.begin("freespeaking")) {
Serial.println("Error setting up MDNS responder!");
googleHomeConnection( "ja", "mDNSエラーです" );
while (1) {
delay(1000);
}
}
Serial.println("mDNS responder started." );
// Web Server
server.on("/", handleSpeechPath);
server.begin();
}
void loop() {
// put your main code here, to run repeatedly:
MDNS.update();
server.handleClient();
// 必要ならば再接続
while (wifiMulti.run() != WL_CONNECTED) {
Serial.println("Re-connecting...");
delay(1000);
}
}
void googleHomeConnection( String lang_str1, String talk_str1 ){
Serial.println("connecting to Google Home...");
if (ghn.device( displayName, lang_str1.c_str() ) != true) {
Serial.println(ghn.getLastError());
return;
}
Serial.print("found Google Home(");
Serial.print(ghn.getIPAddress());
Serial.print(":");
Serial.print(ghn.getPort());
Serial.println(")");
if (ghn.notify( talk_str1.c_str() ) != true) {
Serial.println(ghn.getLastError());
return;
}
Serial.println("");
}
String ipToString(uint32_t ip){
String result = "";
result += String((ip & 0xFF), 10);
result += ".";
result += String((ip & 0xFF00) >> 8, 10);
result += ".";
result += String((ip & 0xFF0000) >> 16, 10);
result += ".";
result += String((ip & 0xFF000000) >> 24, 10);
return result;
}
void handleSpeechPath() {
String lang = server.arg("lang");
String phrase = server.arg("phrase");
if (!server.authenticate(www_username, www_password)) {
return server.requestAuthentication();
}
if (phrase == "") {
server.send(200, "text/html", htmlroot);
MDNS.update();
return;
}
Serial.println("handleSpeechPath");
Serial.println(lang.c_str());
Serial.println(phrase.c_str());
googleHomeConnection( lang.c_str(), phrase.c_str() );
delay(1000);
server.send(200, "text/html", htmlroot);
MDNS.update();
}