赤外線リモコンのデータをM5StickCで受信する


概要

赤外線リモコンからM5StickCに赤外線データを送ると、
・赤外線方式(メーカー名)とその実データをLCDでスクロール表示する
・ブザーが鳴る
という物を作ってみました。

M5StickCは持ち運びが容易なので、これを持ち運んで各所のリモコンを調査できたらいいなー、という狙いです。※M5StickCは自身で赤外線の送信機能を持っていますが、今回はそれを使いません

実物の写真:

使用したもの

結線

以下の図のように結線しました。

※後で気づきましたが、赤外線受光モジュールと5Vの間に47Ω(1/10W)の抵抗を入れた方が良さそうです(参考資料の「6.製品の実装について」の項参照)

Arduinoのライブラリ準備

受信した赤外線データをデコードするためにIRremoteというライブラリを使用します。
ただし、私の環境ではビルドエラーになってしまったので、下記の「ビルドエラー対策」が必要でした。

インストールの方法

「スケッチ → ライブラリをインクルード → ライブラリを管理」から「IRremote」で検索してインストールする(私はバージョン2.2.3を使用しました)

ビルドエラー対策

以下のように「IRremote」のソースコードを修正します

図:該当するコードのファイル

図:該当コード(sendPinにピン番号を指定する(今回は使わないのでとりあえず-1に))

※今回は受信のみだったのでこれで良かったのですが、このライブラリで送信を行う場合はそれ用のピン番号を指定する必要がありそうです。

Arduinoのスケッチ

Arduinoのスケッチは下に載せた「M5StickIRreceiver.ino(全体)」の通りです。

前回の記事をベースにして、赤外線受信の処理とブザー鳴動の処理を追加しました。

大きく変わったのは「赤外線のデコード部」を追加した点です。(下のコード参照)
そこでは、
 (1)ブザーを鳴らす
 (2)LCDに表示する文字列("Maker: " + 方式 + "Data:" + 16進数の実データ)を作る
 (3)現在の表示を消す
という処理を行っています。

M5StickIRreceiver.ino(赤外線のデコード部を抜粋)
  if (irrecv.decode(&results)) {
    digitalWrite(SPEAK_PIN,HIGH); //・・・・(1)
    delay(100);
    digitalWrite(SPEAK_PIN,LOW);
    Serial.println(results.value, HEX);
    Serial.println(results.decode_type);
    irrecv.resume(); // Receive the next value
    if ( ( 0<= results.decode_type ) && ( results.decode_type < MAX_MAKER_INDEX) ) {
      sprintf(sbuf, "Maker: %s, Data:%07x", maker[results.decode_type], results.value); //・・・・(2)
      Serial.println(sbuf);
      irDataIsReceived = true;
      tcount = 0;
      dispLength = 0;
      scrollText.fillSprite(TEXT_SPRITE_COLOR); //・・・・(3)
    }
    else {
      sprintf(sbuf, "Maker: %s",maker[MAX_MAKER_INDEX]);
      irDataIsReceived = true;
      tcount = 0;
      dispLength = 0;
      scrollText.fillSprite(TEXT_SPRITE_COLOR);
    }
  }

 

M5StickIRreceiver.ino(全体)
#include <M5StickC.h>
#include <IRremote.h>

int SPEAK_PIN = 26;

int RECV_PIN = 36;
IRrecv irrecv(RECV_PIN);
decode_results results;
int getMaker;
boolean irDataIsReceived;

TFT_eSprite scrollText = TFT_eSprite(&M5.Lcd); // Sprite object

int tcount;
int dispLength;
int scrollWidth;
int startOffset;

#define M5STICK_TFT_W 160
#define M5STICK_TFT_H 80

#define TFT_BG_COLOR TFT_BLACK
#define TEXT_SPRITE_H M5STICK_TFT_H
#define TEXT_SPRITE_W M5STICK_TFT_W*4
#define TEXT_SCROLL_H M5STICK_TFT_H
#define TEXT_SCROLL_W M5STICK_TFT_W
#define TEXT_SCROLL_PIXEL 2
#define TFT_TEXT_POSI_X 30 // vertical position
#define TFT_TEXT_POSI_Y 0 // horizontal position
#define TEXT_SPRITE_POSI_X 0 // vertical position
#define TEXT_SPRITE_POSI_Y 0 // horizontal position
#define TEXT_FONT_SIZE 4
#define TEXT_SPRITE_COLOR TFT_BLACK
#define SCROLL_INTERVAL_MS 50

