赤外線リモコンを解析してネットワーク経由で送信する！- その2「信号解析」

こんにちは、[前回のその1](https://qiita.com/amosoisin/items/127ea07140cc88c5563d)から引き続き「赤外線リモコンを解析してネットワーク経由で送信する！」について書いて行こうと思います。今回は「赤外線信号の解析」について書いていきます。

# 信号解析を始めよう、、、だけど
まずはセオリーどおりArduinoのライブラリ「IRRemote」を入れて「IRRecvDumpV2」を使って赤外線信号を解析してみました。調べるとすぐに出てきますね。結果は、、、

> Encoding UNKNOWN

。。。こうなると同ライブラリのsendNECだったり、sendSony等が使えない。。。
ちなみに使っているリモコンは、[SHARP 液晶テレビ(AQUOS) 純正リモコン GB228SA RRMCGB228WJSA](https://www.amazon.co.jp/SHARP-%E7%B4%94%E6%AD%A3%E3%83%AA%E3%83%A2%E3%82%B3%E3%83%B3-GB228SA-RRMCGB228WJSA-0106380517/dp/B07CYYMSZC)です。

# ライブラリを使わずに赤外線を解析しよう!

またライブラリをそのまま使うと解析しづらそうなので、ライブラリを使わない方法を調べてみました。
[Arduinoで赤外線リモコンの信号を解析する](https://www.erestage.com/arduino/remo_recv/)

このコードでは受信した赤外線のON時間とLOW時間を記録して出力します。
このままでも送信することは可能ですが、とても長いので16進数で保存しておきたい！
そこでArduino上で書いてもよいですが、簡単にProcessingで解析してみましょう。

以下にArduinoとProcessingのコードをおいておきます。使い方は起動し、画面上を何度かクリックすることで、シリアル通信が始まります。送信し、受信成功するとプロンプトに16進数の信号が表示されます。

Arduino

```c
#define IRRECV_PIN 12

unsigned long now = micros();
unsigned long lastStateChagedMicros = micros();
int state = HIGH;

void setup(){
  Serial.begin(115200UL);
  pinMode(IRRECV_PIN, INPUT);
  establishedContact();
}

void loop(){
  if (Serial.available() <= 0) return;
  readSignal();
}

void readSignal(){
  if (state == LOW) waitLow();
  else if (waitHigh() == 1) return;

  now = micros();
  int pulse_width = (now-lastStateChagedMicros)/10;
  if (abs(pulse_width) > 1000) {
    Serial.print("\n");
  }
  Serial.print(pulse_width, DEC);
  Serial.print(",");
  lastStateChagedMicros = now;
  state = !state;
}

void waitLow(){
  while (digitalRead(IRRECV_PIN) == LOW){
    ;
  }
}

int waitHigh(){
  unsigned long start = micros();
  while (digitalRead(IRRECV_PIN) == HIGH){
    if (micros() - start > 1000){
      // wait over 1000us
      return 1;
    }
  }
  return 0;
}

void establishedContact(){
  while (Serial.available() <= 0){
    //Serial.println("0,0");
    delay(300);
  }
}

```

Processing

```java
import processing.serial.*;
Serial myPort;

int[] recv_data = new int[200];
float x, pre_x;
boolean state = true;
boolean requested = false;
boolean received = false;
float t_margin, b_margin, r_margin, l_margin;
int base_t = 0;

void setup(){
  myPort = new Serial(this, Serial.list()[0], 115200);
  size(1800, 200);
  configure();
  initial();
}

void draw(){
  drawWave();
  drawFrame();
}

void drawWave(){
  if (!requested || !received) return;
  initial();
  evalBaseTime();
  strokeWeight(3);
  for (int i = 1; i < recv_data.length; i++){
    stroke(0,200,100);
    x += recv_data[i] / 4.5;
    if(state) line(pre_x+l_margin, t_margin+5, x+l_margin, t_margin+5);  // HIGH
    else line(pre_x+l_margin, height-b_margin-5, x+l_margin, height-b_margin-5);  //LOW
    line(x+l_margin, t_margin+5, x+l_margin, height-b_margin-5); 
    pre_x = x;
    state = !state;  // HIGH <=> LOW
    if (i==1) {
      fill(255, 0, 0);
      textSize(20);
      text(recv_data[1], x+l_margin-20, height-b_margin+20);
    }
  }
}

void evalBaseTime(){
  int sum = 0;
  int cnt_t = 0;
  for (int i = 3; i < recv_data.length; i+=2){
    sum += recv_data[i];
    cnt_t ++;
  }
  if (cnt_t <= 0) return;
  base_t = sum/cnt_t;
  evalBinary();
}

void evalBinary(){
  String bin ="";
  for (int i = 4; i < recv_data.length; i+=2){
    bin += (recv_data[i] > 2*base_t) ? "1" : "0";
  }
  bin = "1" + bin;
  long dec = Long.parseLong(bin, 2);
  String hex = Long.toHexString(dec);
  println(hex);
}

void configure(){
  t_margin = 30;
  b_margin = 70;
  l_margin = 50;
  r_margin = 200;
}

void initial(){
  background(0);
  x = 0;
  pre_x = 0;
  //requested = false;
  received = false;
}

void drawFrame(){
  strokeWeight(1);
  stroke(255,0,0);
  DisplayGraphFrame();
  DisplayInfo();
}

void DisplayGraphFrame(){
  // vertical axis |
  line(l_margin, t_margin, l_margin, height-b_margin);
  line(width-r_margin, t_margin, width-r_margin, height-b_margin);
  // horizontal axis -
  line(l_margin, t_margin, width-r_margin, t_margin);
  line(l_margin, height-b_margin, width-r_margin, height-b_margin);
}

void DisplayInfo(){
  fill(0);
  stroke(255);
  rect(width-r_margin+20, t_margin, 100, 30);
  fill(255);
  textSize(25);
  text("T="+base_t, width-r_margin+25, t_margin+23);
}

void serialEvent(Serial myPort){
  if (!requested) return;
  String recvString = myPort.readStringUntil('\n');
  recvString = trim(recvString);
  if (!received && recvString != null){
    recv_data = int(split(recvString, ","));
    received = true;
  }
}

void mousePressed(){
  myPort.write("REQUEST");
  requested = true;
}
```


まず基本となる「T」を解析します。
赤外線信号の詳しいことは[ボクにもわかる地上デジタル 地デジ方式編 - 赤外線リモコン方式](http://www.geocities.jp/bokunimowakaru/std-commander.html)を参考にしました。
かいつまんで説明すると、赤外線信号は、
[リーダーON], [リーダーOFF]、[ON時間]、[OFF時間]、[ON時間]、[OFF時間]、、、
となります。
ON時間は全てほぼ同じで、**ON時間をT**とします。
[リーダーON]、[リーダーOFF]は信号の始まりを表します。同じリモコンであればこれらはすべて同じで、**Tの何倍か**を記録しておきます。
ON時間はTとなので、**OFF時間1Tであれば"0"**、**OFF時間が3Tであれば"1"**となります。
それで得た2進数信号はリーダー信号は含まれていないのでビットの始まりがわからないので、**先頭に"1"をつけて16進数に変換する**ことで信号が得られます。

こうして「Tの値」「リーダー信号がTの何倍か」「16進数の信号」が得られます。

#次回は
今回得られた「Tの値」「リーダー信号がTの何倍か」「16進数の信号」を使って赤外線の[送信用の関数](https://qiita.com/amosoisin/items/a1878284d5f49fa6b17a)を作っていきます。