はじめに
こんにちは、こうへいです。同居人と録画してあるイントロクイズのテレビ番組を見ながらクイズ対決することがあるのですが、リモコンを先に取って映像を止めた方が解答権を得られるという方法にしたことでリモコン争奪戦が勃発したため、けが人が出る前に自動化することにしました。
要求機能
早押しボタンに要求される機能を以下のように定義します。
- 2人で使用できる。
- それぞれが片手で持てるリモコンサイズのボタンを所持する(有線で可)。
- 1番早くボタンを押した人が分かる。
- ボタンが押された瞬間に効果音が鳴り、テレビ(赤外線信号方式)の映像を止める。
- リセットボタンが押された瞬間に効果音が鳴ると同時に状態がリセットされ、テレビ(赤外線信号方式)の映像が再生される。
- 起動時に音が鳴る。
設計
方針
メインモジュールとリモコンモジュールの2つに分け、開発を進めます。今回、プレイヤー1を赤色、プレイヤー2を緑色と色分けしたいと思います。
- ・メインモジュール
- 早押し結果表示用のLEDランプ×2、リセットボタン、効果音用のブザー、テレビ映像操作用の赤外線LEDが取り付けられたボックス。マイコンを使用してそれぞれを制御する。リモコンモジュールを2つ接続できる。
- ・リモコンモジュール
- ボタンのみがついている片手で持てるサイズのボックス。有線のケーブルでメインモジュールと接続できる。
ソフト設計
要求
シーケンスプログラムだとボタンを押すタイミングによってどちらかのプレイヤーが有利になってしまうため、マイコンの外部割り込み機能を使って確実に早押しボタンを実現します。そのため、2つのピンで割り込み処理が可能なマイコンが必要となります。また、ブザーを使った音声出力、赤外線LEDを使ったテレビ映像操作にPWM出力ピンが2つ必要です。さらに、事前にテレビの赤外線リモコンから出力される、再生と停止の信号を分析、保存しておく必要があります。
上記仕様を満たすマイコンとして、Arduino Unoを採用します。今回はArduino Unoはそのまま使用せず、マイコンチップATmega328P-PUとその周辺回路を基板に実装して最小構成で使います。
処理の流れ
プログラムは割り込み処理を使った状態遷移で書きたいと思います。以下に状態遷移図を載せます。
プログラム
以下に作成したArduinoのコードを載せます。接続するピンの情報は後述の回路設計を参照ください。また、リセットボタンをプルダウンで接続するはずが、誤ってプルアップとしてしまっため、立ち下がりエッジを検出するようにしています(64行目)。
#define IR_SEND_PIN 6
#include <IRremote.hpp>
#define WAIT_BUTTON 0
#define PUSHED_RED 1
#define PUSHED_GREEN 2
#define WAIT_RESET 3
#define PUSHED_RESET 4
// 再生ボタン
// Protocol=NEC Address=0xBC45 Command=0x13 Raw-Data=0xEC13BC45 32 bits LSB first
// 一時停止ボタン
// Protocol=NEC Address=0xBC45 Command=0x17 Raw-Data=0xE817BC45 32 bits LSB first
uint16_t address = 0xBC45;
uint16_t play_command = 0x13;
uint16_t stop_command = 0x17;
const int button_red_pin = 2;
const int button_green_pin = 3;
const int speaker_pin = 5;
const int led_red_pin = 9;
const int led_green_pin = 10;
const int reset_switch_pin = 11;
volatile int state = WAIT_BUTTON;
bool pre_reset = false;
void setup() {
IrSender.begin();
pinMode(speaker_pin, OUTPUT);
pinMode(led_red_pin, OUTPUT);
pinMode(led_green_pin, OUTPUT);
pinMode(reset_switch_pin, INPUT);
// ボタン監視
attachInterrupt(digitalPinToInterrupt(button_red_pin), pushButtonRed, RISING);
attachInterrupt(digitalPinToInterrupt(button_green_pin), pushButtonGreen, RISING);
// 起動音
BeepStartUp();
}
void loop() {
bool reset = digitalRead(reset_switch_pin);
// 状態遷移
switch(state){
case WAIT_BUTTON:
// ボタン押下待機
break;
case PUSHED_RED:
// 赤ボタン押下直後
IrSender.sendNEC(address, stop_command, 0);
digitalWrite(led_red_pin, HIGH);
BeepPushed();
state = WAIT_RESET;
break;
case PUSHED_GREEN:
// 緑ボタン押下直後
IrSender.sendNEC(address, stop_command, 0);
digitalWrite(led_green_pin, HIGH);
BeepPushed();
state = WAIT_RESET;
break;
case WAIT_RESET:
// リセットボタン待機
if(pre_reset == true && reset == false){ // 立ち下がりエッジの検出
state = PUSHED_RESET;
}
pre_reset = reset;
break;
case PUSHED_RESET:
// リセットボタン押下直後
IrSender.sendNEC(address, play_command, 0);
digitalWrite(led_red_pin, LOW);
digitalWrite(led_green_pin, LOW);
BeepReset();
state = WAIT_BUTTON;
break;
}
}
void pushButtonRed(){
if(state == WAIT_BUTTON){
state = PUSHED_RED;
}
}
void pushButtonGreen(){
if(state == WAIT_BUTTON){
state = PUSHED_GREEN;
}
}
void BeepPushed(){
tone(speaker_pin, 330*4, 150); // ミ
delay(150);
tone(speaker_pin, 262*4, 400); // ド
delay(400);
}
void BeepReset(){
tone(speaker_pin, 294*4, 150); // レ
delay(150);
}
void BeepStartUp(){
tone(speaker_pin, 262*4, 100); // ド
delay(100);
tone(speaker_pin, 294*4, 100); // レ
delay(100);
tone(speaker_pin, 330*4, 100); // ミ
delay(100);
tone(speaker_pin, 349*4, 100); // ファ
delay(100);
}
マイコンチップへの書き込み
以下のサイトの手順1~7に従ってブートローダーの書き込みとプログラムの書き込みを行います。
回路設計
パーツリスト
LEDランプ、赤外線LEDはそれぞれブラケットを用いてメインモジュールに取り付けることを考えて選びました。ブザーは固定穴付きのものを、リセットボタンはブラケット付きのものを選びました。リモコンボタンはコンパクトにするためにタクトスイッチを使用し、直接押すことにします。メインモジュール内の各部品は全てXHコネクタで1つの基板に接続する予定です。メインモジュールとリモコンモジュールの接続にはDCプラグを使用します(通信線の規格ではないですが、余っていたので)。電源供給のコネクタはUSB type-Aを考えています。
カテゴリ | パーツ名 | 単価 | 個数 | 価格 |
---|---|---|---|---|
LEDランプ | ブラケット入りLED 5mm 赤色 | ¥100 | 1 | ¥100 |
ブラケット入りLED 5mm 緑色 | ¥100 | 1 | ¥100 | |
カーボン抵抗 680Ω 100個 | ¥1 | 2 | ¥2 | |
ブザー | 圧電スピーカー | ¥110 | 1 | ¥110 |
リモコンボタン | タクトスイッチ 赤色 | ¥10 | 1 | ¥10 |
タクトスイッチ 緑色 | ¥15 | 1 | ¥15 | |
カーボン抵抗 10kΩ 100個 | ¥1 | 2 | ¥2 | |
リセットボタン | パネル用押しボタン 白 | ¥120 | 1 | ¥120 |
カーボン抵抗 10kΩ 100個 | ¥1 | 1 | ¥1 | |
赤外線LED | 赤外線LED 5mm 850nm 10個 | ¥15 | 1 | ¥15 |
LED用ブラケット 5mm 10個 | ¥40 | 1 | ¥40 | |
トランジスタ PN2222 | ¥14 | 1 | ¥14 | |
カーボン抵抗 68Ω 100個 | ¥1 | 1 | ¥1 | |
カーボン抵抗 6.8kΩ 100個 | ¥1 | 1 | ¥1 | |
赤外線リモコン受信モジュール 2個 | ¥50 | 1 | ¥50 | |
電源 | USB type-A−type-Aケーブル 0.5m | ¥500 | 1 | ¥500 |
USB type-Aコネクタ 基板実装用 | ¥50 | 1 | ¥50 | |
マイコン | ATmega328P-PU | ¥400 | 1 | ¥400 |
ICソケット 28P | ¥70 | 1 | ¥70 | |
水晶発振子 16MHz | ¥30 | 1 | ¥30 | |
セラミックコンデンサ 22pF 10個 | ¥10 | 2 | ¥20 | |
カーボン抵抗 10kΩ 100個 | ¥1 | 1 | ¥1 | |
通信 | DCプラグ−DCプラグケーブル 50cm 2個 | ¥349 | 2 | ¥698 |
DCジャック 15個 | ¥34 | 4 | ¥136 | |
その他 | XHコネクタ ハウジング 2P | ¥5 | 7 | ¥35 |
XHコネクタ ポスト 2P | ¥10 | 7 | ¥70 | |
XHコネクタ ハウジング用コンタクト 100個 | ¥2 | 14 | ¥28 | |
ユニバーサル基板 両面スルーホール 20枚 | ¥42 | 1 | ¥42 | |
合計 | ¥2,661 |
回路図
以下にKiCADで設計した回路図を載せます。各回路の設計についても少し説明します。
LED点灯回路
$I_F=5[mA]$でLEDを点灯させることを考えると、$I_F-V_F$グラフから順方向電圧は$V_F=1.9[V]$程度になるため、抵抗$R$は、
$$R=\frac{E-V_F}{I_F}=\frac{5.0-1.9}{5\times10^{-3}}=620[\Omega]$$
と計算できます。E6系列で抵抗を探すと$680[\Omega]$があるため、こちらを採用して再計算すると、
$$I_F=\frac{E-V_F}{R}=\frac{5.0-1.9}{680}=4.5[mA]$$
となります。当初の設計よりは若干小さい電流ですが特に問題ありません。
スイッチ回路
プルダウン抵抗を慣例的に$R=10[k\Omega]$とします。リセットボタンのみ誤ってプルアップとしてしまいました。
赤外線信号送信回路
赤外線LEDは可視光LEDとは異なり、大きな電流($I_F=40〜100[mA]$程度)を必要とします。ArduinoのI/Oピンで扱える最大電流は約$40[mA]$とギリギリのため、トランジスタを使って増幅回路を作ります。
電流増幅率$h_{FE}=100$のトランジスタを使って$I_C=50[mA]$を流したい場合、ベース電流$I_B$は、
$$I_B=\frac{I_C}{h_{FE}}=\frac{50}{100}=0.5[mA]$$
以上としたいです。このとき、$I_C-V_{BE}$グラフからエミッタベース間電圧は$V_{BE}=0.75[V]$となるため、ベース抵抗$R_B$は、
$$R_B=\frac{E-V_{BE}}{I_B}=\frac{5.0-0.75}{0.5\times10^{-3}}=8.5[k\Omega]$$
となります。E6系列で抵抗を探すと$6.8[k\Omega]$があるため、こちらを採用して再計算すると、
$$I_B=\frac{E-V_{BE}}{R_B}=\frac{5.0-0.75}{6.8\times10^3}=0.625[mA]$$
となります。つまり、$I_C\leq62.5[mA]$の範囲で増幅可能です。当初の設計よりは若干大きい電流ですが大きい分には特に問題ありません。
つづいて、赤外線LED点灯回路を考えます。$I_F=50[mA]$でLEDを点灯させることを考えると、$I_F-V_F$グラフから順方向電圧は$V_F=1.3[V]$程度になるため、抵抗$R$は、
$$R=\frac{E-V_F-V_{CE}}{I_F}=\frac{5.0-1.3-0.05}{50\times10^{-3}}=73[\Omega]$$
と計算できます(コレクタエミッタ間電圧はデータシートから$V_{CE}=0.05[V]$とします。)。E6系列で抵抗を探すと$68[\Omega]$があるため、こちらを採用して再計算すると、
$$I_F=\frac{E-V_F-V_{CE}}{R}=\frac{5.0-1.3-0.05}{68}=53.7[mA]$$
となります。当初の設計よりは若干大きい電流ですが特に問題ありません。
ATmega328P-PUのピン配置
ATmega328P-PUのI/Oピン接続は以下のようにしました。
- 早押しボタン ー> D2, D3(INT)
- ブザー ー> D5(PWM)
- 赤外線LED ー> D6(PWM)
- LEDランプ ー> D9, D10
- リセットボタン ー> D11
Arduino – ATmega168/328P-Arduino Pin Mappingより引用
配線図
以下にKiCADで設計した配線図を載せます。
赤外線信号の解析
下記のサイトに従ってIRremoteライブラリをインストールし、ReceiveDump.inoを実行します。赤外線リモコンの再生ボタン、停止ボタンはそれぞれ以下のように表示されました。
- ・再生ボタン
Protocol=NEC Address=0xBC45 Command=0x13 Raw-Data=0xEC13BC45 32 bits LSB first
- ・停止ボタン
Protocol=NEC Address=0xBC45 Command=0x17 Raw-Data=0xE817BC45 32 bits LSB first
機械設計
筐体はすべて3Dプリンタで作ります。
メインモジュール
以下、Autodesk Fusionで設計したメインモジュールです。机の上において使う想定で、安定するように大きめに作っています。サイズは200×150×60[mm]、重量は221[g]です(部品除く)。
リモコンモジュール
以下、Autodesk Fusionで設計したリモコンモジュールです。片手で持ちやすいサイズ感にしています。また、2パーツに分かれており、分かりやすいように色付けしています。サイズは100×30×17[mm]で、重量は16[g]です(部品除く)。
パーツリスト
以下、筐体の制作に必要なパーツリストです。フィラメントはANYCUBIC PLAフィラメント 深黒 1kgを利用しており、1kgあたり2,300円で計算しています。
カテゴリ | パーツ名 | 単価 | 個数 | 価格 |
---|---|---|---|---|
メインモジュール | PLAフィラメント 221[g] | ¥508 | 1 | ¥508 |
六角穴付ボルト M3×6mm | ¥10 | 2 | ¥20 | |
六角穴付ボルト M2×4mm | ¥4 | 4 | ¥16 | |
リモコンモジュール | PLAフィラメント 16[g] | ¥37 | 2 | ¥74 |
六角穴付ボルト M3×6mm | ¥10 | 8 | ¥80 | |
合計 | ¥698 |
完成品
以下、制作物の完成品です。今回かかった費用は、回路部品2,661円+機械部品698円で計3,359円です。
こちら動作の様子です。ボタンを押すことでテレビの再生/停止をコントロールできていることが確認できます。
コメント