公開日:2020/11/14
title.png

13th Season


#13-6 赤外線リモコンを作ろう

 いつもPC周りに使っているHDMIマトリクスセレクタの、リモコンのとあるボタンが反応しなくなってきました。
 筆者が使っているHDMIマトリクスセレクタは、4入力から2つの出力を選択して出力することが出来るHDMIのセレクタです。メインPCからの2本の出力、サブPCからの出力1本、HDDレコーダからの出力1本の中から、任意の2本を出力することが出来ます(下図)。

a136_irrc.png

 リモコンのボタンが8個あって、モニタ(A)に出力するHDMIを入力(1)〜(4)の中より選択するボタンが4個と、同じくモニタ(B)の選択ボタンが4個です。いくつか反応しにくいボタンはあるのだけど、特に、モニタ(B)の入力(4)選択ボタンが反応しなくなってしまいました。
 モニタ(B)には普段PCの画面を表示しているのだけど、時々TVを見たり録画した番組を見る時に切り替えようとすると、ボタンが反応しなくて格闘してしまいます。ぎゅっと力を入れて押し続けていると、たまに反応する時があるので、「やれやれ」って感じです。

 HDMIセレクタの本体にもボタンはあるのですが、1個しかボタンが無くて、押す度に(1) → (2) → (3) → (4) → (1) → ……と、トグルするタイプなため、(1)から(4)に切り替えるには、3回ボタンを押す必要があって面倒だし、何より、関係のない(2)と(3)を一旦選択してしまうため、PC側マルチモニタのプラグ&プレイが一々反応して厄介なのです。

 兎にも角にも、筆者が遭遇する「リモコン故障(不具合)」の現象は大抵、このような「ボタンが反応しない系」なので、もう機構上、どうしようもないのでしょうね……?
 有名どころのTVやHDDレコーダなどのリモコンは、互換品がサードパーティから安く販売されていたりするので、何とか対処可能なこともあるのですが、マイナーなHDMIセレクタのリモコンなんて売っていないし、辛うじてメーカに交換用のリモコン在庫があったとしても、バカ高い修理費を取られるのが落ちだろうし……。

 なので今回は、Arduino、特に328duinoを使って赤外線リモコンを作って見たいと思います。

基本設計

 今回のアプリケーションは、「赤外線リモコン化328duino」です。170穴ミニブレッドボードに載せてこその328duinoなので、赤外線リモコンに必要な回路は全て1個の170穴ミニブレッドボード(下図)に押し込みたいという野望を持って基本設計を進めます。

a136_bb170tp.jpg

 電源以外はこの上に全部載せます。

U/I(入力:タクトスイッチ)

 いつもはシリアルモニタからコマンドを送って動作するタイプの手抜きU/I(ユーザインターフェイス)なのですが、今回のアプリケーションはPCが起動していなくても操作できるように、シリアルモニタを必要としないシステムにしなくてはなりません。

 そこで、入力インターフェイスとしてタクトスイッチと、出力インターフェイスとしてLED表示を持たせようと思います。いずれも、ミニブレッドボード上に電流制限抵抗を配置するだけのスペースが無い為、ATmega328の内蔵プルアップ抵抗を使います。
 また、この使い方ではスイッチの一方の端子をGNDに落とす必要があるのですが、GNDまでの配線もケチってI/OポートにGNDの代わりをさせてしまいます(下図)。

a136_switch.png

 上の図では、328duinoの端子DxとDyをブリッジする様にタクトスイッチが繋がっています。この時、以下の様に使うと、Dx端子からタクトスイッチの状態を読み取ることが出来るという訳です。

  // setup()関数内初期設定
  digitalWrite (DY, LOW);
  pinMode (DY, OUTPUT);
  pinMode (DX, INPUT_PULLUP);

  // スイッチ状態読み出し
  if (digitalRead (DX)==HIGH)
  {
    // スイッチはリリース状態
  }
  else
  {
    // スイッチが押されている
  }


 狭いミニブレッドボード上には、あまり多くの部品を配置できないので、こうやって部品点数を削減して使います。このような感じで、何とか2個のタクトスイッチを配置したいと思います。

 2ボタンで何種類の赤外線リモコン信号を送信できるか?と言う事ですが、普通に考えると2通りの信号しか送信できないわけです。2ボタンの同時押しで3つ目の信号を送るというインターフェイスもありですが、「同時に押した」状態の検出は意外にしんどいです。
 そこで、ボタンの長押しと言う状態を検出しておいて、動作モードを設けることにしました。

 以降、2つのスイッチをSW1,SW2と呼称します。
 SW1の長押しでモードの選択状態に入ったら、次のSW1短押しでモード1、SW2短押しならモード2に遷移することにします。同様に、SW2の長押しでもモード選択状態に入ることが出来るので、次のSW1短押しでモード3、SW2短押しならモード4に遷移することにします。
 各モードでSW1とSW2による2種類の信号を送信できるので、2ボタン×4モード=8種類の信号送信が可能となります。

