後からこのブログを書いていますが、自作Alexaデバイスの一作目です。アレクサにエンジンを動かしてもらうやり方は、「アレクサ、お風呂湧かして」と同じです。むしろこちらが一作目なので、簡単です。なので、別の視点でまとめます。
Alexaデバイス化したArduinoマイコンのESP8266をAlexaアプリで音声制御します。そして、ESP8266がエンジンスターターを動かします。
Amazon Alexaで自作のIOTデバイスを使う方法は何通りか考えられます。次のブログが良くまとめらえています。この中で「fauxmoESP ライブラリ等のエミュレータを使う」方法が、外部のIOTサービスを使わないで済むので簡単に構築できます。要するにエミュレータライブラリを使って、そのライブラリが定義している固有の製品に化ける訳です。
fauxmoESPの紹介にはPhilips Hue lightをエミュレートしていると書いてあります。私が利用しているEspalexaも同じくPhilips Hue APIをエミュレートしています。そして、両ライブラリーともESP8266とEPS32に適用します。
ESP32はWifi/Bluetoothの両方に対応ですが、ESP8266はWifiのみです。ESP32は多機能高性能ゆえに他のArduino系のマイコンとちょっと違った使い勝手になります。また、普通のブレッドボードではpinが刺せません。私はWifiが使える体格が小さいマイコンとしてESP8266を選んでいます。
ESP8266 上下両側にピンが刺せる ESP32 片側の1列しかピンが刺せない |
エンジンスターター
私の車のエンジンスターターは安価で分解が可能でした。左が外観写真、右が内部基板です。黄色のボタンを押すとエンジンが動きます。メカニカルなタスクスイッチ式なので改造に手が出せます。アンサーバックはありません。
ICはPT2264と刻印されています。中華製のエンコーダICでデータシートがありました。
引用元:Fixed Code Remote Controller (PT2262, PT2264) (YCF102 Series)
簡素な作りですので、ボタンスイッチの役割をESP8266で代行すれば、Alexa対応エンジンスターターができそうです。
回路
リモコンスイッチのON/OFFをフォトカプラで代行させます。Alexaデバイス(ESP8266)の出力をフォトカプラの一次側に入力し2次側をスイッチングします。
下記の回路はリモコンスイッチの代用にトグルスイッチにしています。スイッチ端子は図示の抵抗値を持っていました。アンテナの接続はイメージです。
タクトスイッチの2次側電圧はスイッチOFFのとき0V、ONのとき2.8V(?)が出ています。
「アレクサ、エンジンをONにして」と命令したら、D8からHigh(3.3V)が出力され、フォトカプラの一次側がONします。すると同時に2次側が導通し、実際の回路ではスターター信号が発信され、スターター基板の確認用LEDも点灯します。
上記の情報によるとPT2264のOperation Voltage:9-12V DCでしたが、ESP8266の5Vpin電圧で車のエンジンがかかりましたので、特に昇圧はしませんでした。
エミュレータライブラリについて
AlexaデバイスをエミュレートするライブラリはEspalexaを使っています。先にも書きましたが、よく使われているfauxmoESPと同じようにPhilips Hueをエミュレートします。
Espalexaの使い方
GitHubからダウンロードしてArduino IDEにインストールします。
GitHubのEspalexaページに説明がありますが、自分の覚えのためにあらためて載せておきます。
基本的な構文
最初にWifiとEspalexaライブラリをインクルードします。この2つのライブラリは必ず必要です。
callback functionはデバイスごとにひとつづつ必要です。名称は任意です。
#ifdef ARDUINO_ARCH_ESP32 #include <WiFi.h> #else #include <ESP8266WiFi.h> #endif #include <Espalexa.h> // Change this!! const char* ssid = "..."; const char* password = "wifipassword";//callback functions //callback functions void firstLightChanged(uint8_t brightness); Espalexa espalexa;
続いて、パラメータを3つ定義します。ひとつ目はデバイス名(任意の名称:Light 1)、二つ目はAlexaから呼ばれるcallback function(これは先に定義しています。デバイスの状態が変わるたびに呼び出されます。)、3つ目はデバイスのタイプを定義します。無くても良いみたいです。3つ目のパラメータの詳細はサンプルプログラムに説明があります。
void setup() { *** 省略 *** // Define your devices here. espalexa.addDevice("Light 1", firstLightChanged); //simplest definition, default state off espalexa.begin();
loop関数の中でループさせます。
void loop() { espalexa.loop(); delay(1); }
最後にcallback functionの中でやりたいことを書きます。
//our callback functions void firstLightChanged(uint8_t brightness) { Serial.print("Device 1 changed to "); //do what you need to do here //EXAMPLE if (brightness) { Serial.print("ON, brightness "); Serial.println(brightness); } else { Serial.println("OFF"); } }
より複雑な(詳細な)構文
例えば、「アレクサ、エアコンを25度にして」などと、温度を指定することができます。
callback functionを次のように書き、
//callback functions //new callback type, contains device pointer void alphaChanged(EspalexaDevice* dev);
セットアップは、第3のパラメーターを"EspalexaDeviceType::dimmable"にします。
void setup() *** 省略 *** // Define your devices here. espalexa.addDevice("Alpha", alphaChanged, EspalexaDeviceType::dimmable); //Dimmable device調光できる
そして、callback functionの設定で、"d"に数値を入れた音声指定を作ることができます。
void betaChanged(EspalexaDevice* d) { if (d == nullptr) return; uint8_t degrees = d->getDegrees(); //for heaters, HVAC, ...
これは、現在の状態を見ることにも使えます。d->getValue()とするならば、取得できる値は0-255でそれぞれ下記表の意味になります。すなわち、取得できる値によって、その時点での電源のON/OFFを読み取ることができます。
No | 値 | 状態 |
---|---|---|
1. | 0 | OFF |
2. | 1 - 254 | Dimmed |
3. | 255 | ON |
Alexaデバイスのスケッチ
サンプルスケッチの”EspalexaFullyFeatured.ino”を元にして、不要と思われるところを削っています。十分に削り切れていません。
d->getValue()でON/OFFの状態を取得し、それに応じた処理をしています。手持ちのスターターはエンジンが動いているときに再度スイッチが押されると、エンジン停止の命令になります。それを有効にしたい場面もあるだろうし、困る場面もあるだろうし....。どちらかに決めるしかありません。
/* * Espalexaでデバイスを制御する基本形(シンプルな1個のON/OFFスイッチ) */ #ifdef ARDUINO_ARCH_ESP32 #include <WiFi.h> #else #include <ESP8266WiFi.h> #endif #include <Espalexa.h> // Change this!! const char* ssid = "..."; const char* password = "wifipassword"; // prototypes boolean connectWifi(); //callback functionsコールバック関数 void alphaChanged(EspalexaDevice* dev); boolean wifiConnected = false; Espalexa espalexa; const int priPin =15; const int secPin =13; const int holdTime = 2000; void setup() { Serial.begin(115200); pinMode(secPin,INPUT); pinMode(priPin,OUTPUT); // Initialise wifi connection wifiConnected = connectWifi(); if(wifiConnected){ // Define your devices here. espalexa.addDevice("Alpha", alphaChanged, EspalexaDeviceType::onoff); //non-dimmable device オンオフのみ espalexa.begin(); } else { while (1) { Serial.println("Cannot connect to WiFi. Please check data and reset the ESP."); delay(2500); } } } void loop() { espalexa.loop(); delay(1); } //our callback functionsコールバック関数 void alphaChanged(EspalexaDevice* d) { if (d == nullptr) return; //this is good practice, but not requiredこれは良いやり方ですが、必須ではありません //do what you need to do hereここで必要なことをしてください //EXAMPLE Serial.print("A changed to "); if (d->getValue()){ Serial.println("ON"); digitalWrite(priPin,HIGH); delay(holdTime); digitalWrite(priPin,LOW); Serial.println((String)"Switch on for "+holdTime/1000+" sec"); } else { Serial.println("OFF"); digitalWrite(priPin,LOW); Serial.println("Not receiving signal now !"); } } // connect to wifi ? returns true if successful or false if not boolean connectWifi(){ boolean state = true; int i = 0; WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.println(""); Serial.println("Connecting to WiFi"); // Wait for connection Serial.print("Connecting..."); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); if (i > 20){ state = false; break; } i++; } Serial.println(""); if (state){ Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } else { Serial.println("Connection failed."); } return state; }