(第二話) 音量を一気に上げるだけのイタズラ専用ボタンを作ってみた

さて、第二話はコードを書いていきます。

どういった動作をさせたいかと言うと、第一話でも提示した以下の画像の流れを繰り返せば良いです。

という事でプログラミングしていきます。プログラミングには第一話でも述べたArduinoというソフトウェアを使います。また、Arduino Leonaldo Pro Microはパソコンと接続しておきます。
コードを書いていく前にArduinoの種類の設定とライブラリの追加を行います。

では、「ツール」から「ボード:”Arduinoの種類の名前”」から「Arduino Leonardo」を選択してください。

次にライブラリの追加を行います。
ライブラリとは、他の人が作成した関数(処理)等を再利用できるようにまとめたようなものです。厳密に言うと長くなってしまうので、ここでは説明しませんが、簡単に言ってしまえば、関数(処理)が集まったものというような感じです。詳しく知りたい方がいたら調べてみてください。

ではライブラリを追加をしていきましょう。
今回使うライブラリはライブラリ管理に公開されているので、「スケッチ」→「ライブラリをインクルード」→「ライブラリを管理」を選択します。

選択すると以下のようなウィンドウが開くので、右上の検索ボックスに「HID Project」と入力してください。

入力すると「HID-Project by NicoHood」と言うものが表示されるので、「インストール」ボタンをクリックします。(画像はインストール済みのものです)

インストールが終わったらウィンドウを閉じてください。

ではコードを書いていきます。
結論から言ってしまうと以下のようなコードになります。

では簡単に説明していきます。

まず1行目の

#include "HID-Project.h"

についてですが、「ファイル名”HID-Project.h”の内容を使うよ!」という宣言です。
これによりファイルの中に書かれている関数を使用することができます。ちなみにHID-Project.hという名前は、先ほど追加したライブラリ固有のものです。

続いて2行目。

#define mituruButton 16

これはmituruButtonという名前の定数を16にするという意味です。
これによりmituruButton = 16とすることができます。定数ですので、プログラム中に書き換えることはできません。

では setup関数(4 ~ 7行目)を見ていきます。
まず5行目の

pinMode(mituruButton, INPUT_PULLUP);

についてです。pinModeはArduinoで用意されている関数です。
使い方についてですが、

pinMode(ピンの番号, ピンのモード);

となっています。
ピンの番号についてですが、基板上に書かれている数字を指します。

ピンのモードは入力(INPUT)、出力(OUTPUT)、プルアップ抵抗ありの入力(INPUT_PULLUP)の3種類があります。

で、今回は入力をするのですが、プルアップ抵抗ありのモードを使用していきます。

なぜプルアップ抵抗ありのモードを使用するかの説明の前に、ボタンスイッチについて説明していきます。

まず、ボタンが押されているのか押されていないのかの判断の方法としては、0と1の2パターンで判断します。電気が流れていれば1、流れていなければ0となります。

その0と1の判断基準は電気が流れているかいないかと言いましたが、厳密には1に相当する基準がそれぞれあります。
例えば電圧が5Vで1とし、0Vで0としましょう。

では以下の画像をみてください。

このように電気が流れるのか流れないのか微妙なケースがあったとします。
また、あくまでもボタンを押すのは人、つまり手の僅かな振動などが加わってきます。

その場合の電圧の動きの例を以下に示します。

適当すぎて怒られそう()

先ほど言ったように、5Vで押されている、0Vで押されていないと判断します。
では、2.5Vなどの中間だったらどうなるのか?
言ってしまうと、不明です。つまり、押されたのか押されていないのかの判断ができなくなり、誤動作の原因となります。

そこで、1に相当する電圧(ここでは5V)と配線の間に抵抗を入れることにより、ボタンが押されていない時には1に相当する電圧を掛け、ボタンが押された時に0になるようにするのがプルアップ抵抗です。

実際の計算は次のサイトを見るとわかりやすいです。
制御工学の基礎あれこれ プルアップ/プルダウン抵抗とは

よってプルアップ抵抗有りの入力モードを使います。

つまり、今回書いたコード

pinMode(mituruButton, INPUT_PULLUP);

は「mituruButton」の値(16)のピンを「プルアップ抵抗有りの入力」に設定すると言う意味です。

では次の

Consumer.begin();

についてですが、接続されているデバイスにレポートを送信開始という意味です。(だった気が)

