親指 で操作する！ タクトスイッチで作る自作キーボード

## １．はじめに
「親指の潜在能力に気づいていますか？」
　スペースキーを押すだけの親指ではもったいない。 
　ホームポジションから手を移動させずに、頻繁に使うキーを快適に操作できる、
　親指活用型の自作キーボードを作りました。
　ここではその紹介と製作方法を詳しくご説明します。

※注意事項: 製作基板は販売しておりません。展示会で無料配布予定です。
## 2．開発経緯・紹介
 （１）開発経緯
　自作キーボードの製作を考えたきっかけは、作業効率を改善したいと思ったことです。
　よく使用するキーがキーボード中央から離れているため、指の基本位置（ﾎｰﾑﾎﾟｼﾞｼｮﾝ)
 　から移動してしまい、次の入力が遅れることが気になっていました。
　既存の製品では満足いくものを見つけることができなかったため、
  　「スペースキーしか押していない親指をもっと活用してみよう！」というアイデアから
   　製作を始めました。


（２）紹 介
　①製作した自作キーボードは、次の写真の赤枠部分です。
　　※スペースキーの下にあり、親指で押せる範囲に配置しています。
　　![25.06.01 自作キーボード1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/801125/7c14a45c-afc5-4893-8aa0-55ace1d8a2c1.jpeg)
　　※キースイッチは、タクトスイッチで11個あります。
  　　　11個のうち4個にタクトスイッチキャップ(黒色)を付けています。

  
　②各キースイッチの機能説明は以下の表にまとめています。
　　※プログラムの書き換えでカスタマイズ可能です。
＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝  
【　通　常　】
|　上 側キー |Ctrl　|HOME　  |上矢印　|　END　| ( |　)　　　
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|　下 側キー |  Alt  |左矢印  |下矢印　|右矢印|'　又は　"|

　　 **'**　又は　**"**　は、短く押すと、**'**　 長く押すと、 **"**　が入力されます。

＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝
【　左上のCtrlキーを押しながら、他のキーを押すと、次の通り変更されます。】
|　上 側キー  |Ctrl|Ctrl+PgUp  |Win+上|Ctrl+PgDn|｛　　　 | 　｝　　
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|　下 側キー  |  [なし]  |Win+左  |Alt+Tab|Win+右|[なし] |

　Win＋上・左・右は、ウィンドウ画面配置操作です。
　　・Win＋上：画面を最大化、長押しで画面を閉じる。
　　・Win＋左：画面を左に配置する。
　　・Win＋右：画面を右に配置する。

　・Alt+Tab：アプリ切り替えで現在のアプリを変更する。
＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝

## 3.作り方
（１）材料
　・基板 1枚(非売品)
　・マイコンボード　1個：Seeed Studio XIAO RP2040 　 (注意！ RP2040です。)
　・タクトスイッチ 11個：おすすめは秋月電子通商 ﾀｸﾄｽｲｯﾁｾｯﾄ100個【販売ｺｰﾄﾞ101282】
　・タクトスイッチキャップ 数個：個人の好みで取り付けてください。(アマゾン等で購入)
　・【ねじ(サイズM2) 6本】または【両面テープ】  (設置固定で使用、個人の考えで選択)
　　　注意：ねじ使用の場合、キーボードを破損する恐れがあるため自己責任で使用のこと

（２）手順
　①タクトスイッチの端子線をニッパーで短く切断する。
 　　(はんだ付け後に切断すると、端子線が出っ張るため)
　　試行錯誤して適切な切断長さを把握してください。
  　　(タクトスイッチ1個切断し、基板に取付けて確認、必要に応じて更に切断)
   　　　 　切断前　　　　　　切断後
　　![25.06.01 自作キーボード2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/801125/b46454f5-ec6a-4af8-8887-4dcf4769a44e.jpeg)

　②はんだ付け
　　・マイコンボードを基板にはめ込み、ハンダ付けにて基板と接続する。
　　・最後に、タクトスイッチをはんだ付けする。
　　　※しっかりと差込み、浮き上がりがないように 注意してください。 
  
  　　【はんだ付け前】(基板 と マイコンボード)
　　![25.06.01 自作キーボード3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/801125/b9bb814f-f72c-402a-bc42-e4b1f6dc4b7d.jpeg)

  　　【はんだ付け後】
　　![25.06.01 自作キーボード4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/801125/40470f51-256d-41e8-9d9c-7769e7bab65f.jpeg)

　
 ③プログラムの書き込み(ソフトはArduino IDE)