シーケンス→ ↓別モードに遷移するまで繰り返し↓ LED表示
SW1長押し SW1短押し モード1 SW1短押し 信号1送信 青色点灯
SW2短押し 信号2送信
SW2短押し モード2 SW1短押し 信号3送信 赤色点灯
SW2短押し 信号4送信
SW2長押し SW1短押し モード3 SW1短押し 信号5送信 紫色点灯
SW2短押し 信号6送信
SW2短押し モード4 SW1短押し 信号7送信 緑色点灯
SW2短押し 信号8送信

※最初の長押しで「モード選択状態」に入ると、LEDが消灯します。続く短押しでモードが確定すると、モードに従った色でLEDが点灯します。

 もっと複雑なシーケンスを使えばモードを増やすことも可能ですが、あまり増やしても操作を覚えられないので、この辺り(4モード、8信号)が落としどころでしょう。
 2ボタンのU/Iでも、プログラム次第で意外と応用が利くでしょう?

 ミニブレッドボードへの実装に拘らないなら、4×4キーパッドなどを接続してボタン数を稼ぐことは可能なので、その辺りの拡張に関しては各自対応してください。

U/I(出力:LED表示)

 ボタンのシーケンスでモードを遷移するので、今、どのモードに入っているか?モード遷移シーケンスは正しく受け付けられたか?を知るためには、何らかの表示系U/Iが必要となります。狭いミニブレッドボード上に配置できる表示系U/Iは、単体のLEDくらいな物でしょう。
 LEDとて、通常は以下図(使い回し)の様に電流値を調整するための電流制限抵抗が必要となるため、限られたミニブレッドボードのスペースを圧迫します。

ar2_lschem.png

 ここにも、前節のスイッチで使用した、ATmega328の内蔵プルアップ抵抗を使って部品点数を削減します(下図)。

a136_led.png

 足を短く描いた方のDy側をカソードだとすると、プログラムは以下の様になります。

  // setup()関数内初期設定
  digitalWrite (DY, LOW);
  pinMode (DY, OUTPUT);
  pinMode (DX, INPUT);  // 消灯

  // LED点滅
  while (true)
  {
    pinMode (DX, INPUT_PULLUP);  // 点灯
    delay (500);
    pinMode (DX, INPUT);  // 消灯
    delay (500);
  }


※絶対やってはいけないこと: DXを出力(OUTPUT)設定して、HIGHを出力してはいけません。ATmega328およびLEDの絶対最大定格を超える電流が流れて、デバイスにダメージを与えてしまいます。

 ATmega328のデータシートで内蔵プルアップ抵抗の抵抗値を確認しておくと、Rpu=20〜50[kΩ]となっています。LEDのVf=2V程度として、電源電圧Vcc=5Vとすると、プルアップ抵抗RpuからLEDを通ってGNDに流れる電流は、If=(Vcc−Vf)/Rpu≒60〜150[μA]と、いつもLEDを光らせるのに流している電流値(数mA)に比べてだいぶ小さいです。
 当然、LEDは暗くなりますが、光っているのが視認できれば良しとしましょう。暗い方が、返って消費電流を抑えられてGoodです。

 アノード側Dx端子に隣接する別端子もINPUT_PULLUPに設定して、LEDのアノードにショートすると電流値が倍になるので、もう少し明るく光らせることは可能です。今回は、LEDのアノードに3本、カソードに1本の計4本のI/Oポートを割り当てているのですが、ショートして明るく光らせるのが目的ではなく、ここにRGB LED(下図)を接続して、光る色で現在のモードを表示しようと思います。

ar46_rgbled.jpg

電源の確保

 PCが起動していなくても、またPCに接続していなくても操作できるように、PCからの給電に頼らないシステムにしたいと思います。と言うことは、乾電池(またはeneloopなど充電池)からの給電が基本です。
 以下図の様な電池ボックスを使って、電池2〜4本(直列)で直接給電します。

a136_cellbox.jpg

 単三型電池4本を直列に収納可能な電池ボックスですが、途中の電極にピンソケットを半田付けしてあって、2本〜4本直列の電圧を取り出せるように改造してあります。アルカリ乾電池(1.5V)とeneloop充電池(1.2V)で、取り出す電圧が異なるのでまとめておきます。

