アレクサにやってもらっていることで、一番便利に使えてるのがこれかぁと思っています。これは、自作Alexaデバイスの2作目になります。
- 仕様
- しくみ
- 給湯器リモコンの改造
- 概要
- リモコン分解
- スイッチ検出箇所と動作
- 基板改造
- 制御回路
- 予備実験
- 2次側(リモコンスイッチ置き換え)
- 制御回路基板
- ソフトウエアをつくる
- シミュレートツール
- シミュレートツールのスケッチ
- Alexaライブラリ
- Espalexa
- ロジックについて考えてみた
- Alexaデバイスのスケッチ
- Alexaアプリへの登録
仕様
- 給湯器のリモコンをAlexaデバイスにする
- Alexaへ頼むのは、運転(入/切)、自動(湯はり入/切)、おいだき(入/切)
- 各々の入/切は状態を確認できるようにする
しくみ
私は結構なAmazonファンで、映像、音楽のサブスクリプション サービスの利用を始め、Echoデバイスを家中に置いて、どこからでもAlexaに話しかけれるようにしています。テレビとかエアコンは市販のIOTデバイスを利用して操作できるようになっています。これを自作Alexaデバイスでも行おうという内容です。
しくみを説明するのに最適な図と文があったので、拝借させていただきます。
fauxmoESPを単純にalexaと連携させて、aleaxからの音声制御にてESP8266のポートをon、offさせて他の機器を制御するという簡単な仕組みを目指します。<省略>
alexaで蛍光灯を制御する全体図は↓こんな感じ。第3世代のamazon echoを使うので、ESP8266とalexaを連携するライブラリはfauxmoESPというライブラリを使用します。
引用元:ESP8266とalexaで家電を制御する fauxmoESP編 ?照明を音声コントロール?
Arduino類の小型マイコンでAlexaデバイスを作れるライブラリがGitHubにあります。ESP8266とEPS32用のライブラリが揃っているようです。今回はESP8266を使います。
Echoデバイスを通してAlexaアプリが命令を音声認識すると、小型マイコンのESP8266と連携して給湯器のリモコンをON/OFF操作するという流れになります。
給湯器リモコンの改造
概要
我が家の給湯器リモコンはリンナイのMC-220Vという機種で、当たり前ですが、指でスイッチのON/OFFを行います。このスイッチと並列にフォトカプラを設け、ESP8266の信号でフォトカプラの一次側(1アノードと2カソード間)をON/OFFすると、2次側(3エミッタと4コレクタ間)の回路がスイッチングするように改造します。
フォトカプラは一次側と二次側が電気的に絶縁されているので、一次側の信号が二次側に紛れ込んで誤動作させるという不具合を防ぐことができます。給湯器というガスを使う製品ですから安全最優先で考えます。
 |
フォトカプラ TLP785 (TOSHIBA製) |
リモコン分解
リモコンのMC-220Vはドライバーなしで分解できました。裏から赤のツメを外すと基板は黄色のツメで固定されているだけです。

表面(液晶面)にスイッチが3つ並んでいます。右から、次のようになっていました。スイッチの一次電圧は5Vでした。
- 運転入/切 SW4 スイッチONで信号線(5V)をGNDと短絡
- 自動(風呂給湯)SW3 スイッチONで信号線(5V)をGNDと短絡
- おいだき SW2 スイッチONで信号線(5V)をGNDと短絡
SW4には結線可能なピン穴(黄色丸)がありましたが、他のSWには適切なピン穴がなく、各々の信号線はタクトスイッチの足に半田付けすることにしました。

スイッチ検出箇所と動作
安全最優先の考えを徹底するため、2次側のスイッチの通電/非通電の状態を検出するようにします。このリモコンはスイッチONでそれぞれのLEDが点灯するタイプでしたので、スイッチ動作の検出用にこのLED信号を利用することにしました。ESP8266がこの状態を読みとり、不要な操作やありえない操作、あってはならない操作を禁止するソフトにします。LEDへかかる電圧を調べたところ、信号電圧は下表の値でしたので、ESP8266はLow/Highで読み取りが可能であることがわかりました。
 |
運転入/切 |
 |
自動(湯はり) |
 |
おいだき |
基板改造
スイッチ信号3つとひとつのLED信号は基板の表から取り出しました。

