どんな家電もIoTに対応！AIスピーカー等から操作できる小型デバイスの作成

# １．はじめに
　近年、Google HomeやAmazon Echoを代表するAIスピーカー（スマートスピーカー）が普及してきましたが、その期待値が最も大きいことは「家電制御」ではないでしょうか。代表的なものではテレビ、エアコン、照明、細かいレベルではお風呂の湯張り、炊飯器、コーヒーメーカー、等様々な家電をイメージできます。前者は、赤外線で操作できる家電であり、専用の外部デバイスを購入すればAIスピーカーと連携することができます。一方、後者については実現が難しいのが現状であり、家庭にあるほとんどの一般家電はこれに該当すると思います。そこで、今回はこれらの赤外線がついていない家電に着目し、自作小型デバイス（以下、本デバイス）を作成することでIoTに対応していない一般家電を制御可能にしてみました。

【自作デバイス実物】
![20170923_033930.jpg](https://qiita-image-store.s3.amazonaws.com/0/195176/53069402-864f-28a1-8c6d-ac9a4b6bca71.jpeg)





# ２．デバイスの概要
 　一般家電を制御にするためにはどうすればよいか・・・この問題を「家電についている物理ボタンをどう制御するか」という問題に置き換えて考えます。物理ボタンを外部から制御するための方法として、大きく２つの方法があります。
１.家電についているボタンを外部デバイスから物理的に押下する。
２.電気的にボタンの押下をエミュレートする。

上記１番は、[MicroBot Push](https://robotstart.info/2017/06/16/microbot-push.html)等が挙げられます。非常に導入が簡単であり、製品を購入すれば誰でも家電をスマホやAIスピーカー等から制御ができます。しかし、対象家電の形状等により取り付けに制限があると共に、まれに位置ずれ等によりボタン押下に失敗するケースがあります。
　そこで、今回は２番の方法を用いて実現します。具体的には、物理ボタンの挙動（電気回路の遮断→電気回路の通電→電気回路の遮断）を外部デバイスにてエミュレート(スイッチング制御)します。この方法では、基本的にほとんどの家電に対応でき、確実に制御することができます。ただし、家電の電子基板と外部デバイスをリード線等で接続する必要があり、ある程度技術的要素が必要なので実施する場合は自己責任でお願いします。（家電の保証もなくなる）
　
　物理ボタンの挙動をエミュレートする方法として、「フォトカプラ」という素子と「マイコン(ESP8266)」を組み合わせた制御を考えました。概念図を以下に記載します。
![概念図.jpg](https://qiita-image-store.s3.amazonaws.com/0/195176/362576ad-3420-5559-ab49-226e0842b02e.jpeg)


　フォトカプラの基本的な動作は[こちら](https://yuki-no-yabo.com/how-to-use-photocoupler/)が参考になると思います。本素子を用いることで家電の電気回路に影響を与えることなく物理ボタンの挙動（電気回路の遮断/通電）を再現することができます。※ただし、家電の物理スイッチからフォトカプラへのバイパス回路が必要
　また、[ESP8266のマイコン](https://qiita.com/engksk/items/16f1ac61eedcbb4dfaa8)を使用することで、WifiからAIスピーカ等からの実行命令を受けることができるため、Wifiが届く範囲内であればロケーションフリーで本デバイスを設置することができます
これらのパーツを用いた一連の動作は下記の通りです。

```profile:一連の動作概要
①AIスピーカーやスマホ経由でマイコンへ家電制御を指示(Wifi経由、HTTP(POST)を使用)
②マイコンは、指示内容に従いフォトカプラへの通電ON/OFFを実行
③フォトカプラへの通電に連動して、家電の物理ボタン回路(バイパス回路)への通電 ON/OFFを実行
④家電は、物理ボタンが押下されたとみなし、所定の動作を実施
```

# ３．準備物
　本デバイスを作成する上で必要な準備物は下記の通りです。
(ここでは、1つのデバイスで2つの物理ボタンを制御できるものを作成)

| NO. |      項目     |                  製品名                  | 数量 |   購入先   |
|:---:|:-------------:|:----------------------------------------:|:----:|:----------:|
|   1 | マイコン      | ＥＳＰ－ＷＲＯＯＭ－０２開発ボード       |  1個 | 秋月電子   |
|   2 | フォトカプラ  | ＴＬＰ７８５（ＧＢランク）               |  2個 | 秋月電子   |
|   3 | 動作確認用LED | ＬＥＢＷＬ３４Ａ０６ＡＡ００             |  1個 | 秋月電子   |
|   4 | ピンソケット  | Ｌ型ピンソケット　１×１０（１０Ｐ）      |  1個 | 秋月電子   |
|   5 | バニラ基盤    | 片面ガラスコンポジット・ユニバーサル基板 |  1個 | 秋月電子   |
|   6 | ワイヤー      | ジャンパーワイヤー(オス-オス)            |  1式 | 秋月電子   |
|   7 | ケース        | CS75N-W                                  |  1個 | タカチ電機 |
# ４．作成
　本デバイスの作成方法を「ハードウェア編」と「ソフトウェア編」に分けて説明していきます。
## ４．１　ハードウェア編
**＜デバイスの基盤作成＞**
　基板上にマイコン、フォトカプラ、ピンソケット、LEDを配置し、回路を作成していきます。作成する回路図は下記の通りです。
![GeeralRemote.jpg](https://qiita-image-store.s3.amazonaws.com/0/195176/8f735eb1-a2b7-64e1-978c-e81c611556ef.jpeg)
図中、一番右側のIN,OUTが家電と接続する部分となるため、この部分にピンソケット等を配置すれば家電との脱着が楽になります。

**＜ケース作成＞**
　作成した基盤を収納するためのケースを作成します。そのままでは、ケースに収まらないため、USB、ピンソケット、コンデンサ、LED部分を切り抜きます。切り抜き後、基盤を収納したらデバイスの完成です。

【完成写真】
![20170923_033930.jpg](https://qiita-image-store.s3.amazonaws.com/0/195176/9e79e458-cd9b-17e2-900a-2fd7cee0eb5a.jpeg)
【内部写真】
![20170923_032311.jpg](https://qiita-image-store.s3.amazonaws.com/0/195176/96448f35-1b62-14f9-62db-d27cd85cce35.jpeg)
【基盤裏面写真】
![20170923_032319.jpg](https://qiita-image-store.s3.amazonaws.com/0/195176/1576c1d6-62ae-3c98-f15f-bd2861a80a5e.jpeg)


**＜家電との接続＞**
　導線（ジャンプワイヤー等）を使用し、制御したい家電の物理ボタンと作成したデバイス間にバイバス回路を作成します。
　まずは、制御したい家電の物理ボタンの基盤部分を確認します。家電は概ね物理ボタンにタクトスイッチを使用しているので、家電を分解しながら該当するタクトスイッチを探します。タクトスイッチが見つかったら、テスターを用いながらプラス部分／マイナス部分に該当するピンを見つけます。
(給湯機リモコンの例)
![給湯機リモコン分解.jpg](https://qiita-image-store.s3.amazonaws.com/0/195176/fd40e39f-8b07-7573-4ee3-1ca91f6ae8c8.jpeg)
次に該当するピンに導線をはんだ付けし、家電から導線を出します。
最後に、導線を作成したデバイスのピンソケットに接続して完了です。

【接続完成写真】
![20170925_012621(1).jpg](https://qiita-image-store.s3.amazonaws.com/0/195176/241611c6-aa8a-b3b7-7e20-836d620b26b2.jpeg)

## ４．２　ソフトウェア編
　上記で自作したデバイスのマイコンにプログラムを埋め込みます。
期待する主な動作は下記の通りです。
　①初期化
　②ホームネットワークへの接続(Wifi)
　③Webサーバーの起動(AIスピーカやスマホ等からの処理依頼待受け)
　④受信したコマンドに従って処理を実行

マイコンはArduinoを使用しているので、[こちら](https://keijirotanabe.github.io/blog/2017/02/07/esp8266-Arduino-170207/)等を参考にしながら以下に記載するプログラム(スケッチ)を書き込みます。
※環境に合わせて「Wi-Fi設定」のSSID,PASS,IPアドレスを記載してください。

```
#include <ESP8266WiFi.h>  // for WIFI

// WIFIチェック用LEDPIN設定
#define LEDPINWIFI  (12)
// 制御入力ピン1設定
#define RemotePin1  (4)
// 制御入力ピン2設定
#define RemotePin2  (5)

// Wi-Fi設定
const char* ssid = "XXXXXX";
const char* password = "XXXX";
IPAddress ip( XXX, XXX, XXX, XXX );
WiFiServer g_server(80);

// POSTパラメータ項目名設定
const char* param = "COMMAND";

// コマンド名設定
// 制御１
const char*   sRemote1    = "REMOTE1";
// 制御２
const char*   sRemote2    = "REMOTE2";

// ボタン押下時間(ms)
#define PUSH_TIME  (300) 

// その他設定
#define LEN_RNRN  (4)   // 4 : "\r\n\r\n"の文字数

void setup()
{
  // Pin初期化処理
  pinMode(LEDPINWIFI, OUTPUT);
  pinMode(RemotePin1, OUTPUT);
  pinMode(RemotePin2, OUTPUT);

  // シリアル通信の初期化処理
  Serialbegin(115200);
  delay(10);

  // WiFiネットワーク接続
  Serialprint("Connecting to ");
  Serialprintln( ssid );
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serialprint(".");
  }
  Serialprintln("");
  Serialprintln("WiFi connected");

  // 固定IPアドレスの設定
  WiFi.config( ip, WiFi.gatewayIP(), WiFi.subnetMask() );

  // WEBサーバー開始
  g_server.begin();
  Serialprintln("Server started");

  // IPアドレスの表示
  Serialprintln(WiFi.localIP());

  // WiFiチェック用LED動作更新
  digitalWrite(LEDPINWIFI, HIGH);
  //WIFIConnectedLED();
}

void loop()
{
  // クライアントからのアクセス
  WiFiClient client = g_server.available();
  if( client )
  {
    String strHttpRequest;
    int iIndexRNRN;
    
    Serialprintln("new client");
    // HTTPリクエスト読み込み
    ReadHttpRequest( client, strHttpRequest );

    // HTTPリクエストあり
    if( 0 != strHttpRequest.length() )
    {
      Serialprintln( strHttpRequest );
      iIndexRNRN = strHttpRequest.indexOf( "\r\n\r\n" );

      // POSTデータあり（空行「\r\n\r\n」より後ろにデータがあればそれはPOSTデータ）
      if( -1 != iIndexRNRN && '\0' != strHttpRequest.charAt(iIndexRNRN + LEN_RNRN) )
      {
        Serialprintln("POSTデータあり");
        HandlePostData( strHttpRequest, iIndexRNRN, client );
      }
      else
      {
        Serialprintln("Postメッセージフォーマットエラー");
        PrintHtml(client, "NG", "Post Message Format Error1" );
      }
    }

    // コネクションを閉じる。
    client.stop();
    Serialprintln("client disconnected");
  }
}

// HTTPリクエスト読み込み
void ReadHttpRequest( WiFiClient& client, String& rstrHttpRequest )
{
  int iCounter = 0; // HTTPリクエストがまったくないときに処理を抜ける用カウンタ。
  while( client.connected()
    && 100 > iCounter )
  {
    iCounter++;
    if( !client.available() )
    { // データが利用できるようになるまで待つ。
      Serialprint(".");
      // Webブラウザに対して、データを送信するための時間を与える。
      delay(1);
      continue;
    }
    // HTTPリクエストの一括読み込み
    rstrHttpRequest = client.readString();
    break;
  }
  return;
}

// POSTデータに関する処理
void HandlePostData( const String& strHttpRequest, int iIndexRNRN, WiFiClient& client )
{
  // "Command=?"
  int iIndexCommand = strHttpRequest.indexOf(param , iIndexRNRN + LEN_RNRN );
  if( -1 != iIndexCommand )
  {
    int iIndexEnd =strHttpRequest.indexOf("&" , iIndexCommand );
    if(iIndexEnd == -1)
    {
          Serialprintln("最終引数");
         iIndexEnd = strHttpRequest.length();
    }
    String chCommand = strHttpRequest.substring(iIndexCommand+8,iIndexEnd);  //
    Serialprintln(chCommand);

    // リモート制御１
    if(chCommand == sRemote1)
    {
        Serial.println("制御１実行");
        for(int i=0;i<1;i++)
        {
          // ボタン押下動作エミュレート
          digitalWrite(RemotePin1, HIGH);
          delay(PUSH_TIME);
          digitalWrite(RemotePin1, LOW);
          delay(100);
        }
        PrintHtml(client, "OK" ,"Remote1 Execute!");
        
    }
    // リモート制御２
    else if(chCommand == sRemote2)
    {
        Serial.println("制御２実行");
        for(int i=0;i<1;i++)
        {
          // ボタン押下動作エミュレート
          digitalWrite(RemotePin2, HIGH);
          delay(PUSH_TIME);
          digitalWrite(RemotePin2, LOW);
          delay(100);
        }
        PrintHtml(client, "OK" ,"Remote2 Execute!");
    }
    else
    {
      Serial.println("指定したコマンドなし");
      PrintHtml(client, "None" ,"Command NotFound");
    }
  }
  else
  {
    Serialprintln("指定されたコマンドがありません。");
    PrintHtml(client, "NG", "Post Message Format Error2" );
  }
}

void WIFIConnectingLED()
{
   digitalWrite(LEDPINWIFI, LOW);
   delay(200);
   digitalWrite(LEDPINWIFI, HIGH);
   delay(200);
}

// HTML出力
void PrintHtml( WiFiClient& client, String sStatus  ,String sMessage)
{
    String str = "";

    if(sStatus == "OK")
    {
      str += "HTTP/1.1 200 OK\n";
    }
    else if(sStatus == "None")
    {
      str += "HTTP/1.1 201 Command None\n";
    }
    else
    {
      str += "HTTP/1.1 400 NG\n";
    }
  
    str += "Content-Type: text/html\n"
           "\n";
  
    str += "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n"
           "<html lang=\"ja\">\n"
           "<head>\n"
           "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n"
           "<meta http-equiv=\"Content-Style-Type\" content=\"text/css\">\n"
           "<title></title>\n"
           "</head>\n"
           "<body>\n";

    str += sMessage;   
    str += "</body>\n"
           "</html>";
           client.print(str);
}
```

　上記プログラム書き込み後、再度電源を入れなおせば自動的にプログラムが実行されます。デバイスのLEDが点灯すれば、Wifiの接続が完了し、ポート80番(HTTP)で待受け状態になっていることを知らせてくれます。
　あとはAIスピーカやスマホから下記パラメータいずれかをHTTP(POST)で送信すればボタン制御が実行されます。

【HTTP(POST)パラメータ設定】
①「COMMAND=REMOTE1」・・・マイコン(IO4)に接続されたフォトカプラ(対象の家電の物理ボタン)が動作
①「COMMAND=REMOTE2」・・・マイコン(IO5)に接続されたフォトカプラ(対象の家電の物理ボタン)が動作

# ５．動作確認
　実際に作成したデバイスの動作を確認していきます。
本デバイスに対してAIスピーカもしくはスマホ等からHTTP(POST)を使用して処理依頼を送信します。
スマホ(android)単体で使用する場合は、下記アプリ等が活用できると思います。
・HTTP Request Shortcuts
・Tasker(プラグインにResTaskを活用)

私は、自作したホームオートメーションシステムを活用してAIスピーカ/スマホ両方から実行できるようにしています。もしご興味がある方は[**コチラ**](https://qiita.com/masat12/items/3fcfe1b24359d05d4ae2)も合わせてご参照ください。

今回は上記記事内の「パターン２（スマホ音声操作）」を用いて動作確認を行ってみたいと思います。
![図1.png](https://qiita-image-store.s3.amazonaws.com/0/195176/e8b90356-a8ca-d1dc-ce92-90ffc31e1a35.png)


今回、動作確認に使用した一般家電は下記の３つです。
【動作確認家電】
①三菱製給湯器(SRT-S37U-IJ)　⇒　お風呂の湯張りをスマホから操作
②パナソニック製コーヒーメーカー(NC-A55)　⇒　コーヒーの抽出をスマホから操作
③一条工務店製電動ブラインド（電動ハニカムシェード）　⇒　電動ブラインドをスマホから操作
　※こちらはプロトタイプとして作成したため、自作デバイスの形が異なります

実際に動作させている様子を下記ＵＲＬよりご確認ください。
**[「自作デバイスで一般家電をIoTに対応させてみた」(YouTube)](https://youtu.be/X7hlgJXAAGA)**

# ６．最後に
　本デバイスは、家電の基盤と直接接続しなければならない難易度がある一方、デバイス自体の作成は比較的簡単に出来ると思います。MicroBot Push等の製品で満足できない方はチャレンジしてみては如何でしょうか。

また、本文中にも記載しましたが、関連記事として自作ＡＩスピーカに関する内容も公開しておりますので、ご興味がある方はご覧頂ければ幸いです。

【関連記事】
[Raspberry pi3をベースにしたAIスピーカーをガッツリ作ってみた ~自作して分かった課題と対策~(Qiita)](https://qiita.com/masat12/items/3fcfe1b24359d05d4ae2)

以上