第17回 チャレンジ課題

キーボードを製作するための基本的な知識を一通り習得しましたので、ここでチャレンジ課題に挑戦してみましょう!

目次

課題

課題は3種類用意してみました。

❶と❷の課題は、今までのスケッチを多少改良する程度で実現できると思います。

❸の課題は、今までのスケッチをベースにしてもかなり手を入れることになりそうです。そこで、いくつかヒントを書いていますが、参考程度にしてみてください。

❶ スイッチを押すと文字入力と同時にLEDを点灯

第15回の記事で、「a」と「b」の文字を入力できるキーボードを製作しました。

スケッチは次のように作成して、2個のスイッチをpollメソッドで状態を調べて、pushedメソッドでスイッチが押されたか判定しています。

/*
 * 「a」と「b」の2種類の文字が入力できるキーボード
 *     左スイッチ: 「a」の入力
 *     中央スイッチ:「b」の入力
 */

// ライブラリヘッダファイル
#include <Keyboard.h>
#include <avdweb_Switch.h>

// スイッチの接続端子
#define SWITCH_HIDARI A5 // 左側スイッチ
#define SWITCH_CHUO   A4 // 中央スイッチ

// スイッチクラスのインスタンスを生成
Switch hidariSwitch(SWITCH_HIDARI); // 左側スイッチのインスタンス
Switch chuoSwitch(SWITCH_CHUO);     // 中央スイッチのインスタンス


void setup() {
  // キーボード接続時の処理
  Keyboard.begin();

  // スイッチ接続ピンの設定
  pinMode(SWITCH_HIDARI, INPUT_PULLUP);
  pinMode(SWITCH_CHUO,   INPUT_PULLUP);
}

void loop() {

  // スイッチライブラリに左側スイッチ状態の調査を指示
  hidariSwitch.poll();
  
  // 左側スイッチが押されたら「a」をPCに送信
  if( hidariSwitch.pushed() ){
    Keyboard.print("a");
  }


  // スイッチライブラリに中央スイッチ状態の調査を指示
  chuoSwitch.poll();

  // 中央スイッチが押されたら「b」をPCに送信
  if( chuoSwitch.pushed() ){
    Keyboard.print("b");
  }

}

現在のブレッドボードにはLEDが接続されていますので、スイッチが押された時に、PCに文字を送ると同時に、LEDをピカッと光らせてみてください。スイッチに応じて光らせるLEDも変えてみてください。

❷ 強制コピペキーボード

PCで作業をしていると、コピペ機能はよく使用しますよね。

コピペする時は、「ctrl + C」や「command + C」を押してコピー、「ctrl + V」や「command + V」を押してペーストする、という操作をします。

コピペする際、このようにコピーとペーストで2回のキーボード操作をしています。

でも、コピーしたものはペーストするわけですから、ペーストのキー操作をするのは面倒ではないでしょうか?

コピーの操作をしたら、その直後に自動的にペーストしてくれてもいいのに、と思います。

そこで、強制コピペキーボード、というものを作ってみようと思います。

次の動作をするようなスケッチを作成してみてください。

  1. テキストエディタなどでコピーする文字列を選択する
  2. ブレッドボードの左側のスイッチを押すと、選択した文字列をコピーする
  3. 2秒後に自動的に(強制的に)ペーストする

このキーボードを使用するとき、ユーザはコピーしたら素早くペーストする位置にカーソルを持っていく必要がありますので、ちょっと緊張しますね。

コピーやペーストのプログラムは第15回で次のように作成していますので、参考にしてみてください。

/*
 * カット・コピー・ペーストだけのキーボード
 *  左側スイッチ: カットキー
 *  中央スイッチ: コピーキー
 *  右側スイッチ: ペーストキー
 */

// commandまたはctrlキーの定義

//   macOSの場合は次の行のコメントをはずしてください (先頭の//を取る)
//#define shortcutKey KEY_LEFT_GUI

//   Windowsの場合は次行のコメントをはずしてください (先頭の//を取る)
//#define shortcutKey KEY_LEFT_CTRL 

// ライブラリをインクルード
#include <Keyboard.h>
#include <avdweb_Switch.h>

// スイッチの接続端子
#define SWITCH_HIDARI A5 // 左側スイッチ
#define SWITCH_CHUO   A4 // 中央スイッチ
#define SWITCH_MIGI   A3 // 右側スイッチ