電池種 直列 電圧 備考
アルカリ乾電池(1.5V) 2本 3V fMAX≒10MHz
3本 4.5V fMAX≒17.5MHz
4本 6V 絶対最大定格違反。降圧が必要。
eneloop充電池(1.2V) 2本 2.4V fMAX≒6.4MHz
3本 3.6V fMAX≒13MHz
4本 4.8V fMAX≒19MHz

※ATmega328の定格最大動作周波数スペックfMAXを記しました。

 ブレッドボードに配置する部品点数を削減するため、今回は328duino(第二形態)を使い、内蔵RC発振器で動作させます。この場合、設定可能な周波数fcpu=8〜15[MHz]程度なのですが、「#10-5 328duinoをクロックアップ」の実験にて、fmax=8MHzであれば、2Vを下回っても動作させることが出来ることが判っています(下図、参考)。

a105_vccmin.png

 ただし、今回のアプリケーションでは、328duino以外に赤外線(IR)LEDを発光させる必要があります。電源電圧を下げ過ぎてしまうと、IR LEDの発光強度が足りず、赤外線リモコン信号が遠くまで届かないケースも想定されるので、そこは実験して見て、電源電圧で調整してみようと思います。

 最小構成(と言うより、ATmega328の単体チップその物と言って良い)の328duinoとは言えど、数10mAは消費するはずなので、実用化の際にはスイッチが必要になります。実用化の折には、電源スイッチの付いたモバイルバッテリや、スイッチ付き電池ボックスなどが解の1つになると思われます。

赤外線発光

 赤外線リモコンの原理については「#3-9 キッチンタイマを作る」にて解説済みです。
 タイマを使って38kHzデューティ1/3のPWMを出力するので、内蔵RC発振器で動作させる328duino @8MHzでも十分なパフォーマンスがあるはずです。328duinoクラスライブラリを使えば、最悪クロックアップして処理能力を向上することが出来ますので、保険としておきます。

 赤外線の発光素子としては、反射型赤外線センサTCRT5000L(下図)のエミッタ(IR LED)を使います。

ar88_tcrt.jpg

※TCRT5000Lは、プラスティックのケースとエミッタ・ディテクタは接着されていないので、スポっと抜けます。抜いてしまうと、端子配置がわからなくなってしまうので、抜く前に油性マジックなどで印を付けておきましょう。

 TCRT5000Lのエミッタは、If=60mA(ABS.MAX)がスペックです。ただし、今回はミニブレッドボード上にNPNトランジスタを配置するスペースがありませんので、328duinoのI/Oポートでドライブする必要があります。ATmega328のI/Oポートに流せる電流値はIo=40mA(ABS.MAX)なので、この値を超えないよう、また100%程度のマージンを取ってIf=20mAを狙おうと思います。
 TCRT5000Lのスペックより、Vf=1.25V(TYP)ですので、328duinoの電源電圧Vcc=5Vの場合を想定して、電流制限抵抗Rf=(Vcc−Vf)/If≒187.5Ωとなります。手持ち抵抗値の組み合わせで、187.5Ωとの誤差が小さく、かつ、小面積で実装可能な組み合わせとして、220//1,000=180.328[Ω]が見つかりました。

※合成抵抗の求め方は後ほど解説します。

 今回は電池駆動なので、電源電圧が5Vよりも低い場合に上の抵抗値を使ってしまうと、赤外線の発光強度が足りずにリモコンの到達距離が短くなってしまう場合があります。実験で確かめてから微調整しましょう。

 尚、普通に市販されているIR LED(波長940nm)を使っても良いです(と言うか、そっちが正道)。筆者の手元にはTCRT5000Lしか無かったので、これを使うだけですから。

赤外線リモコン信号のキャプチャ

 送信する赤外線リモコン信号はNEC方式に留まらず、各社オリジナルの信号を使用している場合にも対応可能な様に、赤外線リモコン信号を受信器🐍でキャプチャ(取り込み)して、今回のプログラム内に記述することにします。

 任意のリモコンから赤外線リモコン信号をキャプチャするには、「#4-3 赤外線リモコン(送信)」の記事をご参照ください。「#4-3」記事中の回路やプログラムを転用することが可能です。

 従って、今回のアプリケーションでは赤外線リモコン信号のキャプチャに関して、プログラム、回路共対応しません。

