0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

DAIKINなどのリモコンの解析

Last updated at Posted at 2022-12-23

DAIKINのリモコンのフォーマット

ArduinoでDAIKINのエアコンをコントロールしたい。

IRremoteのライブラリ ではなぜかうまくいかない・・・なぜだ・・・

結論から言うと、家電協のフォーマットに従っていた。ただし、IRremoteはうまく扱えなかった。

仕方ないので計測してみよう。
IMG_20221223_173103082_4.jpg

回路は
2.png

フォトトランジスタ(図ではダイオード)につける抵抗は50kΩにしました。抵抗が小さい方がしっかり反応します。

結果は
1.png

ON,OFFが繰り返している様子が見て取れる。

ON時の波形をさらに細かく見ると次のようになる。
image.png
ON時は37kHzで振動している

PWMの割合に関しては50%ではないのが少し気になる(後述50%でも問題なくエアコンは反応した)。

(注意)
フォトトランジスタは光が入ると電流を流すようになるので、出力電圧はLOWに落ちる。
つまり、赤外線LEDはフォトトランジスタで計測された結果のHIGH,LOWを反転させる必要がある。

赤外線リモコンのフォーマットについて

にある通り

  • NEC
  • 家電協
  • SONY

の3つがある。
それぞれの特徴としては、

NEC

38kHz搬送
T=0.562ms

家電協

33~40kHz搬送
T=0.35ms~0.5ms

SONY

40kHz搬送
T=0.6ms

センサーについて

4.jpg
右がフォトトランジスタ、左がよくつかわれる赤外線リモコン用のセンサーだ。

ピンアサイン

image.png

2つの違い

実は違いがある!

赤外線リモコン用のセンサーは37kHzの光のみを選択的に計測する
日常にあふれる光から特定周波数のみを見ることで外乱に強いセンサになる。

一方で出力が37kHzで振動していては、取り扱いにくいため平滑化が行われる。

フォトトランジスタの計測結果は
1.png

赤外線リモコン用のセンサの計測結果は
5.jpg

振動が取り除かれている。

赤外線リモコン用のセンサの方が取り扱いやすいが、赤外線LEDを使ってリモコンを作ろうとしたときに37kHzで振動させることを忘れてしまう点には注意したい。

読み取る

Arduinoでパルスの間隔を計測する。pin2にリモコン用センサの出力をつなぐ。
pin2の外部割込みを使用し、立下り(FALLING)の時間間隔をus単位で計測する。
最後に割り込みされてから、一定以上時間が経った時に送信終了とみなし、結果をUSBで転送する。

Ir_read_us
#define box_length 300

volatile unsigned long time_box[box_length];
volatile int pos = 0;
volatile unsigned long before_time;
volatile boolean flag = false;

void setup() {
  pinMode(2,INPUT);
  attachInterrupt(0,time_save,FALLING);
  Serial.begin(115200);
}

void time_save(){
  unsigned long now_time = micros();
  time_box[pos] = now_time - before_time; 
  before_time = now_time;
  pos++;
  if(pos>box_length){
    pos = 0;
    flag = true;
  }
}

void loop() {
  if(flag == true){
    Serial.println("overflow");
  }
  unsigned long now_time = micros();
  if( now_time - before_time > 500000  and  pos > 0){
    for(int i = 0 ; i <  pos ; i++){
      Serial.print(time_box[i]);
      Serial.print(",");
    }
    Serial.println(" ");
    pos = 0;
  }
  delay(10);

}

結果

8256416,864,864,868,864,868,25728,5192,1732,868,864,868,1728,868,864,864,868,1732,864,1732,1732,864,1732,1732,1732,1732,1732,864,868,1728,868,864,868,864,868,864,864,868,864,868,1728,868,1732,864,868,864,1732,1732,864,868,864,868,864,864,868,864,868,864,868,864,868,864,868,864,1732,1732,1732,864,1732,864,1732,1732,35064,5192,1732,864,868,864,1732,868,864,864,868,1732,864,1732,1732,868,1728,1732,1732,1732,1732,864,868,1732,864,864,868,760,972,864,868,864,868,788,940,1732,868,864,868,864,1728,868,868,864,868,864,864,868,864,868,868,792,936,868,864,864,868,864,868,864,1732,864,1736,864,1732,864,35064,5196,1728,864,868,868,1728,868,864,868,864,1732,868,1728,1732,868,1732,1732,1728,1732,1732,796,936,1732,864,868,864,868,864,868,864,864,868,864,868,864,864,868,864,868,864,868,1732,864,868,1732,864,864,1732,864,868,864,1736,1728,864,1736,864,868,764,964,864,868,868,864,868,864,864,868,864,868,864,1732,868,1728,1732,1732,1732,1732,864,868,864,868,864,868,864,868,864,868,864,864,868,1728,1736,864,868,860,868,868,864,868,864,864,868,1732,1732,864,868,864,864,868,864,868,864,868,864,868,864,868,864,864,868,864,1732,868,864,868,864,864,1732,1732,868,864,864,868,864,868,864,1732,868,864,864,868,864,868,868,864,1732,864,1732,1732,1732,864,1732,1732, 

