はじめに
ESP32の赤外線送信用プログラムがひとまずできたので公開。
ソースコードは下記にアップロードしています。
https://github.com/mickie895/ESP32_IR_Sender/blob/master/ESP32_IR_Sender.ino
赤外線リモコンのフォーマット
http://elm-chan.org/docs/ir_format.html
上記リンクを見る限り、おおよそのリモコンの信号はLEDを38kHzの周期でPWM制御させれば作成できそうだった。
点滅・消灯を制御する周期はリモコンごとに一定なため、そこを割り込みか一定時間ごとのループで実装するかを考えれば良さそう。
今回はPWM信号+割り込みで対応することができた。
ポイント
ESP32はArduinoと互換性はあるが、割り込み及びLEDのPWM制御の部分に互換性がないためにArduinoの送信スケッチ例を使うことができず、一から処理を作成することになった。
割り込み部分
割り込み部分はESP32のライブラリをインポートしたときについてきたスケッチ例(ESP32→Timer→RepeatTimer)を利用した。
※ESP32のセットアップはQiita上にまとめられていました。
グローバル変数にいくつか変数を用意し、
hw_timer_t * timer = NULL;
volatile SemaphoreHandle_t timerSemaphore;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
// 割り込み処理カウンター
volatile byte tick_required = 0;
タイマー割り込み用の関数を用意し、
void IRAM_ATTR onTimer(){
// Increment the counter and set the time of ISR
portENTER_CRITICAL_ISR(&timerMux);
tick_required++;
portEXIT_CRITICAL_ISR(&timerMux);
// Give a semaphore that we can check in the loop
xSemaphoreGiveFromISR(timerSemaphore, NULL);
// It is safe to use digitalRead/Write here if you want to toggle an output
}
setup()に割り込み用の処理を追加し、
(ESP32が80MHzなのでプリスケーラーを80に設定することにより、1μsごとにタイマーがカウントアップし、tick_usを超えるたびに割り込みがかかるようになる。)
// Create semaphore to inform us when the timer has fired
timerSemaphore = xSemaphoreCreateBinary();
// Use 1st timer of 4 (counted from zero).
// Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more
// info).
timer = timerBegin(0, 80, true);
// Attach onTimer function to our timer.
timerAttachInterrupt(timer, &onTimer, true);
// Set alarm to call onTimer function every "tick_us" microseconds.
// Repeat the alarm (third parameter)
timerAlarmWrite(timer, tick_us, true);
// Start an alarm
timerAlarmEnable(timer);
onTimer()関数でインクリメントする"tick_required"をメインループにて監視し、1以上になれば次のLEDのPWMを書き込むようにした。
なお、割り込みが無い場合はLEDの点滅周期の半分だけ待機させるようにした。
これで消費電力を抑えつつ割り込み後の処理が送れないようになっている(はずである)。
LEDのPWM制御
LEDのPWM制御の部分は下記アドレスを参考にした。
https://garretlab.web.fc2.com/arduino/lab/ledc/
setup()上でLEDの準備を行い
ledcSetup(0,38000,6);
ledcAttachPin(IR_OUT, 0);
ledcWrite(0, 0);
割り込みが入ったときの処理でledcWrite()で点滅・消灯を切り替えれば良い。
※今回はledcSetupの2つ目の引数を6にしているので、0から63(2^6 - 1)までの制御になっている。
送信例
以上の割り込みとPWMの手順を用いて適当なテレビのリモコンの信号をスケッチ例のIRrecvDumpV2で送ったところ正しく送信ができた。
任意のリモコン通信の信号を送る場合、グローバル変数上の下記の変数の中身を書き換える。例えばAEHAフォーマットならtick_usが382、tick_header_onが8、tick_header_offが4になります。
// 1Tickの長さ
int tick_us = 562;
// 1bitが0のときと1のときのtickの長さ
// ※最初のtickは点滅tick、それ以外は消灯tick
byte tick_one_length = 4;
byte tick_zero_length = 2;
// 実際に送信する内容
byte ir_buffer[20];
char ir_buffer_size = 0;
// ヘッダのtick数
int tick_header_on = 16;
int tick_header_off = 8;
また、setup()中にてir_bufferの中身も書き込んでください。下記はアップロードした信号の例です。
#ifdef DEBUG_TEST
// テスト用のバイト列。
ir_buffer[0] = 0x80;
ir_buffer[1] = 0x63;
ir_buffer[2] = 0x0F;
ir_buffer[3] = 0xF0;
ir_buffer_size = 4;
#endif
※IRrecvDumpV2で手に入るON/OFFの周期は下位ビットから順番に送信されているため、16進数に直す場合は注意が必要。例えば順番に10001100が送られていた場合、0x31が対応する数値となる。
今後はBluetoothで接続し、信号を送受信できるようにする予定。