編集履歴一覧に戻る
nihsokのアイコン画像

nihsok が 2026年08月04日23時27分03秒 に編集

初版

タイトルの変更

+

赤外線リモコンの作成(前半:リモコンからの送信信号の取得)

タグの変更

+

赤外線

+

赤外線リモコン

+

ESP32

+

エアコン

+

スマートホーム

メイン画像の変更

メイン画像が設定されました

記事種類の変更

+

製作品

ライセンスの変更

+

(MIT) The MIT License

本文の変更

+

家電用のリモコンで使われている赤外線信号をコピーして、同じ信号をマイコン操作で送信できるようにする。 シンプルな構成にするため、単機能のみとする。今回はエアコン(クーラー)のリモコンを使用した。 前半記事:リモコンの信号を取得する。(このページ) 後半記事:取得した信号を送信する。(準備中) # 使用したもの - ESP-WROOM-02 - リモコン受光 OSRB38C9AA 2.7-5.5V 37.9kHz - ジャンパワイヤ(3本) - USBケーブル(適宜) 赤外線モジュールはカホパーツセンターにて110円で購入。データシートは秋月より。(https://akizukidenshi.com/goodsaffix/OSRB38C9AA.pdf) ![キャプションを入力できます](https://camo.elchika.com/e29c2610a73a360abc0dca5e77ae28d4e2d81a03/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66336238363735322d663630612d343866342d613963622d3331306133646133386665322f38336233303464342d653836622d343364662d383731302d363832633632353032316261/) ## リモコン エアコンはHITACHIの白くまくん、リモコンはRAR-8P2 3114 プロトコルは解析されている https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.h HITACHI_AC424という53-byte/424-bitのメッセージが使われており、一般的なフォーマット (https://elm-chan.org/docs/ir_format.html) とは違いそう。 # セットアップ ## 配線 | | ESP32 | OSRB38C9AA | |:---:|:---:|:---:| | 赤 | VIN |VCC| | 黒 | GND |GROUND| | 緑 | GPIO13 |OUTPUT| ## 原理 38 kHzの赤外線を受けていないときはHIGH、受けているときはLOWが出力される。 直感とは逆だった。38 kHzの搬送波は無視してオン/オフのみ得られる。 # 結果 time_pulse_us() でピンの状態の変化を取得した。 ```python:正負 from machine import Pin, time_pulse_us from array import array output = Pin(13,Pin.IN) data = array('H',[0]*1000) i = 0 while True: data[i] = time_pulse_us(output,0,10_000_000) i+= 1 t = time_pulse_us(output,1,1_000_000) if t<0: break data[i] = t i+= 1 print(data[:i],i) ``` > array('H', [30021, 49606, 3396, 1620, 454, 1194, 454, 349, 453, 349, 452, 350, 452, 349, 453, 349, 452, 350, 453, 351, 453, 345, 454, 348, 454, 348, 453, 349, 452, 1191, 456, 347, 437, 350, 453, 352, 452, 346, ... 値の入った配列長は853であったため、これを使い条件分岐のタイムロスを減らすことを考える。 ```python:改良版 from machine import Pin, time_pulse_us from array import array output = Pin(13,Pin.IN) data = array('H',[0]*1000) for i in range(0,860,2): data[i] = time_pulse_us(output,0,2_000_000) data[i+1] = time_pulse_us(output,1,1_000_000) print(data[:]) ``` >array('H', [29968, 49672, 3405, 1625, 460, 1199, 439, 377, 459, 354, 460, 353, 460, 352, 460, 354, 459, 355, 460, 357, 460, 350, 461, 353, 459, 353, 461, 354, 460, 1196, 461, 354, 459, 354, 460, 356, 459, 351, ... あまり変わらない。time_pulse_usの特性上信号が継続して変化しているときにはタイムロスが避けられないので、オンオフどちらか片方だけ取ることを考える。 ## オンの時間のみ取得 以下のようにすると、ループの中の処理はすべてオフのときに行われるはず。 ```python:オンのみ from machine import Pin, time_pulse_us from array import array output = Pin(13,Pin.IN) data = array('H',[0]*1000) i = 0 while True: data[i] = time_pulse_us(output,0,10_000_000) i+= 2 if i>30:break print(data[:i]) ``` > array('H', [29991, 0, 3395, 0, 425, 0, 427, 0, 423, 0, 426, 0, 426, 0, 425, 0, 427, 0, 425, 0, 426, 0, 425, 0, 426, 0, 426, 0, 427, 0, 425, 0]) array('H', [30037, 0, 3396, 0, 428, 0, 431, 0, 428, 0, 427, 0, 426, 0, 427, 0, 427, 0, 426, 0, 426, 0, 428, 0, 429, 0, 429, 0, 426, 0, 429, 0]) array('H', [30020, 0, 3429, 0, 457, 0, 456, 0, 454, 0, 455, 0, 454, 0, 455, 0, 455, 0, 454, 0, 455, 0, 454, 0, 454, 0, 455, 0, 454, 0, 457, 0]) array('H', [29968, 0, 3444, 0, 480, 0, 481, 0, 477, 0, 477, 0, 477, 0, 474, 0, 473, 0, 475, 0, 472, 0, 470, 0, 470, 0, 472, 0, 470, 0, 471, 0]) array('H', [30077, 0, 3485, 0, 518, 0, 516, 0, 518, 0, 520, 0, 520, 0, 518, 0, 516, 0, 519, 0, 519, 0, 520, 0, 513, 0, 515, 0, 521, 0, 518, 0]) array('H', [29730, 0, 3405, 0, 520, 0, 521, 0, 523, 0, 521, 0, 521, 0, 519, 0, 524, 0, 522, 0, 443, 0, 505, 0, 503, 0, 521, 0, 521, 0, 523, 0]) 何回か試したところ、値がブレている。 ## オフの時間のみ取得 同様に、オフの時間のみ取得する。仕様上はオンの長さは一定で、オフの長さで0と1とを区別するのでこちらの方が重要のはず。 ```python:オフのみ from machine import Pin, time_pulse_us from array import array output = Pin(13,Pin.IN) data = array('H',[0]*10000) i=-1 while True: data[i] = time_pulse_us(output,1,10_000_000) i+= 2 if i>30:break print(data[:i]) ``` > array('H', [0, 49660, 0, 1576, 0, 1132, 0, 272, 0, 293, 0, 320, 0, 293, 0, 342, 0, 318, 0, 277, 0, 321, 0, 293, 0, 1165, 0, 319, 0, 324, 0]) array('H', [0, 49669, 0, 1581, 0, 1171, 0, 327, 0, 327, 0, 335, 0, 325, 0, 327, 0, 421, 0, 337, 0, 327, 0, 342, 0, 328, 0, 1170, 0, 325, 0]) array('H', [0, 49754, 0, 1589, 0, 1259, 0, 340, 0, 335, 0, 337, 0, 338, 0, 415, 0, 416, 0, 420, 0, 412, 0, 415, 0, 337, 0, 337, 0, 1259, 0]) array('H', [0, 49775, 0, 1639, 0, 1235, 0, 389, 0, 340, 0, 388, 0, 388, 0, 387, 0, 390, 0, 391, 0, 388, 0, 390, 0, 388, 0, 389, 0, 1230, 0]) array('H', [0, 49719, 0, 1635, 0, 1234, 0, 389, 0, 390, 0, 389, 0, 388, 0, 388, 0, 389, 0, 392, 0, 386, 0, 387, 0, 388, 0, 388, 0, 1232, 0]) array('H', [0, 49753, 0, 1669, 0, 1265, 0, 418, 0, 417, 0, 418, 0, 416, 0, 416, 0, 418, 0, 419, 0, 415, 0, 418, 0, 417, 0, 417, 0, 1260, 0]) こちらも値がブレている。最後の方の1250くらいの長さのが1か? ## ここまででわかったこと オンは 420~520 us、オフは 270~420 usがひと単位と推測。 ばらつきが 100~150 usくらいあり気になる。キャリアが 38 kHz で30%のdutyだと、1/38e6/2≈9 usのブレはありうるがそれよりも格段に大きい。 オフの時間の長さの方が重要なはずなので、(ヘッダ以外では)あまりみなくてよい || On (Leader) | Off (Leader) | On (Header) | Off (Header) | On | Off (1) | On | Off (0) | ...| |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| |参照値|29784us|49290us|3416us|1604us|463us|1208us|463us|372us|...| |ざっくり版|30000us|49700us|3400us|1600us|500us|1200us|500us|400us|...| |推測|60H|130L|7H(少したりない)|4L|H|3L|H|L|...| 参照値は前述のGithubより (https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) これでも整数倍にはなっていない。 とりあえず決め打ちで0と1にデコードしてみる ```python from machine import Pin, time_pulse_us from array import array output = Pin(13,Pin.IN) data = array('H',[0]*1000) head = array('H',[0]*2) for i in range(-1,2): head[i] = time_pulse_us(output,1,10_000_000) i = 0 while True: t = time_pulse_us(output,1,10_000_000) if t<0: break data[i] = 0 if t<1000 else 1 i+= 1 bits = "".join(str(b) for b in data[:i]) print(head,bits) ``` >array('H', [49718, 1633]) 10000000 00001000 00000000 00000010 11111101 11111111 00000000 00110011 11001100 00011001 11100110 11001000 00110111 00110110 11001001 00000000 11111111 00000000 11111111 00000000 11111111 00000000 11111111 00000000 11111111 11001000 00110111 00001111 11110000 00000000 11111111 00000000 11111111 00000001 11111110 11000000 00111111 10000000 01111111 00010001 11101110 00000000 11111111 00000000 11111111 11111111 00000000 11111111 00000000 11111111 00000000 11111111 00000000 53byteなので仕様通り(8bitずつ区切っている)。4byte目以降はビット反転とセット(誤り訂正のための冗長化)。 13byte目の下位6bitに気温の情報が入っているとのことだが、 - 設定温度27度のとき (00)110110 - 設定温度28度のとき (00)001110 だったので、リトルエンディアンと考えれば整合的な結果になっている。 # まとめ エアコンの赤外線リモコンの信号を受信して解析した。 - パルス幅はあまり安定していない。 - パルスのオン・オフ(長・短)およびヘッダ部どれも整数倍にはなっていなさそう - リピート信号のようなものは含まれていなさそう これらが、今回の環境(HITACHI白くまくん)だけか、そういうものなのかは今後要調査。 それでも大まかな構造はとらえられ、01にデコードした結果気温に対応した値も見つけられた。後半パートでは実際にこの信号を送信してみる。