## プログラム内容
```cpp:202505_keyboard.ino
#include <Keyboard.h>

// キーボード操作の基本関数
struct Key {
  static void press(uint8_t key) { Keyboard.press(key); delay(50); }    // キーを押し続ける
  static void clear() { Keyboard.releaseAll(); }                        // 全てのキーを離す
  static void wait(int n) { delay(100 * n); }                          // 待機（100ms単位）
  static void tap(uint8_t key) {                                       // キーを1回押す
    Keyboard.press(key);
    delay(50);
    Keyboard.release(key);
    delay(100);
  }
  static void tapF8(uint8_t key) {                                     // 指定した文字を入力し、半角に変換
    Keyboard.press(key);                                               // 文字を入力
    delay(50);
    Keyboard.release(key);
    delay(100);                                                        // 待機時間を増やす
    
    Key::clear();                                                      // F8を押す前に全てのキーをクリア
    Keyboard.press(KEY_F8);                                           // F8で半角に変換
    delay(50);
    Keyboard.release(KEY_F8);
    delay(100);                                                        // 待機時間を増やす
    Key::clear();                                                      // 確実にキーをリリース
  }
};

// digitalReadを短縮した関数（デジタルピンの状態を読み取る）
inline bool pin(uint8_t p) { return digitalRead(p); }

void setup() {
  Keyboard.begin();
  // INPUT_PULLDOWN: 内部のプルダウン抵抗を有効にし、以下を実現
  // - ボタンが押されていない時：確実にLOW(0)を読み取る
  // - ボタンが押された時：電源のHIGH(1)を読み取る
  // - チャタリングやノイズによる誤動作を防止
  // 各ピンを個別に設定（forループは使用しない）
  pinMode(D0, INPUT_PULLDOWN);
  pinMode(D1, INPUT_PULLDOWN);
  pinMode(D2, INPUT_PULLDOWN);
  pinMode(D3, INPUT_PULLDOWN);
  pinMode(D4, INPUT_PULLDOWN);
  pinMode(D5, INPUT_PULLDOWN);
  pinMode(D6, INPUT_PULLDOWN);
  pinMode(D7, INPUT_PULLDOWN);
  pinMode(D8, INPUT_PULLDOWN);
  pinMode(D9, INPUT_PULLDOWN);
  pinMode(D10, INPUT_PULLDOWN);
}


// キーの長押し時間をカウントする変数（単位：100ms）
// 例：holdTime = 3 は300ms経過を意味する
int holdTime = 0;

void loop() {
  // 単独キー操作（他のキーと組み合わせない、基本的なキー入力）
  if (pin(D2)) Key::tap(KEY_RIGHT_ARROW);                                    // →
  if (pin(D3)) Key::tap(KEY_DOWN_ARROW);                                    // ↓
  if (pin(D4)) Key::tap(KEY_LEFT_ARROW);                                    // ←
  if (pin(D8)) Key::tap(KEY_UP_ARROW);                                     // ↑
  if (pin(D9)) Key::tap(KEY_END);                                          // End
  if (pin(D10)) { Key::press(KEY_LEFT_SHIFT); Key::tapF8('8'); }          // Shift + 8 → (
  if (pin(D0)) { Key::press(KEY_LEFT_SHIFT); Key::tapF8('9'); }           // Shift + 9 → )

  // 長押し判定による記号入力
  // - 300ms未満：Shift + 7 → '
  // - 300ms以上：Shift + 2 → "
  while (pin(D1)) {
    holdTime++;                                                            // 100msごとにカウントアップ
    Key::wait(1);
    if (!pin(D1)) {
      Key::press(KEY_LEFT_SHIFT);
      Key::tapF8(holdTime < 3 ? '7' : '2');
      break;
    }
  }

  // Altキー（押している間のみ有効）
  while (pin(D5)) {
    Key::press(KEY_LEFT_ALT);
  }

  // Homeキーまたはクリップボード履歴
  // - 500ms未満：Home
  // - 500ms以上：Win + v（クリップボード履歴）
  while (pin(D7)) {
    holdTime++;
    Key::wait(1);
    if (!pin(D7)) {
      if (holdTime < 5) {                                              
        Key::tap(KEY_HOME);
      } else {
        Key::press(KEY_LEFT_GUI);
        Key::tap('v');
      }
      break;
    }
  }

  // 複合キー操作（D6キーと他のキーの組み合わせ）
  while (pin(D6)) {
    Key::press(KEY_LEFT_CTRL);
    
    // 記号入力
    if (pin(D10)) { Key::clear(); Key::tapF8('}'); }                      // 入力される文字：{
    if (pin(D0)) { Key::clear(); Key::tapF8('|'); }                       // 入力される文字：}
    
    // ページ移動
    if (pin(D7)) Key::tap(KEY_PAGE_UP);                                   // Ctrl + PageUp
    if (pin(D9)) Key::tap(KEY_PAGE_DOWN);                                 // Ctrl + PageDown
    
    // ウィンドウ操作（Win + 方向キー）
    if (pin(D2)) { Key::clear(); Key::press(KEY_LEFT_GUI); Key::tap(KEY_RIGHT_ARROW); }  // Win + → （画面右側に寄せる）
    if (pin(D4)) { Key::clear(); Key::press(KEY_LEFT_GUI); Key::tap(KEY_LEFT_ARROW); }   // Win + ← （画面左側に寄せる）

    // ウィンドウ操作（長押し判定）
    // - 500ms未満：Win + ↑ （ウィンドウを最大化）
    // - 500ms以上：Alt + F4（アプリケーションを終了）
    if (pin(D8)) {
      Key::clear();
      Key::press(KEY_LEFT_GUI);
      Key::tap(KEY_UP_ARROW);
      Key::clear();
      Key::wait(5);                                                       // 500ms待機
      if (pin(D8)) {
        Key::press(KEY_LEFT_ALT);
        Key::tap(KEY_F4);
      }
    }
    
    // アプリケーション切り替え（Alt + Tab）
    if (pin(D3)) {
      Key::clear();
      Key::press(KEY_LEFT_ALT);
      Key::tap(KEY_TAB);
      while (pin(D6)) {
        if (pin(D3)) Key::tap(KEY_TAB);
      }
    }

  }

  Key::clear();                                                          // 全てのキーを解放
  holdTime = 0;                                                          // 長押しカウンタをリセット
} 
```