プログラム(スケッチ)の書き込み

 前節の様に、必要な赤外線リモコン信号はUNO等の機体と赤外線受信器を使ってキャプチャした後、今回のプログラムに組み込み、328duinoへコンパイル&書き込みする必要があります。
 従って、PCからのプログラム書き込みには対応しておく必要があります。

 FTDI等のUSB⇔UART変換モジュールを使って、シリアルI/F(DTR,TXO,RXI)経由で書き込める様に、書き込み回路を作り込んでおきたいと思います。念のため、SPI端子も空けておくと、UNO等を「Arduino as ISP」として使っての書き込みも可能となりますので、保険をかけておきます。

 また、書き込みの際には、FTDI(シリアル)あるいはUNO(as ISP)とのI/Oレベルを合わせるため、Vcc=5Vを給電するようにしてください。

おまけ

 手持ちの抵抗を組み合わせて、任意の抵抗値を作り出す時に使えるOlive+のスクリプトをリンクしておきます。

  合成抵抗スクリプト:resistance.olv

 スクリプト(.olv)の実行には拙作Olive+が必要ですので、ダウンロードコーナなどから入手してインストールしておいてください。
 使用する前に、最初にやっておくべきことが有ります。上のスクリプトの先頭付近に以下の様な記述箇所があります。

  varray ra:1=10,100,220,330,1000,2000,5100,10000,100000,1000000;

 ここに、皆様が所有している抵抗値をすべてカンマ区切りで記載してください(末尾はセミコロン(;)です)。所有していない抵抗値は削除してください。resistance.olvは、ここ(配列変数ra)に定義された抵抗値を組み合わせて、合成抵抗を作ろうとします。
 Olive+でresistance.olvを実行すると、以下の様なウインドウが開きます。

a136_regscr.png

 求めたい抵抗値rt(目標値)を適切に設定してください。許容誤差trのパラメータは変更する必要はありません。先ほどの187.5Ωの組み合わせを求める場合、「rt=187.5;」と数値を書き換えてから、OKボタンを押してください。

 resistance.olvは、以下の組み合わせで配列変数raに記載されたすべての抵抗を試し、目標値に一番近くなる組み合わせをレポートしてきます。

 結果は、resistance.olvと同じディレクトリに作成される「resistance_out.txt」と言うファイルに出力されますので、この中から選択してください(以下、出力例)。

  187.5: (0.003,3) 186.992 = 1000//(10 + 220)
  187.5: (0.004,3) 186.711 = 330//(100 + 330)
  187.5: (0.006,3) 186.389 = 220//(220 + 1000)
  187.5: (0.006,3) 188.571 = 330//(220 + 220)
  187.5: (0.007,3) 188.774 = 220//(330 + 1000)
  187.5: (0.015,3) 190.328 = 10 + (220//1000)
  187.5: (0.018,3) 190.784 = 220//2000//5100
  187.5: (0.018,3) 190.909 = 100 + (100//1000)
  187.5: (0.022,3) 183.333 = 220//(100 + 1000)
  187.5: (0.026,3) 192.453 = 5100//(100 + 100)
  187.5: (0.030,3) 181.818 = 2000//(100 + 100)
  187.5: (0.037,3) 180.65 = 220//(10 + 1000)
  187.5: (0.037,3) 194.346 = 220//2000//10000
  187.5: (0.038,2) 180.328 = 0 + (220//1000)
  187.5: (0.038,3) 180.295 = 220//1000//1e+06
  187.5: (0.038,3) 180.328 = 220//2000//2000
  187.5: (0.040,3) 180.003 = 220//1000//100000
  187.5: (0.041,3) 195.238 = 100 + (100//2000)
  187.5: (0.046,3) 196.078 = 10000//(100 + 100)

 一番誤差の小さい組み合わせから順に出力されています。左端の数値が目標抵抗値、カッコ内は目標と組み合わせ結果の誤差と、組み合わせる抵抗の数、カッコの右が合成結果の抵抗値、イコール(=)の右が抵抗の組み合わせ方です。

※もし、resistance_out.txtの中身がソートされていないようなら、Olive+のバージョンが古いので、最新バージョンへアップデートしてください。

 187.5Ωに一番近い組み合わせは、一番上の行の「1kΩ//(10Ω+220Ω)=186.992Ω(目標との誤差約0.3%)」だったのですが、並・直列となるこの組み合わせは、ミニブレッドボードに実装するのは無理でした。そこで、赤く着色した、2並列で180.328Ω(目標との誤差約3.8%)となる組み合わせに妥協して選択しました。

 必要な抵抗値次第では、「へぇ、そんな組み合わせがあったんだ?」ってビックリするような結果を返すこともしばしばです。ご活用ください。


 今回はここまで。次回、詳細設計から先、試作機のテストまで実施して、一旦クローズします。


btn_arduino.png

btn_prev.png btn_next.png

home