« Arduino Micro を使って、試しに USB スライドパッド マウスを作ってみた(小型USBキーボード自作のための準備色々) | トップページ

2016年2月10日 (水)

Arduino Micro を使って、USB 小型 キーボードを自作 (製作途中、暫定公開)

小型USBキーボード自作の途中経過、詳しい説明は後日。とりあえず動きました。

・過去のブログ記事「USBキーボードは自作できるのか下調べをしてみる」と合わせて読んでください。

2017/02/22:キーボードLED関連を追記、下の方に。
2017/06/09:さらに下の方に、動画追加、暫定、詳しいことはまたそのうち書きます。
2017/10/17:雑感を下のほうに追加。

※注意!:私、はっきり言って、ほぼ素人です!!!、電子工作をちょろっとと、無印 C言語を少しかじったことがあるだけです。間違い等がある可能性が十分にありますので、ここはあくまで参考程度として、みなさん各自で試行錯誤してくださいませ。ここの間違い指摘は大歓迎です。

画像クリックで拡大します。

・使用したマイコンボードは、 Arduinoシリーズで、USB対応の ATmega32U4 を搭載した Arduino Micro

Pict0008

・使用している Aruduino の開発環境は、http://www.arduino.org/Arduino IDE 1.7.x

・今現在想定している、キー配置。QWERTY英語配列。
Image

・現在のキーマトリクス
_01

・回路図(結線図)
Image01
・Arduino は、デフォルトでアナログ入出力に設定されている I/Oピンを、デジタル入出力としても使用可能です。https://www.arduino.cc/en/Tutorial/RowColumnScanning
・但し、そのピンの対比は従来のArduino と Arduino Leonardo & Micro では異なっています。
・\Arduino\hardware\arduino\avr\variants\leonardo\pins_arduino.h
と \Arduino\hardware\arduino\avr\variants\micro\pins_arduino.h
に書かれていました。(A0 - A5 は、D18 - D23)

// ATMEL ATMEGA32U4 / ARDUINO LEONARDO
//
// D0                PD2                    RXD1/INT2
// D1                PD3                    TXD1/INT3
// D2                PD1        SDA         SDA/INT1
// D3#               PD0        PWM8/SCL    OC0B/SCL/INT0
// D4        A6      PD4                    ADC8
// D5#               PC6        ???         OC3A/#OC4A
// D6#       A7      PD7        FastPWM     #OC4D/ADC10
// D7                PE6                    INT6/AIN0
//
// D8        A8      PB4                    ADC11/PCINT4
// D9#       A9      PB5        PWM16       OC1A/#OC4B/ADC12/PCINT5
// D10#      A10     PB6        PWM16       OC1B/0c4B/ADC13/PCINT6
// D11#              PB7        PWM8/16     0C0A/OC1C/#RTS/PCINT7
// D12       A11     PD6                    T1/#OC4D/ADC9
// D13#              PC7        PWM10       CLK0/OC4A
//
// A0        D18     PF7                    ADC7
// A1        D19     PF6                    ADC6
// A2        D20     PF5                    ADC5
// A3        D21     PF4                    ADC4
// A4        D22     PF1                    ADC1
// A5        D23     PF0                    ADC0
//

・D12ピンにスイッチ付けてあります、プルアップ入力でグランド落とし。これは Arduino Microからのキーボード情報の出力のON/OFFに使用。一度プログラムミスで、でたらめなキー出力が止まらなくなって酷い目に遭ったので、その対策です。
・キーマトリクスの出力側ですが、最初は負論理の 74HC138 を使用して組んでみたのですが、動かしてみたら、マトリクスの同一ラインのキー同時押し(例:左シフトキーと CapsLock キー)が認識出来ないことが分かって、正論理の 74HC238 に交換しました。
・今回はアドレスデコーダを使用していますが、シフトレジスタの 74HC16474HC595 でも構いません、というかシフトレジスタのほうがいいかも。
・74HC238を使っているのは、ただの個人的な気分です。出力を決め打ち出来るのが気に入ってます。
・キースイッチからArduinoへの入力は抵抗を介してグランドに落としてプルダウンしてください。しないと不定になります(なりました。ので追加しました。負論理の74HC138を使っていたときは入力をプルアップで使用していたので気がつきませんでした)

・今回使用したタクトスイッチ。かなり昔にジャンクで購入したもの。77個、77key

Pict00119

・回路とプログラムの検証用に試験的に組んだ、自作の小型キーボード。キートップはまだ未製作。押しづらいですが動作確認は出来ます。

Image00011

Pict00014

・ブレッドボードに Arduino Micro と 74HC238 x2 を配置。

Pict000111

・キーボードは、キーピッチ 10mm x 10mm、横幅約14cm
・2mmピッチのユニバーサル基板とタクトスイッチで手作り、かなりいい加減で行き当たりばったりです。

Pict00115

・裏のマトリクス配線はぐちゃぐちゃ、ですが、誤配線は無し。

Pict00118

 

・Arduino Micro 用スケッチ(ソースプログラム)
ここから先のソースコードは、Arduino IDE 1.7.x や1.6.x 初期の、
#include "Keyboard.h"
#include "Mouse.h"

の記載が必要ないArduino IDE のバージョンでのみ動作します。
Arduino IDE 1.6.x の途中から及び1.8.xでは USB HID に関係する部分が大きく変更されているために、ここからのスケッチは使用不可能です。

// set pin numbers for keyboard active switch, and LED:
const int switchPin = 12;            // switch to turn on and off keyboard control
const int ledPin = 13;               // keyboard active LED

// keyboard active switch:
boolean keyboardIsActive = false;    // whether or not to control the keyboard
int lastSwitchState = HIGH;          // previous switch state

// key matrix out pin numbers:
// Please refer to \Arduino\hardware\arduino\avr\variants\leonardo\pins_arduino.h
// and \Arduino\hardware\arduino\avr\variants\micro\pins_arduino.h for the digital-out port of ArduinoMicro.
const int keyMatrixOutPin[4] = {
  20, 21, 22, 23
};

// key matrix in pin numbers:
const int keyMatrixInPin[8] = {
  2, 3, 4, 5, 6, 7, 8, 9
};

const int keyScanColMax = 14;
const int keyScanRowMax = 8;

const int keyFnCol = 0;
const int keyFnRow = 0;


boolean keyPlessMap[keyScanColMax][keyScanRowMax];

byte pressKey[6];
byte keyID = 0x00;
int pressKeyCount;

