適当のごった煮

Pythonと境界標とQGISを中心にいろいろと

照明用赤外線リモコンのデータ読み取りと送信

スポンサードリンク

Arduinoを使ってLEDシーリングライトの赤外線リモコンのコードの読み取りと、送信を試しました。対象は、大光電機のDCLシリーズのLED照明のリモコンです。

リモコンは下記のようなボタン構成で、チャンネル1と2の全光、消灯、シーンに加えて明るさ調節用の上下ボタンのコードを読み取りました。

f:id:tekito-gottani:20181019175852j:plain

まずはArduinoの7番ピンに受信モジュールだけを接続して、下記スケッチでリモコンの送信コードを読み取ります。このとき、リモコンのチャンネル1と2でそれぞれ同じボタンを3回ずつ押して違いを確認します。

#define infPin 7

void setup() {
  pinMode(infPin, INPUT); // 赤外線受信

  Serial.begin(115200);
  Serial.println("Serial Start.");
}

unsigned long int l[100], h[100];

int countTime(){
  unsigned long int startl, starth, t;
  int i;

  while(digitalRead(infPin) == HIGH){;} // 未送信状態のHIGH。読み飛ばし

  for (i=0; i<100; i++) {
    startl = micros();
    while(digitalRead(infPin) == LOW){;}
    l[i] = micros() - startl;

    starth = micros();
    while(digitalRead(infPin) == HIGH){
      t = micros() - starth;
      if (t > 1000000) { // 1秒以上信号を受信しなかったら読み取り終了
        h[i] = 0;
        return(i);
      }
      delayMicroseconds(45); // 読み取りに影響しない範囲で待つ
    }
    h[i] = micros() - starth;
  }
  return(i); // 一つの信号が長さ100ビット以上か、リモコンのボタンを続けて何度も押した場合
}

void loop() {
  int i, k;

  while(1){
    k = countTime();
    for (i=0; i<=k; i++){
      Serial.print(i);
      Serial.print(" : ");
      Serial.print(l[i]);
      Serial.print(" : ");
      Serial.println(h[i]);
    }
  }
}

f:id:tekito-gottani:20181019180038j:plain

「全光」ボタンを押したときのシリアル出力の結果を保存して、チャンネルごとに3回分の平均をとると下記のようになりました。また、意図的に長く押したデータを取ってリピートコードも調べました。短く押したつもりですが、長押しした場合のデータと比べると、全光のデータにリピートコードが1回入っています。

f:id:tekito-gottani:20181019180114j:plain

全光データの35番目のHighが「0」なのは、データ受信が1秒以上なかったら受信待ちを打ち切ってデータ出力しているためです。ここから、以下のようなことが読み取れます。

  • リーダーコードは、L:10ms H:5ms
  • 終了コードは、L:700us H:45msとストップビット(L:700us)
  • 送信データは32ビット(図中1~32)で前半16ビットは固定
  • チャンネル1と2では24と32ビット目が異なる
  • リピートコードは、L:10ms H:2.45msとストップビット(L:700us)

分かりやすくするために、データ部分を、Lowはほぼ695usで一定であるため、Highが600us以下なら「0」、Highが1500us以上なら「1」、リーダーコード等は「-」として、対象とするボタンすべてについて受信結果を整理すると下表のようになりました。

f:id:tekito-gottani:20181019180223j:plain

17ビット目から32ビット目までがデータコード部分で、前半8ビットの反転データが後半8ビットであることが分かります。24ビット目がチャンネルコードであることも分かります。

あとはこれらのデータ通りに赤外線LEDを点滅させればリモコンの代用としてArduinoからコードを送信できるはずです。

Lowは一定時間Lowにするだけですが、Highはキャリア周波数(38kHz)の一周期(1÷38kHz≒26us)に合わせてHigh/Lowの方形波を送信しなければなりません。

単純に考えれば、13usずつHigh/Lowを送信すれば良さそうですが、Arduinoを2個使って送受信テストを行いましたが受信できませんでした。そこで、送信時間を固定し、High/Lowの出力時間を変化させて実際の送信にかかった時間をテストしました。

#define infPin 5

void setup() {
  pinMode(infPin, OUTPUT); // 赤外線送信
  Serial.begin(115200);
  Serial.println("Serial Start.");
}