// スイッチクラスのインスタンスを生成
Switch hidariSwitch(SWITCH_HIDARI);
Switch chuoSwitch(SWITCH_CHUO);
Switch migiSwitch(SWITCH_MIGI);


void setup() {
  Keyboard.begin();  // キーボードの接続処理

  // スイッチ接続ピンの設定
  pinMode(SWITCH_HIDARI, INPUT_PULLUP);
  pinMode(SWITCH_CHUO,   INPUT_PULLUP);
  pinMode(SWITCH_MIGI,   INPUT_PULLUP);
}

void loop() {

  // 左側スイッチ(カットキー)の処理
  hidariSwitch.poll();  // スイッチライブラリに左側スイッチ状態の調査を指示
  
  // 左側スイッチが押されたか判定して、押されていたらカットキーの組み合わせを送信する
  if( hidariSwitch.pushed() ){
    Keyboard.press(shortcutKey);  // Command/Ctrlキーを押す
    Keyboard.press('x');          // 'x'キーを押す
    delay(100);                   // ちょっと待つ
    Keyboard.releaseAll();        // 両方のキーを離す
  }

  // 中央スイッチ(コピーキー)の処理
  chuoSwitch.poll();  // スイッチライブラリに中央スイッチ状態の調査を指示
  
  // 中央スイッチが押されたか判定して、押されていたらコピーキーの組み合わせを送信する
  if( chuoSwitch.pushed() ){
  
    Keyboard.press(shortcutKey);  // Command/Ctrlキーを押す
    Keyboard.press('c');          // 'c'キーを押す
    delay(100);                   // ちょっと待つ
    Keyboard.releaseAll();        // 両方のキーを離す
  }

  // 右側スイッチ(ペーストキー)の処理
  migiSwitch.poll();  // スイッチライブラリに右側スイッチ状態の調査を指示

  // 右側スイッチが押されたか判定して、押されていたらペーストキーの組み合わせを送信する
  if( migiSwitch.pushed() ){
  
    Keyboard.press(shortcutKey);  // Command/Ctrlキーを押す
    Keyboard.press('v');          // 'v'キーを押す
    delay(100);                   // ちょっと待つ
    Keyboard.releaseAll();        // 両方のキーを離す
  }

}

❸ モード選択

今までのスケッチでは、キーボードの種類を変更したい場合、スケッチを書き込む必要があります。

でも、キーボードの種類を変えたいときにいちいちスケッチを書き込むのは面倒ですよね。

かといって、キーボード自体を何セットか準備するのもお金がかかります。

そこで、キーボードの種類を変更できる機能を持つプログラムを作ってみたいと思います。

次のような動作をするキーボードのスケッチを作成してみてください。

  • 動作開始直後は、左スイッチが「1」、中央スイッチが「2」を入力できるようにする。
    この時、緑色LEDを点灯する(他のLEDは消灯)
  • 右スイッチをシングルクリックすると、左スイッチが「A」、中央スイッチが「B」を入力できるようにする。
    この時、黄色LEDを点灯する(他のLEDは消灯)
  • 右スイッチをダブルクリックすると、左スイッチがコピー機能、中央スイッチがペースト機能になるようにする。この時、赤色LEDを点灯する(他のLEDは消灯)
  • 右スイッチを長押しすると❶のスイッチが「1」、中央スイッチが「2」を入力できるようにする。
    この時、緑色LEDを点灯する(他のLEDは消灯)

この課題は、今までのスケッチにかなり手を加えることになると思います。

そのとき、実装方法がよくわからない点が出てくるかもしれません。

そこで、次の実装方法をヒントにしてみてください。(必ずしもこのようにする必要はありません)

  • モード情報の扱い
    このキーボードは3種類のモードを持っています。そのモードに応じてPCに送るデータを変えています。このモード情報は常に覚えておく必要があります。
    このモード情報の保持ですが、loop関数内でローカル変数に代入するとloop関数終了時に破棄されてしまいます。そこで、モードの情報は、グローバル変数か、static変数に代入しておく必要がありそうです。
  • PCに送るデータ
    モードに応じてPCに送るデータを変更する必要があります。
    実際にデータ送信の部分では、モードに応じて処理を分ける必要があります。モードは3種類ですので、ifで処理を分けるより、switchを使用した方が見通しが良くなりそうです。

解答例

❶ スイッチを押すと文字入力と同時にLEDを点灯

スイッチが押されたとき、Keyboard.printで文字データをPCに送っています。

そのタイミングでLEDを光らせるようにしてみました。

