ELEGOO Lesson 14 IR Receiver Module
IRレシーバ、赤外線(infrared)受光器です。赤外線リモコンの信号を受信することができます。
ELEGOOキットにはリモコンも付属してます。赤外線送信用のLEDが付属してないのは残念なところ。Arduinoのプログラムから送信することはできないです。(このへんは、またいずれ…。)
リモコンの赤外線は人間の目には見えない光ですが、スマホのカメラで撮影すると光っているのを見ることができます。
ピカー
IRレシーバは、光を受けて動作が変化するという点で、フォトセルと似てるところもありますが、フォトセルは黄色や緑色の光を検出するのに向いていて、光の強さにより抵抗が変わり、出力はアナログ。
それに対しIRレシーバは、38KHzの赤外線を受信するように調整してあり、出力はデジタル。38KHzの赤外線を検出したときにLOWになり、それ以外だとHIGH(5V)になります。
IRレシーバの接続は、VCC(5V) / GND / DATA の3本。DATAはデジタルピンに接続します。
Lesson 14 のプログラムでは、受信は、付属している IRremote ライブラリを用いて行っています。
int receiver = 11; // Signal Pin of IR receiver to Arduino Digital Pin 11 IRrecv irrecv(receiver); // create instance of 'irrecv' decode_results results; // create instance of 'decode_results'
ピン番号を設定して、IRrecv と結果受信用の decode_results を定義。
class decode_results { public: int decode_type; // NEC, SONY, RC5, UNKNOWN union { // This is used for decoding Panasonic and Sharp data unsigned int panasonicAddress; unsigned int sharpAddress; }; unsigned long value; // Decoded value int bits; // Number of bits in decoded value volatile unsigned int *rawbuf; // Raw intervals in .5 us ticks int rawlen; // Number of records in rawbuf. };
setup()内で、
irrecv.enableIRIn(); // Start the receiver
で受信スタート。
何か受信したら、
irrecv.decode(&results)
が true になり、results に結果が入ります。
translateIR();
で内容のチェックをして
irrecv.resume();
で引き続き受信継続。これを繰り返し。
translateIR()の中では、results.value の値によって押されたボタンを判別しています。
結果はこういう感じ。
ボタン長押しした時はREPEATになるみたいです。decode内でメーカー判定などして、メーカーと results.value の値で何のボタンかを判別するようですね。(このLesson のプログラムだと、メーカー判別はあんまり意味なさそうです)
結局のところ、IRremote ライブラリが全部やってくれてしまっている感じなので、中身もちょっと見てみます。
こちらで解説されているライブラリですね。ライブラリとしては送信にも対応していました。
A Multi-Protocol Infrared Remote Library for the Arduino
http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
データの読み込みは、IRremote.cpp の 割り込み処理 ISR(TIMER_INTR_NAME) の中で行われています。
ピンからデータを読み込み、HIGHになっている時間(SPACE) / LOWになっている時間(MARK)をバッファ(irparams.rawbuf[])に格納しています。
このデータを、decode() に渡して、メーカー判別&デコードしていました。
long decodeNEC(decode_results *results); long decodeSony(decode_results *results); long decodeSanyo(decode_results *results); long decodeMitsubishi(decode_results *results); long decodeRC5(decode_results *results); long decodeRC6(decode_results *results); long decodePanasonic(decode_results *results); long decodeLG(decode_results *results); long decodeJVC(decode_results *results); long decodeSAMSUNG(decode_results *results);
各メーカー用の処理を、デコードが成功するまで、順に呼んでます。それぞれの中は、各メーカーに特化したハードコーディングでのデコードになってる感じ。
\Arduino\libraries\IRremote\IRremote.h で #define DEBUG を有効にすると、信号判定のログが見れます。
判定までにこれだけの手順。ひえー。めんどくさそうー。
IRremoteInt.h の中では、Arduino機種ごとにどのタイマー使うかの定義もありました。
どのメーカにも該当しなかった場合は、long IRrecv::decodeHash(decode_results *results) で
「それっぽいハッシュ値」を生成しているみたいです。
ほんとにちゃんとデータのでコードをしているわけではなくて、ライブラリ独自で値を付与している感じ。ここで取得したリモコンデータの値を他のライブラリにもっていっても使えない、ってことかな。
ちなみに、東芝REGZAのリモコンがあったので試してみたところ
何らかの通信は取れてる感じ。
このライブラリ最新版では、ESP32(M5Stack)も、受信だけは対応してました。
z3t0/Arduino-IRremote
https://github.com/z3t0/Arduino-IRremote
SEND_PIN が無くてエラーになるみたいなので、IRremote.h の SEND_PIN 定義を適当な値(const int sendPin = -1;とか)に修正します。どうせSEND使えないので。詳細はこのあたり。おそらく、そのうち修正されると思います。
ESP32 Error – error: ‘SEND_PIN’ was not declared in this scope
https://github.com/z3t0/Arduino-IRremote/issues/582
サンプルプログラムのIRrecvDemo、ほぼ、ELEGOOの Lesson と同じ内容なのですが、results.value の値をそのままHEX表示してました。これなら、REGZAリモコンからの送信もそのままの値が見れますね。
(メーカー判定できてないので、decodeHash で生成しているライブラリ独自のハッシュ値になっていると思います。)
M5Stack用に少し修正。ピンは5番使いました。
#include <IRremote.h> #include <M5Stack.h> int RECV_PIN = 5; IRrecv irrecv(RECV_PIN); decode_results results; void setup() { M5.begin(); // In case the interrupt driver crashes on setup, give a clue // to the user what's going on. Serial.println("Enabling IRin"); irrecv.enableIRIn(); // Start the receiver Serial.println("Enabled IRin"); //M5.Lcd.clear(BLACK); //M5.Lcd.setTextSize(4); } void loop() { if (irrecv.decode(&results)) { Serial.println(results.value, HEX); M5.Lcd.println(results.value, HEX); irrecv.resume(); // Receive the next value } delay(100); }
実行!
FFFFFFFFは、長押しの時に出てました。
プログラム内でコメントにしている部分、M5.Lcd.clear(BLACK); とか M5.Lcd.setTextSize(4); を入れると、なぜかIRレシーバからの読み込みが動作しなくなってしまいます。なんでだろう…?
これ解決できれば、赤外線リモコンをM5Stackの汎用コントローラにできるのになー。
少し進展ありました。 M5.Lcd.clear(BLACK); M5.Lcd.setTextSize(2); の後、短いDelayを入れてirrecv.resume(); を呼ぶことでIRレシーバ読み込み動作できました。ついでなので、メーカー判定結果(results.decode_type)も表示するようにしてみました。
#include <IRremote.h> #include <M5Stack.h> int RECV_PIN = 5; IRrecv irrecv(RECV_PIN); decode_results results; void setup() { M5.begin(); // In case the interrupt driver crashes on setup, give a clue // to the user what's going on. Serial.println("Enabling IRin"); irrecv.enableIRIn(); // Start the receiver Serial.println("Enabled IRin"); M5.Lcd.clear(BLACK); M5.Lcd.setTextSize(2); delay(500); } void loop() { if (irrecv.decode(&results)) { Serial.println(results.value, HEX); M5.Lcd.println(results.decode_type); M5.Lcd.println(results.value, HEX); irrecv.resume(); // Receive the next value } delay(100); }
実行!
出ました!ちなみにメーカータイプは以下の通りでした。単にenumで宣言してあるだけなので、バージョンによって値は変わりそうです。(実際、ELEGOO付属バージョンのライブラリでは、NECが1になってました。)
UNKNOWN:-1
UNUSED:0
RC5:1
RC6:2
NEC:3
SONY:4
PANASONIC:5
JVC:6
SAMSUNG:7
WHYNTER:8
AIWA_RC_T501:9
LG:10
SANYO:11
MITSUBISHI:12
DISH:13
SHARP:14
DENON:15
PRONTO:16
LEGO_PF:17
付属のリモコン、REGZAリモコンともに、メーカータイプは3(NEC)でしたー。
これで、うまく動作しているように見えるのですが、
M5.Lcd.println(results.decode_type);
を、改行なしの
M5.Lcd.print(results.decode_type);
にするだけで、動かなくなるんですよ、これが…。なんでだ。まだスッキリしない感じです。