以下は過去のプログラムです。
参考になると思い、改訂の際、削除せずに残しました。

```cpp:20240709_keyboard22_11_03.ino
#include <Keyboard.h>

void setup() {
  Serial.begin(9600);
  Keyboard.begin();
  pinMode(D0,INPUT_PULLDOWN);
  pinMode(D1,INPUT_PULLDOWN);
  pinMode(D2,INPUT_PULLDOWN);
  pinMode(D3,INPUT_PULLDOWN);
  pinMode(D4,INPUT_PULLDOWN);
  pinMode(D5,INPUT_PULLDOWN);
  pinMode(D6,INPUT_PULLDOWN);
  pinMode(D7,INPUT_PULLDOWN);
  pinMode(D8,INPUT_PULLDOWN);
  pinMode(D9,INPUT_PULLDOWN);
  pinMode(D10,INPUT_PULLDOWN);
             }

//共通内容を関数にした。
void ctrl() {Keyboard.press(KEY_LEFT_CTRL);  delay(50);}
void shift(){Keyboard.press(KEY_LEFT_SHIFT); delay(50);}
void alt()  {Keyboard.press(KEY_LEFT_ALT);   delay(50);}
void win()  {Keyboard.press(KEY_LEFT_GUI);   delay(50);}
void r(){Keyboard.releaseAll();}
void com1(char a){Keyboard.print(a); delay(150);} 
void com2(char a){Keyboard.print(a); r(); delay(150); Keyboard.press(KEY_F8);} 
// KEY_F8は 日本語入力の際、全角から半角に変換するためのものです。

void loop() {

  if( digitalRead(D1) ){shift(); com2('-');   }    // ＝
  if( digitalRead(D2) ){com1(KEY_RIGHT_ARROW);}    // 右矢印
  if( digitalRead(D3) ){com1(KEY_DOWN_ARROW); }    // 下矢印
  if( digitalRead(D4) ){com1(KEY_LEFT_ARROW); }    // 左矢印
  if( digitalRead(D5) ){shift(); com2('2');   }    // ”
  if( digitalRead(D7) ){com1(KEY_HOME);       }    // HOME
  if( digitalRead(D8) ){com1(KEY_UP_ARROW);   }    // 上矢印
  if( digitalRead(D9) ){com1(KEY_END);        }    // END
  if( digitalRead(D10)){shift(); com2('8');   }    // （
  if( digitalRead(D0) ){shift(); com2('9');   }    //  )
  
  while( digitalRead(D6) ){
        ctrl();                                                     // Ctrl
        if( digitalRead(D10))  {r(); com2('}');}                    // {      D6+D10にて
        if( digitalRead(D0) )  {r(); com2('|');}                    // }      D6+D0にて
        if( digitalRead(D5) )  {r(); com2('&');}                    // ’      D6+D5にて
        if( digitalRead(D7) )  {com1(KEY_PAGE_UP);    }       // Ctrl+PAGE_UP D6+D7にて
        if( digitalRead(D9) )  {com1(KEY_PAGE_DOWN);  }     // Ctrl+PAGE_DOWN D6+D9にて
        if( digitalRead(D2) )  {r(); win(); com1(KEY_RIGHT_ARROW);} // Win+右 D6+D2にて
        if( digitalRead(D4) )  {r(); win(); com1(KEY_LEFT_ARROW); } // Win+左 D6+D4にて
        if( digitalRead(D8) )  {r(); win(); com1(KEY_UP_ARROW); r(); delay(500); if( digitalRead(D8)) {alt();com1(KEY_F4); } } //D6+D8
        if( digitalRead(D3) )  {r(); alt(); com1(KEY_TAB);while( digitalRead(D6) ){ if(digitalRead(D3)){com1(KEY_TAB);}}} //D6+D3
//上記 D6+D8 Win＋上。0.5秒以上押し続けるとアプリ終了
//上記 D6+D3 Alt+Tab  アプリの切り替え 
                          }
r(); 
}
```