スケッチの変更は次の項目になります。

  • LED接続端子の名前定義(#define
  • LED接続端子の出力設定(pinMode
  • 文字データを送信するところに、LEDの点滅を追加(digitalWritedelay
/*
 *  Arduino入門 基礎編パート2 第17回
 *    チャレンジ課題1 解答例
 */

// ライブラリヘッダファイル
#include <Keyboard.h>
#include <avdweb_Switch.h>

// スイッチの接続端子
#define SWITCH_HIDARI A5 // 左側スイッチ
#define SWITCH_CHUO   A4 // 中央スイッチ

// LEDの接続端子
#define GREEN_LED  8
#define YELLOW_LED 6

// スイッチクラスのインスタンスを生成
Switch hidariSwitch(SWITCH_HIDARI); // 左側スイッチのインスタンス
Switch chuoSwitch(SWITCH_CHUO);     // 中央スイッチのインスタンス


void setup() {
  // キーボード接続時の処理
  Keyboard.begin();

  // スイッチ接続端子の設定
  pinMode(SWITCH_HIDARI, INPUT_PULLUP);
  pinMode(SWITCH_CHUO,   INPUT_PULLUP);

  // LED接続端子の設定
  pinMode(GREEN_LED,  OUTPUT);
  pinMode(YELLOW_LED, OUTPUT);
}

void loop() {

  // スイッチライブラリに左側スイッチ状態の調査を指示
  hidariSwitch.poll();
  
  // 左側スイッチが押されたら「a」をPCに送信してLEDを点滅
  if( hidariSwitch.pushed() ){
    Keyboard.print("a");
    digitalWrite(GREEN_LED, HIGH);
    delay(50);
    digitalWrite(GREEN_LED,LOW);
  }


  // スイッチライブラリに中央スイッチ状態の調査を指示
  chuoSwitch.poll();

  // 中央スイッチが押されたら「b」をPCに送信してLEDを点滅
  if( chuoSwitch.pushed() ){
    Keyboard.print("b");
    digitalWrite(YELLOW_LED, HIGH);
    delay(50);
    digitalWrite(YELLOW_LED,LOW);
  }

}

❷ 強制コピペキーボード

第15回のカット、コピー、ペーストのスケッチを元にして、左側スイッチが押された時に、「コピー」▶︎「時間待ち」▶︎「ペースト」操作をするように変更しました。

/*
 *  Arduino入門 基礎編パート2 第17回
 *    チャレンジ課題2 解答例
 */

// commandまたはctrlキーの定義

//   macOSの場合は次の行のコメントをはずしてください (先頭の//を取る)
//#define shortcutKey KEY_LEFT_GUI

//   Windowsの場合は次行のコメントをはずしてください (先頭の//を取る)
//#define shortcutKey KEY_LEFT_CTRL 

// ライブラリをインクルード
#include <Keyboard.h>
#include <avdweb_Switch.h>

// コピー操作とペースト操作の時間間隔(単位:ms)
#define KEY_INTERVAL 2000

// スイッチの接続端子
#define SWITCH_HIDARI A5 // 左側スイッチ

// スイッチクラスのインスタンスを生成
Switch hidariSwitch(SWITCH_HIDARI);

void setup() {
  Keyboard.begin();  // キーボードの接続処理

  // スイッチ接続ピンの設定
  pinMode(SWITCH_HIDARI, INPUT_PULLUP);
}

void loop() {

  hidariSwitch.poll();  // スイッチライブラリに左側スイッチ状態の調査を指示
  
  // 左側スイッチが押されたか判定して、押されていたらコピーして、2秒後にペーストする
  if( hidariSwitch.pushed() ){
    // コピー操作
    Keyboard.press(shortcutKey);  // Command/Ctrlキーを押す
    Keyboard.press('c');          // 'c'キーを押す
    delay(100);                   // ちょっと待つ
    Keyboard.releaseAll();        // 両方のキーを離す

    delay(KEY_INTERVAL);  // 時間待ち

    // ペースト操作
    Keyboard.press(shortcutKey);  // Command/Ctrlキーを押す
    Keyboard.press('v');          // 'v'キーを押す
    delay(100);                   // ちょっと待つ
    Keyboard.releaseAll();        // 両方のキーを離す
  }

}

❸ モード選択

PCに送信する文字データはloop関数内で処理しますが、送る文字データはモードによって変更する必要あがあります。また、このモードは保持する必要がありますので、グローバル変数かstatic変数で用意する必要があります。

そこで、次の点に注意してスケッチを作成することにしました。

  • モードの値の定義と、モードを保持する変数
  • loop関数内のモード切り替え処理
  • loop関数内のモードに応じた文字データ送信処理

それぞれの実装について次のように検討しました。

❶ モードの値の定義と、モードを保持する変数

キーボードには3種類のモードがありますので、今回は次のようにenumで宣言することにしました。

// モード値の定義       1と2      AとB      コピーペースト
enum KeyboardMode {MODE_1_2, MODE_A_B, MODE_COPY_PASTE};

また、モードを保持する変数はloop関数内で何度も使用しますので、動作中はずっと保持する必要があります。

そこで、モードは次のようにloop関数内でstatic変数として宣言するようにしました。動作開始直後は「1と2」なので、その値で初期化しておきます。

static uint8_t keyboard_mode = MODE_1_2;

❷ loop関数内のモード切り替え処理

loop関数内では、右側スイッチの操作のよってモードを切り替えます。切り替え処理は、最初に右側スイッチの状態を確認して、押された状態に応じてモードの変数を次のように切り替えます。

  // --------- モードスイッチの処理 ---------
  migiSwitch.poll();

  // スイッチ状態に応じてモードとLED点灯状態変更
  if( migiSwitch.singleClick() ) {  // シングルクリックの場合
    keyboard_mode = MODE_A_B;
  }

  if( migiSwitch.doubleClick() ) {  // ダブルクリックの場合
    keyboard_mode = MODE_COPY_PASTE;
  }

  if( migiSwitch.longPress() ) {  // 長押しの場合
  }

また、モードを切り替えるときに、LEDの点灯状態を切り替えます。この切り替え処理は、何度も同じような処理が出てくるので関数にしてみました。

// キーボードのモードLED表示関数
//   引数: キーボードのモード
//   返り値: なし
void controlModeLed(KeyboardMode mode) {
  switch(mode) {  // モードに応じてLED点灯状態変更
    case MODE_1_2:  // 1と2
      digitalWrite(GREEN_LED,  HIGH);
      digitalWrite(YELLOW_LED, LOW);
      digitalWrite(RED_LED,    LOW);
      break;

    case MODE_A_B:  // AとB
      digitalWrite(GREEN_LED,  LOW);
      digitalWrite(YELLOW_LED, HIGH);
      digitalWrite(RED_LED,    LOW);
      break;

    case MODE_COPY_PASTE:  // コピーペースト
      digitalWrite(GREEN_LED,  LOW);
      digitalWrite(YELLOW_LED, LOW);
      digitalWrite(RED_LED,    HIGH);
      break;
  }
}

❸ loop関数内のモードに応じた文字データ送信処理

loop関数内では、keyboard_modeに現在のモードが代入されていますので、この値に応じて送信する文字データを切り替えます。

ifでも切り替えられますが、この場合はswitchを使った方が見やすくなるかな、と思います。

  // モードに応じて左側スイッチの処理を行う
  if( hidariSwitch.pushed() ){
    switch( keyboard_mode ) {
      case MODE_1_2:  // 1と2の場合
      Keyboard.print("1");
      break;

      case MODE_A_B:  // AとBの場合
      Keyboard.print("A");
      break;

      case MODE_COPY_PASTE:  // コピーペーストの場合
      Keyboard.press(shortcutKey);  // Command/Ctrlキーを押す
      Keyboard.press('c');          // 'c'キーを押す
      delay(100);                   // ちょっと待つ
      Keyboard.releaseAll();        // 両方のキーを離す
      break;
    }
  }

これで一通りの処理ができますので、次のようにスケッチにまとめてみました。

/*
 *  Arduino入門 基礎編パート2 第17回
 *    チャレンジ課題3 解答例
 */

// commandまたはctrlキーの定義

//   macOSの場合は次の行のコメントをはずしてください (先頭の//を取る)
//#define shortcutKey KEY_LEFT_GUI

//   Windowsの場合は次行のコメントをはずしてください (先頭の//を取る)
//#define shortcutKey KEY_LEFT_CTRL 

// ライブラリヘッダファイル
#include <Keyboard.h>
#include <avdweb_Switch.h>

// スイッチの接続端子
#define SWITCH_HIDARI A5 // 左側スイッチ
#define SWITCH_CHUO   A4 // 中央スイッチ
#define SWITCH_MIGI   A3 // 右側スイッチ

// LEDの接続端子
#define GREEN_LED  8
#define YELLOW_LED 6
#define RED_LED    4

// スイッチクラスのインスタンスを生成
Switch hidariSwitch(SWITCH_HIDARI); // 左側スイッチのインスタンス
Switch chuoSwitch(SWITCH_CHUO);     // 中央スイッチのインスタンス
Switch migiSwitch(SWITCH_MIGI);     // 右側スイッチのインスタンス

// モード値の定義       1と2      AとB      コピーペースト
enum KeyboardMode {MODE_1_2, MODE_A_B, MODE_COPY_PASTE};

// キーボードのモードLED表示関数
//   引数: キーボードのモード
//   返り値: なし
void controlModeLed(KeyboardMode mode) {
  switch(mode) {  // モードに応じてLED点灯状態変更
    case MODE_1_2:  // 1と2
      digitalWrite(GREEN_LED,  HIGH);
      digitalWrite(YELLOW_LED, LOW);
      digitalWrite(RED_LED,    LOW);
      break;

    case MODE_A_B:  // AとB
      digitalWrite(GREEN_LED,  LOW);
      digitalWrite(YELLOW_LED, HIGH);
      digitalWrite(RED_LED,    LOW);
      break;

    case MODE_COPY_PASTE:  // コピーペースト
      digitalWrite(GREEN_LED,  LOW);
      digitalWrite(YELLOW_LED, LOW);
      digitalWrite(RED_LED,    HIGH);
      break;
  }
}

void setup() {
  // キーボード接続時の処理
  Keyboard.begin();

  // スイッチ接続端子の設定
  pinMode(SWITCH_HIDARI, INPUT_PULLUP);
  pinMode(SWITCH_CHUO,   INPUT_PULLUP);

  // LED接続端子の設定
  pinMode(GREEN_LED,  OUTPUT);
  pinMode(YELLOW_LED, OUTPUT);
  pinMode(RED_LED,    OUTPUT);

  // LEDのモード表示 1212ABAB
  controlModeLed(MODE_1_2);
}

void loop() {
  // モードを記憶しておく変数(初期値はMODE_1_2にしておく)
  static uint8_t keyboard_mode = MODE_1_2;

  // --------- モードスイッチの処理 ---------
  migiSwitch.poll();

  // スイッチ状態に応じてモードとLED点灯状態変更
  if( migiSwitch.singleClick() ) {  // シングルクリックの場合
    keyboard_mode = MODE_A_B;
    controlModeLed(MODE_A_B);
  }
  if( migiSwitch.doubleClick() ) {  // ダブルクリックの場合
    keyboard_mode = MODE_COPY_PASTE;
    controlModeLed(MODE_COPY_PASTE);
  }
  if( migiSwitch.longPress() ) {  // 長押しの場合
    keyboard_mode = MODE_1_2;
    controlModeLed(MODE_1_2);
  }


  //  --------- 左側スイッチの処理---------
  hidariSwitch.poll();
  
  // モードに応じて左側スイッチの処理を行う
  if( hidariSwitch.pushed() ){
    switch( keyboard_mode ) {
      case MODE_1_2:  // 1と2の場合
      Keyboard.print("1");
      break;

      case MODE_A_B:  // AとBの場合
      Keyboard.print("A");
      break;

      case MODE_COPY_PASTE:  // コピーペーストの場合
      Keyboard.press(shortcutKey);  // Command/Ctrlキーを押す
      Keyboard.press('c');          // 'c'キーを押す
      delay(100);                   // ちょっと待つ
      Keyboard.releaseAll();        // 両方のキーを離す
      break;
    }
  }


  //  --------- 中央スイッチスイッチの処理---------
  chuoSwitch.poll();

  // モードに応じて中央スイッチの処理を行う
  if( chuoSwitch.pushed() ){
    switch( keyboard_mode ) {
      case MODE_1_2:  // 1と2の場合
      Keyboard.print("2");
      break;

      case MODE_A_B:  // AとBの場合
      Keyboard.print("B");
      break;

      case MODE_COPY_PASTE:  // コピーペーストの場合
      Keyboard.press(shortcutKey);  // Command/Ctrlキーを押す
      Keyboard.press('v');          // 'v'キーを押す
      delay(100);                   // ちょっと待つ
      Keyboard.releaseAll();        // 両方のキーを離す
      break;
    }
  }

}

更新履歴

日付内容
2021.10.4新規投稿
2025.2.16チャレンジ課題内容一部変更
解答例追加
通知の設定
通知タイミング
guest
0 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
目次