WROOM-02を使った赤外線リモコンの調査

概要

ESP-WROOM02に温度/湿度センサーを接続し、温度や湿度を定期的に計測することができた。温度が測れるのならば、室温に応じてエアコンをオンにしたりオフにしたりするのも面白かろうと思い、リモコンのフリをする機能も追加してみることにした。

当初は、赤外線を出力するIrLEDを買ってきて適当につなげば実現できるんだろうと思っていたのだけど、始めてみるとリモコンが出力するフォーマットやコードの解析が必要なことが分かった。今回は、赤外線受信モジュールとWROOM-02を接続し、リモコンの送信内容を調べて、送信している内容をコード化するお話。

回路構成

前回の温度/湿度センサーを接続した回路を一部修正し、赤外線受信モジュールを配置した。秋月電子のAE-ESP-WROOM02と3.3V出力の低損失三端子レギュレータ、そしてリモコン用赤外線受信モジュール(OSRB38C9AA)から構成している。 AE-FT231Xを使ったUSB-シリアル変換部分は、独立した小さなブレッドボードに載せてあり、3本のケーブル(TxD, RxD, GND)で接続している。

wroom02-ir-1

この受信モジュール (OSRB38C9AA) は以下の理由で選択した。

  • ふつうの家電リモコンと同じく、波長 が940nm、搬送波の中心周波数が38kHz。
  • 2個で100円
  • 3.3V電源で動作

詳しくは、秋月電子の通販のページにあるデータシートを参照。

OSRB38C9AA
赤外線受信モジュール OSRB38C9AA

ブレッドボードに載せるとこんな具合になる。各足は、写真右からVcc、Gnd、Outputという役割をもち、結線も簡単。受信モジュールのOutputをWROOM02のGPIO2に接続した。

リモコンの赤外線通信について

家電用リモコンのデータ形式については、赤外線リモコンの通信フォーマット というページを参考にさせていただいた。また、NECフォーマットと呼ばれる信号パターンの総本山であるルネサスエレクトロニクス社のホームページにFAQとして掲載されている  赤外線リモコンの信号はどうなっているのですか? というページも分かりやすい。

データの表現方法

リモコンから家電製品へ送信されるのは、リモコンのボタンに対応したコマンドコードだろう。コマンドコードは、リモコンの規格にしたがって以下のように送信されているようである。

フレーム

コマンドコードは、フレームと言われる単位で送信される。フレーム内には、リモコンの規格に応じて数バイト分あるいは数十バイト分のデータビットが含まれている。

irsignal
赤外線リモコンの送出フレーム

フレームは リーダー と呼ばれる信号パターンから始まり、数バイトまたは数十バイトのコマンドデータがビット単位(LSBが先頭)に続く。最後に、フォーマットの規格によっては トレーラー (または、フレームスペース)と呼ばれる終端を表す信号パターンで終わる。トレーラーがない場合でも、データと思われるビットを受信した後で無信号状態になれば、フレーム終了と見なせる。
連続的に複数のフレームを送信するときには、トレーラーに引き続いて次のフレームのリーダーを送信する。

ビット データの表現方法

各ビットは、38kHz(デューティー比1/3)で点滅する赤外光が送信されている時間(オンの時間)と、送信されていない時間(オフの時間)の長さの違いで表現される。

赤外線リモコンの変調方式
赤外線リモコンの変調方式

基準時間Tの間オンで、引き続くTの間オフの場合が論理値0、それに対して、Tの間オンで続く3×Tの間オフならば論理値1となる。なおこれは、今回扱うNECフォーマットやAEHAフォーマットと呼ばれる形式の場合で、必ずしもすべてがこうではなさそうである。
1バイト(8ビット)のデータは最下位ビット(LSB)から順に送信されるが、バイトとバイトの間に特別な区切りはない。

フォーマットごとのタイミングデータ

基準時間Tや、リーダー、トレーラーの長さは規格によって異なっていて、各フォーマットの標準的な値は以下のようになっているようだ。

NECフォーマット

  • 基準時間T : 約560μ秒
  • リーダー:オン:16×T、オフ:8×T
  • トレーラー:無し(代わりにストップビットがある)。
  • ストップビット : 1Tのオンのあと10msec前後のオフ。
    (そのような送信形式が多い)。

追記: NECフォーマットの家電用リモコンをいくつか調べたところ、キーを押したままにしたときにリピートを表現する信号が送信されていることが分かった。以下のようなタイミングのようだ。