void sousin(int hh, int ll){ // 「1」出力時のHigh/Low時間

  unsigned long int start, total;
  int i;

  start = micros();
  for (i=0; i<23; i++){
    digitalWrite(infPin, HIGH);
    delayMicroseconds(hh);
    digitalWrite(infPin, LOW);
    delayMicroseconds(ll);
  }
  total = micros() - start;

  Serial.print("hh = ");
  Serial.print(hh);
  Serial.print(" ll = ");
  Serial.println(ll);
  Serial.print("total = ");
  Serial.print(total);
  Serial.print(" average = ");
  Serial.println(total/23);
}

void loop() {
  int i, j;

  digitalWrite(infPin, LOW); // 最初はLow
  for (i=7; i<15; i++){
    for (j=7; j<15; j++){
      sousin(i, j);
      delay(1000);
    }
  }
}

様々なHigh/Lowの組み合わせで時間を計測したところ、High/Lowの送信時間の合計が20usになる組み合わせで、実際の送信時間の平均が26usになりました。

f:id:tekito-gottani:20181019180337j:plain

続いて実際の送信時間が26usになった組み合わせで、送受信テストを行いました。どの組み合わせで送信したのかがわかるように、送信データに変化をつけて試しました。

#define infPin 5

void setup() {
  pinMode(infPin, OUTPUT); // 赤外線送信
  Serial.begin(115200);
  Serial.println("Serial Start.");
}

void sousinOn(int hh, int ll){ // 「1」出力時のHigh/Low時間

  int i;
  for (i=0; i<23; i++){
    digitalWrite(infPin, HIGH);
    delayMicroseconds(hh);
    digitalWrite(infPin, LOW);
    delayMicroseconds(ll);
  }
}

void sousinOff(int ll){ // 「0」出力時のLow時間

  digitalWrite(infPin, LOW);
  delayMicroseconds(ll);
}

void loop() {
  int h[] = {7, 8, 9, 10};
  int l[] = {13, 12, 11, 10};

  digitalWrite(infPin, LOW); // 最初はLow
  for (int i=0; i<4; i++){
    Serial.println(h[i]);
    for (int j=0; j<=i; j++){ // iの値によって送信回数を変化させる
      sousinOn(h[i], l[i]);
      sousinOff(600);
    }
    delay(2000);
  }
}

結果は、どの組み合わせでも受信できました。下記の通り、4つの送信と4種類の受信結果が確認できました。

f:id:tekito-gottani:20181019180353j:plain

あとは受信したコード通りに送信すればリモコンの代わりに操作できます。秋月電子通商 5mm赤外線LEDを使い、100オームの抵抗を接続しました。High/Lowは、7/13の組み合わせを用いました。

#define infPin 5

void setup() {
  pinMode(infPin, OUTPUT); // 赤外線送信
  Serial.begin(115200);
  Serial.println("Serial Start.");
}

void sousin(int hh, int ll){

  int j=hh/26; // 方形波の出力回数を決める

  // High信号送信
  for (int i=0; i<j; i++){
    digitalWrite(infPin, HIGH);
    delayMicroseconds(7);
    digitalWrite(infPin, LOW);
    delayMicroseconds(13);
  }
  // Low信号送信
  digitalWrite(infPin, LOW);
  delayMicroseconds(ll);
}

void loop() {
  char* data[] = {"11100111001100001110100100010110_on",
                  "11100111001100001101000100101110_off",
                  "11100111001100000100010110111010_scene",
                  "11100111001100001101100100100110_up",
                  "11100111001100001111100100000110_down"};

  digitalWrite(infPin, LOW); // 最初はLow
  
  for (int i=0; i<5; i++){
    // リーダーコード
    sousin(10000, 5000);
    // データコード
    for (int j=0; j<32; j++){
      if (data[i][j] == '0'){
        sousin(600, 600);
      } else {
        sousin(600, 1800);
      }
    }
    // 終了コード
    sousin(600, 45000);
    sousin(600, 0);
    Serial.println(data[i]);
    delay(5000);
  }
}

f:id:tekito-gottani:20181019180659j:plain

スケッチを書き込み、赤外線LEDを2mほど離れた天井に向けて試したところ、思った通りの照明操作ができました。High/Lowの組み合わせを10/10にしても同じ結果を得られたことから、デューティー比に関しては、それほどシビアではないように感じました。同様に、1ビットごとのHigh/Low送信時間に関しても10~20%くらい幅があるようです。

f:id:tekito-gottani:20181019180709j:plain

これにいくつかのボタンをつければリモコンと同じように……と思いましたが、すでに使えるリモコンが存在するので、リモコンを再現できたところで終わりです。

参考

ルネサス KnowledgeBase