```cpp:20240405_keyboard22_11_03.ino
#include <Keyboard.h>

#define IN0  D0
#define IN1  D1
#define IN2  D2
#define IN3  D3
#define IN4  D4
#define IN5  D5
#define IN6  D6
#define IN7  D7
#define IN8  D8
#define IN9  D9
#define IN10  D10

void setup() {
  Serial.begin(9600);
  Keyboard.begin();
  pinMode(IN0,INPUT_PULLDOWN);
  pinMode(IN1,INPUT_PULLDOWN);
  pinMode(IN2,INPUT_PULLDOWN);
  pinMode(IN3,INPUT_PULLDOWN);
  pinMode(IN4,INPUT_PULLDOWN);
  pinMode(IN5,INPUT_PULLDOWN);
  pinMode(IN6,INPUT_PULLDOWN);
  pinMode(IN7,INPUT_PULLDOWN);
  pinMode(IN8,INPUT_PULLDOWN);
  pinMode(IN9,INPUT_PULLDOWN);
  pinMode(IN10,INPUT_PULLDOWN);
  
}

void loop() {

  if( digitalRead(IN1) )        {
        Keyboard.press(KEY_LEFT_SHIFT);
        delay(100);
        Keyboard.print("-");
        delay(100);
        Keyboard.releaseAll();  }
   
  if( digitalRead(IN2) ){
        Keyboard.press(KEY_RIGHT_ARROW);
        delay(150);     }

  if( digitalRead(IN3) ){
        Keyboard.press(KEY_DOWN_ARROW);
        delay(150);     }

  if( digitalRead(IN4) ){
        Keyboard.press(KEY_LEFT_ARROW);
        delay(150);     }

  if( digitalRead(IN5) ){
        Keyboard.press(KEY_LEFT_CTRL);
        delay(200);     }
        else{Keyboard.releaseAll();}

  if( digitalRead(IN6) )        {
        Keyboard.press(KEY_LEFT_SHIFT);
        delay(100);
        Keyboard.print("2");
        delay(100);
        Keyboard.releaseAll();  }
 
  if( digitalRead(IN7) ){
        Keyboard.press(KEY_HOME);
        delay(200);     }

  if( digitalRead(IN8) ){
        Keyboard.press(KEY_UP_ARROW);
        delay(150);     }
  
  if( digitalRead(IN9) ){
        Keyboard.press(KEY_END);
        delay(200);     }

  if( digitalRead(IN10) )       {
      Keyboard.press(KEY_LEFT_SHIFT);
      delay(100);
      Keyboard.print("8");
      delay(100);
      Keyboard.releaseAll();    }

  if( digitalRead(IN0) )        {
      Keyboard.press(KEY_LEFT_SHIFT);
      delay(100);
      Keyboard.print("9");
      delay(100);
      Keyboard.releaseAll();    }

}
```
以上となります。