Smart HomeをDIYする

Apple HomeKit は対応製品が少なくて高価なのでHomebridgeでがんばります

ESP32で赤外線リモコンを作ってHomeKitから使う

ESP32に赤外線LEDを取り付けて、スマート赤外線リモコンとして動作させます。今回は、シーリング照明をon/offする赤外線パターンを用意して、赤外線LEDを点滅させる簡単な構成です。さらに、MQTT経由でメッセージが来た時に発光するようにし、Homebridge/HomeKitに接続しました。これでiPhone/Macのホーム画面に照明として表示され、操作可能です。

Nature RemoのローカルAPI

Nature Remoの回で、Nature RemoのローカルAPIを使う方法を紹介しました。

diysmarthome.hatenablog.com

この方法を使えば、Nature Remo

から、任意の赤外線パターンを送出させることができます。例えば、以下のターミナルコマンドを打つと、ここの部屋の赤外線リモコン対応のシーリングライトがon/offします。

curl -XPOST "http://192.168.xxx.xxx/messages" -H "X-Requested-With: local" -d '{"format":"us","freq":40,"data":[7000,4760,280,560,280,1400,280,560,280,1400,280,560,280,1400,280,560,280,1400,280,560,280,1400,280,560,280,560,280,1400,280,1400,280,1400,280,1400,280]}'

IPアドレス部分はNature Remoのアドレスです。Nature Remoがhttpサーバになっていて、そこに赤外線明滅パターンをjson形式で送れば、それを送出してくれる仕掛けです。dataというキーで指定した整数型配列の部分が明滅パターンです。単位はマイクロ秒で、この例ですと、

  1. 7000μs発光する
  2. 4760μs消灯する(この長いペアが開始の印です)
  3. 280μs発光する
  4. 560μs消灯する(このペアがビット0です)
  5. 280μs発光する
  6. 1400μs消灯する(このペアがビット1です)
  7. (省略)
  8. 280μs発光する
  9. 1400μs消灯する(最後のビットです。1です)
  10. 280μs発光する(これに続く長い消灯により終了を示します)

という手順を示してます。消灯時間を確定するために、発光期間で始まり、発光期間で終了します。なので、配列の要素数は必ず奇数個です。

この明滅パターンさえ用意できれば、任意の赤外線リモコン信号をNature Remoから送出できます。Natureのクラウドを利用しないので、クラウドサービスに障害があっても、サービスが中断されても大丈夫です。でも、赤外線パターンを送出するだけの簡単な機能なら、高価なNature Remoを買う必要もない気がします。DIYしましょう!

赤外線パターンの解析

Nature Remoの記事にも簡単に書きましたが、今回は図入りで説明します。対象としたシーリング照明は、Amazaonでかなり昔に購入した格安製品です(今は売られてません)。リモコンには6個のボタンがついています。上で紹介した点滅パターンは、リモコンの「切/入」ボタンを押した時のパターンです。Nature Remoの学習機能を使って、web apiで取得した結果を、280μsの整数倍に整形した数列です。時系列でグラフにすると以下のようになってます。

1が点灯、0が消灯です。最初に長めのon/offがあって、その後280μsのパルスが17個出ます。パルスの間隔は、パルス幅の2倍のものと、パルス幅の5倍のものがあります。間隔が狭い方を0、広い方を1だと考えると、図のように0101010101001111と読めます。ちょうど16ビットあります。各バイトのMSBから順番に送信されていると仮定すると、16進数で0x554Fです。

いろいろ試したところ、最初の1バイトはリモコンのチャンネルを切り替えると変化し、CH2が0x55, CH1が0xAAでした。次の1バイト(ここでは0x4F)が命令のようです。

リモコンにはこのほかに、常夜灯(暗い点灯)、明るく、暗くのボタンがあります。4個のボタンの命令バイトは、

  • 0x4F: 「切/入」
  • 0xDF:「常夜灯」
  • 0x67:「明るく」
  • 0xCD:「暗く」

でした。「切/入」はトグルなので、状態を把握できないので使いにくいです。リモコン電源ボタンがトグル式でも、隠しコマンドで、on/off専用コマンドが存在することもあるらしいです。このリモコンは、チャンネル指定が1バイト、コマンドが1バイトなので、256通りの命令が可能です。すべての命令をプログラムで試してみたのですが、onだけ、offだけのコマンドは残念ながら見つかりませんでした。

赤外線リモコンサーバを設計する

LAN経由でコマンドを受け取って、赤外線パターンを送出するサーバのようなものを作ります。今回の構成は以下です。

マイコンにはESP32を使います。WiFiがあって無線サーバに仕上げやすいですし、安価です。さらには赤外線リモコンのための回路も備わっているらしいです。Nature Remo(他の会社のスマートリモコンもそうですが)などがWeb API使っているのに対して、これから作るサーバはMQTTを使うことにします。オーバーヘッドが少ないし、HomebridgeのMqttthingプラグインからも利用できて便利です。MQTTブローカには、いつものようにRaspberry Piで動かしているMosquittoを使います。同じRaspberry PiでHomebridgeも動いてます。