NEC format, Repeat

フレーム終端のストップビット(1T期間のオン)のあと、10msec前後のオフ期間があり、その時点でもキーが押されていれば、16Tのオンと4Tのオフ、そしてストップビットが出力される。その後95msec前後のオフ期間があり、再度リピート用の信号が出力されていた。

AEHAフォーマット

  • 基準時間T : 約425μ秒
  • リーダー:オン:8×T、オフ:4×T
  • トレーラー:8msec以上のオフ。

今回は手持ちのリモコンの都合で、これら2つのフォーマットに対応してみることにした。

38kHz信号の周期は約26μ秒なので、T=560μ秒ならば、1Tの間に約22回、T=425μ秒なら約16回の点滅ということになる。リモコンとして赤外線を送信するときにはIrLEDを点滅させることになるが、ソフトウェアでのポートの上げ下げでなんとかなる速度である。

リモコン用赤外線受信モジュール

リモコン用赤外線受信モジュールは、赤外線の波長や搬送波周波数ごとにいろいろと用意されているが、おおよその機能構成は以下のようなものだと思う。

irreceiver
赤外線受信モジュールの構成概略
  • PD : フォトダイオード。波長 940nmの光(赤外線)を受けると電流を発生する。
  • AMP : PD出力を、光の強さに応じた電圧として出力。
  • BPF : バンドパスフィルター。38kHzを中心とした成分のみを通過させ、それ以外の成分を減衰させる。
  • DEMODULATOR : BPFの出力がある電圧レベル以上ならば、論理値1に相当する電圧を、レベル以下ならば論理値0に相当する電圧を生成する。そして、ある時定数で積分することで38kHzを連続して検出している期間(オンの期間)にわたって論理値1相当のレベルを出力する。最終段のシュミットトリガ回路で整形されたロジックレベル信号として出力していると思われる。

※ OSRB38C9AAはアクティブL出力なので、オンの期間にはLレベル、オフの期間にはHレベルを出力する。

こういった仕組みをもつモジュールなので、モジュールの出力をポートで読んで、オンやオフの持続時間を測ってやれば、リモコンが送出するコマンドコードにたどり着けるんじゃないかな、と分かった。

プログラム

AE-ESP-WROOM02で動作しているプログラム (ESP_REMOCON_DUMP1.ino) を以下に示す。リモコンを赤外線送受信モジュールに向けて確認したいボタンを押すと、その操作に対応したコード列を16進表示でシリアルモニタに表示する。

94行目~103行目には、各配列要素がもつhigh時間とlow時間や、そこから導いたビット値などをシリアルモニタにダンプ出力する部分で、上記では#if 1 で括ってある。結果が安定して得られるようならば、#if 0 としておく方がうるさくなくてよい。

void setup()

シリアルの初期化と受信モジュール出力を受けるポートを設定し、開始メッセージを表示する。
しばらくArduinoやWROOM02に触らないと、機材にどのスケッチが入っているのか忘れるので、シリアルモニタにソースファイル名を表示するようにしている。

irdata_t構造体

ソースのコメントにあるように、赤外線受信モジュールが出力する情報を記録するための構造体であり、H期間とL期間のタイミング情報を保持するための要素をもつ。
この構造体の配列の要素一つ一つが、受信データのビット値(および、リーダー、トレーラー情報)をもつことになる。

void loop()

受信~解析を行う。

