Arduinoで家にある赤外線リモコン全部解析

## 概要
家電を操作する"リモコン"は内蔵の赤外線LEDの光り方によって信号を家電に伝送しています. その信号を解析して, Arduinoのようなマイコンで赤外線LEDを同じように光らせれば, Arduinoから家電を操作できます. この記事はその第一歩, 家にある家電製品のリモコン信号をひたすら解析した方法と, 解析した結果を載せています.

追記 20220505: Yamazenの扇風機 YWX-BGD301 を追加しました

## 本記事の想定する読者
- リモコン信号を解析する方法を知りたい
- リモコン信号を解析するコードが知りたい
- 解析結果が知りたい

## 赤外線信号のフォーマット
- 赤外線信号のフォーマットについては多くのウェブページに説明があり, その多くで[こちら](http://elm-chan.org/docs/ir_format.html)が参照されています. この記事以上にわかりやすく説明できないのでこれを見てください. 
- 英語記事ですが上記記事内でも引用されている[こちら](https://www.sbprojects.net/knowledge/ir/)には受信側の仕組みも解説されています. 
- 簡単にまとめると, 
    - 赤外線リモコンでは最初にLeaderと呼ばれる部分があり, それに続いて0/1の信号が1Byteずつエンコードされて送られる.
    - 変調単位T[us]という変数があって, 1を送る時はhighを1Tのあとlowを1T, 0を送る時はhighを1Tのあとlowを3Tのようにデコードされる
    - LeaderはHighとLowが既定の長さ送信されるパターンで, それぞれの長さはフォーマットによって異なる. たとえばhighを16T, lowを8Tなど

## 赤外線信号のデコード方法
### 材料
|素子|型番|単価[円]|リンク|
|:-----------|:------------|------:|:-----|
|赤外線リモコン受信モジュール|OSRB38C9AA|50|[秋月](http://akizukidenshi.com/catalog/g/gI-04659/)|
|Arduino Uno|Arduino Uno Rev3|2,940|[秋月](http://akizukidenshi.com/catalog/g/gM-07385/)|

### 回路
赤外線リモコン受信モジュールをArduinoに直で繋ぐだけ. 受信モジュールの足は左から信号, GND, Vccです. 信号はGPIO2につないでいます
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261215/b0927e06-e37c-7d3f-65ea-5f239b8a5705.jpeg" width=50%>

### スケッチ
- 赤外線リモコンを解析/送信するためのIRremoteと呼ばれるライブラリが存在しているためそれを使うのが定石ですが, ある一定の長さ以上の信号を解析できない制約があり, 以下に解析した家電の中ではエアコンの信号は解析できません.
- ということで解析するためのプログラムを書きました. [こちらのコード](https://gist.github.com/chaeplin/a3a4b4b6b887c663bfe8)を改変して使いやすくしたものです.

```c
#define maxLen 800 // 信号の最大長
#include <stdio.h>

// リモコン信号を読むためのコード
// 参考 https://gist.github.com/chaeplin/a3a4b4b6b887c663bfe8

// 受光モジュールはGPIO2に接続
// 受信内容はシリアルモニタで確認

volatile unsigned int irBuffer[maxLen]; // stores timings - volatile because changed by ISR
volatile unsigned int x = 0; // 入ってきた信号の長さ Pointer thru irBuffer - volatile because changed by ISR

void setup() {
  Serial.begin(115200);
  attachInterrupt(0, rxIR_Interrupt_Handler, CHANGE);
}

void loop() {
  delay(5000);
  if (x) { // same as if x >= 1
    detachInterrupt(0);
    for (int i = 1; i < x; i++) {
      irBuffer[i - 1] = irBuffer[i] - irBuffer[i - 1];
    }
    x--;

    // 9000usより長い信号があった場合は, その次からデコードする
    // たとえばエアコンのリモコンの場合, 13000usのインターバルをはさんで2回信号が送信されているが, 後半だけあればエアコンを操作できるようなので
    // 前半は表示しない. 
    int startpoint = 0;
    for (int i = 0; i < x; i++) {
      if(irBuffer[i]>9000){
        startpoint = i+1;
      }
    }
    
    if (x - startpoint < 30){ //短すぎる信号はノイズなのでカット
      attachInterrupt(0, rxIR_Interrupt_Handler, CHANGE);
      return;
    }

    //raw array
    Serial.print(F("Raw: ("));
    Serial.print((x-startpoint));
    Serial.print(F(")"));
    Serial.print(F("["));
    for (int i = startpoint; i < x; i++) {
      Serial.print(irBuffer[i]);
      Serial.print(F(", "));
    }
    Serial.println(F("]"));
    
    // hex array. 8T 4T { } T
    Serial.print(F("hex: ("));
    Serial.print((x - startpoint - 3)/2); // length of signal [bit]
    Serial.print(F(") {"));
    
    int cnt = 0; // digit
    int hexval = 0; // sum of 8digits C_0 C_1 ... C_7
    
    for (int i = startpoint + 2; i < x - 1; i++) {
      if ((i - startpoint)%2 == 1){
        if (irBuffer[i]> 900){ // T=450usならおよそ2Tを閾値としていることになる
          //Serial.print(1);
          hexval += 1 << cnt;
        }else{
          //Serial.print(0);
        }
        cnt++;
        if (cnt == 8){
          if(hexval < 16){ // padding
            Serial.print(0);
          }
          Serial.print(hexval, HEX);
          Serial.print(F(" "));
          cnt = 0;
          hexval = 0;
        }
      }
    }
    x = 0;
    Serial.println(F("}"));

    attachInterrupt(0, rxIR_Interrupt_Handler, CHANGE);
  }

}

void rxIR_Interrupt_Handler() {
  if (x > maxLen) return; //ignore if irBuffer is already full
  irBuffer[x++] = micros(); //just continually record the time-stamp of signal transitions
}
```

### 使い方
- Arduino Unoにプログラムを書きこんでシリアルモニタを開き, 受信モジュールに向かってリモコンを押すと, デコードされたhex列が表示されます.
    - 前述の信号フォーマットを見るとわかるように, バイナリから16進数に直すときはC0, C1,...,C7の順, つまり1の位が一番左で2の位,4の位,...,128の位という順になります. なのでバイナリで01100011は16進数でC6です.
- Rawを見ると, 信号のhigh/lowの生の長さが表示されています. ここから変調単位TとLeaderの長さを計算できます.
    - たとえばRawが7040, 3520, 440, 440, 440, 1320, 440, 1320,...ならT=440us, Leaderは16T, 8T, そのあとに信号の011が続いています

## 解析結果
### まとめ
|家電名|メーカー|型番|T [us]|Leader_high[T]|Leader_low[T]|signal[Byte]|
|:--|:--|:--|--:|--:|--:|--:|
|オイルヒーター|Delonghi|RHJ75V0915|554|16|8|4|
|扇風機|Toshiba|F-ALX60|560|16|8|4|
|扇風機|Panasonic|F-CS338|842|4|4|3|
|扇風機|Yamazen|YWX-BGD301|565|16|8|4|
|テレビ|Sharp|LC-22K7|420|8|4|6|
|シーリングライト|Panasonic|HK9493|435|8|4|5|
|エアコン|Panasonic|CS-228-CF|437|8|4|19|
|エアコン|MITSUBISHI|不明|428|8|4|18|

- Panasonicのシーリングライトのみリモコンの型番です.

### Delonghiヒーター
|command|hex|
|:--|:--|
|on/off|00fe00ff|
|+|00fe04fb|
|-|00fe05fa|
|eco|00fe01fe|

### Toshiba Fan
|command|hex|
|:--|:--|
|on/off|4bb412ed|
|random|4bb415ea|
|wind|4bb409f6|
|ontimer|4bb40bf4|
|offtimer|4bb40af5|

### Panasonic Fan
|command|hex|
|:--|:--|
|on/off|3b487c|
|1/fゆらぎ|3b478c|
|首振り|7b4788|
|wind+|fb4780|
|wind-|fb45a0|
|明るさ控えめ|7b45a8|
|温度センサ運転|7b4698|
|入りタイマ|bb45a4|
|切りタイマ|bb4784|

### Yamazen Fan
|command|hex|
|:--|:--|
|on/off|06a31fe0|
|wind|06a31be4|
|首振り|06a30ff0|
|random|06a317e8|
|timer|06a313ec|

### Sharp TV
|command|hex|
|:--|:--|
|on/off    |aa5a8f1216d1 |
|1         |aa5a8f124e32 |
|2         |aa5a8f124f22 |
|3         |aa5a8f1250c2 |
|4         |aa5a8f1251d2 |
|5         |aa5a8f1252e2 |
|6         |aa5a8f1253f2 |
|7         |aa5a8f125482 |
|8         |aa5a8f125592 |
|9         |aa5a8f1256a2 |
|0/10      |aa5a8f1257b2 |
|11        |aa5a8f125842 |
|12        |aa5a8f125952 |
|decide    |aa5a8f1252d1 |
|digital   |aa5a8f128982 |
|bs        |aa5a8f128ab2 |
|cs        |aa5a8f128ba2 |
|reclist   |aa5a8f12ad3f |
|chup      |aa5a8f1211a1 |
|chdown    |aa5a8f121291 |
|sndup     |aa5a8f1214f1 |
|snddown   |aa5a8f1215e1 |
|chinput   |aa5a8f121381 |
|chlist    |aa5a8f1260f2 |
|return    |aa5a8f12e401 |

### Panasonic Ceiling Light
|command|channel1 |channel2  |channel3  |
|:-----|:---------|:---------|:---------|
|on    |2c52092d24|2c5209353c|2c52093d34|
|off   |2c52092f26|2c5209373e|2c52093f36|
|full  |2c52092c25|2c5209343d|2c52093c35|
|night |2c52092e27|2c5209363f|2c52093e37|
|high  |2c52092a23|2c5209323b|2c52093a33|
|low   |2c52092b22|2c5209333a|2c52093b32|
|warm  |2c523991a8|2c523995ac|2c523999a0|
|cool  |2c523990a9|2c523994ad|2c523998a1|

### Panasonic Airconditioner
- 信号を解析したところ 8T, 4T, 8Byte, 1T, 10ms, 8T, 4T, 19Byte, 1Tのような信号が出ていたが, 前半の8T, 4T, 8Byte, 1T部分が無くてもエアコンは操作できるようなので前半は省略. 後半の8T, 4T, 19Byte, 1Tの19Byteの部分のみ記載
- エアコンの信号はこれまでの家電のリモコンと異なり, ボタンを押した後リモコンに表示されているすべての情報を送付している

#### 1Byteごと解説
- 1-5B
  - 0220e00400
- 6B
  - 電源on1/off0 入りタイマあり1/なし0 切りタイマあり1/なし0　冷房1100/暖房0100/ドライ0010
- 7B 温度
  - 0 温度1位 2位 4位 8位 16位 32位 64位
- 8B
  - 00000001 (80)
- 9B
  - 風向1000/0100/1100/0010/1010/1111 風量1100/0010/1010/1110/0101
- 10B
  - 00000000 (00)
- 11B-13B 入り/切りタイマ時間, タイマなし(000660)
  - 入りタイマ
    - 00 + 8bit + 000000 11110000
  - 切りタイマ
    - 00010110 100000 + 8bit + 00
  - 8bit:時間 0~11
    - 反転4bit + 4bit
    - 1,2,4,8
    - 0: 1111 0000
    - 1: 0111 1000
- 14B
  - 静か/パワフル/指定なし 00000100/10000000/00000000 + 内部クリーン00000010
- 15B
  - 0 温度上限下限1/そうでなければ0 00000 小数点以下.5あり1/なし0
  - 温度上限下限を表しているのではなく, 単に音を2回鳴らすかどうかのフラグ
- 16B
  - 00000001
- 17B
  - 00000000
- 18B
  - 01101000
- 19B
  - checksum 1-18Biteの和
  - C0 C1 ... C7 の方法で和を取る

### MITSUBISHI Airconditioner
- Panasonicのエアコンと同様に信号の前半部分は省略
- 1-5B
  - c4d3648000/23cb260100
- 6B
  - 入り0x20/切り0x00
- 7B
  - 冷房/除湿/暖房 18/10/08
- 8B
  - 温度-16 (16-31/0x00-0x0F)　除湿の場合は0x08
- 9B
  - 除湿 弱/標準/強 0x34/32/30 冷房/暖房の場合は0x36/0x30
- 10B 
  - 音 2回/1回/0回 +80/+40/+00 (上位2bit) 電源on/off時は00なら消音, それ以外なら既定の音が鳴る
  - 風速 自動/1/2/3 +00/01/02/03 (中位3bit)
  - 風向 自動/1/2/3/4/5/スイング +00/08/10/18/20/28/38 (下位3bit)
- 11B
  - 0x00

- 12B
  - 切りタイマー時間 0.5時間 = 0x03. 0.5h-12.0h 0x03-0x48. 別のタイマー使用時/タイマー未使用時は0x00

- 13B
  - 入りタイマー時間 0.5時間 = 0x03. 0.5h-12.0h 0x03-0x48. 別のタイマー使用時/タイマー未使用時は0x00

- 14B
  - タイマー変更(タイマー種類の変更も時間の変更も) あり/なし +0x01/00
  - タイマー状態 切り/入り/なし +0x02/04/00

- 15B
  - 内部クリーン ON/OFF 0x04/0x00

- 16B
  - パワフル/なし 0x01/0x00

- 17B
  - 0x00

- 18B
  - チェックサム

## 備考
- Panasonicのシーリングライトの信号を受信した際のピッというスピーカーは外せる.(自己責任でお願いします)

## 本記事の続き
- [coming soon 毎朝自動的に電気が点くデバイスを作った]()
- [coming soon Arduinoでslackから家電を操作できるようにした]()

## 参考にさせていただいた記事など
- リモコン信号フォーマットの解説
    - http://elm-chan.org/docs/ir_format.html
- リモコン信号のデコードプログラム
    - https://gist.github.com/chaeplin/a3a4b4b6b887c663bfe8