今回HID(Human Interface Device)としてコンピュータに認識させてコンピュータの操作を行なっていますが、何が押されたのかを”レポート”としてコンピュータに送ってあげることで、キーボードやマウスなどとして認識させることができます。

その為、この関数を使っています。ちなみにこの関数はライブラリのものです。

コードについてですが、

Consumerと言うクラスのbeginという関数を呼び出しています。また括弧の中身がない(括弧だけ)のは、関数に渡す値はないことを意味します。

そういえばそれぞれの命令の最後に ; がありますが、これは「ここで一つの命令の終わりだよ!」というのを示します。

C言語では必ず命令は 関数名 (関数に渡す値); で構成されます。

ではsetup関数のおさらいです。

void setup() {
  pinMode(mituruButton, INPUT_PULLUP);
  Consumer.begin();
}

まずmituruButtonというピンを、プルアップ抵抗有りの入力モードに設定、次にHIDのレポートの送信を始める、と言った動作がsetup関数となっています。

では続いてloop関数(setup関数終了後に繰り返し実行される関数)をみていきましょう。

void loop() {
  if (!digitalRead(mituruButton)) {
        for(int VolCount = 0; VolCount < 40 ; VolCount++){
          Consumer.write(MEDIA_VOLUME_UP);
          delay(10);
          Consumer.releaseAll();
          delay(10);
        }
        delay(1000);
  }
}

このプログラムについてですが、わかりやすくするために緑色の部分を抜いてみます。

このようになりました。

A

if (!digitalRead(mituruButton)) {
   //Bの処理
   delay(1000);
}

B

for(int VolCount = 0; VolCount < 40 ; VolCount++){
  Consumer.write(MEDIA_VOLUME_UP);
  delay(10);
   Consumer.releaseAll();
   delay(10);
}

ではまずAから。

Aはif文を使用しています。if文とは、「もしも〜ならば〜する」というものを実現してくれます。以下のような構成となっています。条件式に一致すると真(1)が返されます。一致しなければ偽(0)が返されます。

if(条件式){
  //ここに条件式について真(1)が返された時に処理する命令を書く
}else{
  //ここに条件式について偽(0)が返された時に処理する命令を書く
  //尚、偽(0)が返された時に処理する命令が無ければ、else{ }は省略することができます。
}

で、条件式は

!digitalRead(mituruButton)

となっています。このdigitalReadは指定したピン番号の値を読み取ります。
読み取った値はHIGH(1)かLOW(0)で返されます。
以下の構成となっています。

digitalRead(ピン番号)

つまり、mituruButton(16)のピンを読み取るようにしています。また最初に ! がついていますが、これは否定を示します。
1の時0、0の時1とするのように結果を反転します。

ではなぜ反転させる必要があるのか。
先ほどのプルアップ抵抗について以下のように述べました。

ボタンが押されていない時には1に相当する電圧を掛け、ボタンが押された時に0になるようにするのがプルアップ抵抗です。

つまりボタンが押されていなければ1となり、押されていれば0が返されます。

そのため、をいれて反転させることによりボタンが押された時に1が返されるようにしています。

次にdelayについてですが、これは遅延を意味します。
構成は以下の通りです。

delay(時間);

尚、時間の単位は1 ms (0.001秒)です。そのため、1000を指定すると1秒プログラムが一時停止します。

つまり、Aだけ見ると、もしボタンが押されていたら1秒間プログラムを一時停止することになります。

では次にBを見ていきます。

Bはfor文を用いています。
for文は繰り返しに用います。構成は次の通りです。

for(初期化式 ; 条件式 ; 処理実行後の式){
   //ここに繰り返したい処理を書く
}

まず、初期化式で変数の定義、もしくは変数の初期化を行います。
初期化とは、「この変数の最初の値はXXだよ〜」って設定してあげることです。
変数とは、プログラム内で変更することができる値を指します。

数学でいう y = ax + bの y と x を指します。

また、条件式は{ }内を実行するための条件を書きます。if文の条件式と同じです。

処理実行後の式には { }内の処理を一度実行した後、変数等をどうするかを書きます。

では今回書いたコードを以下に示します。

for(int VolCount = 0; VolCount < 40 ; VolCount++){
  Consumer.write(MEDIA_VOLUME_UP);
  delay(10);
   Consumer.releaseAll();
   delay(10);
}

