目次
本記事で使用するもの
以下のリンクのスターターキットを購入した場合、上記で述べた使用するものは、PC以外すべて入手することができます。
Arduinoをまだ購入していないという方やArduinoで動かすことができるたくさんのパーツが欲しいという方は、購入を検討してみてください。
赤外線リモコンについて
赤外線リモコンとは、人間には見えない光である赤外線を発光させることにより、信号を送ることができるリモコンで、テレビ、照明、エアコンなどの操作にも利用されています。
上記で紹介したスターターキットにも、以下のような赤外線リモコンが入っており、ボタンを押すことにより赤外線の信号を送ることができます。
赤外線リモコンは、押されたボタンなどに応じて、赤外線の発光パターンを変えて、信号を送ります。
しかし、Arduinoには赤外線を受信する機能はないため、信号を直接受信することはできません。
そこで、赤外線の信号を電気の信号に変換する役割を行うのが、以下の写真のような赤外線受信モジュールです。
この赤外線モジュールは赤外線の信号を電気の信号に変換することができるので、Arduinoに接続すれば、間接的に赤外線の信号を読み取ることができます。
信号を読み取る方法としてライブラリを使用して読み取る方法がありますが、今回の記事では、後々、自分でカスタマイズが出来るように、一からスケッチを作成して、信号を読み取りたいと思います。
赤外線リモコンの信号を受信する
赤外線リモコンからの信号に応じて、何かの処理を行うためには、まず、どのような信号が送られているのかを解析する必要があります。
そこで、まず初めに、以下のように、受信モジュールの信号を読み取り、その情報をシリアルモニタに表示させるということを行いたいと思います。
空のスケッチを書き込む
回路を作成する前に、空のスケッチ(「ファイル」タブの「New Sketch」をクリックしたときに出てくる何の処理も書かれていないスケッチ)を書き込みます。
そうすることにより、前に書き込んだスケッチが、新しく作成しようとしている回路に影響を及ぼすことを防げます。
新しい回路を作成するときは、いつもこの作業をやっておくと、安全に回路を作成することができます。
回路を作成する
赤外線モジュールが以下の写真のように、基盤と一体になっているようなものの場合は、以下のような回路を作成します。
赤外線モジュールが以下の写真のように、基盤と一体になっていないようなものの場合は、以下のような回路を作成します。
ピンの並びについては、赤外線モジュールの種類によって異なる場合があるかもしれないので、可能であれば、データシートを確認して回路を作成してください。
スケッチを書き込む
回路の作成が終わりましたら、以下のようなスケッチを書き込みます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
/* Arduinoで遊ぼう(24,赤外線リモコン); 2023/10/21 IR_Receiver_test //スケッチの詳細はこちら↓ https://mecha-norimaki.com/arduino_start_24/ */ //測定した時間を入れる変数 unsigned long now_time,old_time; //パルス信号の長さを入れる配列 unsigned int pulse_data[640]; //ピンの状態を入れる変数 bool val; bool old_val=HIGH; //データの記録の回数をカウントする変数 int pulse_count=-1; //データの受信が終わったらtrueになるフラグ bool receive_flag; void setup(){ //シリアル通信の初期処理 Serial.begin(9600); //2番ピンを入力ピンに設定(デフォルト設定なので省略しても良い) pinMode(2,INPUT); //受信する準備が出来ていることをシリアルモニタで知らせる Serial.println("Ready to receive..."); Serial.println(""); } void loop() { //2番ピンの状態を読み取る val=digitalRead(2); //2番ピンの状態によって処理を分岐させる if(!val){ //2番ピンの状態がHIGHからLOWになっていた場合 if(val!=old_val){ //現在の時間を取得 now_time=micros(); //pulse_countが0以上のときデータの取得処理を行う if(pulse_count>=0){ //パルスの長さを計算し配列に入れる pulse_data[pulse_count]=now_time-old_time; } //pulse_countの値を1増やす pulse_count++; //取得した時間を変数に入れておき、過去の時間として利用する old_time=now_time; } }else{ //2番ピンの状態がLOWからHIGHになっていた場合 if(val!=old_val){ //現在の時間を取得 now_time=micros(); //パルスの長さを計算し配列に入れる pulse_data[pulse_count]=now_time-old_time; //pulse_countの値を1増やす pulse_count++; //取得した時間を変数に入れておき、過去の時間として利用する old_time=now_time; }else if(pulse_count>=0&µs()-old_time>15000){ //2番ピンの状態がHIGHのままで15000[μs]経過した場合、receive_flagをtrueにする receive_flag=true; } } //読み取った2番ピンの状態を変数にいれておき、過去の状態として利用する old_val=val; //receive_flagがtrueの場合、シリアルモニタへの表示処理を行う if(receive_flag){ //パルス信号のデータを表示する Serial.println("Pulse_data"); Serial.print("("); Serial.print(pulse_count); Serial.print(") "); for(int i=0;i<pulse_count;i++){ Serial.print(pulse_data[i]); if(i<pulse_count-1){ Serial.print(","); }else{ Serial.println(""); } } Serial.println(""); //受信する準備が出来ていることをシリアルモニタで知らせる Serial.println("Ready to receive..."); Serial.println(""); //2秒間待機 delay(2000); //フラグやカウントの変数をリセットする receive_flag=false; pulse_count=-1; } } |
スケッチの書き込みが終わりましたら、シリアルモニタを開き、入力部分の右に表示されている設定が「9600baud」、自動スクロールが「ON」になっているのを確認します。
その後、リモコンを赤外線受信モジュールに向けてボタンを押すと、以下のように数値がいくつか表示されていくのが確認できます。
スケッチの解説
このスケッチでは、「micros()」という時間を測定するための関数を利用することにより、信号のパルスの長さを測定し、その情報をまとめるというものになっています。
その手順について、もう少し詳しく説明していきたいと思います。
まず、「micros()」について、の簡単な説明をしたいと思います。
「micros()」はArduinoが動作を始めてから、現在までの時間をマイクロ秒単位で返す関数です。
今回のスケッチでは、信号の「HIGH」と「LOW」が切り替わったタイミングでこの関数を使用し、時間を取得しています。
また、取得したときの時間を「now_time」、一つ前に取得した時間を「old_time」という変数に格納し、それらを引き算することにより、パルスの長さを測定しています。
次に、信号の切替わりを検出する方法について、説明したいと思います。
今回のスケッチでは、ループの初めに、「digitalRead()」を利用して、ピンの状態を「val」という変数に格納しています。
そして、条件分岐の処理の先にある91行目では、「old_val」という変数に「val」の値を格納しており、これらの変数を比較することにより、信号の切替わりを検出しています。
この検出の際に、先程説明した「micros()」を利用したパルスの長さの測定、データの数を記録する変数「pulse_count」を1増やすという処理をおこなっています。
ここで、最初の切替わりでは、パルスの長さのデータを取得しないため、「pulse_count」の初期値は「-1」としています。
また、信号が「HIGH」の状態で、一定の時間(上記の例では15000[μs])切り替わらなかった場合は、受信完了とみなし「receive_flag」という変数を「true」にします。
データの出力は、「receive_flag」が「true」のときのみ、実行され、まとめてきたデータをシリアルモニタに表示させていきます。
表示が終了した際は、フラグとカウントの変数である「receive_flag」と「pulse_count」を初期化するため、もう一度データの取得が終了するまで、表示の処理は実行されません。
ここで、「15000」という数字については調整が必要になる場合があるかもしれません。
例えば、リモコンのボタンを長押しした際に、表示が全く行われず、ボタンを離したとき、いっきに大量の数値が表示される場合は、連続して送られてくる信号が一体化してしまっているので、「15000」という数字を小さくする必要があります。
また、同じボタンを押しているのに、押している長さで、データの数が異なる場合は、信号が一体化している可能性が高いです。
私の持っているリモコンの中では、「PlayStation2」のリモコンがスパンの短いリモコンで、「10000」くらいまで値を小さくしなければ、うまく受信することができませんでした。
逆にデータが途中で、途切れている感じがした場合は、信号の中に長いパルスが含まれているため、「15000」という数字を大きくする必要があります。
ちなみに、私が持っているリモコンでは、「DAIKINのエアコン」が「35000」くらいの長いパルス信号を途中で送っていました。
今回のスケッチは、複数の種類のリモコンに対応することができますが、受信完了とするまでの時間を固定しているため、条件次第では、対応しきれないリモコンが出てきます。
ご自身が利用したいリモコンに合わせて受信完了とする時間の設定をおこなってください。
信号のデータを解析するためのスケッチの説明は以上となります。
赤外線リモコンの信号をエンコードする
先程のスケッチで、ボタンに応じてどのような信号が出力されているのかという解析が行えるようになりました。
よって、特定のボタンが押されたときの信号のデータと、取得した信号のデータを比較するようにすれば、特定のボタンが押されたことを検知することが出来ると考えられます。
しかし、上記のような比較の場合、信号の長さには毎回誤差が生じるため完全な一致はほとんどないという点や、リモコンによっては、かなり大量のデータを比較しないといけないといった問題が生じます。
そこで、先程のスケッチで取得したデータの中で、比較に必要な情報だけを残して、データをエンコード(圧縮)するといったことを行おうと思います。
データの特徴を見る
まずは、先程のスケッチで測定したパルスの長さのデータを見てみます。
よく見てみると、細かい数値の変化はなく、似たような数値が繰り返し使われているのが確認できます。
実は、赤外線リモコンの通信方法にはいくつかフォーマットがあり、大体の赤外線リモコンはそれらのフォーマットに則って信号を送信しています。
以下に日本でよく使われている通信フォーマット2つの信号を大雑把に紹介したいと思います。
NECフォーマット
家製協フォーマット
上記のフォーマット以外にもSONYフォーマットや会社独自のフォーマットも存在しており、どれも異なる特徴を持っていますが、大体のフォーマットで共通している特徴があります。
それは、信号のデータ部分に使用しているパルスの長さは2種類しか存在していないという点です。
データを表すためのパルス信号の長さが2種類しか存在せず、その並び方によってデータを表しているとすれば、それらを2進数で表したとしてもデータの判別を行うことができます。
パルスの長さのデータは「unsigned int型」で表していたので、1つのデータで16bit使用していますが、2進数で表した場合、1bitしか使用しないため、データは1/16に圧縮されることになります。
ここで、データを0か1に分ける閾値は、以下のように、測定したパルスの長さの値を参考にします。
上手く閾値を設定すれば、複数のリモコンに対応することも可能です。
ここで選定した値は、以下で書き込むスケッチで使用しますので、ご自身で選定をして、値をメモしておいてください。
私は色々なリモコンの信号を見て、閾値を「800」としました。
スケッチを書き込む
以下のようなスケッチを書き込みます。
黄色でマークされている部分が先程のスケッチから追加、変更した部分です。
また、168行目の数値「800」の部分は、先程メモをした数値に変更してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
/* Arduinoで遊ぼう(24,赤外線リモコン); 2023/10/21 IR_Receiver_test_2 //スケッチの詳細はこちら↓ https://mecha-norimaki.com/arduino_start_24/ */ //測定した時間を入れる変数 unsigned long now_time,old_time; //パルス信号の長さを入れる配列 unsigned int pulse_data[640]; //ピンの状態を入れる変数 bool val; bool old_val=HIGH; //データの記録の回数をカウントする変数 int pulse_count=-1; //データの受信が終わったらtrueになるフラグ bool receive_flag; //エンコードしたデータの数をカウントする変数 int encode_count; //エンコードしたデータを入れる配列 unsigned int encode_data[40]; void setup(){ //シリアル通信の初期処理 Serial.begin(9600); //2番ピンを入力ピンに設定(デフォルト設定なので省略しても良い) pinMode(2,INPUT); //受信する準備が出来ていることをシリアルモニタで知らせる Serial.println("Ready to receive..."); Serial.println(""); } void loop() { //2番ピンの状態を読み取る val=digitalRead(2); //2番ピンの状態によって処理を分岐させる if(!val){ //2番ピンの状態がHIGHからLOWになっていた場合 if(val!=old_val){ //現在の時間を取得 now_time=micros(); //pulse_countが0以上のときデータの取得処理を行う if(pulse_count>=0){ //パルスの長さを計算し配列に入れる pulse_data[pulse_count]=now_time-old_time; } //pulse_countの値を1増やす pulse_count++; //取得した時間を変数に入れておき、過去の時間として利用する old_time=now_time; } }else{ //2番ピンの状態がLOWからHIGHになっていた場合 if(val!=old_val){ //現在の時間を取得 now_time=micros(); //パルスの長さを計算し配列に入れる pulse_data[pulse_count]=now_time-old_time; //pulse_countの値を1増やす pulse_count++; //取得した時間を変数に入れておき、過去の時間として利用する old_time=now_time; }else if(pulse_count>=0&µs()-old_time>15000){ //2番ピンの状態がHIGHのままで15000[μs]経過した場合、receive_flagをtrueにする receive_flag=true; } } //読み取った2番ピンの状態を変数にいれておき、過去の状態として利用する old_val=val; //receive_flagがtrueの場合、シリアルモニタへの表示処理を行う if(receive_flag){ //パルス信号のデータをエンコードする encode(); //パルス信号のデータを表示する Serial.println("Pulse_data"); Serial.print("("); Serial.print(pulse_count); Serial.print(") "); for(int i=0;i<pulse_count;i++){ Serial.print(pulse_data[i]); if(i<pulse_count-1){ Serial.print(","); }else{ Serial.println(""); } } Serial.println(""); //エンコードしたデータを表示する Serial.println("Encode_data"); Serial.print(pulse_count); Serial.print(","); for(int i=0;i<=encode_count;i++){ Serial.print(encode_data[i]); if(i<encode_count){ Serial.print("U,"); }else{ Serial.println("U"); } } Serial.println(""); //受信する準備が出来ていることをシリアルモニタで知らせる Serial.println("Ready to receive..."); Serial.println(""); //2秒間待機 delay(2000); //フラグやカウントの変数をリセットする receive_flag=false; pulse_count=-1; } } //パルス信号のデータをエンコードする関数 void encode(){ //encode_countを-1で初期化する encode_count=-1; //パルス信号のデータの数だけ処理を繰り返す for(int i=0;i<pulse_count;i++){ //繰り返しの数を16で割ったときの余りが0のときencode_countを1増やし、これから編集するencode_dataの要素を初期化する if(i%16==0){ encode_count++; encode_data[encode_count]=0; } //データを左に1シフトする encode_data[encode_count]<<=1; //パルスの長さが800以上のとき、一番右のbitデータを1とする(800の部分はパルス信号のデータを見て決める) if(pulse_data[i]>=800){ encode_data[encode_count]|=1; } } } |
スケッチが正しければ、リモコンを赤外線受信モジュールに向けてボタンを押したとき、以下のようにエンコードしたデータも表示されるようになります。
スケッチの解説
今回のスケッチは、「void encode()」という新たな関数を作成し、データを以下のようにエンコードしてまとめています。
では、その仕組みについて、説明したいと思います。
まず、データに応じて、0と1に分けているのは、164行目から170行目の以下の文で、ビット演算を利用しています。
164 165 166 167 168 169 170 |
//データを左に1シフトする encode_data[encode_count]<<=1; //パルスの長さが800以上のとき、一番右のbitデータを1とする(800の部分はパルス信号のデータを見て決める) if(pulse_data[i]>=800){ encode_data[encode_count]|=1; } |
for文により、データの数だけ上記のような処理が繰り返されますが、配列「encode_data[40]」は「unsigned int型」なので、16bit分のデータしか入れることができません。
よって、16bit分データをいれたら、次の要素に入れるように、158行目から162行目で以下のような文を記述しています。
158 159 160 161 162 |
//繰り返しの数を16で割ったときの余りが0のときencode_countを1増やし、これから編集するencode_dataの要素を初期化する if(i%16==0){ encode_count++; encode_data[encode_count]=0; } |
ここで、作成した関数「encode()」は、信号の受信を確認した後に実行され、その後、結果をシリアルモニタに表示しています。
ここで、数値の後に、「U」をつけているのは、次の節で説明します。
信号のデータをエンコードするスケッチの説明は以上となります。
信号に応じた処理をする
上記までのスケッチで、信号の比較を行う準備が完了したため、次は、以下のように特定のボタンが押されたことを検知するといったことを行いたいと思います。
以下で紹介するスケッチでは、エンコードしたデータの数値を使用しますので、2つほど好きなリモコンのボタンを押して、エンコードしたデータをメモしておいてください。
このとき、受信が上手くいかない場合もあるので、複数回同じボタンを押してデータを何度か確認してください。
私はスターターキットに入っていたリモコンの「1」と「2」ボタンを押して結果は以下の通りでした。
スケッチを書き込む
以下のようなスケッチを書き込みます。
黄色でマークされている部分が先程のスケッチから追加、変更した部分です。
ここで、106行目と109行目の引数の部分は先程メモしておいた数値に変更してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
/* Arduinoで遊ぼう(24,赤外線リモコン); 2023/10/21 IR_Receiver_test_3 //スケッチの詳細はこちら↓ https://mecha-norimaki.com/arduino_start_24/ */ //測定した時間を入れる変数 unsigned long now_time,old_time; //パルス信号の長さを入れる配列 unsigned int pulse_data[640]; //ピンの状態を入れる変数 bool val; bool old_val=HIGH; //データの記録の回数をカウントする変数 int pulse_count=-1; //データの受信が終わったらtrueになるフラグ bool receive_flag; //エンコードしたデータの数をカウントする変数 int encode_count; //エンコードしたデータを入れる配列 unsigned int encode_data[40]; void setup(){ //シリアル通信の初期処理 Serial.begin(9600); //2番ピンを入力ピンに設定(デフォルト設定なので省略しても良い) pinMode(2,INPUT); //受信する準備が出来ていることをシリアルモニタで知らせる Serial.println("Ready to receive..."); Serial.println(""); } void loop() { //2番ピンの状態を読み取る val=digitalRead(2); //2番ピンの状態によって処理を分岐させる if(!val){ //2番ピンの状態がHIGHからLOWになっていた場合 if(val!=old_val){ //現在の時間を取得 now_time=micros(); //pulse_countが0以上のときデータの取得処理を行う if(pulse_count>=0){ //パルスの長さを計算し配列に入れる pulse_data[pulse_count]=now_time-old_time; } //pulse_countの値を1増やす pulse_count++; //取得した時間を変数に入れておき、過去の時間として利用する old_time=now_time; } }else{ //2番ピンの状態がLOWからHIGHになっていた場合 if(val!=old_val){ //現在の時間を取得 now_time=micros(); //パルスの長さを計算し配列に入れる pulse_data[pulse_count]=now_time-old_time; //pulse_countの値を1増やす pulse_count++; //取得した時間を変数に入れておき、過去の時間として利用する old_time=now_time; }else if(pulse_count>=0&µs()-old_time>15000){ //2番ピンの状態がHIGHのままで15000[μs]経過した場合、receive_flagをtrueにする receive_flag=true; } } //読み取った2番ピンの状態を変数にいれておき、過去の状態として利用する old_val=val; //receive_flagがtrueの場合、シリアルモニタへの表示処理を行う if(receive_flag){ //パルス信号のデータをエンコードする encode(); //信号データと先に調べたデータが一致した場合、特定の表示をおこなう if(check(67,49152U,5461U,16704U,5141U,2U)){ Serial.println("1 U^I^U"); Serial.println(""); }else if(check(67,49152U,5461U,16464U,5381U,2U)){ Serial.println("2 =^._.^="); Serial.println(""); }else{ //パルス信号のデータを表示する Serial.println("Pulse_data"); Serial.print("("); Serial.print(pulse_count); Serial.print(") "); for(int i=0;i<pulse_count;i++){ Serial.print(pulse_data[i]); if(i<pulse_count-1){ Serial.print(","); }else{ Serial.println(""); } } Serial.println(""); //エンコードしたデータを表示する Serial.println("Encode_data"); Serial.print(pulse_count); Serial.print(","); for(int i=0;i<=encode_count;i++){ Serial.print(encode_data[i]); if(i<encode_count){ Serial.print("U,"); }else{ Serial.println("U"); } } Serial.println(""); } //受信する準備が出来ていることをシリアルモニタで知らせる Serial.println("Ready to receive..."); Serial.println(""); //2秒間待機 delay(2000); //フラグやカウントの変数をリセットする receive_flag=false; pulse_count=-1; } } //パルス信号のデータをエンコードする関数 void encode(){ //encode_countを-1で初期化する encode_count=-1; //パルス信号のデータの数だけ処理を繰り返す for(int i=0;i<pulse_count;i++){ //繰り返しの数を16で割ったときの余りが0のときencode_countを1増やし、これから編集するencode_dataの要素を初期化する if(i%16==0){ encode_count++; encode_data[encode_count]=0; } //データを左に1シフトする encode_data[encode_count]<<=1; //パルスの長さが800以上のとき、一番右のbitデータを1とする(800の部分はパルス信号のデータを見て決める) if(pulse_data[i]>=800){ encode_data[encode_count]|=1; } } } //パルス信号のデータを比較する関数(可変長引数) bool check(int bit, ...){ //測定したデータの数が引数で渡したデータの数の部分と一致していないとき関数をfalseにして終了する if(pulse_count!=bit){ return false; } //可変個の引数を扱うための変数 va_list args; //va_list を初期化し、可変個の引数の使用を開始する va_start(args,bit); //エンコードしたデータと引数で渡したデータが一致しているか確認して、一致していなければ、関数をfalseにして終了する for(int i=0;i<=encode_count;i++) { if(encode_data[i]!=va_arg(args,unsigned int)){ va_end(args); return false; } } //ここまでのチェックをすべて通過できた場合、関数をtrueにする va_end(args); return true; } |
スケッチが正しければ、メモしたボタンを押したとき、以下のように絵文字が表示されるようになります。
スケッチの解説
今回のスケッチは、「bool check()」という新たな関数を作成して、データの比較をしています。
では、作成した「bool check()」について、説明していきたいと思います。
まず、関数の引数部分を見てみると、型を指定している引数は1つだけで、その後ろは「. . .」と記述しています。
184 185 |
//パルス信号のデータを比較する関数(可変長引数) bool check(int bit, ...){ |
実は、この関数は引数の数を指定しない可変長引数いうものを利用しています。
この可変長引数の関数は、引数の数が定まっていなくても関数を使うことができます。
そのため、引数の数が異なっていても同じ関数を使うといったことが可能となります。
ここで、「. . .」の前の引数(上記の例では「bit」)は、通常の関数と同じように使用することができます。
今回の場合は、データの数が一致しているのかを見るため「pulse_count」と「bit」の値が不一致かどうかを確かめ、不一致の場合は、「return false」により、関数の値を「false」として関数を終了しています。
187 188 189 190 |
//測定したデータの数が引数で渡したデータの数の部分と一致していないとき関数をfalseにして終了する if(pulse_count!=bit){ return false; } |
それ以降の引数を使用する場合は、少し変わった手順を踏まなければいけません。
可変個の引数を使うときは、まず初めに「va_list」という型の変数を宣言して、「va_start()」という関数を使用します。
192 193 194 195 196 |
//可変個の引数を扱うための変数 va_list args; //va_list を初期化し、可変個の引数の使用を開始する va_start(args,bit); |
このとき、「va_start()」の引数は、宣言した「va_list型」の変数と「. . .」の前の引数(上記の例では「bit」)を使用します。
次に引数の値を使いたいときは、「va_arg()」という関数を呼び出します。
200 |
if(encode_data[i]!=va_arg(args,unsigned int)){ |
この「va_arg()」の引数には、宣言した「va_list型」の変数と引数の型名を使用します。
ここで、関数(上記の例だと「check()」)を呼び出す際に使用した引数と、「va_arg()」の引数で使用した型名が異なる場合は、思っていた動作と異なる動作を行う可能性があります。
そのため、今回の場合、引数の数字の後ろに「U」を付けることにより、引数の数字を「unsigned int型」に固定するという対策を行っています。
また、可変長引数の処理を終えるときは、「va_end()」という関数を呼び出します。
201 |
va_end(args); |
この「va_arg()」の引数には、宣言した「va_list型」の変数を使用します。
198行目から204行目では、for文を利用して、encode_data[40]の値と可変長引数の値を比較していき、不一致の場合は、可変長引数の処理を終わらせた後、関数の値を「false」として関数を終了しています。
198 199 200 201 202 203 204 |
//エンコードしたデータと引数で渡したデータが一致しているか確認して、一致していなければ、関数をfalseにして終了する for(int i=0;i<=encode_count;i++) { if(encode_data[i]!=va_arg(args,unsigned int)){ va_end(args); return false; } } |
ここで、上記の例を見ると、配列に関しては、番号(上記の例だと「i」)で要素をしていますが、「va_arg()」は何番目の引数を使うといった指定をおこなっていません。
実は、「va_arg()」の値は、呼び出されると、順番に値が変更される仕様になっています。
ちなみに、「va_arg()」の値は、呼び出されると勝手に変更され、変更した値はもとに戻すことができないため注意が必要です。
最後に206行目から208行目では、可変長引数の処理を終わらせた後、関数の値を「true」として関数を終了しています。
206 207 208 |
//ここまでのチェックをすべて通過できた場合、関数をtrueにする va_end(args); return true; |
作成した関数は105行目から112行目の条件分岐に使用され、受信したデータと引数のデータが一致した場合、特定の表示を行うようにして、それ以外はパルス信号の長さのデータとエンコードしたデータの表示を行うようにしています。
105 106 107 108 109 110 111 112 |
//信号データと先に調べたデータが一致した場合、特定の表示をおこなう if(check(67,49152U,5461U,16704U,5141U,2U)){ Serial.println("1 U^I^U"); Serial.println(""); }else if(check(67,49152U,5461U,16464U,5381U,2U)){ Serial.println("2 =^._.^="); Serial.println(""); }else{ |
特定のボタンが押されたことを検知するスケッチの説明は以上となります。
終わりに
皆さんリモコンの信号を受信することが出来たでしょうか。
今回は、ボタンに応じて、特定の文字を表示させる処理を行いましたが、これをLEDの点灯、消灯を行う処理など別の処理に置き換えれば、様々なものを操作することが可能です。
また、仕組みさえ分かっていれば、自分の行いたいことに合わせて、受信方法を最適なものにカスタマイズしていくことも可能なはずです。
是非、色々なリモコンで色々なものを操作してみてください。
次回の記事ではDCモータの制御を行いたいと思います。
本記事はここまでです。ご清覧ありがとうございました。
広告
楽天モーションウィジェットとGoogleアドセンス広告です。
気になる商品がございましたら、チェックをしてみてください。