今回使用する受信モジュールがアクティブL出力(38kHz信号を受信中はL)なので、IR_PINの最初の立下り遷移を待ち、その後立上りまたは立下りを検出するごとに、irdata_t構造体の配列(irdata[])に検出したモジュール時刻(micros()が返す値)をセットしていく。立下り検出時に、irdata[data_count].high にモジュール時刻をセットしていることに注意。
この検出の繰り返しは、1秒間経過するか配列が一杯になるまで続ける。手持ちのリモコンでは、要素数500に達することはなかったが、対象のリモコンに応じて最大要素数( #define DATA_COUNT 500 )を変更する必要があるだろう。

そういえば、WROOM02のmicros() の分解能はAVRマイコン同様の4μ秒なのだろうか?esp8266 sdk のsystem_get_time() を呼んでいることは分かったが、それ以上は調べなかった。

受信が終わると、配列に蓄積された .highおよび .low のモジュール時刻を、オン/オフの継続時間( .high_Tおよび.low_T )に書き換えている。次に、受信したデータがAEHA規格と見なすべきなのかNEC規格と見なすべきなのかを決めるために、.high_T の値と各規格のT値を誤差を勘案しながら比較し多数決をとっている。きわめて適当なやり方だが、手持ちのリモコンには対応できた。

最後に、採用したT値と、.high_Tおよび .low_T から、各要素が表しているビット値を求めて .value に格納している。

void decode_data()

irdata[]に格納されたビット値から、8ビット単位のデータを生成する。
まずはリーダーに相当するパターン(Tの16倍または8倍の期間の .high_T と、8倍または4倍の期間の .low_T をもつ要素)を検出しフレームが開始したとみなす。
そして、各データビットがLSBから順に送信されてきているはずなので、シフトしながら8ビットのデータを復元してバイトに直しシリアルモニタに出力する。
最後に、8msec以上の無信号状態を検出した時点でフレームが終了したものと見なしている。NECフォーマットの場合、最終バイトの次に、1ビットのストップビットが来るのだが、バイトを構成できないので取り捨てられる。

シリアルモニタの出力

Tという会社のテレビリモコンの電源ボタンを押すと、シリアルモニタには以下のような内容が表示された。

シリアルモニタの内容
シリアルモニタの内容

最後の表示から、0x40, 0xbf, 0x12, 0xed という4バイトが送信されていることが分かった。2バイト目は1バイト目の、4バイト目は3バイト目の、それぞれビット反転されたデータであることが分かる。

シリアルモニタに出力されたタイミング情報を見ると、オンの時間が規定のT値(560μ秒)に対して10%ほど長く、オフの時間は逆に8%程度短い。プログラムによる立上りと立下りの検出や記憶のタイミングに違いがあるとしても、50μ秒も相違するとは考えにくい。出力波形を見ていないので理由は不確かだが、この赤外線受信モジュール特有の現象のような気がしている。他のリモコンでも試してみたが、このような関係をもつ場合が多かった。

各種リモコンでのテスト結果

今回のメインのターゲットはエアコン用のリモコンなのだけど、照明やテレビのエアコンも手近にあったので試してみた。すべてについて書いていくと長くなるので、エアコン用に限って載せることにした。

リモコンいろいろ
リモコンいろいろ

※ 以下に示す内容は、今回掲載した回路/プログラムを使って個人の趣味の範疇で得た結果であり、内容が正しいことや再現性を保証するものではありません。

エアコン用リモコンについて

液晶がついているような立派なエアコン用のリモコンは、エアコン本体とは独立して内部に運転中か否かという状態を保持している。また、エアコンの運転モード(冷房、暖房、ドライなど)や設定温度、風向、風量など、人間が前回操作して設定した内容をリモコンが記憶しており、運転開始指示(「運転」とか「冷房」などのボタン操作)と同時に送り出すようである。
例えば、テレビ用や照明用の場合、ある1つのボタンが送り出すコマンドデータは1種類であり内容も単純なのだが、エアコンの場合にはリモコンが記憶している状態や設定内容により、データ内容が変わることもあった。

テレビや照明ならば、リモコンの操作結果に応じて即時にその内容を確認できる(テレビが映った、チャンネルが変わった、音量が変化した、照明がついた、暗くなった、明るくなった…)。それに対して、すっかり多機能になったエアコンの場合には、リモコンの液晶に表示されているとおりに本体が動いてくれないと、何をやってくれているのか分からない。そのため、表示されている設定内容をすべて送り出す必要があるのだろう。

以下、試してみたリモコンのいくつかのデータとメモを書いておくが、メーカー名や製品名はあからさまには書かないことにしたのでご了承ください。

D社の製品

エアコン専業メーカーのイメージがあるが、実際のところはどうなのか。AEHAフォーマットで、トレーラーを挟んで2フレーム連続して送信され、最初のフレームが20バイト、第二フレームが19バイトだった。

いずれも風量は「自動」。冷房動作中に温度設定を24℃から25℃、あるいは、25℃から24℃に変更すると、各温度での運転開始時と同じコードがでていた。
なお、第一フレームは上記例ではすべて同じだが、「気持ちのいい風」的な設定をしておくと、第一フレームの最終バイトのみが変化し、「気持ちのいい風」状態を維持するようになっていた。
見た感じ、第二フレームの最後のバイトがチェックコード(フレームデータの和の下位1バイト)のようである。

H社の製品

この会社のクーラーは、この20年ほどの間に数台買い替えている。今使っているクーラーのリモコンはデカくてびっくりした。

送信データもデカくて52バイトである。AEHAフォーマットで1フレームに詰め込まれていた。
運転開始ボタンはなく、「冷房」や「暖房」というボタンを押すことで指定動作で運転を開始する。リモコンの状態が「停止中」の場合、室温操作を行ってもデータは送信されず、リモコンの温度表示のみが変化する。

冷房開始と停止とでは、28バイト目が変化している(0xf1 –> 0xe1)。29バイト目はその反転だろう。

同じ操作をしてもまったく同じ結果にならないのでちょっと悩んだのだけど、このリモコンは、最後から2番目のバイトでリモコン操作のシーケンス番号を出しているようだ。最終バイトはその反転になっている。果たして、シーケンス番号も再現してやる必要があるのかどうか。シーケンス番号を再現しないとリモコンとして機能しないならば、単純な学習リモコンでは泣きを見ることになる。

F社の製品

もともとはGという会社だったように記憶している。AEHAフォーマットで1フレーム。あっさりしたデータで好感が持てる。
運転停止状態では、運転モード(冷房、暖房…)、温度、風量の変更操作をしても、リモコンの状態も変化しないようで液晶には時刻のみが表示される。

運転停止がシンプルである。ちょっと調べれば、各バイトの意味をつかむのも容易そうに思える。調査を始める前は、たいていのリモコンが出すコードはこの程度のもんだろうと高を括っていた。

P社の製品

家電大手のP社の安いエアコン。AEHAフォーマットで、トレーラーを挟んで2フレーム連続送信。1フレーム目が8バイト、2フレーム目が19バイトである。

F社のものほどではないが、とてもシンプルで分かりやすい。やはり、リモコンが「停止中」のときは、温度変更操作をしてもデータは送信されない。よく使うボタン(オン/オフ、温度変更)以外はカバーの中に隠されていて、ごちゃごちゃしておらず使いやすい。

その他のリモコン

テレビ用および照明用のリモコンを試してみたが、NECフォーマットを採用していたのは先にシリアルモニタ出力を載せたテレビ用だけだった。このフォーマットはコマンドデータを表現できるバイト数が少ないので、エアコン等ではあまり使われていないのかもしれない。

なお、照明用リモコンのうち、量販店で安売りしていたM社の製品の送信フォーマットは、NECでもAEHAでもなく独自に近いもののようで、T値が700μ秒近く、オンとオフの組合せによるビット値1の表現方法も異なっているようだった。こういうのが多いのであれば、受信データをバイト列に復元して記憶するよりも、オン/オフ時間自体を記憶していく方がよいのかもしれない。ただ、コマンドコード中の1バイトを表現するために、low時間とhigh時間を8つずつだから、sizeof(short) ×16バイトの領域が必要になるのが難点だろう。

きょうのまとめ

WROOM02の処理能力をもってすれば、たいていの赤外線リモコンが送信するコマンドコードは十分に解析可能なことが分かった。

次は、今回調査したコードを使ってIrLEDを点滅させてみるが、WROOM02をwebサーバーとして構成し、httpリクエストのパラメータに応じたコマンドコードを送出させる仕組みをまず作ることにする。そして、今回の回路とスケッチを使い、リモコンと同じコードが再現できているかどうかを調べてみる。

今回調べたリモコンでは、コマンドコードは最長で52バイトだったことから、webサーバー型の赤外線リモコンを構成する際には、16進数表現した文字列をGETリクエストのパラメータとして使うことができる。

のんびりやってるうちに季節が進んでしまって、クーラーを自動的にオン/オフすることに魅力を感じなくなってきた。その代わり、フォーマットとコマンドコードを送り込む、あるいは記憶しておくことで、複数の家電製品に対応したリモコンが作れるわけである。パンッと手を打つと、テレビとエアコンと照明が一度にオンになるような仕組みも作れるだろう。そっちの方が面白そうな感じ。

追記

Arduino nano を使って赤外線データの解析を行う話も追加しました。データ収集のロジックを若干改善しています。メモリが少ないので収集可能なバイト数は40バイトまでになりますが、ハードウェアが簡単なので便利になりました。

NECフォーマットのストップビットとリピート表現を追加しました。プログラムでの対応は、Arduino nano用にのみ実施しました。