TVリモコン対応スピーカーアンプ

IOデータのREC-ON(EX-BCTX2)があるので、それにモニターを付ければ寝室の壁掛けTVにちょうど良いと、フィリップスの27インチモニター(273V5LHAB)を買って設定した。当たり前かもしれないが、そこでやっとボリュームをリモコンで操作できないことに気付いた。これは不便。REC-ON付属の赤外線リモコンで操作できるスピーカーアンプを探すも、適切なものを見つけることはできなかった。こういった需要はあまりないのだろうか?

色々調べてみるとArduinoを使ってできそうで、じゃあということで自作してみた。使った部品は以下の通り。

  • UNO R3 ATMEGA328P-16AU CH340G Micro USB Compatible board : eBayで320円ぐらい
  • PAM8403 ミニ 5V デジタル アンプ基板 アナログボリューム付き:アマゾンで120円
  • 28BYJ-48 4フェーズステッピングモータとULN2003ドライバボード:アマゾンで190円
  • 1838赤外線レシーバー(10個セット):アマゾンで140円
  • 2P2T ロッカスイッチ 電源スイッチ Mサイズ 21*15MM(10個セット):アマゾンで185円
  • DIP28 28ピンICソケット(10個セット):アマゾンで165円
  • デュポンコネクター ジャンパー ケーブル メス-メス 1P-1P 2.54mm 20cm:アマゾンで105円
  • ストレート雄ピンヘッダー 2.54mm:アマゾンで150円
  • 3x7cm FR4プロトタイプブレッドボード サイズ30x70mm(5枚セット):アマゾンで150円
  • ALTEC LANSING 2.5″(64mm) 小型 スピーカーユニット 8Ω/5W:Yahooで1470円
  • その他、家に残っていたMDFの板と配線材料

金額的には合計で3000円ぐらいで、スピーカーを除けば1500円ぐらいになる。Arduinoの互換ボードMicroUSBコネクタのものが欲しかったのでeBayで注文したが、こだわらなければアマゾンでも同じぐらいの値段で買える。使っている部品の信頼性が信用できないので、いつでも交換可能なように基本的にはコネクタで接続することとした。直接はんだ付けするのであればICソケット、ピンヘッダーやジャンパーケーブルは不要。

構成としては、Arduinoに赤外線レシーバーを付けて、ボリュームのUp/Downに対応してステッピングモーターでアナログボリュームを回す。電子ボリュームでも良いが、ボリューム付きのアンプとステッピングモーターの組み合わせで310円と安価なので、こっちにした。構成的にはこっちの方が高音質?

ステッピングモータの軸はそのままでは厚すぎてボリュームの軸の溝に入らないので、グラインダーで薄く削った。

出来上がったハードはこんな感じ。Arduinoボードのピン7を赤外線レシーバーに接続し、ピン9,10,11,12をステッピングモータードライバーに接続している。ステッピングモーターとデジタルアンプの電源はArduinoの5V/GNDに直結している。
音質は思っていたよりもまともで、全然問題のないレベル。少なくともモニターのスピーカーから出ていた音に比べれば高音質になった。




Arduinoやアンプを実装してある基板は、スピーカーの中央部に上から落とし込んである。MDFむき出しなので見た目は良くない。そのうち気が向いたら表面に何か貼り付けよう。

赤外線レシーバーは、付属のライブラリではなく、GitHubのものをダウンロードして使用。これを使おうとすると、私の環境では標準で付いてくるIR用のライブラリとコンフリクトが起きてそのままではコンパイルできなかった。色々調べてみたけれどもスマートな解決方法は見つからず、結局標準ライブラリを削除してコンフリクトを解消した。
このライブラリに付いてくるexamplesにIRrecvDumpとIRrecvDumpV2があるので、これらを使って赤外線レシーバーのハードをチェックすると共に、送られてくるコードをチェックしてプログラムに使用する。今回のリモコン設定ではボリュームアップが1168(B010010010000)、ボリュームダウンが3216(B110010010000)となった。10進数の値が関連なさそうに見えるのはエンディアンが逆になっているためで、バイナリで見ると1ビット反転しただけのコードになっている。
ステッピングモーターの駆動については、花夢電科雑多猫さんのサイトを参照させて頂いた。

最終的なコードは以下の通り。
音量の微調整がやり易いように、ボリュームアップ、ボリュームダウンの最初は1ステップだけモーターを回し、リモコンが押され続けていると300ms後から連続してモーターを回し続けるようになっている。

//------------------------------------------------------------------------------
// Include the IRremote library header
//
#include 

//------------------------------------------------------------------------------
// Tell IRremote which Arduino pin is connected to the IR Receiver (TSOP4838)
//
int recvPin = 7;
#define volumeUp 1168
#define volumeDn 3216
IRrecv irrecv(recvPin);

//
// Motor drive definitions
//
int mtPin1 = 9;
int mtPin2 = 10;
int mtPin3 = 11;
int mtPin4 = 12;
int mtInterval = 1500;
int n = 10;
int mtLookUp[10] = {B00000, B01000, B01100, B00100, B00110, B00010, B00011, B00001, B01001, B000000};
unsigned long last = 0;

//+=============================================================================
// Configure the Arduino
//
void  setup ( )
{
//  Serial.begin(9600);   // Status message will be sent to PC at 9600 baud
  pinMode(mtPin1, OUTPUT);
  pinMode(mtPin2, OUTPUT);
  pinMode(mtPin3, OUTPUT);
  pinMode(mtPin4, OUTPUT);
  irrecv.enableIRIn();  // Start the receiver
}

// Motor drive routines

void setOutput(int out)
{
  digitalWrite(mtPin1, bitRead(mtLookUp[out], 0));
  digitalWrite(mtPin2, bitRead(mtLookUp[out], 1));
  digitalWrite(mtPin3, bitRead(mtLookUp[out], 2));
  digitalWrite(mtPin4, bitRead(mtLookUp[out], 3));
}

void anticlockwise()
{
  for(int i = 1; i <= n-1; i++) { setOutput(i); delayMicroseconds(mtInterval); } } void clockwise() { for(int i = n-2; i >= 0; i--)
  {
    setOutput(i);
    delayMicroseconds(mtInterval);
  }
}

//
void  loop ( )
{
  decode_results  results;        // Somewhere to store the results
  int wait;

  if (irrecv.decode(&results)) {  // Grab an IR code
    switch(results.decode_type) {
      case SONY:
        if (millis() > last + 100) {
           wait = 300;
        } else {
           wait = 1;
        }
        switch(results.value) {
          case volumeUp:
            anticlockwise();
//            Serial.println("Up");
            break;
          case volumeDn:
            clockwise();
//            Serial.println("Down");
            break;
          default:
            break;
        }
        delay(wait);
        last = millis();
        break;
      default:
        break;
    }
    irrecv.resume();              // Prepare for the next value
  }
}

コメントはまだありません

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

*

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください