M5StickCで赤外線リモコンを使う

本体に内蔵している赤外線ユニットを利用してNECフォーマットの赤外線送信を実験しました。現時点の情報ですので最新情報はM5StickC非公式日本語リファレンスを確認してください。

概要

M5StickCにはGPIO9に赤外線リモコン用のIRが内蔵されており、汎用的なリモコンとして利用が可能です。

ローレベルな関数を利用して、直接送信することも可能ですが、一般的な利用を想定して外部ライブラリを利用しています。

利用ライブラリ(IRremoteESP8266)

IRremoteESP8266はArduinoの標準ライブラリからインポートして利用することができるライブラリで、IRremoteという有名な赤外線リモコンのライブラリをESP8266とESP32に特化した形でフォークしたものです。

元になったIRremoteは受信しかできませんが、IRremoteESP8266を利用することで送信も可能です。

受信テスト

送信できたのかを確認するためには、受信環境を用意したほうがテスト簡単です。テレビとそのリモコンがあれば、送信コードを調べることで最低限確認ができますが、前回利用した赤外線受信ユニットを利用して実験を行いました。

まずは、本物のリモコンを操作して、どんなコードが飛んでいるのかを確認して、そのコードと同じものを送信できるようにします。

受信側はIRremoteESP8266のサンプルスケッチであるIRrecvDumpV2を利用すると簡単に分析が可能です。受信ユニットを接続したPINの番号を変更するだけで動きました。もちろん前回実験したときと同じデータが受信できました。

送信側コード(GitHub)

#include <M5StickC.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <IRutils.h>
const uint16_t kIrLed = 9;              // M5StickCはGPIO9にIRが内蔵
IRsend irsend(kIrLed);                  // IR送信を宣言
const uint32_t CUSTOMER_CODE = 0x00ff;  // カスタマーコードをセット
// リモコンコード保存用構造体
struct REMOTE {
  char name[9];
  uint8_t command;
};
// リモコンコード一覧
REMOTE remote[] = {
  { "POWER" , 0x45 },
  { "VOL+"  , 0x46 },
  { "VOL-"  , 0x15 },
  { "0"     , 0x16 },
  { "1"     , 0x0c },
  { "2"     , 0x18 },
  { "3"     , 0x5e },
  { "4"     , 0x08 },
  { "5"     , 0x1c },
  { "6"     , 0x5a },
  { "7"     , 0x42 },
  { "8"     , 0x52 },
  { "9"     , 0x4a },
};
int cursor = 0; // カーソル位置
void setup() {
  M5.begin();     // M5StickC初期化
  irsend.begin(); // IR初期化
  // リモコン項目表示
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 8);
  for ( int i = 0 ; i < ( sizeof(remote) / sizeof(REMOTE) ) ; i++ ) {
    M5.Lcd.print((cursor == i) ? ">" : " ");
    M5.Lcd.println(remote[i].name);
  }
}
void loop() {
  M5.update();  // ボタン状態更新
  // M5ボタンで送信
  if ( M5.BtnA.wasPressed() ) {
    // 送信4Byte(カスタマーコード2Byte+リモコンコード+反転リモコンコード)
    uint64_t send = 0;
    send = (uint64_t)reverseBits(CUSTOMER_CODE >> 8, 8) << 24;    // カスタマーコード(上位8bit)
    send += (uint64_t)reverseBits(CUSTOMER_CODE & 0xff, 8) << 16; // カスタマーコード(下位8bit)
    send += reverseBits(remote[cursor].command, 8) << 8;          // リモコンコードを順番入れ替えて送信
    send += reverseBits(remote[cursor].command, 8) ^ 0xff;        // リモコンコードのビット反転(パリティ)
    irsend.sendNEC(send);                                         // 送信
    // デバッグ表示
    Serial.printf("Send IR : 0x%08LX", send);
    Serial.printf("(customer=0x%04X, ", CUSTOMER_CODE);
    Serial.printf("command=0x%02X)\n", remote[cursor].command);
  }
  // 右ボタンでカーソル移動
  if ( M5.BtnB.wasPressed() ) {
    cursor++;
    cursor = cursor % ( sizeof(remote) / sizeof(REMOTE) );
    // カーソル描画
    M5.Lcd.setCursor(0, 8);
    for ( int i = 0 ; i < ( sizeof(remote) / sizeof(REMOTE) ) ; i++ ) {
      M5.Lcd.println((cursor == i) ? ">" : " ");
    }
  }
  delay(100);
}