400~450の倍数の間に大体入っている。

の家電協のフォーマットだろう。
3.png
T=430usとして、
0ならT,T
1ならT,3T
みたいな時間間隔なので、このデータを0,1に変換してみよう。

自動的に計測結果からデータを読み出す

Python3でデータに変換する。

方針

外部割り込みはFALLINGのみである。

  • 信号の長さが2Tなら0,4Tなら1として考えられる。
  • 12Tなら先頭(Leader)であると考えられる。
  • 一番最後のビットは0,1判別不可

5無題.png
ということで、一番最後に1を追加する。

  • [Leader,data,Delay,data,Delay,data]のような構成をしている。先程の最後のビットと同じでDelayの前のビットも0,1の判別ができない。よって、Delay判定をした際に1をデータ前に追加

コード

p = "864,864,868,864,868,25728,5192,1732,868,864,868,1728,868,864,864,868,1732,864,1732,1732,864,1732,1732,1732,1732,1732,864,868,1728,868,864,868,864,868,864,864,868,864,868,1728,868,1732,864,868,864,1732,1732,864,868,864,868,864,864,868,864,868,864,868,864,868,864,868,864,1732,1732,1732,864,1732,864,1732,1732,35064,5192,1732,864,868,864,1732,868,864,864,868,1732,864,1732,1732,868,1728,1732,1732,1732,1732,864,868,1732,864,864,868,760,972,864,868,864,868,788,940,1732,868,864,868,864,1728,868,868,864,868,864,864,868,864,868,868,792,936,868,864,864,868,864,868,864,1732,864,1736,864,1732,864,35064,5196,1728,864,868,868,1728,868,864,868,864,1732,868,1728,1732,868,1732,1732,1728,1732,1732,796,936,1732,864,868,864,868,864,868,864,864,868,864,868,864,864,868,864,868,864,868,1732,864,868,1732,864,864,1732,864,868,864,1736,1728,864,1736,864,868,764,964,864,868,868,864,868,864,864,868,864,868,864,1732,868,1728,1732,1732,1732,1732,864,868,864,868,864,868,864,868,864,868,864,864,868,1728,1736,864,868,860,868,868,864,868,864,864,868,1732,1732,864,868,864,864,868,864,868,864,868,864,868,864,868,864,864,868,864,1732,868,864,868,864,864,1732,1732,868,864,864,868,864,868,864,1732,868,864,864,868,864,868,868,864,1732,864,1732,1732,1732,864,1732,1732"

#print(p)


l = p.split(',')
l_int = []

for m in l:
    m = int(m)
    l_int.append(m)

print(l_int )


code = ""

low = 350
high = 500

for time in l_int:
    
    if(low*2 < time < high*2):
        code += '1';
    elif(low*4 < time < high*4):
        code +='0'
    elif(low*12 < time < high*12):
        code +='L'
        
        
    else:
        code += '1'    #Delayの前のデータは1,0判別不能なため
        code += 'D'
        print(time)
        
code += "1"     #末尾のデータは構造上判別不能なのでとりあえず1にしておこう
        
print(code)
        

結果

'111111DL01110111101001000001101111111111010111001111111111111111000101001DL01110111101001000001101111111111101111011111111111111111110101011DL011101111010010000011011111111111111111101101101110010111111111111111010000011111111111110011111111110011111111111111111011111001111111011111111010001001'

とりあえず、同じようにLEDを発光させる

Timer1:37kHz発生
Timer2:450us割り込み

として、

1-> T,3T
0->T,T
D->30T    (Delay)
L->8T,4T  (Leader)

になるようクラスを作る。

方針

オブジェクト指向でやってみる。
赤外線データというオブジェクト"Code"を作る

  • 送信データ
  • 送信位置
  • フェーズ情報

image.png

こんな感じに区分けする。タイマー割り込みが入るたびに"phase"はインクリメントされる。

  • nextメソッド、タイマー割り込みが入ったときに呼び出される想定
1-> T,3T
0->T,T
D->30T    (Delay)
L->8T,4T  (Leader)

に従って、"phase"が既定値を超えると送信位置を動かす。

setup,loop内

 /* OC1A,OC1B 37kHz出力
     PWMモード:Fast PWM
     分周比:1
     OCR1A:コンペアマッチLOW
     OCR1B:コンペアマッチLOW
  */

かなりレジスタを叩いて作った。
正直読み解けなくなってしまった。

搬送波38kHzを作るためのタイマーと、送信時間単位Tを作るタイマーの2つ必要になるが、
一つにまとめようとして難しくなった記憶がある。