void setup() {
  M5.begin();
  M5.Lcd.setRotation(1);
  M5.Lcd.fillScreen(TFT_BG_COLOR);

  scrollText.setColorDepth(8);
  scrollText.createSprite(TEXT_SPRITE_W, TEXT_SPRITE_H);
  scrollText.fillSprite(TEXT_SPRITE_COLOR);
  scrollText.setTextColor(TFT_WHITE); // White text, no background

  tcount = 0;
  dispLength = 0;
  scrollWidth = 0;
  startOffset = 0;

  pinMode(SPEAK_PIN, OUTPUT);
  digitalWrite(SPEAK_PIN, LOW);

  irrecv.enableIRIn(); // Start the IR receiver
  irDataIsReceived = false;
}

#define MAX_MAKER_INDEX 18

void loop() {
  static const char *maker[MAX_MAKER_INDEX+1] = {
      "UNUSED",
      "RC5",
      "RC6",
      "NEC",
      "SONY",
      "PANASONIC",
      "JVC",
      "SAMSUNG",
      "WHYNTER",
      "AIWA_RC_T501",
      "LG",
      "SANYO",
      "MITSUBISHI",
      "DISH",
      "SHARP",
      "DENON",
      "PRONTO",
      "LEGO_PF",
      "UNKNOWN", // note:decode_type == -1,
  };

  char sbuf[100];

  // Push the sprites onto the TFT at specied coordinates
  scrollText.pushSprite(TFT_TEXT_POSI_Y, TFT_TEXT_POSI_X);

  if (tcount > 0) {
    scrollText.scroll(-TEXT_SCROLL_PIXEL); // scroll text left
    tcount--;
  }
  else {
    startOffset = M5STICK_TFT_W; //start position is edge of tft
    if (dispLength > 0) {
      scrollWidth = dispLength + startOffset;
    }
    else {
      scrollWidth = 0;
    }
    tcount = scrollWidth / TEXT_SCROLL_PIXEL;
    if (irDataIsReceived) {
      dispLength = scrollText.drawString(sbuf, TEXT_SPRITE_POSI_Y + startOffset, TEXT_SPRITE_POSI_X, TEXT_FONT_SIZE);
    }
    else {
      String drawStr = "Waiting IR Data.....";
      dispLength = scrollText.drawString(drawStr, TEXT_SPRITE_POSI_Y + startOffset, TEXT_SPRITE_POSI_X, TEXT_FONT_SIZE);
    }
    if (TEXT_SPRITE_W <= dispLength) {
      dispLength = TEXT_SPRITE_W; // disp length is too long -> TEXT_SPRITE_W
    }
    scrollText.setScrollRect(0, 0, dispLength + startOffset, TEXT_SCROLL_H, TEXT_SPRITE_COLOR); // Update ScrollRect
  }

  if (irrecv.decode(&results)) {
    digitalWrite(SPEAK_PIN,HIGH);
    delay(100);
    digitalWrite(SPEAK_PIN,LOW);
    Serial.println(results.value, HEX);
    Serial.println(results.decode_type);
    irrecv.resume(); // Receive the next value
    if ( ( 0<= results.decode_type ) && ( results.decode_type < MAX_MAKER_INDEX ) ) {
      sprintf(sbuf, "Maker: %s, Data:%07x", maker[results.decode_type], results.value);
      Serial.println(sbuf);
      irDataIsReceived = true;
      tcount = 0;
      dispLength = 0;
      scrollText.fillSprite(TEXT_SPRITE_COLOR);
    }
    else {
      sprintf(sbuf, "Maker: %s",maker[MAX_MAKER_INDEX]);
      irDataIsReceived = true;
      tcount = 0;
      dispLength = 0;
      scrollText.fillSprite(TEXT_SPRITE_COLOR);
    }
  }

  delay(SCROLL_INTERVAL_MS); // wait so things do not scroll too fast

}


終わりに

表示器があり、バッテリー内蔵のデバイスはやはり良いです。また、M5StickCは赤外線送信の機能も持っているため、リモコンとして使うこともできるのでこれもまた良いです。

ちなみに、これで集めた赤外線リモコンの情報を使用して、MQTTを経由させてESP-IDFのESP32で各機器を制御したい、というのが今回のきっかけでした。(過去記事に関連)

あと、ウチのリモコンはNECばかりなので、もしかしたら他のメーカー方式で何かあるかも・・・・です。ご了承ください。

それでは、見ていただいてありがとうございました。
тнайк чoμ_〆(・ω・。)

参考記事

更新履歴

  • 2019-07-29:新規作成
  • 2019-07-29:タイトルと結線の図を変更
  • 2019-07-30:M5StickIRreceiver.ino(全体)を修正
  • 2019-08-03:恐らく使用したと思われるブザーの型番を記載(念のため3Vで鳴ることは確認済み)