SPRESENSEアプリケーションをリモコンで動かす方法


Arduino でお馴染みの IR 赤外線リモコンを SPRESENSE ボードにポーティングしてみました。単純に動かすだけも良いのですが、SPRESENSE がもつ Arduino マルチコア環境を使って、IR 受信機をメインコアではなくサブコアで動かしてみます。

用意するもの

 

接続

結線はこんな感じにしました。IR 受光部には 5V を供給して受信信号は 11 番ピンで受けています。IR 送信部はハードウェア PWM に対応しているピン(3, 5, 6, 9) を選択する必要があるので、ここでは 3 番ピンを使っています。抵抗は省略して書いています。拡張基板のI/Oジャンパは3.3Vに設定しました。

ちなみに受光部基板の3ピンは文字の表記も無く少々分かりづらいのですが以下の配置になっています。

赤外線リモコンの通信フォーマット

詳しい説明はここに譲ります。
http://elm-chan.org/docs/ir_format.html

簡単に説明すると、
周波数38kHz/40kHz程度のPWM信号(duty比1/3)を間欠的に動作させることによって 0, 1 信号を送ります。
間欠動作させる間隔は最小で 350-600 μsec 程度で、NEC、家製協、SONYなどフォーマットによって決められています。
その他、データ長や長押ししたときのRepeat信号などもフォーマットによって規定されています。
大抵のフォーマットは後述するライブラリがサポートしてくれています。

ライブラリ

Arduino-IRremote ライブラリをベースに Spresense 用に移植しました。
https://github.com/baggio63446333/Spresense-IRremote

プログラムの動作としては、

  • 送信部: フォーマットにしたがってPWM信号を出して止めてを繰り返します。PWM信号の出力/停止期間は数百μsecのビジーループで調整しています。
  • 受信部:50usecの周期タイマーを動かして信号のHIGH/LOWを読み出してサンプリングしています。

Arduino の libraries フォルダ以下にインストールしてください。

$ cd ~/Arduino/libraries
$ git clone https://github.com/baggio63446333/Spresense-IRremote

オリジナルの examples に入っているサンプルは一通り動作します。それ以外に SPRESENSE 固有のサンプルを追加しています。

リモコン送受信の同時動作サンプル

SPRESENSE の Arduino マルチコア環境を利用して、IR 送信機、IR 受信機をそれぞれ専用のコアに割り当てて動かします。送信機も受信機もそれなりに高速な I/O 処理をしなければならないのですが、一つのボードで両方を同時に動作させています。IR を使って CPU 間通信を実現しているといえなくもない。

Arduinoマルチコア環境については、公式サイト を参考にしてください。

Spresense-IRremote/examples/SpresenseIRcommunicationTest

Core スケッチ 用途
MainCore Main.ino SubCore1,2 起動
SubCore1 IRrecvDumpV2.ino IR 受信動作
SubCore2 IRsendDemo.ino IR 送信動作

動作コアの選択を変えながら各スケッチをビルドして書き込みます。Main.inoスケッチを開いてMainCoreを選択してビルドして書き込み、IRrecvDumpV2.inoスケッチを開いてSubCore 1を選択してビルドして書き込み、IRsendDemo.inoスケッチを開いてSubCore 2を選択してビルドして書き込みます。

SubCore2 から赤外線を送信して、SubCore1 で受信してシリアルモニタに表示しています。

(Timing の時間をみると規定よりも少し短かめですが意図した値が通信できているのでこれでヨシとします)

リモコン対応オーディオプレーヤーサンプル

前述した通り IR 制御にはそれなりに高速な I/O 処理が必要なので、特にオーディオのようなリアルタイム処理が必要とされるアプリケーションと組み合わせて動作させることは難しいのですが、リモコン受信部を専用のサブコアに割り当てて動かすことで、簡単にリモコン制御に対応することができます。

Spresense-IRremote/examples/SpresenseIRaudioPlayer
オーディオプレーヤースケッチは公式の Audio チュートリアル~プレイリスト再生 をベースにしました。

Core スケッチ 用途
MainCore player_playlist_remocon.ino オーディオプレーヤー
SubCore1 IRrecvSub.ino IR 受信動作

動作コアの選択を変えながら各スケッチをビルドして書き込みます。player_playlist_remocon.inoスケッチを開いてMainCoreを選択してビルドして書き込み、IRrecvSub.inoスケッチを開いてSubCore 1を選択してビルドして書き込みます。

SubCoreのスケッチは単純にレシーバーの値をMainCoreへ送信するだけです。

IRrecvSub.ino
#include <IRremote.h>
#include <MP.h>
#include "common.h"

int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {
  MP.begin();
  irrecv.enableIRIn();
}

void loop() {
  if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX);
    MP.Send(MSGID_IR, results.value); // Send IR value to MainCore
    irrecv.resume();
  }
  delay(100);
}

MainCoreスケッチでは、setup()の中でSubCoreを起動してポーリングモードでの受信設定を行います。

player_playlist_remocon.ino
  /* Launch subcore */
  MP.begin(SUBCORE_IR);
  /* receive with non-blocking */
  MP.RecvTimeout(MP_RECV_POLLING);

loop()の中でSubCoreから送られてきた IR 値によってプレーヤーの処理を変更しています。

player_playlist_remocon.ino
  ret = MP.Recv(&msgid, &irvalue, SUBCORE_IR);
  if (ret == MSGID_IR) {
    switch (irvalue) {
    case 0x00ff38c7: action = (s_state == Stopped) ? MENU_PLAY : MENU_STOP; break;
    case 0x00ff18e7: action = MENU_VOLUP; break;
    case 0x00ff4ab5: action = MENU_VOLDOWN; break;
    case 0x00ff5aa5: action = MENU_NEXT; break;
    case 0x00ff10ef: action = MENU_BACK; break;
    case 0x00ffb04f: action = MENU_REPEAT; break;
    case 0x00ff6897: action = MENU_RANDOM; break;
    default: break;
    }
  }

リモコンで選曲しているログです。

まとめ

既に作成済みのメインアプリケーションに対して、最小限の変更で簡単にリモコン機能を追加することができます。誰かの参考になればうれしいです~ written by baggio