コード

Code.h
#ifndef CODE
#define CODE
#include <Arduino.h>

class Code {
  private:
    int phase = 0;     //各文字の現在のフェーズ
    int pos = 0;       //文字位置

    String code_string;  
    
  public:
    Code(String n);
    void next();
    int state();
};

#endif
Code.cpp
#include "Code.h"



Code::Code(String n) {
  this->code_string = n;
}

int Code::state() {
  char type =  code_string[pos];

  if (type == '1') {      //1
    if ( phase == 0) {
      return 1;
    } else {
      return 0;
    }
  } else if (type == '0') { //0
    if ( phase == 0) {
      return 1;
    } else {
      return 0;
    }
  } else if (type == 'H') { //Header
    if ( phase % 2 == 0 ) {
      return 1;
    } else {
      return 0;
    }
  } else if (type == 'L') { //Leader
    if ( phase <= 7 ) {
      return 1;
    } else {
      return 0;
    }
  }else{
    return 0;
  }
}

void Code::next() {
  phase =  phase + 1;
  char type =  code_string[pos];

  if (type == '1') {      //1
    if ( phase >= 2) {
      pos ++;
      phase = 0;
    }
  } else if (type == '0') { //0
    if ( phase >= 4) {
      pos ++;
      phase = 0;
    }
  } else if (type == 'H') { //Header
    if ( phase >= 12) {
      pos ++;
      phase = 0;
    }
  } else if (type == 'L') { //Leader
    if ( phase >= 12) {
      pos ++;
      phase = 0;
    }
  } else if (type == 'D') { //Delay
    if ( phase >= 50) {
      pos ++;
      phase = 0;
    }
  } else {                 //END
    if ( phase >= 100) {
      pos = 0;
      phase = 0;
    }
  }

}

Ir_send
#include "Code.h"

volatile boolean flag = true;
volatile Code code("111111DL01110111101001000001101111111111010111001111111111111111000101001DL01110111101001000001101111111111101111011111111111111111110101011DL011101111010010000011011111111111111111101101101110010111111111111111010000011111111111110011111111110011111111111111111011111001111111011111111010001001E");
//volatile Code code("00");

void setup() {

  pinMode(10, OUTPUT); //OC1B
  pinMode(9, OUTPUT); //OC1A

  /*タイマ1初期化*/
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR2A = 0;
  TCCR2B = 0;

  /* OC1A,OC1B 37kHz出力
     PWMモード:Fast PWM
     分周比:1
     OCR1A:コンペアマッチLOW
     OCR1B:コンペアマッチLOW
  */
  TCCR1A |= (1 << WGM11) | (0 << WGM10)
            | (1 << COM1A1) | (1 << COM1A0);
  TCCR1B |= (1 << WGM13) | (1 << WGM12) | (1 << CS10);
  ICR1 = 431;
  OCR1A = 215;

  //450us 割り込み
  TCCR2A |= (1 << WGM21) | (1 << WGM20);
  TCCR2B |= (1 << WGM22) | (1 << CS22) | (0 << CS21) | (0 << CS20)  ;
  OCR2A = 113;
  //OCR2A = 255;

  TIMSK2 |= (1 << OCIE2A);
  //TIMSK2 &= ~(1 << OCIE2A);

}
ISR (TIMER2_COMPA_vect) {

  flag = code.state();
  //digitalWrite(10,flag);
  if (flag) {
    TCCR1A |= (1 << COM1A1) | (1 << COM1A0);
  } else {
    TCCR1A &= ~((1 << COM1A1) | (1 << COM1A0));
  }
  code.next();
}

void loop() {
  
  
}

で無事エアコンが反応した。

累計作業時間20hぐらい。
だいぶ時間をかけてしまった。

同じことをやりたい方はロジックアナライザは持っていた方が絶対うまくいく

1000円ぐらいなのでぜひ買おう

追記:IRIS OHYAMA 照明リモコン解析

アイリスオーヤマ LED シーリングライト 6畳 10段階調光タイプ リモコン付き 常夜灯 明るさメモリ機能 おやすみタイマー リビング 寝室 和室 台所 天井照明 CL6D-5.0

を購入したので、こちらでも行ってみる。

前述の手法通り、信号計測から始める
image.png

38kHzが搬送波であることがわかった。

image.png
全体としては、こんな波形

白ー>常夜灯
1回目3445268,3028,6492,2040,2012,1020,1024,1020,1024,1020,1024,2012,1020,1024,1020,1024,1020,1024,2012,1024,2012,1020,1024,1020,1024,1020,1024,1020,1020,1024,1020,1024,1020,1020,1024,1020,2012,1024,2012,2012,1024,2012,2016
2回目
7629524,3008,6536,2016,2012,1024,1020,1020,1024,1020,1024,2012,1020,1024,1020,1020,1024,1020,2012,1024,2012,1020,1024,1020,1020,1024,1020,1020,1024,1020,1024,1020,1020,1024,1020,1024,2012,1020,2016,2012,1024,2012,2012, 