ESP32には、「MQTTの特定のトピックにメッセージがやってきたら、プログラムで用意した赤外線パターンを送出する」という機能を実現したいと思います。

部品を集める

秋月電子のページを見ながらLEDとFETを選びました。

  • 赤外線LED:赤外線リモコンで使われる波長は950nmらしいので、近い波長にしました。945nmです。標準電流は50mAです。
  • MOS FET:一番普通そうなMOS FET。ドレイン電流は200mA流せるようです。赤外線LEDを十分点灯させられます。

ESP32が送料込み700円くらいに加えて、LED 10円、FET 20円、抵抗など部品代がかかっても1000円程度で作れます。

Arduino IDEにIRライブラリを入れる

Arduino IDEに赤外線リモコン用のライブラリを入れました。ライブラリマネージャで、irremoteという単語で検索したところ、IRRemoteESP8266というのが見つかりました。ESP8266という名前ですが、ESP32でも動くようです。これをインストールしました。

ライブラリをインストールすると、Arduino IDEのスケッチ例にIRremoteESP8266というメニュー項目が増えていました。これを参考に、簡単なプログラムを作ります。このライブラリは、いろいろなメーカーの赤外線パターンに対応しているようです。Panasonic, Daikin, Mitsubishiなどほとんどの製品が揃ってます。今後はそのような高度な機能も活用したいですが、今回は、一番簡単な、生の赤外線パターンを出すプログラムを作りました。

最初に、部屋のシーリング照明をon/offするパターンを5秒ごとに送出するプログラムを作りました。以下のようにとても簡単に作れます。

/*IRremoteESP8266:demonstrates sending IR codes with IRsend.*/
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>

const uint16_t kIrLed = 4;  //GPIO pin. Recommended: 4 (D2).
IRsend irsend=IRsend(kIrLed);  // Set the GPIO to be used.

uint16_t rawData[35] = {  7000,4760,280,560,280,1400,280,560,280,1400,280,560,280,1400,280,560,280,1400,280,560,280,1400,280,560,280,560,280,1400,280,1400,280,1400,280,1400,280
};

void setup() {
  irsend.begin();
  Serial.begin(115200);
}

void loop() {
  Serial.println("Sending a rawData.");
  irsend.sendRaw(rawData, 35, 38);  //Send a raw data at 38kHz.
  delay(5000);
}

回路を組む

赤外線LEDをドライブする回路を作っておきます。GPIOの出力そのままでは光量が少ないので、FETを介して赤外線LEDを点灯させます。以下の回路にしました。

MOS FETの2N7000は、onになるゲート電圧が標準2.1V, max 3Vらしいです。ESP32の3.3V出力でもonにできるように、高めに分圧してあります。onの時のゲート電圧は、ほぼ3.3Vのままになると思います。GPIO直結じゃなくて分圧したのは、過渡状態が良くなるらしいからです。セキュリティシステムでブザーを動かした時と同じ構成です。

LEDは、明るく点灯するように5V供給にしました。LEDの順方向電圧降下を測定したところ1.2Vでした。FETのonの時の順方向電圧は0.14Vくらいらしいので無視できます。保護抵抗を100Ωにしたので、FETがonの時には、40mA弱流れると思います。( 正確には(5-1.2-0.14) / 100 = 37mA と見積もってましたが、実測したらもう少し少なかったです)

ブレッドボードの配置図は以下です。

実際に配線した様子が以下です。この写真を撮った時は、デバッグのために可視光青色LEDを取り付けてます。

点灯消灯プログラムのテスト

回路が完成したので、上記で紹介したプログラムを走らせます。プログラムの内容は簡単です。sendRawという関数で、赤外線パターンを送出させてます。引数は信号パターンの配列、配列要素数、赤外線変調周波数(kHz)です。これを5秒ごとに繰り返してます。可視光LEDを付けると、5秒ごとに一瞬光っているのがわかります。光っている前後2秒を切り出したGIFアニメです。

赤外線LEDに変えると、部屋の照明が5秒ごとに消灯・点灯しました。とてもウザいのですが、動いて嬉しいです。

前述のように、この照明のリモコンには、on/offトグルスイッチしかありません。なのでこの信号パターンを送るたびに、点灯、または消灯します。トグル動作は、現在の状態が不明なので、家電制御としては使いにくいです。

MQTTから操作する

MQTTのメッセージで、赤外線信号を出すようにプログラムしました。ewpmqttclientを使います。詳しくはこちらをご覧ください。

diysmarthome.hatenablog.com

今回は、irremote/ceilinglightというトピックに、何らかのメッセージが来たら、シーリング照明をトグルするパターンを出すというプログラムです。極力簡単にしました。クライアントIDも適当にランダムな文字列にしておきました。