残り2つのLED信号は、運転と自動(湯はり)ですが、基板裏から取り出します。
リード線はリモコン背面で束ねてESP8266へつなぎます。ハンダが剥離しないようリード線は要所要所をホットボンドで基板に固定しておきました。
制御回路
ことば使いは大袈裟ですが、フォトカプラ側の回路を示します。
予備実験
フォトカプラはTLP785を使用します。まず、フォトカプラでうまくリモコンスイッチが代行できるのか確認実験を行います。
リモコンのタクトスイッチの電流を5mAと仮定して、1kΩの抵抗をフォトカプラのエミッタ、コレクタ間につないだバイパス回路を作って試してみました。フォトカプラの一次側のON/OFFで、うまくリモコンが操作でき給湯器が作動しました。
2次側(リモコンスイッチ置き換え)
回路を簡単にするために一次側のLEDの電流制限抵抗(順電流を与える抵抗)を1個で考えます。というのは、このリモコンはモメンタリーなプッシュスイッチです。3つ並列に並んでいますが、それらを同時に押すことはありません。フォトカプラではONがモメンタリーになるようにON時間を決めます。すなわちフォトカプラが3個並んでいますが、実際はひとつづつしかONにしませんので一つの電流制限抵抗で設計します。
利用するESP8266(Wemos D1 mini)のデジタルpin出力はHighで3.3V、電流は最大12mAです。
データシートによるとコレクタ電流(2次側電流)5mAを流せる一次側の順電流は3mAです。また、推奨の順電流は16mA(標準)、順電圧1.3V(最大)です。デジタルpinの電流制限を考慮して9mA(12mAの-25%)流すことを考えます。よって、下記図の回路にしてフォトカプラをひとつだけ使うときは9mAが流れます。
順電流9mAを流せる抵抗は、(3.3-1.3V)/(9/1000)=222Ω なので、手持ちの220Ωにします。
制御回路基板
できた基板です。ESP8266はヘッダpinで基板に重ね付けします。決めたピン配は下表に示します。
ソフトウエアをつくる
シミュレートツール
ソフト開発に給湯器リモコンを使いっぱなしにするわけにはいかないので、リモコンを模擬できる回路をブレッドボードで組みました。スイッチ3個、LED3個がArduino Leonardoで動きます。下に映っているESP8266が給湯器リモコン用のAlexaデバイスになります。フォトカプラはもちろんこのESP8266で動かします。
Arduino初心者だったので、タクトスイッチのチャタリングに結構悩まされました。安定して再現性良くスイッチが切り換ってくれないとソフトの検証になりません。チャタリング対策に<
Switches.h>というライブラリを利用させていただきました。このライブラリのおかげて安定した再現性のあるタクトスイッチの切り換えができるようになりました。
シミュレートツールのスケッチ
Alexaライブラリ
ESP8266をAlexaデバイスにするには専用のライブラリを使う必要があります。
最初に引用させていただいたブログ始め、よく利用されているライブラリは『fauxmoESP』です。fauxmoESPについて調べていくと、「fauxmoESPは10台以上のデバイスでは使えない」らしいです。ひとつのライブラリ(Arduino 1個)で10台が使えないのか、Alexaアプリ全体で10台が使えないのか、ちゃんと調べる前に制限のない他のライブラリを探しました。その時点でAlexaアプリに登録しているデバイスは、ライトやエアコンなど10台を超えていました。今になって思えば、きっと前者の制限だろうと思うのですが、検証もその後の調査もしていません。
Espalexa
そこで、デバイス数の制限のない「Espalexa」というライブラリを利用することにしました。 ESP8266とESP32で使えるようです。使ってみてわかったのですが、Phillips社のHueというスマート電球向けのライブラリのようです。Alexaアプリには照明デバイスとして登録されます。
ロジックについて考えてみた
リモコンスイッチ事象を考えてみたのが、次のメモです。赤文字の条件でスイッチを作動させることにしました。
Alexaデバイスのスケッチ
if文で個々のスイッチが有効になる条件を決めていますが、基本はサンプルスケッチを組み合わせただけです。wifiで繋がります。
#ifdef ARDUINO_ARCH_ESP32
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif
#include <Espalexa5.h>
#include <Switches.h>
boolean connectWifi();
void untenChanged(uint8_t brightness);
void jidouChanged(EspalexaDevice* dev);
void oidakiChanged(EspalexaDevice* dev);
const char* ssid = "********";
const char* password = "********";
boolean wifiConnected = false;
Espalexa espalexa;
const int untenPin =15;
const int untLEDPin =13;
const int jidouPin =12;
const int jidLEDPin =14;
const int oidakiPin =16;
const int oidLEDPin =5;
const int holdTime = 2000;
unsigned long prev, curr, interval;
Switches sw1(untLEDPin, false, SWITCH_LEVEL);
Switches sw2(jidLEDPin, false, SWITCH_HtoL);
Switches sw3(jidLEDPin, false, SWITCH_LEVEL);
Switches sw4(oidLEDPin, false, SWITCH_LEVEL);
void setup()
{
interval = 60*60*1000;
Serial.begin(115200);
pinMode(untLEDPin,INPUT);
pinMode(untenPin,OUTPUT);
pinMode(jidLEDPin,INPUT);
pinMode(jidouPin,OUTPUT);
pinMode(oidLEDPin,INPUT);
pinMode(oidakiPin,OUTPUT);
wifiConnected = connectWifi();
if(wifiConnected){
espalexa.addDevice("Unten", untenChanged);
espalexa.addDevice("Jidou", jidouChanged, EspalexaDeviceType::onoff);
espalexa.addDevice("Oidaki", oidakiChanged, EspalexaDeviceType::onoff);
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);
if (sw2()){prev=millis();
}
}
void untenChanged(uint8_t brightness) {
Serial.print("Unten SW changed to ");
if (brightness > 128 && sw1()){
Serial.println((String)"ON, brightness "+brightness);
digitalWrite(untenPin,HIGH);
delay(holdTime);
digitalWrite(untenPin,LOW);
Serial.println((String)"Switch on for "+holdTime/1000+" sec");
}
else if (brightness <= 128 && !sw1()) {
Serial.println((String)"OFF, brightness "+brightness);
digitalWrite(untenPin,HIGH);
delay(holdTime);
digitalWrite(untenPin,LOW);
Serial.println("Not receiving signal now !");
}
else {return;}
}
void jidouChanged(EspalexaDevice* d) {
if (d == nullptr) return;
Serial.print("Jidou changed to ");
if (d->getValue()){
if ( sw1() && sw3() ){
Serial.println("ON");
digitalWrite(untenPin,HIGH);
delay(holdTime);
digitalWrite(untenPin,LOW);
Serial.println((String)"お湯 Switch on for "+holdTime/1000+" sec");
delay(holdTime);
Serial.print("and ");
digitalWrite(jidouPin,HIGH);
delay(holdTime);
digitalWrite(jidouPin,LOW);
Serial.println((String)"お風呂 Switch on for "+holdTime/1000+" sec");
}
else if ( !sw1() && sw3() ){
Serial.println("ON");
digitalWrite(jidouPin,HIGH);
delay(holdTime);
digitalWrite(jidouPin,LOW);
Serial.println((String)"お風呂 Switch on for "+holdTime/1000+" sec");
}
}
else {
Serial.println("OFF");
digitalWrite(jidouPin,HIGH);
delay(holdTime);
digitalWrite(jidouPin,LOW);
Serial.println("Not receiving signal now !");
}
}
void oidakiChanged(EspalexaDevice* d) {
if (d == nullptr) return;
Serial.print("Oidaki changed to ");
if (d->getValue()){
if (!sw1() && sw4() ){
curr = millis();
if ( sw3() && (curr - prev) <= interval) {
Serial.println("ON");
digitalWrite(oidakiPin,HIGH);
delay(holdTime);
digitalWrite(oidakiPin,LOW);
Serial.println((String)"Switch on for "+holdTime/1000+" sec");
}
}
}
else {
Serial.println("OFF");
digitalWrite(oidakiPin,HIGH);
delay(holdTime);
digitalWrite(oidakiPin,LOW);
Serial.println("Not receiving signal now !");
}
}
boolean connectWifi(){
boolean state = true;
int i = 0;
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
Serial.println("Connecting to WiFi");
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;
}
Alexaアプリへの登録
登録の仕方はAlexa対応のライトやエアコンと同じです。「アレクサ、デバイスを探して」と話しかけるか、アプリの「デバイスを追加」をタップします。
スケッチで設定した名前のデバイスが見つかるので、Alexaアプリでわかりやすい「お湯」「お風呂」「おいだき」に名称変更しました。
- espalexa.addDevice("Unten", .....
- espalexa.addDevice("Jidou", .....
- espalexa.addDevice("Oidaki", .....
アプリ上では下記のように表示されます。照明として認識されますが、espalexaライブラリのON/OFFしか使わないので問題ないです。ただし、アレクサに話しかけるとき、スイッチが想定される言葉しか有効になりません。(アレクサが言うことを聞きません。)
例えば、
お湯をつけて、お湯を入れて、お風呂をいれて、お風呂を消してはOK
お湯を出して、お風呂を沸かしてはNG