常夜灯ー>切る
1回目
23184812,3004,6512,2016,2012,1020,1020,1024,1020,1020,1024,2012,1020,1020,1024,1020,1024,1020,2012,1020,2016,1020,1020,1024,1020,1020,1020,1024,1020,1020,1024,1020,1020,1024,1020,1020,2012,1024,2012,2012,1020,2012,2012, 
2回め
9243732,3028,6488,2040,2016,1020,1020,1024,1020,1020,1024,2012,1020,1024,1020,1024,1020,1024,2012,1024,2012,1020,1024,1020,1024,1020,1024,1020,1024,1020,1020,1024,1020,1024,1020,1024,2012,1020,2016,2012,1024,2012,2012, 


切るー>白
1回目
47751868,3004,6488,2040,2012,1020,1020,1020,1024,1020,1020,2012,1024,1020,1020,1024,1020,1024,2008,1020,2012,1024,1020,1020,1020,1024,1020,1020,1020,1024,1020,1020,1024,1020,1020,1020,2012,1024,2012,2012,1020,2012,2012, 
2回目
8974264,3028,6488,2016,2032,1024,1020,1024,996,1020,1024,2036,1024,1024,1016,996,1024,1020,2040,1020,2012,1000,1016,1024,1024,1044,1020,1020,1000,1020,1020,1024,1044,1020,1020,1024,1988,1020,2036,2012,1024,1988,2012, 

測定結果からわかること

T=1020として
x,3T,6.5T,2T,2T,[T,2T]

の信号列でないかと考えられる。

  • 前述のリモコンフォーマット全てに一致しない
  • 3つの動作が全て同じ信号である可能性が高い
Analysis_IRsignal_IRIS_OHYAMA
p = "9243732,3028,6488,2040,2016,1020,1020,1024,1020,1020,1024,2012,1020,1024,1020,1024,1020,1024,2012,1024,2012,1020,1024,1020,1024,1020,1024,1020,1024,1020,1020,1024,1020,1024,1020,1024,2012,1020,2016,2012,1024,2012,2012"

#print(p)


l = p.split(',')
l_int = []

for m in l:
    m = int(m)
    l_int.append(m)

print(l_int )


code = ""

low = 900
high = 1100

for time in l_int:
    
    if(low < time < high):
        code += '0';
    elif(low*2 < time < high*2):
        code +='1'
    elif(low*3 < time < high*3):
        code +='L'
        
    elif(low*6.5 < time < high*6.5):
        code +='M'
        
        
    else:
        code += '1'    #Delayの前のデータは1,0判別不能なため
        code += 'D'
        print(time)
        
code += "1"     #末尾のデータは構造上判別不能なのでとりあえず1にしておこう
        
print(code)

結果

ライトON操作
計測1回目
1DLM11000000100000010100000000000000010110111

計測2回目
1DLM11000000100000010100000000000000010110111

常夜灯操作
1DLM11000000100000010100000000000000010110111

3回とも一致する。

つまり、どの操作も同じ信号を使っている。今回のプログラムで正常に解析できたと言える。

送信プログラム

ESP32でやってみる。

  • 省エネルギー:Deep Sleepを使う

でDeep Sleepを使う。複数ピンを外部起動スイッチにするには

マスクで作る

ここで、BUTTON_PIN_BITMASKの作り方は

pin4なら、0x10=2^5になる。0x08ではない。

引っかかったポイント

何もしていない状態で何度もDeepSleepから抜けてしまう

TimerEnd(timer)をtimerを起動しない状態で実行したからだと思われる。

赤外線LED駆動

単純に抵抗方式では光が弱く、反応が不安定
トランジスタで電流を増やそうとしても不安定。

他の照明について

どこの製品かは不明

波形

全消
image.png

搬送波が捉えられていない

全灯
image.png

搬送波は38kHzだった。

解析

image.png

この波形からわかること

  • Long,shortの2つの情報で構成されている
  • Long,shortの2つの時間的長さは同じ
  • ON区間の差で2情報に差をつけている。

Long

image.png
1305us:384us

short

image.png
468us:1205us

Long,short2つで信号の時間的長さは同じなため、前述のプログラムでは変換できない。
さらに1:2みたいなきれいな比率から外れている。

解析プログラム

ON区間の時間を計測する方法がある。
一旦、手作業でも変換できるのでそのまま行く

LLSLSSSLSLSL

となる。

データを直接覚える方式

わざわざ解析するより、送信IRのONOFF時間を覚えてまるまるコピーして送信するほうが正攻法・省エネルギー・効率的かもしれない。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?