setup()でMQTTクライアントの初期化をします。初期化が完了すると、onConnectionEstablished()という関数が呼ばれるので、その中でコールバック関数(特定のトピックを受信したら呼び出される関数)を指定します。ここでは、onMessageReceived()という名前にしました。この関数の中で赤外線を送出します。main()は、MQTTクライアントの処理を行うだけの仕事をしてます。

/* IRremoteESP8266 over MQTT */
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <espmqttclient.h>

EspMQTTClient *client;

//WiFi
const char SSID[] = "XXXXXXXX"; //WiFi SSID
const char PASS[] = "xxxxxxxx"; //WiFi password
//MQTT
char CLIENTID[] = "IRremote_3835782"; //something random
const char  MQTTADD[] = "192.168.xxx.xxx"; //Broker IP address
const short MQTTPORT = 1883; //Broker port
const char  MQTTUSER[] = "xxxx";//Can be omitted if not needed
const char  MQTTPASS[] = "XXXX";//Can be omitted if not needed
const char  SUBTOPIC[] = "irremote/ceilinglight";

const uint16_t kIrLed = 4;  //GPIO pin. Recommended: 4 (D2).
IRsend irsend=IRsend(kIrLed);  // Set the GPIO to be used.

// IR-data to toggle a ceiling light
uint16_t rawData[35] = {
7000,4760,280,560,280,1400,280,560,280,1400,280,560,280,1400,280,560,280,1400,280,560,280,1400,280,560,280,560,280,1400,280,1400,280,1400,280,1400,280
};

void setup() {
  irsend.begin();
  Serial.begin(115200);
  while (!Serial);      //  wait for serial port to connect.
  client = new EspMQTTClient(SSID,PASS,MQTTADD,MQTTUSER,MQTTPASS,CLIENTID,MQTTPORT); 
}

void onMessageReceived(const String& msg) {
  Serial.println("a rawData capture from Nature Remo");
  irsend.sendRaw(rawData, 35, 38);  // Send a data at 38kHz.
}

void onConnectionEstablished() {
  Serial.println("MQTT connection established.");
  client->subscribe(SUBTOPIC, onMessageReceived); //set callback
}

void loop() {
  client->loop();  
  delay(100);
}

どんなメッセージを受け取っても動作します。NULLでも良いです。例えば、ターミナルから、mosquitto_pubコマンドを使ってこのトピックに空白文字を送ってみます。

% mosquitto_pub -h 192.168.xxx.xxx -u xxxx -P XXXX-t irremote/ceilinglight -m ''

その結果、シーリング照明が消灯または点灯しました。これで、MQTTメッセージを使って赤外線パターンを送出できるようになりました。

Homebridgeで設定する

MQTTで動作するプログラムができたので、Homebridgeで設定して、HomeKitから使えるようにします。Homebridgeは、MQTTブローカと同じRaspberry Piの上で動いてます。

 

 

このHomebridgeにインストールされているMqttthingプラグインを使い、その設定をします。Mqttthingプラグインの設定画面から、スイッチアクセサリを一つ作って、ここで使ったirremote/ceilinglightトピックを指定します。Homebridgeのwebインタフェースで、以下のように設定します。

 

設定をテキストで確認すると以下のようになってます。HomebridgeとMQTTブローカが同じRaspberry Piで動いているので、アドレスはlocalhostです。デフォルトなので、設定では省略されてます。

{
    "type": "lightbulb-OnOff",
    "name": "Ceiling Light",
    "username": "xxxx",
    "password": "XXXX",
    "topics": {
        "setOn": "irremote/ceilinglight"
    },
    "accessory": "mqttthing"
}

この設定で、iPhone/Macでon/offする操作を行うと、setOnで指定したトピックにtrue/falseという文字列を送ります。今回はtrueかfalseに関係なく、どちらの場合も設定した同じ赤外線パターンを送出します。

HomeKitから操作する

Homebridgeを再起動すると、iPhoneMacのホーム.appに、今回設定した照明が現れました。Ceiling Lightという名前の照明です。

これをクリックすると、ESP32が赤外線パターンを送出します。天井の照明が、消灯または点灯します。画面の表示とシーリング照明のon/off状態は、どちらも操作するたびにトグルしているだけなので、不一致になる可能性もあります。

なので実際のシーリング照明のon/offには、隣の「あかり」と書かれたボタンを使ってます。こちらは、壁に取り付けたZigbeeスイッチをコントロールします。

まとめ

ESP32に赤外線LEDをつけて、MQTTメッセージで動作するスマートリモコンを作りました。今回はシーリングライトの電源トグル信号を送るだけの簡単な機能です。使用したライブラリIRremoteESP8266には、生の赤外線明滅パターンを送るだけではなく、各社家電製品のフォーマットに対応した機能もあります。これを使えば、エアコンなどの複雑な制御も実現できると考えてます。