わかりやすくするために { }内をCとしましょう。

C

Consumer.write(MEDIA_VOLUME_UP);
delay(10);
Consumer.releaseAll();
delay(10);

for文の動きを説明する前にCの説明を行います。
delayは先ほど説明したので省略させて頂きます。

ではまず、ライブラリ内関数のConsumer.writeから。
これは( )括弧内のキーを押す(押し続ける)関数です。MEDIA_VOLUME_UPという音量を上げるキーの定数を関数に渡しています。

このライブラリで使用できる定数はこちらで確認することができます。

次にConsumer.releaseAllはConsumer.writeで押したキーを全てリセット(キーを離す)する関数です。この関数は値を渡すことができないので、括弧の中はなしにします。

つまり、Cの流れは…

  • 音量を上げるキーを押す(押し続ける)
  • 0.010秒待つ
  • キーを全て離す
  • 0.010秒待つ

という風になっています。

ではBの説明に入ります。

B

for(int VolCount = 0; VolCount < 40 ; VolCount++){
   //Cの処理
}

となっていましたね。
では流れを説明します。

先ほど言ったようにfor文は

for(初期化式 ; 条件式 ; 処理実行後の式){
   
}

という形で構成されます。
では流れに行く前に条件式の演算子(比較の方法)について説明します。

a < baはbより小さい (bは含まない)
a <= baはb以下 (b含む)
a > baはbより大きい (aは含まない)
a >= baはb以上 (a含む)
a == baはbと等しい
a != baはbと等しくない
a++aに1足す
a–aから1引く

では流れについて説明していきます。

for文は主に以下の流れとなります。

  1. 初期化式を実行
  2. 条件式を評価 (条件に合うか確認)
  3. 条件に合えば { }内の処理を実行、合わなければ { }内は無視してfor文終了
  4. 処理実行後の式を実行
  5. 2に戻る

ではこれらをBに当てはめていきます。

for(int VolCount = 0; VolCount < 40 ; VolCount++){
   //Cの処理
}
  1. int形式(整数)でVolCountという名前の変数を作成し、0とする。
  2. VolCount の中身は 40 未満か?
  3. 40未満だったらCの処理を実行、40以上だったらCの処理は実行しないで次へ
  4. VolCountに1を足す
  5. 2に戻る

という流れになります。
ではAをまとめてみていきましょう。

Aは以下のコードでしたね。

if (!digitalRead(mituruButton)) {
   //Bの処理
   delay(1000);
}

まず、mituruButtonの値を見て値を反転。反転した値が1ならばBの処理へ。
Bと言えば…

  1. int形式(整数)でVolCountという名前の変数を作成し、0とする。
  2. VolCount の中身は 40 未満か?
  3. 40未満だったらCの処理を実行、40以上だったらCの処理は実行しないで次へ
  4. VolCountに1を足す
  5. 2に戻る

そして、Bの処理が終わったら(VolCountが40以上になったら)、1秒間待つ。
ちなみに遅延を入れているのは、処理が早すぎで処理が追いつかずフリーズ、処理重複防止で入れています。

これがloop関数の処理となります。
{ }内の処理が終わったら、プログラムは本来終了しますが、Arduinoのloop関数は{ }内の処理が終わったら、loop関数をもう一度実行してくれます。

これによりボタンが押されたか、押されてないかの判定がずっと行えます。

これで今回使用するプログラムが完成しました。

完成したら、プログラムをマイコンと呼ぶコンピュータに書き込んであげる必要があります。
書き込むにはArduino Leonaldo Pro Microをパソコンと接続した状態で、ポートの設定(接続されている場所を教える)を行なってあげる必要があります。

設定の方法ですが、「ツール」→「シリアルポート」から行えます。
macOSだと分かりやすいのですが、WindowsだとCOM1とかで表示されます。COMの数字が毎回変わる可能性があるので、書き込めなかったりしたら番号を変えてみてください。

あ、デバイス管理がわかる方がいれば、そこからCOMの番号を調べることができます。

設定したら矢印のボタンを選択すると書き込めます。

これでプログラムの準備は完了です。

次は実際にボタンを繋いでみます。

結構長めになってしまったので今回はここで締めたいと思います。

ここまでお読み頂きありがとうございました。また、お疲れ様でした。

第3話はこちら!

コメントやツッコミをどうぞ。