上記を実行するとリモコンの一覧がでてきて、右ボタン(Bボタン)でカーソルが動き、下ボタン(Aボタン)で送信をします。

IRremoteESP8266のサンプルスケッチであるIRsendDemoのPINを9に変更してまずは動かしたほうがわかりやすいと思いますが、PIN番号を指定して宣言すればあとは実際に送信するコードを指定するだけで動きます。

赤外線リモコンのコードは会社によって複数フォーマットがあり、今回は一番有名なNECフォーマットで送信しています。

NECフォーマットの特徴としては2バイトのカスタマーコード(会社)があり、ここで自分が利用するコード以外が来たら無視する設定になっています。その後に送信するリモコンコードと、そのコードを反転したパリティの4バイトを送るのですが、ビットの並びが下位から上位の順番なのでreverseBits()関数を利用して順番を入れ替えています。

Send IR : 0x00FFA25D(customer=0x00FF, command=0x45)

上記が0x45(POWERボタン)のコードを送ったときのデータですが、ビットの並びが違うので、実際に送信されたデータを見ただけだとすぐに解析ができません。

実際に利用する場合には、実物のリモコンが送信している0x00FFA25DをPOWERボタンとしてデータ保持しているほうがシンプルなコードになります。

シンプル送信側コード(GitHub)

#include <M5StickC.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
const uint16_t kIrLed = 9;              // M5StickCはGPIO9にIRが内蔵
IRsend irsend(kIrLed);                  // IR送信を宣言
// リモコンコード保存用構造体
struct REMOTE {
  char name[9];
  uint64_t command;
};
// リモコンコード一覧
REMOTE remote[] = {
  { "POWER" , 0x00FFA25DUL },
  { "VOL+"  , 0x00FF629DUL },
  { "VOL-"  , 0x00FFA857UL },
  { "0"     , 0x00FF6897UL },
  { "1"     , 0x00FF30CFUL },
  { "2"     , 0x00FF18E7UL },
  { "3"     , 0x00FF7A85UL },
  { "4"     , 0x00FF10EFUL },
  { "5"     , 0x00FF38C7UL },
  { "6"     , 0x00FF5AA5UL },
  { "7"     , 0x00FF42BDUL },
  { "8"     , 0x00FF4AB5UL },
  { "9"     , 0x00FF52ADUL },
};
int cursor = 0; // カーソル位置
void setup() {
  M5.begin();     // M5StickC初期化
  irsend.begin(); // IR初期化
  // リモコン項目表示
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 8);
  for ( int i = 0 ; i < ( sizeof(remote) / sizeof(REMOTE) ) ; i++ ) {
    M5.Lcd.print((cursor == i) ? ">" : " ");
    M5.Lcd.println(remote[i].name);
  }
}
void loop() {
  M5.update();  // ボタン状態更新
  // M5ボタンで送信
  if ( M5.BtnA.wasPressed() ) {
    // 送信4Byte(カスタマーコード2Byte+リモコンコード+反転リモコンコード)
    irsend.sendNEC(remote[cursor].command);
    // デバッグ表示
    Serial.printf("Send IR : 0x%08LX", remote[cursor].command);
  }
  // 右ボタンでカーソル移動
  if ( M5.BtnB.wasPressed() ) {
    cursor++;
    cursor = cursor % ( sizeof(remote) / sizeof(REMOTE) );
    // カーソル描画
    M5.Lcd.setCursor(0, 8);
    for ( int i = 0 ; i < ( sizeof(remote) / sizeof(REMOTE) ) ; i++ ) {
      M5.Lcd.println((cursor == i) ? ">" : " ");
    }
  }
  delay(100);
}

実物のリモコンが手元にある場合には、受信して確認したデータをそのまま送信したほうが楽だと思います。ネット上で送信データを調べた場合には内部的な数値なのか、送信時のデータなのかを確認してから利用してください。

まとめ

赤外線リモコンは、データ送信時にビット配列を入れ替えるところがちょっと面倒です。またM5StickCのリモコンデータは受信側の性能とあると思いますが、2メートルぐらいの範囲ぐらいまで安定して送信できました。

ただし、実験で利用したELEGOOのリモコンもほぼ同じ距離だったので、安いリモコンはこれぐらいの距離が限界なのかもしれません。

コメント