赤外線信号の変換器を作り、複数の家電を同じ赤外線リモコンから操作する

# はじめに
今回は赤外線信号を読み取り、その値に応じて別の赤外線信号を送信するデバイスを作ってみました。最近は複数の家電を操作できるスマートリモコンが非常に便利ですが、意外と物理的な赤外線リモコンの方が使いやすいと感じられる場面もあるように思い、作ってみた次第です。必要な機能だけに絞り、なるべくシンプルな構成としました。試しに以下のような照明用リモコンについて、普段は使っていないチャンネルを使い、エアコンの操作を割り当てます。
![picture.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951963/6de61825-49e1-f294-5192-dc31cf583a1d.jpeg)
# 開発環境
* Windows 11 Home 22H2
* Arduino IDE 2.2.1
* IRremoteESP8266 2.8.2
# 回路構成
今回はESP32-DevKitCを使いましたが、他のESP32、ESP8266にもライブラリは対応しているため、同じように動くと思います。また、赤外線リモコン受信モジュールや赤外線LEDは汎用品が使えると思います。赤外線信号の到達距離を伸ばすため、赤外線LEDはトランジスタで駆動しました。
![breadboard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951963/854a22a9-9d37-1587-8cf7-df7c5afe5862.png)
# 対応表
照明用リモコンから出る赤外線信号と割り当てる赤外線信号との対応を示します。
| 照明用リモコン | 赤外線信号 | エアコン用リモコン |
|:-:|:-:|:-:|
| 点灯 | 0x3f | 停止 |
| 明るく | 0x3a | 温度 |
| 暗く | 0x3b | 温度 |
| 白色 | 0x27 | 冷房 |
| 暖色 | 0x28 | 暖房 |

エアコンについて、大抵のメーカー製ならライブラリが対応しているので赤外線信号を改めて調べておく必要はないです。今回はダイキン製でした。また、照明用リモコンはNEC製でした。赤外線信号はNECフォーマットになります。アドレス部分とコマンド部分があり、アドレスの値は固定でコマンドの値がボタン毎に異なるため、まずは後述するサンプルコードをそのまま動かして予め解析しておく必要があります。赤外線リモコンの各ボタンを押すと各値がシリアルモニタに表示されるので確認できます。
# スケッチ
[**こちら**](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/examples/SmartIRRepeater/SmartIRRepeater.ino)のサンプルコードを参考にしました。まず、何らかの赤外線信号を受信した場合、受信を中断します。フォーマットなどが合致しているか判定し、正しく受信できていた場合はアドレスやコマンドの値に応じて指定した赤外線信号を送信します。そして、関数`resume`により受信を再開するという流れです。赤外線信号を送信し終わるまでは受信しないようにすることで誤作動を防ぎます。また、関数yieldによりウォッチドッグタイマが原因で強制的に再起動してしまうことを回避しています。ウォッチドッグタイマとはプログラムが暴走したり停止したりしていないか定期的に監視している機能のことです。ただし、ESP32では無効のようです[^1]。関数delayにより受信してから送信するまでの間を少し遅延させると赤外線リモコンから出る赤外線信号との干渉を回避できるようで反応しやすくなりました。
```cpp:ir_converter.ino
#include <IRsend.h>
#include <IRrecv.h>
#include <IRac.h>

#define SEND_PIN 21
#define RECEIVE_PIN 22

IRrecv receiver(RECEIVE_PIN);
IRDaikinESP sender(SEND_PIN);
decode_results results;


void setup() {
  Serial.begin(115200); //115200bps
  
  receiver.enableIRIn();
  sender.begin();
  sender.setPower(false);
  sender.setFan(kDaikinFanAuto); //風量は自動
}

void loop() {
  if (receiver.decode(&results)) { //赤外線信号を受信した場合
    //赤外線信号の受信を中断
    decode_type_t protocol = results.decode_type;
    uint32_t address = results.address;
    uint32_t command = results.command;

    delay(100); //赤外線リモコンから出る赤外線信号との干渉を回避
    if (protocol == NEC) {
      Serial.printf("Address: 0x%x, Command: 0x%x\n", address, command);
      
      if (address == 0x6d82) {
        switch (command) {
          case 0x3f: //点灯
            sender.off(); //停止する
            sender.send();
            break;
          case 0x3a: //明るく
            if (sender.getPower()) { //動作中の場合
              sender.setTemp(sender.getTemp() + 1); //温度を上げる
              sender.send();
            }
            break;
          case 0x3b: //暗く
            if (sender.getPower()) { //動作中の場合
              sender.setTemp(sender.getTemp() - 1); //温度を下げる
              sender.send();
            }
            break;
          case 0x27: //白色
            sender.on(); //起動する
            sender.setMode(kDaikinCool); //冷房を選択
            sender.setTemp(28); //デフォルトの温度は28度
            sender.send();
            break;
          case 0x28: //暖色
            sender.on(); //起動する
            sender.setMode(kDaikinHeat); //暖房を選択
            sender.setTemp(20); //デフォルトの温度は20度
            sender.send();
            break;
          default:
            break;
        }
        Serial.printf("Power: %d, Mode: %d, Temperature: %d\n", sender.getPower(), sender.getMode(), sender.getTemp());
      }
    }
    receiver.resume(); //赤外線信号の受信を再開
  }
  yield();
}
```
# 動作確認
少し離れたところから赤外線リモコンを向ける方が認識しやすかったです。無事に動かすことはできました。
# おわりに
家に余っている赤外線リモコンを有効活用できそうです。また、今回は簡易的な構成ですが、乾電池などで長期的に運用できたら実用的にも使えそうです。
[^1]:https://www.mgo-tec.com/blog-entry-arduino-esp32-multi-task-dual-core-01.html/3