byte keyRept[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte previousKeyRept[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

int i, j;

const byte keyMatrixIdMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x50, 0x52, 0x51, 0x4F, 0x00 },   // Fn, 00, 00, LeftArrow, UpArrow, DownArrow, RightArrow, 00

 { 0x00, 0x42, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, F9, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x43, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // F10, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x4C, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, DEL, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x3A, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F1, 00, X, 00, S, CapsLock, W

 { 0x20, 0x3B, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F2, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x41, 0xE7, 0x37, 0x00, 0x0F, 0x40, 0x12 },   // 9, F8, RightGUI, ., 00, L, F7, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};

/*
const byte keyMatrixNumLkMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x50, 0x52, 0x51, 0x4F, 0x00 },   // Fn, 00, 00, LeftArrow, UpArrow, DownArrow, RightArrow, 00

 { 0x00, 0x42, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, F9, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x43, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // F10, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x4C, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, DEL, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x3A, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F1, 00, X, 00, S, CapsLock, W

 { 0x20, 0x3B, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F2, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x41, 0xE7, 0x37, 0x00, 0x0F, 0x40, 0x12 },   // 9, F8, RightGUI, ., 00, L, F7, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};
*/

const byte keyMatrixFnMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x4A, 0x4B, 0x4E, 0x4D, 0x00 },   // Fn, 00, 00, Home, PgUp, PgDn, End, 00

 { 0x00, 0x48, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, Pause, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x53, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // NumLk, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x49, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, INS, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x44, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F11, 00, X, 00, S, CapsLock, W

 { 0x20, 0x45, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F12, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x47, 0xE7, 0x37, 0x00, 0x0F, 0x46, 0x12 },   // 9, ScrLk, RightGUI, ., 00, L, PrtSc, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};




void setup() {

  // initialize I/O pins:
  
  // initialize the keyboard active switch pins:
  pinMode(switchPin, INPUT_PULLUP);      // the keyboard active switch pin
  pinMode(ledPin, OUTPUT);               // the LED pin
  
  // initialize the key output pins:
  for (int thisPin = 0; thisPin < 4; thisPin++) {
    pinMode(keyMatrixOutPin[thisPin], OUTPUT);
  }
  // initialize the key input pins:
  for (int thisPin = 0; thisPin < 8; thisPin++) {
    pinMode(keyMatrixInPin[thisPin], INPUT);
  }
  
  Serial.begin(9600);
  
  Keyboard.begin();
  
}

void loop() {
  
  // read the activate switch:
  int switchState = digitalRead(switchPin);
  // if it's changed and it's high, toggle the keyboard state:
  if (switchState != lastSwitchState) {
    if (switchState == LOW) {
      keyboardIsActive = !keyboardIsActive;
      // turn on LED to indicate keyboard state:
      digitalWrite(ledPin, keyboardIsActive);
    }
  }
  // save switch state for next comparison:
  lastSwitchState = switchState;


  if (keyboardIsActive) {



    // key matrix scan:
    for (int keyScanCol = 0; keyScanCol < keyScanColMax; keyScanCol++) {
      
      // set 74HC238:
      for (int decoderBit = 0;  decoderBit < 4; decoderBit++) {
        digitalWrite(keyMatrixOutPin[decoderBit], bitRead(keyScanCol, decoderBit));
      }
      
      for (int keyScanRow = 0; keyScanRow < keyScanRowMax; keyScanRow++) {
        if (digitalRead(keyMatrixInPin[keyScanRow]) == HIGH ) {
          keyPlessMap[keyScanCol][keyScanRow] = 1;
        } else {
          keyPlessMap[keyScanCol][keyScanRow] = 0;
        }
      }
    }
    
    // ghost key check:
    
    /* Not been able to yet: */
    
    
    // set press key ID:
    
    keyRept[0] = 0x00;
    for (i = 0; i < 6; i++) {
      pressKey[i] = 0x00;
    }
    pressKeyCount = 0;
    
    
    for (int keyScanCol = 0; keyScanCol < keyScanColMax; keyScanCol++) {
      for (int keyScanRow = 0; keyScanRow < keyScanRowMax; keyScanRow++) {
        if (keyPlessMap[keyFnCol][keyFnRow] == 1) {    // pless Fn_Key:
          if (keyPlessMap[keyScanCol][keyScanRow] == 1) {
            keyID = keyMatrixFnMap[keyScanCol][keyScanRow];
          }
        } else {
          if (keyPlessMap[keyScanCol][keyScanRow] == 1) {
            keyID = keyMatrixIdMap[keyScanCol][keyScanRow];

            Serial.print(keyScanCol);
            Serial.print("\t");
            Serial.print(keyScanRow);
            Serial.print("\t");
            Serial.print(keyMatrixIdMap[keyScanCol][keyScanRow]);
            Serial.print("\n");
            Serial.print("keyID=");
            Serial.print(keyID, HEX);
            Serial.print("\n");

          }
        }
        
        // NumLk_Key mode:
        /* Not been able to yet: */

        // modifier key:
        if ((keyID >= 0xE0) && (keyID <= 0xE7)) {
          keyRept[0] |= ( 1 << (keyID - 0xE0));
          keyID = 0x00;
        }
        
        // Add keyID to the key report only if it's not already present
        // and if there is an empty slot.
        if ((keyID != 0x00) && (pressKeyCount <= 6 )) {
          pressKey[pressKeyCount] = keyID;
          pressKeyCount++;
          keyID = 0x00;
        }
      }
    }
    
    
    Serial.print("pressKey[]=");
    Serial.print("\t");
    for (int i = 0; i < 6; i++) {
      Serial.print(pressKey[i], HEX);
    }
    Serial.print("\n");
    
    
    for (i = 0; i < 6; i++) {
      if ((keyRept[i+2] != pressKey[0]) && (keyRept[i+2] != pressKey[1]) &&
          (keyRept[i+2] != pressKey[2]) && (keyRept[i+2] != pressKey[3]) &&
          (keyRept[i+2] != pressKey[4]) && (keyRept[i+2] != pressKey[5])) {
        keyRept[i+2] = 0x00;
      }
    }
    
/*
    Serial.print("reset keyRept"); 
    Serial.print("\n");
 
    Serial.print("pressKey[]=");
    Serial.print("\t");
    for (int i = 0; i < 6; i++) {
      Serial.print(pressKey[i], HEX);
    }
    Serial.print("\t");
 
    Serial.print("keyRept[]=");
    Serial.print("\t");
    for (int i = 0; i < 8; i++) {
      Serial.print(keyRept[i], HEX);
    }
    Serial.print("\n");
*/

    for (i = 0; i < 6; i++) {
      if ((pressKey[i] == keyRept[0+2]) || (pressKey[i] == keyRept[1+2]) || 
          (pressKey[i] == keyRept[2+2]) || (pressKey[i] == keyRept[3+2]) ||
          (pressKey[i] == keyRept[4+2]) || (pressKey[i] == keyRept[5+2])) {
        pressKey[i] = 0x00;
      }
    }
  
/*
    Serial.print("reset pressKey"); 
    Serial.print("\n");
 
    Serial.print("pressKey[]=");
    Serial.print("\t");
    for (int i = 0; i < 6; i++) {
      Serial.print(pressKey[i], HEX);
    }
    Serial.print("\t");
 
    Serial.print("keyRept[]=");
    Serial.print("\t");
    for (int i = 0; i < 8; i++) {
      Serial.print(keyRept[i], HEX);
    }
    Serial.print("\n");
*/

    for (i = 0; i < 6; i++){
      for (j = 0; j < 6; j++) {
        if ((keyRept[j+2] == 0x00) && (pressKey[i] != 0x00)) {
          keyRept[j+2] = pressKey[i];
          i++;
        }
      }
    }
    
    
    Serial.print("send keyRept[]=");
    Serial.print("\t");
      for (int i = 0; i < 8; i++) {
        Serial.print(keyRept[i], HEX);
      }
    Serial.print("\n");


    if ((keyRept[0] != previousKeyRept[0]) || (keyRept[1] != previousKeyRept[1]) ||
        (keyRept[2] != previousKeyRept[2]) || (keyRept[3] != previousKeyRept[3]) ||
        (keyRept[4] != previousKeyRept[4]) || (keyRept[5] != previousKeyRept[5]) ||
        (keyRept[6] != previousKeyRept[6]) || (keyRept[7] != previousKeyRept[7])) {
      
      HID_SendReport(2,keyRept,sizeof(keyRept));
      
      Serial.print("-------------------------------------");
      Serial.print("\n");
      
      Serial.print("sending keyRept[]=");
      Serial.print("\t");
      for (int i = 0; i < 8; i++) {
        Serial.print(keyRept[i], HEX);
      }
      Serial.print("\n");
      
      
      for (int i = 0; i < 8; i++) {
        previousKeyRept[i] = keyRept[i];
      }
      
    }
    
    
    Serial.print("---------------------------------------------");
    Serial.print("\n");
    
  }
  
}

一応動きますが、作りかけです。バグがある可能性は大いにあります。
2016/02/12:修正が必要な箇所がありました。若干書き直しました。

// set pin numbers for keyboard active switch, and LED:
const int switchPin = 12;            // switch to turn on and off keyboard control
const int ledPin = 13;               // keyboard active LED

// keyboard active switch:
boolean keyboardIsActive = false;    // whether or not to control the keyboard
int lastSwitchState = HIGH;          // previous switch state

// key matrix out pin numbers:
// Please refer to \Arduino\hardware\arduino\avr\variants\leonardo\pins_arduino.h
// and \Arduino\hardware\arduino\avr\variants\micro\pins_arduino.h for the digital-out port of ArduinoMicro.
const int keyMatrixOutPin[4] = {
  20, 21, 22, 23
};

// key matrix in pin numbers:
const int keyMatrixInPin[8] = {
  2, 3, 4, 5, 6, 7, 8, 9
};

const int keyScanColMax = 14;
const int keyScanRowMax = 8;

const int keyFnCol = 0;
const int keyFnRow = 0;


boolean keyPlessMap[keyScanColMax][keyScanRowMax];

byte pressKey[6];
byte keyID = 0x00;
int pressKeyCount;

byte keyRept[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte previousKeyRept[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

int i, j;

const byte keyMatrixIdMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x50, 0x52, 0x51, 0x4F, 0x00 },   // Fn, 00, 00, LeftArrow, UpArrow, DownArrow, RightArrow, 00

 { 0x00, 0x42, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, F9, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x43, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // F10, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x4C, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, DEL, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x3A, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F1, 00, X, 00, S, CapsLock, W

 { 0x20, 0x3B, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F2, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x41, 0xE7, 0x37, 0x00, 0x0F, 0x40, 0x12 },   // 9, F8, RightGUI, ., 00, L, F7, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};

/*
const byte keyMatrixNumLkMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x50, 0x52, 0x51, 0x4F, 0x00 },   // Fn, 00, 00, LeftArrow, UpArrow, DownArrow, RightArrow, 00

 { 0x00, 0x42, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, F9, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x43, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // F10, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x4C, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, DEL, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x3A, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F1, 00, X, 00, S, CapsLock, W

 { 0x20, 0x3B, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F2, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x41, 0xE7, 0x37, 0x00, 0x0F, 0x40, 0x12 },   // 9, F8, RightGUI, ., 00, L, F7, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};
*/

const byte keyMatrixFnMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x4A, 0x4B, 0x4E, 0x4D, 0x00 },   // Fn, 00, 00, Home, PgUp, PgDn, End, 00

 { 0x00, 0x48, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, Pause, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x53, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // NumLk, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x49, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, INS, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x44, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F11, 00, X, 00, S, CapsLock, W

 { 0x20, 0x45, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F12, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x47, 0xE7, 0x37, 0x00, 0x0F, 0x46, 0x12 },   // 9, ScrLk, RightGUI, ., 00, L, PrtSc, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};




void setup() {

  // initialize I/O pins:
  
  // initialize the keyboard active switch pins:
  pinMode(switchPin, INPUT_PULLUP);      // the keyboard active switch pin
  pinMode(ledPin, OUTPUT);               // the LED pin
  
  // initialize the key output pins:
  for (int thisPin = 0; thisPin < 4; thisPin++) {
    pinMode(keyMatrixOutPin[thisPin], OUTPUT);
  }
  // initialize the key input pins:
  for (int thisPin = 0; thisPin < 8; thisPin++) {
    pinMode(keyMatrixInPin[thisPin], INPUT);
  }
  
  Serial.begin(9600);
  
  Keyboard.begin();
  
}

void loop() {
  
  // read the activate switch:
  int switchState = digitalRead(switchPin);
  // if it's changed and it's high, toggle the keyboard state:
  if (switchState != lastSwitchState) {
    if (switchState == LOW) {
      keyboardIsActive = !keyboardIsActive;
      // turn on LED to indicate keyboard state:
      digitalWrite(ledPin, keyboardIsActive);
    }
  }
  // save switch state for next comparison:
  lastSwitchState = switchState;


  if (keyboardIsActive) {



    // key matrix scan:
    for (int keyScanCol = 0; keyScanCol < keyScanColMax; keyScanCol++) {
      
      // set 74HC238:
      for (int decoderBit = 0;  decoderBit < 4; decoderBit++) {
        digitalWrite(keyMatrixOutPin[decoderBit], bitRead(keyScanCol, decoderBit));
      }
      
      for (int keyScanRow = 0; keyScanRow < keyScanRowMax; keyScanRow++) {
        if (digitalRead(keyMatrixInPin[keyScanRow]) == HIGH ) {
          keyPlessMap[keyScanCol][keyScanRow] = 1;
        } else {
          keyPlessMap[keyScanCol][keyScanRow] = 0;
        }
      }
    }
    
    // ghost key check:
    
    /* Not been able to yet: */
    
    
    // reset keyRept[] and pressKey[] and pressKeyCount:
    keyRept[0] = 0x00;
    for (i = 0; i < 6; i++) {
      pressKey[i] = 0x00;
      keyRept[i+2] = 0x00;
    }
    pressKeyCount = 0;
    
    
    // set press key ID:
    for (int keyScanCol = 0; keyScanCol < keyScanColMax; keyScanCol++) {
      for (int keyScanRow = 0; keyScanRow < keyScanRowMax; keyScanRow++) {
        
        keyID = 0x00;
        if (keyPlessMap[keyFnCol][keyFnRow] == 1) {    // pless Fn_Key:
          if (keyPlessMap[keyScanCol][keyScanRow] == 1) {
            keyID = keyMatrixFnMap[keyScanCol][keyScanRow];

            Serial.print(keyScanCol);
            Serial.print("\t");
            Serial.print(keyScanRow);
            Serial.print("\t");
            Serial.print(keyMatrixFnMap[keyScanCol][keyScanRow]);
            Serial.print("\n");
            Serial.print("keyID=");
            Serial.print(keyID, HEX);
            Serial.print("\n");

          }
        } else {
          if (keyPlessMap[keyScanCol][keyScanRow] == 1) {
            keyID = keyMatrixIdMap[keyScanCol][keyScanRow];

            Serial.print(keyScanCol);
            Serial.print("\t");
            Serial.print(keyScanRow);
            Serial.print("\t");
            Serial.print(keyMatrixIdMap[keyScanCol][keyScanRow]);
            Serial.print("\n");
            Serial.print("keyID=");
            Serial.print(keyID, HEX);
            Serial.print("\n");

          }
        }
        
        // NumLk_Key mode:
        /* Not been able to yet: */
        
        // if plessed key is a modifier key:
        if ((keyID >= 0xE0) && (keyID <= 0xE7)) {
          keyRept[0] |= ( 1 << (keyID - 0xE0));
        } else if ((keyID != 0x00) && (pressKeyCount <= 6 )) {
          // Add keyID :
          pressKey[pressKeyCount] = keyID;
          pressKeyCount++;
        }
      }
    }
    
    
    Serial.print("pressKey[]=");
    Serial.print("\t");
    for (int i = 0; i < 6; i++) {
      Serial.print(pressKey[i], HEX);
    }
    Serial.print("\n");
    Serial.print("pressKeyCount=");
    Serial.print("\t");
    Serial.print(pressKeyCount);
    Serial.print("\n");
    





    for (i = 0; i < pressKeyCount; i++) {
      keyRept[i+2] = pressKey[i];
    }


    if ((keyRept[0] != previousKeyRept[0]) || (keyRept[1] != previousKeyRept[1]) ||
        (keyRept[2] != previousKeyRept[2]) || (keyRept[3] != previousKeyRept[3]) ||
        (keyRept[4] != previousKeyRept[4]) || (keyRept[5] != previousKeyRept[5]) ||
        (keyRept[6] != previousKeyRept[6]) || (keyRept[7] != previousKeyRept[7])) {
      
      HID_SendReport(2,keyRept,sizeof(keyRept));
      
      Serial.print("-------------------------------------");
      Serial.print("\n");
      
      Serial.print("sending keyRept[]=");
      Serial.print("\t");
      for (int i = 0; i < 8; i++) {
        Serial.print(keyRept[i], HEX);
      }
      Serial.print("\n");
      
      
      for (int i = 0; i < 8; i++) {
        previousKeyRept[i] = keyRept[i];
      }
      
    }
    
    
    Serial.print("---------------------------------------------");
    Serial.print("\n");
    
  }
  
}


・NumLock はまだ実装していません。
・ゴーストキーの判定はまだ組み込んでいません。
・\Arduino\hardware\arduino\avr\cores\arduino の中にある「HID.cpp」と「USBAPI.h」を参考、流用、改変して組み込んであります。
・標準で用意されている関数だけを使っています(但し一部イレギュラーな使い方をしています)
・本来なら7個目のキーが押された時にエラーを返さないといけないのですが、切り捨てて無視する事で省いてしまっています。
・デバッグ用に所々にシリアル出力を入れてあります。シリアルモニタで確認出来ます。必要が無くなったら外してください。

追記:2016/02/18
ArduinoのI/Oポートを節約したくて、キーマトリクスの出力側に、シフトレジスタの 74HC164 を使って組んでみました。
こっちのほうがより簡素で良いですね。

Image02

スケッチも変更。

// set pin numbers for keyboard active switch, and LED:
const int switchPin = 12;            // switch to turn on and off keyboard control
const int ledPin = 13;               // keyboard active LED

// keyboard active switch:
boolean keyboardIsActive = false;    // whether or not to control the keyboard
int lastSwitchState = HIGH;          // previous switch state

// key matrix out pin numbers:
// Please refer to \Arduino\hardware\arduino\avr\variants\leonardo\pins_arduino.h
// and \Arduino\hardware\arduino\avr\variants\micro\pins_arduino.h for the digital-out port of ArduinoMicro.

/*
const int keyMatrixOutPin[4] = {
  20, 21, 22, 23
};
*/

// out pin numbers to key matrix shift register :
const int shiftRegClkPin = 23;
const int shiftRegDataPin = 22;

// shift register output data:
const word shiftRegOutData = (B00000000 * 256) + B00000001;


// key matrix in pin numbers:
const int keyMatrixInPin[8] = {
  2, 3, 4, 5, 6, 7, 8, 9
};

const int keyScanColMax = 14;
const int keyScanRowMax = 8;

const int keyFnCol = 0;
const int keyFnRow = 0;


boolean keyPlessMap[keyScanColMax][keyScanRowMax];

byte pressKey[6];
byte keyID = 0x00;
int pressKeyCount;

byte keyRept[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte previousKeyRept[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

int i, j;

const byte keyMatrixIdMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x50, 0x52, 0x51, 0x4F, 0x00 },   // Fn, 00, 00, LeftArrow, UpArrow, DownArrow, RightArrow, 00

 { 0x00, 0x42, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, F9, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x43, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // F10, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x4C, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, DEL, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x3A, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F1, 00, X, 00, S, CapsLock, W

 { 0x20, 0x3B, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F2, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x41, 0xE7, 0x37, 0x00, 0x0F, 0x40, 0x12 },   // 9, F8, RightGUI, ., 00, L, F7, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};

/*
const byte keyMatrixNumLkMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x50, 0x52, 0x51, 0x4F, 0x00 },   // Fn, 00, 00, LeftArrow, UpArrow, DownArrow, RightArrow, 00

 { 0x00, 0x42, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, F9, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x43, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // F10, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x4C, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, DEL, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x3A, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F1, 00, X, 00, S, CapsLock, W

 { 0x20, 0x3B, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F2, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x41, 0xE7, 0x37, 0x00, 0x0F, 0x40, 0x12 },   // 9, F8, RightGUI, ., 00, L, F7, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};
*/

const byte keyMatrixFnMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x4A, 0x4B, 0x4E, 0x4D, 0x00 },   // Fn, 00, 00, Home, PgUp, PgDn, End, 00

 { 0x00, 0x48, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, Pause, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x53, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // NumLk, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x49, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, INS, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x44, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F11, 00, X, 00, S, CapsLock, W

 { 0x20, 0x45, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F12, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x47, 0xE7, 0x37, 0x00, 0x0F, 0x46, 0x12 },   // 9, ScrLk, RightGUI, ., 00, L, PrtSc, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};




void setup() {

  // initialize I/O pins:
  
  // initialize the keyboard active switch pins:
  pinMode(switchPin, INPUT_PULLUP);      // the keyboard active switch pin
  pinMode(ledPin, OUTPUT);               // the LED pin
  
  // initialize the key output pins:
/*
  for (int thisPin = 0; thisPin < 4; thisPin++) {
    pinMode(keyMatrixOutPin[thisPin], OUTPUT);
  }
*/
  pinMode(shiftRegClkPin, OUTPUT);
  pinMode(shiftRegDataPin, OUTPUT);
  digitalWrite(shiftRegDataPin, LOW);
  for (int i = 0; i < keyScanColMax; i++) {
    digitalWrite(shiftRegClkPin, HIGH);
    digitalWrite(shiftRegClkPin, LOW);
  }
  
  // initialize the key input pins:
  for (int thisPin = 0; thisPin < 8; thisPin++) {
    pinMode(keyMatrixInPin[thisPin], INPUT);
  }
  
  Serial.begin(9600);
  
  Keyboard.begin();
  
}

void loop() {
  
  // read the activate switch:
  int switchState = digitalRead(switchPin);
  // if it's changed and it's high, toggle the keyboard state:
  if (switchState != lastSwitchState) {
    if (switchState == LOW) {
      keyboardIsActive = !keyboardIsActive;
      // turn on LED to indicate keyboard state:
      digitalWrite(ledPin, keyboardIsActive);
    }
  }
  // save switch state for next comparison:
  lastSwitchState = switchState;


  if (keyboardIsActive) {



    // key matrix scan:
    for (int keyScanCol = 0; keyScanCol < keyScanColMax; keyScanCol++) {
      
/*
      // set 74HC238:
      for (int decoderBit = 0;  decoderBit < 4; decoderBit++) {
        digitalWrite(keyMatrixOutPin[decoderBit], bitRead(keyScanCol, decoderBit));
      }
*/
      // set 74HC164:
      digitalWrite(shiftRegDataPin, bitRead(shiftRegOutData, keyScanCol));
      digitalWrite(shiftRegClkPin, HIGH);
      digitalWrite(shiftRegClkPin, LOW);
      
      
      for (int keyScanRow = 0; keyScanRow < keyScanRowMax; keyScanRow++) {
        if (digitalRead(keyMatrixInPin[keyScanRow]) == HIGH ) {
          keyPlessMap[keyScanCol][keyScanRow] = 1;
        } else {
          keyPlessMap[keyScanCol][keyScanRow] = 0;
        }
      }
    }
    
    // ghost key check:
    
    /* Not been able to yet: */
    
    
    // reset keyRept[] and pressKey[] and pressKeyCount:
    keyRept[0] = 0x00;
    for (i = 0; i < 6; i++) {
      pressKey[i] = 0x00;
      keyRept[i+2] = 0x00;
    }
    pressKeyCount = 0;
    
    
    // set press key ID:
    for (int keyScanCol = 0; keyScanCol < keyScanColMax; keyScanCol++) {
      for (int keyScanRow = 0; keyScanRow < keyScanRowMax; keyScanRow++) {
        
        keyID = 0x00;
        if (keyPlessMap[keyFnCol][keyFnRow] == 1) {    // pless Fn_Key:
          if (keyPlessMap[keyScanCol][keyScanRow] == 1) {
            keyID = keyMatrixFnMap[keyScanCol][keyScanRow];

            Serial.print(keyScanCol);
            Serial.print("\t");
            Serial.print(keyScanRow);
            Serial.print("\t");
            Serial.print(keyMatrixFnMap[keyScanCol][keyScanRow]);
            Serial.print("\n");
            Serial.print("keyID=");
            Serial.print(keyID, HEX);
            Serial.print("\n");

          }
        } else {
          if (keyPlessMap[keyScanCol][keyScanRow] == 1) {
            keyID = keyMatrixIdMap[keyScanCol][keyScanRow];

            Serial.print(keyScanCol);
            Serial.print("\t");
            Serial.print(keyScanRow);
            Serial.print("\t");
            Serial.print(keyMatrixIdMap[keyScanCol][keyScanRow]);
            Serial.print("\n");
            Serial.print("keyID=");
            Serial.print(keyID, HEX);
            Serial.print("\n");

          }
        }
        
        // NumLk_Key mode:
        /* Not been able to yet: */
        
        // if plessed key is a modifier key:
        if ((keyID >= 0xE0) && (keyID <= 0xE7)) {
          keyRept[0] |= ( 1 << (keyID - 0xE0));
        } else if ((keyID != 0x00) && (pressKeyCount <= 6 )) {
          // Add keyID :
          pressKey[pressKeyCount] = keyID;
          pressKeyCount++;
        }
      }
    }
    
    
    Serial.print("pressKey[]=");
    Serial.print("\t");
    for (int i = 0; i < 6; i++) {
      Serial.print(pressKey[i], HEX);
    }
    Serial.print("\n");
    Serial.print("pressKeyCount=");
    Serial.print("\t");
    Serial.print(pressKeyCount);
    Serial.print("\n");
    





    for (i = 0; i < pressKeyCount; i++) {
      keyRept[i+2] = pressKey[i];
    }


    if ((keyRept[0] != previousKeyRept[0]) || (keyRept[1] != previousKeyRept[1]) ||
        (keyRept[2] != previousKeyRept[2]) || (keyRept[3] != previousKeyRept[3]) ||
        (keyRept[4] != previousKeyRept[4]) || (keyRept[5] != previousKeyRept[5]) ||
        (keyRept[6] != previousKeyRept[6]) || (keyRept[7] != previousKeyRept[7])) {
      
      HID_SendReport(2,keyRept,sizeof(keyRept));
      
      Serial.print("-------------------------------------");
      Serial.print("\n");
      
      Serial.print("sending keyRept[]=");
      Serial.print("\t");
      for (int i = 0; i < 8; i++) {
        Serial.print(keyRept[i], HEX);
      }
      Serial.print("\n");
      
      
      for (int i = 0; i < 8; i++) {
        previousKeyRept[i] = keyRept[i];
      }
      
    }
    
    
    Serial.print("---------------------------------------------");
    Serial.print("\n");
    
  }
  
}

動いてるっぽいです。間違えていたら教えてください。

追記:2016/02/23
・汎用ロジックICの電源の配線を図に入れてなかったので追加して図を差し替え。
・シフトレジスタの初期値を間違えてました(16ビット必要なのに8ビットだった)

・体調の不調のために期間は長くなってしまっていますが、ほぼ素人の私ですけれど実作業に掛かっている時間自体はかなり短くて、それでも一応実用に耐えるであろう回路とプログラムが作成出来たのは、すべて Arduino のおかげです。
Arduino は、とっても楽です。Arduino を開発、維持してくれている皆さんと、そのArduinoを使って色々遊んでネット上に情報を上げてくださっている皆さんに、本当に感謝です。ありがとうございます。

ぶっちゃけ単純な事しかやっていません。
ロジック回路の基本的なことやC言語の基礎的なことしか知らない私でも作れるのですから、USBキーボードの自作、ぜひ挑戦してみてください。情報集めと、配線作業や回路とプログラムのデバッグにかける根性(あきらめの悪さ)があれば、けっこう誰にでも、USBの入力デバイスが簡単に作れてしまいます。
上記スケッチ(プログラムソース)のキーマトリクスとキーコード(Usage ID)を対応させている配列の中身を、「Universal Serial Bus HID Usage Tables」の「Keyboard/Keypad Page (0x07)」にある「Usage ID」を元にして書き換えるだけで、好きなように自由にキー配列の設定が出来ます。
(但し無改造のArduino IDE の関数では上限が101(HID.cpp で LOGICAL_MAXIMUM (101) )になってしまっているため、日本語キーボード特有の「ろ(0x87)」「ひらがな・カタカナ(0x88)」「¥(0x89)」「変換(0x8A)」「無変換(0x8B)」は、このままでは使用出来ません)

自分好みのデバイスが作れるのって楽しいですよ。
特にキーボードはキーレイアウトを自分好みに自由自在に指定出来るのがとても良いです。

 

追記:前に作ったスライドパッドマウスと合わせてみた。

以前、試作した、スライドパッドマウス に、今回のキーボード部分を乗せてみました。
そして、ソフトウェアを統合してみました。

Pict00120

配線は次の図のようになっています。
Image03

キーマトリクスを変更

_02

スケッチ(プログラムソース)はキーボードとマウスを統合してみましたが、ただ混ぜただけなので、後々整理が必要だと思います。自分で読んでもなんか気持ち良くないです。

// set pin numbers for keyboard active switch, and LED:
const int switchPin = 12;            // switch to turn on and off keyboard control
const int ledPin = 13;               // keyboard active LED

// keyboard active switch:
boolean keyboardIsActive = false;    // whether or not to control the keyboard
int lastSwitchState = HIGH;          // previous switch state


// keyboard constant value:

// key matrix out pin numbers:
// Please refer to \Arduino\hardware\arduino\avr\variants\leonardo\pins_arduino.h
// and \Arduino\hardware\arduino\avr\variants\micro\pins_arduino.h for the digital-out port of ArduinoMicro.


// out pin numbers to key matrix shift register :
const int shiftRegClkPin = 23;
const int shiftRegDataPin = 22;

// shift register output data:
const word shiftRegOutData = (B00000000 * 256) + B00000001;


// key matrix in pin numbers:
const int keyMatrixInPin[8] = {
  2, 3, 4, 5, 6, 7, 8, 9
};

const int keyScanColMax = 14;
const int keyScanRowMax = 8;

const int keyFnCol = 0;
const int keyFnRow = 0;


boolean keyPlessMap[keyScanColMax][keyScanRowMax];

byte pressKey[6];
byte keyID = 0x00;
int pressKeyCount;

byte keyRept[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
//byte previousKeyRept[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

int i, j;

const byte keyMatrixIdMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x50, 0x52, 0x51, 0x4F, 0x00 },   // Fn, 00, 00, LeftArrow, UpArrow, DownArrow, RightArrow, 00

 { 0x00, 0x42, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, F9, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x43, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // F10, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x4C, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, DEL, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x3A, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F1, 00, X, 00, S, CapsLock, W

 { 0x20, 0x3B, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F2, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x41, 0x65, 0x37, 0x00, 0x0F, 0x40, 0x12 },   // 9, F8, AppKey, ., 00, L, F7, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};

/*
const byte keyMatrixNumLkMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x50, 0x52, 0x51, 0x4F, 0x00 },   // Fn, 00, 00, LeftArrow, UpArrow, DownArrow, RightArrow, 00

 { 0x00, 0x42, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, F9, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x43, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // F10, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x4C, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, DEL, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x3A, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F1, 00, X, 00, S, CapsLock, W

 { 0x20, 0x3B, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F2, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x41, 0x65, 0x37, 0x00, 0x0F, 0x40, 0x12 },   // 9, F8, AppKey, ., 00, L, F7, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};
*/

const byte keyMatrixFnMap[keyScanColMax][keyScanRowMax] =
{
 { 0x00, 0x00, 0x00, 0x4A, 0x4B, 0x4E, 0x4D, 0x00 },   // Fn, 00, 00, Home, PgUp, PgDn, End, 00

 { 0x00, 0x48, 0x00, 0x28, 0x00, 0x00, 0x2A, 0x00 },   // 00, Pause, 00, ENTER, 00, 00, BACKSPACE, 00

 { 0x53, 0x00, 0xE6, 0x00, 0xE2, 0x00, 0x00, 0x00 },   // NumLk, 00, RightAlt, 00, LeftAlt, 00, 00, 00

 { 0x3E, 0xE0, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00 },   // F5, LeftControl, 00, RightControl, 00, 00, 00, 00

 { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE1, 0x00 },   // 00, 00, 00, 00, 00, RightShift, LeftShift, 00

 { 0x00, 0x49, 0x00, 0x00, 0x2C, 0x00, 0xE3, 0x00 },   // 00, INS, 00, 00, SPACE, 00, LeftGUI, 00

 { 0x1E, 0x35, 0x00, 0x1D, 0x29, 0x04, 0x2B, 0x14 },   // 1, `, 00, Z, ESC, A, Tab, Q

 { 0x1F, 0x44, 0x00, 0x1B, 0x00, 0x16, 0x39, 0x1A },   // 2, F11, 00, X, 00, S, CapsLock, W

 { 0x20, 0x45, 0x00, 0x06, 0x3D, 0x07, 0x3C, 0x08 },   // 3, F12, 00, C, F4, D, F3, E

 { 0x21, 0x22, 0x05, 0x19, 0x0A, 0x09, 0x17, 0x15 },   // 4, 5, B, V, G, F, T, R

 { 0x24, 0x23, 0x11, 0x10, 0x0B, 0x0D, 0x1C, 0x18 },   // 7, 6, N, M, H, J, Y, U

 { 0x25, 0x2E, 0x00, 0x36, 0x3F, 0x0E, 0x30, 0x0C },   // 8, =, 00, ,, F6, K, ], I

 { 0x26, 0x47, 0xE7, 0x37, 0x00, 0x0F, 0x46, 0x12 },   // 9, ScrLk, RightGUI, ., 00, L, PrtSc, O

 { 0x27, 0x2D, 0x38, 0x31, 0x34, 0x33, 0x2F, 0x13 }    // 0, -, /, bslash, ', ;, [, P
};


// mouse constant value:

const int mouseLeftCol = 0;
const int mouseLeftRow = 2;
const int mouseRightCol = 0;
const int mouseRightRow = 1;
//const int mouseMiddleCol = ;
//const int mouseMiddleRow = ;
const int mouseScrollCol = 0;
const int mouseScrollRow = 7;


const int xAxis = A0;         // joystick X axis  
const int yAxis = A1;         // joystick Y axis

// send data when mouse wheel is move:
const int scrollWheelStop = 0x00;
const int scrollWheelUp   = 0x01;
const int scrollWheelDown = 0xFF;

// interval time for slow move:
const int pointerMoveInterval = 50;
const int scrollInterval = 500;

// parameters for reading the joystick:

const int xAnalogInMin = 160;
const int xAnalogInMax = 890;
const int yAnalogInMin = 140;
const int yAnalogInMax = 860;
const int threshold = 30;          // resting threshold


int xPointerMoveDirection = 0;
int yPointerMoveDirection = 0;

const int pointerMoveMaxValue = 3;
const int pointerSlowMoveRange = 5;
const int range = (pointerMoveMaxValue + pointerSlowMoveRange) * 2;  // output range of X or Y movement

int xSendAxisData = 0;
int ySendAxisData = 0;
int sendWheelData = 0;

// timer:
// unsigned long currentMillis;
unsigned long CurrentMillis;
unsigned long xPreviousMillis = 0;        // will store last time mouse move data was updated
unsigned long yPreviousMillis = 0;        // will store last time mouse move data was updated
unsigned long wheelPreviousMillis = 0;

// mouse mode change switch:
boolean scrollMode = false;

// response delay:
const int responseDelay = 3;


void setup() {

  // initialize I/O pins:
  
  // initialize the keyboard active switch pins:
  pinMode(switchPin, INPUT_PULLUP);      // the keyboard active switch pin
  pinMode(ledPin, OUTPUT);               // the LED pin
  
  // initialize the key output pins:
/*
  for (int thisPin = 0; thisPin < 4; thisPin++) {
    pinMode(keyMatrixOutPin[thisPin], OUTPUT);
  }
*/
  pinMode(shiftRegClkPin, OUTPUT);
  pinMode(shiftRegDataPin, OUTPUT);
  digitalWrite(shiftRegDataPin, LOW);
  for (int i = 0; i < keyScanColMax; i++) {
    digitalWrite(shiftRegClkPin, HIGH);
    digitalWrite(shiftRegClkPin, LOW);
  }
  
  // initialize the key input pins:
  for (int thisPin = 0; thisPin < 8; thisPin++) {
    pinMode(keyMatrixInPin[thisPin], INPUT);
  }
  
//Serial.begin(9600);
  
  Keyboard.begin();
  Mouse.begin();
  
}

void loop() {
  
  // read the activate switch:
  int switchState = digitalRead(switchPin);
  // if it's changed and it's high, toggle the keyboard state:
  if (switchState != lastSwitchState) {
    if (switchState == LOW) {
      keyboardIsActive = !keyboardIsActive;
      // turn on LED to indicate keyboard state:
      digitalWrite(ledPin, keyboardIsActive);
    }
  }
  // save switch state for next comparison:
  lastSwitchState = switchState;


  if (keyboardIsActive) {


    // key matrix scan:
    for (int keyScanCol = 0; keyScanCol < keyScanColMax; keyScanCol++) {
      
/*
      // set 74HC238:
      for (int decoderBit = 0;  decoderBit < 4; decoderBit++) {
        digitalWrite(keyMatrixOutPin[decoderBit], bitRead(keyScanCol, decoderBit));
      }
*/
      // set 74HC164:
      digitalWrite(shiftRegDataPin, bitRead(shiftRegOutData, keyScanCol));
      digitalWrite(shiftRegClkPin, HIGH);
      digitalWrite(shiftRegClkPin, LOW);
      
      
      for (int keyScanRow = 0; keyScanRow < keyScanRowMax; keyScanRow++) {
        if (digitalRead(keyMatrixInPin[keyScanRow]) == HIGH ) {
          keyPlessMap[keyScanCol][keyScanRow] = 1;
        } else {
          keyPlessMap[keyScanCol][keyScanRow] = 0;
        }
      }
    }
    
    // ghost key check:
    
    /* Not been able to yet: */
    
    
    // reset keyRept[] and pressKey[] and pressKeyCount:
    keyRept[0] = 0x00;
    for (i = 0; i < 6; i++) {
      pressKey[i] = 0x00;
      keyRept[i+2] = 0x00;
    }
    pressKeyCount = 0;
    
    
    // set press key ID:
    for (int keyScanCol = 0; keyScanCol < keyScanColMax; keyScanCol++) {
      for (int keyScanRow = 0; keyScanRow < keyScanRowMax; keyScanRow++) {
        
        keyID = 0x00;
        if (keyPlessMap[keyFnCol][keyFnRow] == 1) {    // pless Fn_Key:
          if (keyPlessMap[keyScanCol][keyScanRow] == 1) {
            keyID = keyMatrixFnMap[keyScanCol][keyScanRow];

/*
            Serial.print(keyScanCol);
            Serial.print("\t");
            Serial.print(keyScanRow);
            Serial.print("\t");
            Serial.print(keyMatrixFnMap[keyScanCol][keyScanRow]);
            Serial.print("\n");
            Serial.print("keyID=");
            Serial.print(keyID, HEX);
            Serial.print("\n");
*/

          }
        } else {
          if (keyPlessMap[keyScanCol][keyScanRow] == 1) {
            keyID = keyMatrixIdMap[keyScanCol][keyScanRow];

/*
            Serial.print(keyScanCol);
            Serial.print("\t");
            Serial.print(keyScanRow);
            Serial.print("\t");
            Serial.print(keyMatrixIdMap[keyScanCol][keyScanRow]);
            Serial.print("\n");
            Serial.print("keyID=");
            Serial.print(keyID, HEX);
            Serial.print("\n");
*/

          }
        }
        
        // NumLk_Key mode:
        /* Not been able to yet: */
        
        // if plessed key is a modifier key:
        if ((keyID >= 0xE0) && (keyID <= 0xE7)) {
          keyRept[0] |= ( 1 << (keyID - 0xE0));
        } else if ((keyID != 0x00) && (pressKeyCount <= 6 )) {
          // Add keyID :
          pressKey[pressKeyCount] = keyID;
          pressKeyCount++;
        }
      }
    }
    
    
/*
    Serial.print("pressKey[]=");
    Serial.print("\t");
    for (int i = 0; i < 6; i++) {
      Serial.print(pressKey[i], HEX);
    }
    Serial.print("\n");
    Serial.print("pressKeyCount=");
    Serial.print("\t");
    Serial.print(pressKeyCount);
    Serial.print("\n");
 
*/



    for (i = 0; i < pressKeyCount; i++) {
      keyRept[i+2] = pressKey[i];
    }
    HID_SendReport(2,keyRept,sizeof(keyRept));

/*
    if ((keyRept[0] != previousKeyRept[0]) || (keyRept[1] != previousKeyRept[1]) ||
        (keyRept[2] != previousKeyRept[2]) || (keyRept[3] != previousKeyRept[3]) ||
        (keyRept[4] != previousKeyRept[4]) || (keyRept[5] != previousKeyRept[5]) ||
        (keyRept[6] != previousKeyRept[6]) || (keyRept[7] != previousKeyRept[7])) {
 
      HID_SendReport(2,keyRept,sizeof(keyRept));
 
      Serial.print("-------------------------------------");
      Serial.print("\n");
 
      Serial.print("sending keyRept[]=");
      Serial.print("\t");
      for (int i = 0; i < 8; i++) {
        Serial.print(keyRept[i], HEX);
      }
      Serial.print("\n");
 
 
      for (int i = 0; i < 8; i++) {
        previousKeyRept[i] = keyRept[i];
      }
    }
*/
    
    
/*
    Serial.print("---------------------------------------------");
    Serial.print("\n");
*/
    

    // mouse:

    // read for I/O:
    int xReading = readAxis(xAxis, xAnalogInMin, xAnalogInMax);
    int yReading = readAxis(yAxis, yAnalogInMin, yAnalogInMax);
  
    // read the mouseModeSwitch:
//  int modeSwitchState = digitalRead(mouseScrollModeButton);

    int mouseModeSwitchState = keyPlessMap[mouseScrollCol][mouseScrollRow];
    
    if (mouseModeSwitchState == 1) {
      scrollMode = true;
      } else {
      scrollMode = false;
    }
    
    
    if (scrollMode == false) {
      // pointer mode:
      sendWheelData = scrollWheelStop;
      
      // set xAxis data for send:
      if (xReading == 0) {
        // center:
        xSendAxisData = 0;
      } else if (abs(xReading) <= pointerSlowMoveRange) {
        // slow:
        CurrentMillis = millis();
        xSendAxisData = pointerSlowMove(xReading, xPreviousMillis);
        if(xSendAxisData != 0) {
          xPreviousMillis = CurrentMillis;
        }
      } else {
        xSendAxisData = (xReading / abs(xReading)) * (abs(xReading) - pointerSlowMoveRange);
      }
      // set yAxis data for send:
      if (yReading == 0) {
        // center:
        ySendAxisData = 0;
      } else if (abs(yReading) <= pointerSlowMoveRange) {
        // slow:
        CurrentMillis = millis();
        ySendAxisData = pointerSlowMove(yReading, yPreviousMillis);
        if(ySendAxisData != 0) {
          yPreviousMillis = CurrentMillis;
        }
      } else {
        ySendAxisData = (yReading / abs(yReading)) * (abs(yReading) - pointerSlowMoveRange);
      }
    
    } else {
      // scroll mode:
      xSendAxisData = 0;
      ySendAxisData = 0;
      
      if (yReading == 0) {
        // center:
        sendWheelData = 0;
      } else {
        // scroll up and down:
        CurrentMillis = millis();
        int wheelMoveDirection = yReading / abs(yReading);
        if (CurrentMillis - wheelPreviousMillis < (scrollInterval / abs(yReading)) ) {
          sendWheelData = 0;
        } else {
          if (wheelMoveDirection < 0) {
            // scroll up:
            sendWheelData = scrollWheelUp;
          } else {
            //  scroll down:
            sendWheelData = scrollWheelDown;
          }
        }
        if(sendWheelData != 0) {
          wheelPreviousMillis = CurrentMillis;
        }
      }
    }
  
  

    // set mouse data for sending to USB:


    Mouse.move(xSendAxisData, ySendAxisData, sendWheelData);
    // read the mouse button and click or not click:
    setMouseButton(mouseLeftCol, mouseLeftRow, MOUSE_LEFT);
    setMouseButton(mouseRightCol, mouseRightRow, MOUSE_RIGHT);
//  setMouseButton(mouseMiddleCol, mouseMiddleRow, MOUSE_MIDDLE);
    
    
  }
  
//delay(responseDelay);
}


/*
 read axis data:
 */

int readAxis(int thisAxis, int analogInMin, int analogInMax) {
  // read the analog input:
  int analogInRange = analogInMax - analogInMin;
  int center = (analogInRange / 2) + analogInMin;
  
  int reading = analogRead(thisAxis);
  reading = constrain(reading, analogInMin, analogInMax);
  
  int distance = reading - center;
//int threshold = analogInRange / range;        // resting threshold
  // if the output reading is outside from the rest position threshold,  use it:
  if (abs(distance) < threshold) {
    distance = 0;
  } else {
    // map the reading from the analog input range to the output range:
    distance = map(reading, analogInMin, analogInMax, 0, range) - (range / 2);
  }
  // the Y axis needs to be inverted in order to
  // map the movemment correctly:
  if (thisAxis == yAxis) {
    distance = -distance;
  }
  return distance;  // return the distance for this axis:
}


/*
 cursor move low speed:
 */

int pointerSlowMove(int reading, unsigned long previousMillis) {
  unsigned long currentMillis = millis();
  if (reading == 0) {
    return 0;
  } else {
    int interval = pointerMoveInterval / abs(reading);
    int pointerMoveDirection = reading / abs(reading);
    if (currentMillis - previousMillis < interval) {
      return 0;
    } else {
      return pointerMoveDirection;
    }
  }
}


/*
 read the mouse button and click or not click:
 */

int setMouseButton(int keymapCol, int keymapRow, int mouseButton) {
  // if the mouse button is pressed:
  if (keyPlessMap[keymapCol][keymapRow] == 1) {
    // if the mouse is not pressed, press it:
    if (!Mouse.isPressed(mouseButton)) {
      Mouse.press(mouseButton);
    }
  }
  // else the mouse button is not pressed:
  else {
    // if the mouse is pressed, release it:
    if (Mouse.isPressed(mouseButton)) {
      Mouse.release(mouseButton);
    }
  }
}

・スライドパッドのガワを削って、キーボード基板をただ乗せているだけです。固定していません。なんとなく一体化してみたかっただけなので使い勝手は考慮していません。
・キーボードのデータのUSBへの出力で不必要っぽい処理を外しました。
・右GUIキーをアプリケーションキーに変更しています(代わりに Fn+AppKey に GUI_R を割り当て)

本当は仮のキートップを製作してから公開しようと思っていたのですが、なんか精神的にヤバくて、私はこの世から居なくなってしまいそうなので、途中を公開します。

現在、停滞している作業
・検証用プラ製キートップの手作りやっとこさ、なんとかできた!
・キーボード、マウスとして、実際に動作している様子を動画で撮影できた!
・やってることの詳細をブログ記事内に追記

今後予定される、予定している、行いたい、出来ればいいな、作業
・ゴーストキー対策の実装
・NumLockの実装
・Arduinoの標準ライブラリの改変
 ・日本語キーボード特有のキーの有効化
 ・キーボードLEDの機能追加
 ・マルチメディアキーの実装
・キーバックライト用のLED点灯まわりの電子回路の検討、実験
・ATmega32U4(TQFP)と74HC164(SOP)などを用いて、キーボード自作専用のArduino下位互換基板の製作(まずは手半田で)
 ・回路図CADの操作習得と、回路図の描画
  ・動作試作回路の製作
 ・回路基板CADの操作習得と、基板のデザイン製作
 ・※実際に試作基板を発注するところまで行けるかは不透明
・Arduinoの標準ライブラリの、さらなる改変
(Arduinoはかなりの電気食い。USBのConfigurationDescriptorでMaxPowerが500mAに指定されていて、なおかつ実際に消費電力が多いためにモバイルに不向き)
(Arduinoには省電力のためのライブラリ等もあるらしいので、ソフト的にもハード的にも電力を出来るだけ抑えたい)
・3Dモデリングソフトの操作習得と、樹脂製キートップのモデリングおよび試作の3Dプリント

とりあえず思いついているのはこのくらい。
以下、完全に未定

 

つづきます。続いたらいいな…

 

追記:2017/02/22
ちょっとだけ続く続いた。

・USBキーボードのキーボードLED(Num Lock, Caps Lock, Scroll Lock,)の状態を収得。
読んだページ
"Leonardo keyboard leds emulation?"
ここにある"usb_key_leds.diff" を元にして、USBAPI.h と HID.cpp を書き換えて、キーボードのLEDの状態を、ホスト(繋いであるPCなど)から読み出す関数を追加。
USBAPI.h と HID.cpp を書き換えて、Arduino の USB HID の機能を変更する方法は、USB HID のライブラリが、分割されていない(自作スケッチに、Keyboard.h Mouse.h の記述をする必要が無い)Arduino IDE の 1.6.x の初期の頃のと 1.7.x でのみ、有効な方法です。(Arduino IDE 1.6.7以降(1.7.x は除く)や、1.8.x には、USB HID を大幅に拡張するライブラリが、あります。 Arduino HID Project 2.4.4

USBAPI.h(機能追加)


/*
  USBAPI.h
  Copyright (c) 2005-2014 Arduino.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef __USBAPI__
#define __USBAPI__

#include <inttypes.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <util/delay.h>

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;

#include "Arduino.h"

#if defined(USBCON)

#include "USBDesc.h"
#include "USBCore.h"

//================================================================================
//================================================================================
// USB

class USBDevice_
{
public:
USBDevice_();
bool configured();

void attach();
void detach(); // Serial port goes down too...
void poll();
};
extern USBDevice_ USBDevice;

//================================================================================
//================================================================================
// Serial over CDC (Serial1 is the physical port)

struct ring_buffer;

#if (RAMEND < 1000)
#define SERIAL_BUFFER_SIZE 16
#else
#define SERIAL_BUFFER_SIZE 64
#endif

class Serial_ : public Stream
{
private:
int peek_buffer;
public:
Serial_() { peek_buffer = -1; };
void begin(unsigned long);
void begin(unsigned long, uint8_t);
void end(void);

virtual int available(void);
virtual int peek(void);
virtual int read(void);
virtual void flush(void);
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t*, size_t);
using Print::write; // pull in write(str) and write(buf, size) from Print
operator bool();

volatile uint8_t _rx_buffer_head;
volatile uint8_t _rx_buffer_tail;
unsigned char _rx_buffer[SERIAL_BUFFER_SIZE];
};
extern Serial_ Serial;

#define HAVE_CDCSERIAL

//================================================================================
//================================================================================
// Mouse

#define MOUSE_LEFT 1
#define MOUSE_RIGHT 2
#define MOUSE_MIDDLE 4
#define MOUSE_ALL (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)

class Mouse_
{
private:
uint8_t _buttons;
void buttons(uint8_t b);
public:
Mouse_(void);
void begin(void);
void end(void);
void click(uint8_t b = MOUSE_LEFT);
void move(signed char x, signed char y, signed char wheel = 0);
void press(uint8_t b = MOUSE_LEFT); // press LEFT by default
void release(uint8_t b = MOUSE_LEFT); // release LEFT by default
bool isPressed(uint8_t b = MOUSE_LEFT); // check LEFT by default
};
extern Mouse_ Mouse;

//================================================================================
//================================================================================
// Keyboard

#define KEY_LEFT_CTRL 0x80 //0xE0 - 0x60(96)
#define KEY_LEFT_SHIFT 0x81 //0xE1 - 0x60(96)
#define KEY_LEFT_ALT 0x82
#define KEY_LEFT_GUI 0x83
#define KEY_RIGHT_CTRL 0x84
#define KEY_RIGHT_SHIFT 0x85
#define KEY_RIGHT_ALT 0x86
#define KEY_RIGHT_GUI 0x87

//UsageID + 0x88(136)
#define KEY_RETURN 0xB0 //0x28 + 0x88(136)
#define KEY_ESC 0xB1 //0x29 + 0x88(136)
#define KEY_BACKSPACE 0xB2 //0x2A + 0x88(136)
#define KEY_TAB 0xB3

#define KEY_CAPS_LOCK 0xC1 //0x39 + 0x88(136)
#define KEY_F1 0xC2 //0x3A + 0x88(136)
#define KEY_F2 0xC3
#define KEY_F3 0xC4
#define KEY_F4 0xC5
#define KEY_F5 0xC6
#define KEY_F6 0xC7
#define KEY_F7 0xC8
#define KEY_F8 0xC9
#define KEY_F9 0xCA
#define KEY_F10 0xCB
#define KEY_F11 0xCC
#define KEY_F12 0xCD

#define KEY_PRINT_SCREEN 0xCE
#define KEY_SCROLL_LOCK 0xCF
#define KEY_PAUSE 0xD0

#define KEY_INSERT 0xD1 //0x49 + 0x88(136)
#define KEY_HOME 0xD2
#define KEY_PAGE_UP 0xD3
#define KEY_DELETE 0xD4
#define KEY_END 0xD5
#define KEY_PAGE_DOWN 0xD6
#define KEY_RIGHT_ARROW 0xD7
#define KEY_LEFT_ARROW 0xD8
#define KEY_DOWN_ARROW 0xD9
#define KEY_UP_ARROW 0xDA
#define KEY_NUM_LOCK 0xDB //0x53 + 0x88(136)


#define LED_NUM_LOCK 0x01
#define LED_CAPS_LOCK 0x02
#define LED_SCROLL_LOCK 0x04

// Low level key report: up to 6 keys and shift, ctrl etc at once
typedef struct
{
uint8_t modifiers;
uint8_t reserved;
uint8_t keys[6];
} KeyReport;

class Keyboard_ : public Print
{
private:
KeyReport _keyReport;
void sendReport(KeyReport* keys);
uint8_t _ledStatus;
public:
Keyboard_(void);
void begin(void);
void end(void);
virtual size_t write(uint8_t k);
virtual size_t press(uint8_t k);
virtual size_t release(uint8_t k);
virtual void releaseAll(void);
virtual void setLedStatus(uint8_t s);
virtual uint8_t getLedStatus(void);
};
extern Keyboard_ Keyboard;

//================================================================================
//================================================================================
// Low level API

typedef struct
{
uint8_t bmRequestType;
uint8_t bRequest;
uint8_t wValueL;
uint8_t wValueH;
uint16_t wIndex;
uint16_t wLength;
} Setup;

//================================================================================
//================================================================================
// HID 'Driver'

int HID_GetInterface(uint8_t* interfaceNum);
int HID_GetDescriptor(int i);
bool HID_Setup(Setup& setup);
void HID_SendReport(uint8_t id, const void* data, int len);

//================================================================================
//================================================================================
// MSC 'Driver'

int MSC_GetInterface(uint8_t* interfaceNum);
int MSC_GetDescriptor(int i);
bool MSC_Setup(Setup& setup);
bool MSC_Data(uint8_t rx,uint8_t tx);

//================================================================================
//================================================================================
// CSC 'Driver'

int CDC_GetInterface(uint8_t* interfaceNum);
int CDC_GetDescriptor(int i);
bool CDC_Setup(Setup& setup);

//================================================================================
//================================================================================

#define TRANSFER_PGM 0x80
#define TRANSFER_RELEASE 0x40
#define TRANSFER_ZERO 0x20

int USB_SendControl(uint8_t flags, const void* d, int len);
int USB_RecvControl(void* d, int len);

uint8_t USB_Available(uint8_t ep);
int USB_Send(uint8_t ep, const void* data, int len); // blocking
int USB_Recv(uint8_t ep, void* data, int len); // non-blocking
int USB_Recv(uint8_t ep); // non-blocking
void USB_Flush(uint8_t ep);

#endif

#endif /* if defined(USBCON) */

HID.cpp(機能追加)

/* Copyright (c) 2011, Peter Barrett  
** 
** Permission to use, copy, modify, and/or distribute this software for 
** any purpose with or without fee is hereby granted, provided that the 
** above copyright notice and this permission notice appear in all copies. 
**
** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 
** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 
** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 
** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES 
** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
** SOFTWARE. 
*/

#include "USBAPI.h"

#if defined(USBCON)
#ifdef HID_ENABLED

//#define RAWHID_ENABLED

// Singletons for mouse and keyboard

Mouse_ Mouse;
Keyboard_ Keyboard;

//================================================================================
//================================================================================

// HID report descriptor

#define LSB(_x) ((_x) & 0xFF)
#define MSB(_x) ((_x) >> 8)

#define RAWHID_USAGE_PAGE 0xFFC0
#define RAWHID_USAGE 0x0C00
#define RAWHID_TX_SIZE 64
#define RAWHID_RX_SIZE 64

extern const u8 _hidReportDescriptor[] PROGMEM;
const u8 _hidReportDescriptor[] = {

// Mouse
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop) // 54
    0x09, 0x02,                    // USAGE (Mouse)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x85, 0x01,                    //     REPORT_ID (1)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x75, 0x05,                    //     REPORT_SIZE (5)
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x09, 0x38,                    //     USAGE (Wheel)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x81, 0x06,                    //     INPUT (Data,Var,Rel)
    0xc0,                          //   END_COLLECTION
    0xc0,                          // END_COLLECTION

  //  Keyboard
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)  // 47
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x85, 0x02,                    //   REPORT_ID (2)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x08,                    //   REPORT_COUNT (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
   
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
   
    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x19, 0x01,                    //   USAGE_MINIMUM (1)
    0x29, 0x05,                    //   USAGE_MAXIMUM (5)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x05,                    //   REPORT_COUNT (5)
    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs) // LED report
    0x75, 0x03,                    //   REPORT_SIZE (3)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x91, 0x01,                    //   OUTPUT (Constant) // padding
   
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0xe7,                    //   LOGICAL_MAXIMUM (231)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
   
    0xc0,                          // END_COLLECTION

#ifdef RAWHID_ENABLED
// RAW HID
0x06, LSB(RAWHID_USAGE_PAGE), MSB(RAWHID_USAGE_PAGE), // 30
0x0A, LSB(RAWHID_USAGE), MSB(RAWHID_USAGE),

0xA1, 0x01, // Collection 0x01
0x85, 0x03, // REPORT_ID (3)
0x75, 0x08, // report size = 8 bits
0x15, 0x00, // logical minimum = 0
0x26, 0xFF, 0x00, // logical maximum = 255

0x95, 64, // report count TX
0x09, 0x01, // usage
0x81, 0x02, // Input (array)

0x95, 64, // report count RX
0x09, 0x02, // usage
0x91, 0x02, // Output (array)
0xC0 // end collection
#endif
};

extern const HIDDescriptor _hidInterface PROGMEM;
const HIDDescriptor _hidInterface =
{
D_INTERFACE(HID_INTERFACE,1,3,0,0),
D_HIDREPORT(sizeof(_hidReportDescriptor)),
D_ENDPOINT(USB_ENDPOINT_IN (HID_ENDPOINT_INT),USB_ENDPOINT_TYPE_INTERRUPT,0x40,0x01)
};

//================================================================================
//================================================================================
// Driver

u8 _hid_protocol = 1;
u8 _hid_idle = 1;

#define WEAK __attribute__ ((weak))

int WEAK HID_GetInterface(u8* interfaceNum)
{
interfaceNum[0] += 1; // uses 1
return USB_SendControl(TRANSFER_PGM,&_hidInterface,sizeof(_hidInterface));
}

int WEAK HID_GetDescriptor(int /* i */)
{
return USB_SendControl(TRANSFER_PGM,_hidReportDescriptor,sizeof(_hidReportDescriptor));
}

void WEAK HID_SendReport(u8 id, const void* data, int len)
{
USB_Send(HID_TX, &id, 1);
USB_Send(HID_TX | TRANSFER_RELEASE,data,len);
}

bool WEAK HID_Setup(Setup& setup)
{
u8 r = setup.bRequest;
u8 requestType = setup.bmRequestType;
if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType)
{
if (HID_GET_REPORT == r)
{
//HID_GetReport();
return true;
}
if (HID_GET_PROTOCOL == r)
{
//Send8(_hid_protocol); // TODO
return true;
}
}

if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType)
{
if (HID_SET_PROTOCOL == r)
{
_hid_protocol = setup.wValueL;
return true;
}

if (HID_SET_IDLE == r)
{
_hid_idle = setup.wValueL;
return true;
}

if (HID_SET_REPORT == r)
{
if (setup.wLength == 2)
{
uint8_t data[2];
if (2 == USB_RecvControl(data, 2))
{
Keyboard.setLedStatus(data[1]);
}
}
}
}
return false;
}

//================================================================================
//================================================================================
// Mouse

Mouse_::Mouse_(void) : _buttons(0)
{
}

void Mouse_::begin(void)
{
}

void Mouse_::end(void)
{
}

void Mouse_::click(uint8_t b)
{
_buttons = b;
move(0,0,0);
_buttons = 0;
move(0,0,0);
}

void Mouse_::move(signed char x, signed char y, signed char wheel)
{
u8 m[4];
m[0] = _buttons;
m[1] = x;
m[2] = y;
m[3] = wheel;
HID_SendReport(1,m,4);
}

void Mouse_::buttons(uint8_t b)
{
if (b != _buttons)
{
_buttons = b;
move(0,0,0);
}
}

void Mouse_::press(uint8_t b)
{
buttons(_buttons | b);
}

void Mouse_::release(uint8_t b)
{
buttons(_buttons & ~b);
}

bool Mouse_::isPressed(uint8_t b)
{
if ((b & _buttons) > 0)
return true;
return false;
}

//================================================================================
//================================================================================
// Keyboard

Keyboard_::Keyboard_(void)
{
}

void Keyboard_::begin(void)
{
}

void Keyboard_::end(void)
{
}

void Keyboard_::sendReport(KeyReport* keys)
{
HID_SendReport(2,keys,sizeof(KeyReport));
}

extern
const uint8_t _asciimap[128] PROGMEM;

#define SHIFT 0x80
const uint8_t _asciimap[128] =
{
0x00,          // NUL
0x00,          // SOH
0x00,          // STX
0x00,          // ETX
0x00,          // EOT
0x00,          // ENQ
0x00,          // ACK
0x00,          // BEL
0x2a,          // BS Backspace
0x2b,          // TAB Tab
0x28,          // LF Enter
0x00,          // VT
0x00,          // FF
0x00,          // CR
0x00,          // SO
0x00,          // SI
0x00,          // DEL
0x00,          // DC1
0x00,          // DC2
0x00,          // DC3
0x00,          // DC4
0x00,          // NAK
0x00,          // SYN
0x00,          // ETB
0x00,          // CAN
0x00,          // EM
0x00,          // SUB
0x00,          // ESC
0x00,          // FS
0x00,          // GS
0x00,          // RS
0x00,          // US

0x2c,          //' ' //ASCII 32
0x1e|SHIFT,    // !
0x34|SHIFT,    // "
0x20|SHIFT,    // #
0x21|SHIFT,    // $
0x22|SHIFT,    // %
0x24|SHIFT,    // &
0x34,          // '
0x26|SHIFT,    // (
0x27|SHIFT,    // )
0x25|SHIFT,    // *
0x2e|SHIFT,    // +
0x36,          // ,
0x2d,          // -
0x37,          // .
0x38,          // /
0x27,          // 0
0x1e,          // 1
0x1f,          // 2
0x20,          // 3
0x21,          // 4
0x22,          // 5
0x23,          // 6
0x24,          // 7
0x25,          // 8
0x26,          // 9
0x33|SHIFT,    // :
0x33,          // ;
0x36|SHIFT,    // <
0x2e,          // =
0x37|SHIFT,    // >
0x38|SHIFT,    // ?
0x1f|SHIFT,    // @
0x04|SHIFT,    // A
0x05|SHIFT,    // B
0x06|SHIFT,    // C
0x07|SHIFT,    // D
0x08|SHIFT,    // E
0x09|SHIFT,    // F
0x0a|SHIFT,    // G
0x0b|SHIFT,    // H
0x0c|SHIFT,    // I
0x0d|SHIFT,    // J
0x0e|SHIFT,    // K
0x0f|SHIFT,    // L
0x10|SHIFT,    // M
0x11|SHIFT,    // N
0x12|SHIFT,    // O
0x13|SHIFT,    // P
0x14|SHIFT,    // Q
0x15|SHIFT,    // R
0x16|SHIFT,    // S
0x17|SHIFT,    // T
0x18|SHIFT,    // U
0x19|SHIFT,    // V
0x1a|SHIFT,    // W
0x1b|SHIFT,    // X
0x1c|SHIFT,    // Y
0x1d|SHIFT,    // Z
0x2f,          // [
0x31,          // bslash
0x30,          // ]
0x23|SHIFT,    // ^
0x2d|SHIFT,    // _
0x35,          // `
0x04,          // a
0x05,          // b
0x06,          // c
0x07,          // d
0x08,          // e
0x09,          // f
0x0a,          // g
0x0b,          // h
0x0c,          // i
0x0d,          // j
0x0e,          // k
0x0f,          // l
0x10,          // m
0x11,          // n
0x12,          // o
0x13,          // p
0x14,          // q
0x15,          // r
0x16,          // s
0x17,          // t
0x18,          // u
0x19,          // v
0x1a,          // w
0x1b,          // x
0x1c,          // y
0x1d,          // z
0x2f|SHIFT,    // {
0x31|SHIFT,    // |
0x30|SHIFT,    // }
0x35|SHIFT,    // ~ //ASCII 126
0              // DEL
};

uint8_t USBPutChar(uint8_t c);

// press() adds the specified key (printing, non-printing, or modifier)
// to the persistent key report and sends the report.  Because of the way
// USB HID works, the host acts like the key remains pressed until we
// call release(), releaseAll(), or otherwise clear the report and resend.
size_t Keyboard_::press(uint8_t k)
{
uint8_t i;
if (k >= 136) { // it's a non-printing key (not a modifier)
k = k - 136;
} else if (k >= 128) { // it's a modifier key
_keyReport.modifiers |= (1<<(k-128));
k = 0;
} else { // it's a printing key
k = pgm_read_byte(_asciimap + k);
if (!k) {
setWriteError();
return 0;
}
if (k & 0x80) { // it's a capital letter or other character reached with shift
_keyReport.modifiers |= 0x02; // the left shift modifier
k &= 0x7F;
}
}

// Add k to the key report only if it's not already present
// and if there is an empty slot.
if (_keyReport.keys[0] != k && _keyReport.keys[1] != k &&
_keyReport.keys[2] != k && _keyReport.keys[3] != k &&
_keyReport.keys[4] != k && _keyReport.keys[5] != k) {

for (i=0; i<6; i++) {
if (_keyReport.keys[i] == 0x00) {
_keyReport.keys[i] = k;
break;
}
}
if (i == 6) {
setWriteError();
return 0;
}
}
sendReport(&_keyReport);
return 1;
}

// release() takes the specified key out of the persistent key report and
// sends the report.  This tells the OS the key is no longer pressed and that
// it shouldn't be repeated any more.
size_t Keyboard_::release(uint8_t k)
{
uint8_t i;
if (k >= 136) { // it's a non-printing key (not a modifier)
k = k - 136;
} else if (k >= 128) { // it's a modifier key
_keyReport.modifiers &= ~(1<<(k-128));
k = 0;
} else { // it's a printing key
k = pgm_read_byte(_asciimap + k);
if (!k) {
return 0;
}
if (k & 0x80) { // it's a capital letter or other character reached with shift
_keyReport.modifiers &= ~(0x02); // the left shift modifier
k &= 0x7F;
}
}

// Test the key report to see if k is present.  Clear it if it exists.
// Check all positions in case the key is present more than once (which it shouldn't be)
for (i=0; i<6; i++) {
if (0 != k && _keyReport.keys[i] == k) {
_keyReport.keys[i] = 0x00;
}
}

sendReport(&_keyReport);
return 1;
}

void Keyboard_::releaseAll(void)
{
_keyReport.keys[0] = 0;
_keyReport.keys[1] = 0;
_keyReport.keys[2] = 0;
_keyReport.keys[3] = 0;
_keyReport.keys[4] = 0;
_keyReport.keys[5] = 0;
_keyReport.modifiers = 0;
sendReport(&_keyReport);
}

size_t Keyboard_::write(uint8_t c)
{
uint8_t p = press(c);  // Keydown
release(c);            // Keyup
return (p); // just return the result of press() since release() almost always returns 1
}

void Keyboard_::setLedStatus(uint8_t s)
{
_ledStatus = s;
}

uint8_t Keyboard_::getLedStatus(void)
{
return _ledStatus;
}

#endif

#endif /* if defined(USBCON) */

テスト用に組んだブレッドボード(タクトスイッチとLEDだけ)

Pict0022

動作確認に使ったスケッチ(Arduino IDE 1.7.11 を使用)

// set pin numbers for keyboard active switch, and LED:
const int switchPin = 2;             // switch to turn on and off keyboard control
const int ledPin = 13;               // keyboard active LED

// keyboard active switch:
boolean keyboardIsActive = false;    // whether or not to control the keyboard
int lastSwitchState = HIGH;          // previous switch state

const int keyboardInputPin[4] = {
  3, 4, 5, 6
};

const int keyboardLedOutPin[3] = {
  7, 8, 9
};


void setup() {
  // initialize I/O pins:
  
  pinMode(switchPin, INPUT_PULLUP);      // the keyboard active switch pin
  pinMode(ledPin, OUTPUT);               // the LED pin
  
  // switch:
  // initialize the key input pins:
  for (int thisPin = 0; thisPin < 4; thisPin++) {
    pinMode(keyboardInputPin[thisPin], INPUT_PULLUP);
  }
  
  // LED:
  for (int thisPin = 0; thisPin < 3; thisPin++) {
    pinMode(keyboardLedOutPin[thisPin], OUTPUT);
  }

  Keyboard.begin();
//  Serial.begin(9600);
  
  int keyboardLedStatus = 0;
//  Keyboard.setLedStatus(keyboardLedStatus);
  
}

void loop() {
  
  
  // read the activate switch:
  int switchState = digitalRead(switchPin);
  // if it's changed and it's high, toggle the keyboard state:
  if (switchState != lastSwitchState) {
    if (switchState == LOW) {
      keyboardIsActive = !keyboardIsActive;
      // turn on LED to indicate keyboard state:
      digitalWrite(ledPin, keyboardIsActive);
    }
  }
  // save switch state for next comparison:
  lastSwitchState = switchState;
  
  
  // main
  if (keyboardIsActive) {
    
    if (digitalRead(keyboardInputPin[0]) == LOW) {
      Keyboard.write(KEY_NUM_LOCK);
    }
    if (digitalRead(keyboardInputPin[1]) == LOW) {
      capslock();
    }
    if (digitalRead(keyboardInputPin[2]) == LOW) {
      Keyboard.write(KEY_SCROLL_LOCK);
    }
    // print screen.
    if (digitalRead(keyboardInputPin[3]) == LOW ){
      printScreen();
    }
   
    int keyboardLedStatus = Keyboard.getLedStatus();
 /* if ((keyboardLedStatus && LED_NUM_LOCK) == 1){
    digitalWrite(keyboardLedOutPin[0], HIGH);
  } else {
    digitalWrite(keyboardLedOutPin[0], LOW);
  }
 */    
    digitalWrite(keyboardLedOutPin[0], (keyboardLedStatus & LED_NUM_LOCK));
    digitalWrite(keyboardLedOutPin[1], (keyboardLedStatus & LED_CAPS_LOCK));
    digitalWrite(keyboardLedOutPin[2], (keyboardLedStatus & LED_SCROLL_LOCK));
    
    // LED_NUM_LOCK      0x01
    // LED_CAPS_LOCK     0x02
    // LED_SCROLL_LOCK   0x04

//  Serial.print(keyboardLedStatus);
//  Serial.print("\n");
     
    
  }
  delay(300);
}

// PCが日本語キーボードの配列前提で動いてるので、
// Caps Lock は[Shift]+[Caps Lock]
void capslock(void) {
    Keyboard.press(KEY_LEFT_SHIFT);
    Keyboard.press(KEY_CAPS_LOCK);
    delay(100);
    Keyboard.releaseAll();
}

// Windowsで[Windowsキー]+[PrintScreen]をタイプすると、
// ピクチャのスクリーンショットのフォルダ内に
// スクリーンショットが保存されます。
void printScreen(void) {
    Keyboard.press(KEY_LEFT_GUI);
    Keyboard.press(KEY_PRINT_SCREEN);
    delay(100);
    Keyboard.releaseAll();
}

実際に動いている様子(動画)
 

 

次は何ができるだろうか、、、

追記:2017/06/09
さらに進む。

・まず、タクトスイッチがむき出しで並ぶユニバーサル基板の上に乗せる、キーボード成型品に見立てたベース部分を、プラ板の切った貼った穴あけ全て人力の手加工で作って、
それに張り付けるキートップを作るために、LXのキーボードのキートップの成型品におゆまるを押し付けて凹型を作って、そこにタミヤのエポキシパテを詰めて詰めてを繰り返して、80個近い数のキートップを作成。型を小さく作りすぎて1回に8個しか取れなくて、なおかつ精度が悪いんで1個1個をチマチマ削る羽目に、、、

Pict0022

・削り終えたキートップを1個1個エポキシ接着剤で接着。

Pict0006

・持ってなかったインクジェットプリンタを購入して、シール用紙に印刷したオーバーレイシートを100均のクリアファイルに張り付けて穴開けて、キーボードに乗せるオーバーレイシートを作成。キートップにもシール用紙に印刷した文字を張り付け。

Pict0004

・集中力が出なくて作りがむちゃくちゃ雑で、精度が全く出てなくて、もう何もかもがガッタガタ。でも今後ソフトを煮詰める用途くらいには何とか使えるものが出来上がりました。

Pict0066

・大きさは、HP 200LX のキーボード部分と大体同じくらい。今回はソフトとハードの基本の製作と検証が主な目的なので、キー配列はLXに合わせずに、ふつーのUS配列に準拠させました。

Pict0003

・やっとキートップを作ったので、前に動かしたスライドパッドマウスと一緒に、ブレッドボード上の回路と繋いでみました。

Pict0044

Pict0049

・スケッチをちょっと手直しして、Arduino Micro に書き込み、Androidスマートフォン Nexus 5 にUSBホストで繋いで、動画を撮影しました。
・詳しいことはまたそのうち書きます。とりあえず、実際に動いている動画公開

動画での使用機材
・Arduino Micro (ATmega32U4)
・シフトレジスタ 74HC164 x2
・自作の小型QWERTYキーボード基板 (キーボードマトリクス 8x14  タクトスイッチ  77key)
・中古で買った、ニンテンドー3DS 専用拡張スライドパッド
・MicroUSB 巻き取りケーブル
・スマートフォン用 USB OTG 変換ケーブル
・Androidスマートフォン Nexus 5 + LineageOS 14.1(Android 7.1.2)
・ホームアプリ Nova Launcher
・テキストエディタ Jota Text Editor
・日本語入力 Wnn Keyboard Lab

 

実機を製作して使用してみて気が付いたこと

  • 小型のキーボードを単体で、両手で持ちながら打鍵するとき、右端下側のキーのEnter,Shift,カーソルキーがとても打ちづらいです。
    今回スライドパッドにキーボードを仮で乗せてみましたが、そうすると打ちづらさは軽減されました。
    HP200LXのキーレイアウトは優秀だと再確認しました。
    0604200

    参照:過去記事→LXのキーボードのひみつ
     
  • 試作してみたキートップと、キーの構造は、とても打ちやすくて快適で、これで正解だと感じました。今回製作した実験用キーボードでは既存のタクトスイッチを使用していますが、タクトスイッチ内の金属ドームでは、キー押下が硬い(重い)、打鍵音がカチカチとうるさいです。これをラバードームにした場合、多分柔らかすぎるのとクリック感が乏しくなるのではと思います。やはりHP200LXで使用している樹脂シート(PET材質?)のドーム加工に導電材の接点の、押下のときのペコペコ感が、力のかかり具合や静音性で、かなり理想的なのかもしれません。
  • スマートフォンを横画面にして小型QWERTY物理キーボードを何らかの形で一体化した場合、スマートフォンくらいの小型の端末になるとキーボードと画面のタッチパネルとの距離が近いので、画面タッチの操作がそれほど苦にならなくなります。なので一体化した場合はキーボード側にポインティングデバイスは必要ないかもしれません。
     
  • アナログスライドパッド部分は、とても使い心地が良くて、個人的には、このままでも実用に差し支えないのではと思っています。外装さえ作ってしまえば片手用ポインティングデバイスとしてかなり使い勝手が良いものが出来るのではと思います。一般に市販されている様々なポインティングデバイスで、同じような部品を使っているにもかかわらず、製品によって使いやすさがマチマチなのは、その中のソフトウェア(ファームウェア)の適正化(チューニング)をどれだけこだわっているかによるところが大きいのではと感じます。いっそ今回のように自分で自分好みにプログラミングできるのは、快適な操作環境を手に入れる方法としてはとても有効なのではと感じます。(但し、そこまで各個人で構築するとなるとかなりの手間がかかってしまいますが)

実際に作ってみて、使ってみないと、分からない、気が付かないことって多いです。
机上の空論にならないためにも、とにかく作って動かしてみるというのは、とても大事だと実感しています。

おまけのおはなし
この動画ではAndroid端末に繋いだハードウェアキーボードで、ハードウェアキーボードのショートカットを使用しています。
Android 7.x から「キーボードショートカットヘルパー」というものが採用されていまして、

塩田紳二のアンドロイドなう 120 「Android N」の新プレビュー版、Developer Preview 4が登場

動画内の最後の方でキーボードショートカットの一覧を表示させています。
USB HID のGUIキー(Windows では Windowsキー、Android では metaキー)が、Android では検索キー(虫メガネ)として定義されていまして、Android 7.xからは「metaキー」+「/」でショートカット一覧が表示されます。
但し、アプリケーション側での対応が必要らしくて、一覧を出してもアプリのショートカットが出なかったりもします(システム共通のショートカットは表示されます)

あとは、Android では、システム内の、*.kcm ファイルにキーキャラクターマップが登録されていまして、
その中のEscキーの欄に、
key ESCAPE {
    base:                               fallback BACK
    alt, meta:                          fallback HOME
    ctrl:                               fallback MENU
}
と記述されていて、

[Esc] -> Backボタン
[Alt]+[Esc] -> Homeボタン
[Win]+[Esc] -> Homeボタン
[Ctrl]+[Esc] -> Menu

といった動作も割り当てられています。
但し、これもアプリによっては動作しなかったりします。
そうそう、USB HID の Keyboard Application キー (0x65) (Windowsキーボードの右下にあるコンテキストメニューを表示させるキー)は、Android だと 大体使用中のアプリのメニューを表示してくれます。

こんな感じで、Android端末はキーボードで操作すると、ちょっと面白かったりします。しかし、キーボードだけですべての操作を完結させることは不可能なので、どうしてもポインティングデバイスやタッチパネルでの操作が必要になります。

 

これでキーボード側のソフトを煮詰められるぅー、ゴーストキー対策を仕込んだり、ライブラリを交換したり、やることまだまだいっぱいで、いっぱいいっぱい

まだまだ続くっぽい。

なんかとんちんかんなことかいてあったらおしえてください。すぐになおします。
 

|

« Arduino Micro を使って、試しに USB スライドパッド マウスを作ってみた(小型USBキーボード自作のための準備色々) | トップページ

コメント

はじめまして。雀悟狼hpこさえてた「たく」です。
HP200LXのキーボードの再生、自分も昔考えてましたが、
その敷居の高さに断念していました。確かにあれから
20余年、あのキーボードを超えるキーボードはまだ
出現していませんよね。こちらの記事を読んでその
再現が楽しみです!

投稿: 「たく」 | 2016年2月10日 (水) 14時59分

「たく」様はじめまして
「雀悟狼」と聞いて「あれ?なんか覚えが?」と思ったら、HP200LXで動く麻雀ゲームですね!
遊びました!楽しみました!懐かしいです、作者様ですか。ようこそいらっしゃいました。
このキーボード完成するかどうかまだ分かりませんが、構想だけははっきりと頭の中にあるので進めていきたいとは思っています。

投稿: かとうひさし | 2016年2月12日 (金) 00時51分

面白いことをされていますね.勉強されているなら良いのですが市販のポイントデバイス付き小型キーボードの基板を流用されたほうが安く早く簡単に出来ると思います.
意外に制御基板だけなら小さなものですから別ボードならそのまま使い一体であればキーボード部分切ってしまってマトリクスで配線すれば事足りそうです.

投稿: | 2016年4月13日 (水) 10時38分

>市販のポイントデバイス付き小型キーボードの基板を流用されたほうが安く早く簡単に出来ると思います.

そんなことは百も承知でして、昔はそれも検討したことがあったのですが、それではキーアサインが自由にならないのです。
本体側(ホスト側)でキー配置変更ソフトなどで変えれば良いと思うでしょうが、
ハードウェア的な処理で行わせたいFnキーとの同時押しでのキーの追加等の、自分が欲しい自由なキー配置を実現するには、どうしてもキーボード側のマイコンのソフトをいじる必要がある、という方向に落ち着きました。
あと、ポインティングデバイスも自由に選びたい。採用するポインティングデバイスのハードウェアごとに当然ソフトウェアも変わってきますので。
今の仕様ならトラックポイント、タッチパッド、トラックボール、ゲーム機用ジョイスティックパーツ、など部品が手に入りさえすれば何でも使えます。
市販のキーボードの中のマイコンで素性が分かっていて専用ライターなどを使わなくても簡単に中身をプログラミング出来るモノってあるのでしょうか?
もしあったらぜひ紹介して欲しいですお願いします。

投稿: かとうひさし | 2016年4月13日 (水) 12時28分

私も同じくArduino Microを使ってPC用としてですが配列を好きできる物が作れないかと思って
最近手をつけ始めた者です。
同じようなことをやっている人がいないか探してこちらにたどり着きました。

実際に作っている方の情報は重宝しますので、予定作業を完走するところまで続くことを期待しています。

投稿: ichinomoto | 2016年7月 5日 (火) 00時07分

スライドパッド マウスの方は作ってみて上手くいきました、ありがとうございます。
次はキーボードの方も参考にさせてもらって作りたいと思います。
ここまで出来ていますので気が向いたら完成させてください

投稿: Ratna | 2016年7月 6日 (水) 22時47分

私もキーボード作りたいので、拝見させて
もらっています。
Arduino Microといのを使用されているようですが、
最終的にキーボード内部に収納予定ですか?
すごく分厚くなるみたいですけど、どのように加工するのか楽しみです。

投稿: | 2017年9月27日 (水) 20時30分

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/57207/63874933

この記事へのトラックバック一覧です: Arduino Micro を使って、USB 小型 キーボードを自作 (製作途中、暫定公開):

« Arduino Micro を使って、試しに USB スライドパッド マウスを作ってみた(小型USBキーボード自作のための準備色々) | トップページ