分割キーボード間の接続について

筐体間の接続

分割キーボードを作るにあたって、分割した左右のキーボードを接続するかを考える必要がありました。キースイッチへの配線はキーマトリクスになっているので、そのまま分けると結構な本数線が左右の接続に必要になります。ErgoDoxの回路図を参考にするとI2Cを使って片側にマイクロコントローラ、もう片側にIOエキスパンダ(拡張)を装着し、左右間をI2Cに必要な4芯のケーブルで接続していることがわかりました。I2Cは本来は筐体の外に引き回すような用途ではないと思っていたので、この手法にはちょっと驚きました。

最初にPIC版を作ったときに使用していたコントローラ基盤のIOが少ないため、片方にはマイクロコントローラ+IOエキスパンダ、もう片方にはIOエキスパンダを装着しました。I2Cの接続は

  • 筐体A:の基盤内でマイクロコントローラ~IOエキスパンダ
  • 筐体B:コネクタ~ケーブル~コネクタを経由してIOエキスパンダ

とすることにしました。

f:id:hrhg:20170408062404j:plain

ArduinoのProMicro版は、マイクロコントローダだけで16ピンを確保できたので、IOエキスパンダは片方だけにしかついていません。

ちなみにI2Cは並列を複数接続でき、今回使ったIOエキスパンダは、ハードウェアアドレスとして3ビットあるので最大8個接続できます。IOエキスパンダ1個あたりのIO数は16あるので、8x8のキーマトリクスを作った場合、64個のキースイッチが接続できます。それが8個接続できるので理論上512個のキースイッチを制御できそうです。

google 日本語入力 ドラムセットバージョン を作ることも不可能でなさそうです。 japan.googleblog.com

また、確かUltra Hacking KeyboardはI2Cではなく、シリアルで左右の通信をしていたような気がします。他の市販の分割型キーボードはどうやっているかが気になります。

2017/05/01 追記
Ultra Hacking Keyboardの初期段階ではシリアル(UART)のようでしたが、最近はI2Cのようです。 きせのんさんから指摘をいただきました。ありがとうございました。

コネクタ

I2Cの接続は4芯でまかなえるので、コネクタの選定をしました。ErgoDoxではあまり入手が容易でないと思っていた4ピンのケーブルを使っていましたが、4芯といえば電話等でよく使われているRJ11(本当は6線まである)が思いついたので、最初は以下のRJ11を使いました。 akizukidenshi.com

しかし思ったよりコネクタの高さがあり、薄型の筐体を作りたかったことから以下の4ピンのコネクタのものに作り替えました。 akizukidenshi.com

f:id:hrhg:20170408062049j:plain

コネクタについては、cho45さんの 自作用の汎用コネクタ - 氾濫原 の情報が大変参考になります。

ケーブル

最初は以下のプラグを使っていました akizukidenshi.com

が、よりコンパクトにしたかったので、以下のプラグに変更しました。 oyaide.com

より小さな2.5mm版の方が良かったのですが、メス側は取り扱っていないとのこで採用を断念しました。 oyaide.com

はんだ付けは予備はんだををして以下のような感じです。 f:id:hrhg:20170408062030j:plain

最近試作しているキーボードの筐体

3Dプリンタの調整に難儀していますが、最近試作しているキーボードの筐体です。

f:id:hrhg:20170407044854j:plain

  • 上:普通のテンキーレス109もどきを分割型にしたもの。一次しのぎのtypo対策。
  • 中:Let’s Splitもどき。
  • 下:Kinesisみたいな立体型の検証用。最終的にはこれが本命。

Kailhの薄型キースイッチのキーキャップが入手できたので、まあまあ見れるようになりました。薄型キースイッチについては、そのうち別エントリーを書く予定です。

3Dプリンタ Original Prusa i3 MK2を組み立てました

キーボードの筐体を作るときに自分の手元に道具がないと物凄く進捗がはかどらないのに懲りたので、思い切って3Dプリンタを購入してみました。

f:id:hrhg:20170306053343j:plain

機器の選択

他のmakerの方をいろいろ見て情報収集すると

あたりが候補かなと思いました。

本来は射出成型機で一般製品並みのものを作りたいところですが、現在のところ個人で用意ができるような品物ではないようでした。

レーザー加工機

一般的には半導体レーザとCO2レーザがあるようですが、半導体レーザは素材がかなり限定されるので、CO2レーザーが候補でした。 ですが、完成品は予算の範囲外でちょっと手が届かないことと、基本的に平面のものしか扱えないので、候補から外れました。

CNCフライス盤

cho45さんの使っているのを見て、かなり購入する気になっていました。ですが、インターフェース等を作成されている情報を見て、ちょっと手間だなぁと感じました。

3Dプリンタ

Kailhの薄型キースイッチを使いたいのですが、世の中に販売されているキーキャップがまだ存在していないので、自分で作らないといけないことや、kinesisみたいな平面ではない筐体も作りいことから、最終的に3Dプリンタに決めました。

機種の選択

あまり調べていませんが、 Make Magazineの評価でコストパフォーマンスがダントツなことや、 makottoも使っていた ことから、Original Prusa i3 MK2 に決定しました。かなり人気があるようで、1月1日に発注して、届いたのが2月19日でした。 完成品ではなく、後々のメンテナンスができるように構造を把握しておきたいのと、組み立ての費用が浮くのでキットのものを購入しました。あと、完成品だと納期がもっとかかったような気がしました。

組み立て

f:id:hrhg:20170315235806j:plain f:id:hrhg:20170316000412j:plain f:id:hrhg:20170316000047j:plain

途中経過や部品数の確認のために写真を撮りながら、1日1~2時間で、1週間くらいかかりました。 最低限必要な工具として六角レンチやマイナスドライバー、ラジオペンジなどが入っていて、特殊な工作技術は必要ありませんでした。手間取ったところと言えば、あらかじめナットを3Dプリントした部品に埋め込むのがなかなか説明通りにはいきませんでした。あらかじめ合わせる部品をつけないで、反対からネジで締め付ければナットが食い込んでくれるようでした。

マニュアルの英語がを端折りながら進めてしまったせいで、後戻りしてよく読み返したことが何度かありました。

調整

f:id:hrhg:20170306055413j:plain 一応は出力されるようにはなりましたが、まだ完璧に調整できているとは思えていません。手直しする方法の検討がつかないので、しばらくはこのまま進めてみます。後々サポートサイト等で情報がないかを探してみるつもりです。

試し

f:id:hrhg:20170306053401j:plain ワークフローとしては

  • Fusion360STLファイルを作成
  • Slic3rでSTLからgcodeへの変換
  • SDカード経由で、PULSAからgcodeから実際のもプリント物

といった工程で進めています。

Slic3rでは主に以下を設定しています。

  • フィラメントの素材
  • 積層ピッチ
  • サポート材有無

まだサポート材が必要なプリントは行っていません。 Prusa用のSlic3rをダウンロードできるのですが、最初起動することができませんでしたが、フォーラムの情報を参参考にSlic3rに手を加えると、無事起動することができました。

所感

  • 調整が甘いせいか、縦の曲線がうまく出力できていない
  • Slic3rのパラメータもいろいろ試す必要がある
  • 積層ピッチを標準(0.2mm)にしても、結構出力に時間がかかる
  • きれいに仕上げたい場合、後工程で表面処理が必要
  • そこそこ音がするので、防音箱はつくりたい
  • 画面で設計したものが、立体物になって手に取ることができるのは楽しい

といったところです。 子供の壊れたおもちゃを直す部品を作ってみたり、もちろんキーボード作成以外でも今後のモノ作りに活躍してくれそうです。

Tokyo Mechanical Keyboard Meetup Vol.2 に参加してきました

もう2週間経ってしまいましたが、Tokyo Mechanical Keyboard Meetup vol.2に参加しました。 Redditでメカニカルキーボードについての交流し、世界中でmeetupが開催されているそうです。
東京での開催は今回で2回目で、参加者の半分くらいは在日の外国の方らしく基本的に英語での進行でした。今後は3か月間隔くらいで開催することを検討されているようです。

主に以下のサイトで情報が見れるようです。

特に気になったキーボード

市販品や自作キットで作られていたもの等めずらしいキーボードが集まりました。個人的に特に気になったキーボードは以下でした。

FMV-KB211

個人的に今までで一番だったキータッチのものと20年ぶりくらいにご対面。
しかもesrilleのファーム搭載。 f:id:hrhg:20170212154440j:plain f:id:hrhg:20170212154500j:plain

HASU BT HHKB (Alternative Controller for HHKB)

実物を初めて見ました。バッテリーは8時間くらいもつとのことでした。 f:id:hrhg:20170212154625j:plain f:id:hrhg:20170212154632j:plain

自作で作られていた方のもの。下のものは自分で小基板を起こしたとのことでしたが、上の方が配線が簡単だったとのことです。
キーボード自作の同人誌も作られていました。 f:id:hrhg:20170212154648j:plain f:id:hrhg:20170212154641j:plain

Apple Extended Keyboard MOD

これも懐かしいアップルの拡張キーボードを元に改造したキーボード。コンパクトにまとまっていました。 f:id:hrhg:20170212154736j:plain

TypeMatrix & ATREUS

なかなか見る機会がなかった両キーボードも拝見しました。コンパクトでいいですね。 f:id:hrhg:20170212154654j:plain

FabCafe

今回開催されていた場所がFabCafe MTRLでした。fab labに行ったのは初めてだったので、いくつか写真を撮りました。

f:id:hrhg:20170212154803j:plain f:id:hrhg:20170212154815j:plain f:id:hrhg:20170212154826j:plain f:id:hrhg:20170212154837j:plain f:id:hrhg:20170212154851j:plain f:id:hrhg:20170212154856j:plain f:id:hrhg:20170212154858j:plain f:id:hrhg:20170212154901j:plain f:id:hrhg:20170212154904j:plain f:id:hrhg:20170212154906j:plain

ArduinoでUSB接続の分割キーボードを作った話 - 所感

所感など

目次

ハードウェア・ソフトウェア

とりあえずUSB接続のものであれば、欲しいものはだいたい作れそうかなと思いました。特にArduinoを使った場合はほとんどデータシートを見ることもなく、ハードウェアを意識しなくてもとりあえず動くものが作れそうだということが判ったのは収穫でした。

筐体

正直言ってこんなに筐体にはまるとは想定していませんでした。やっぱり手元に

等のデジタル機器が欲しくなってしまいます。

その他

自作キーボードをきっかけに久々にブログを始めてみました。記事を書くよりプログラムを作っている方が全然楽ですが、今年からアウトプットを行えるようにしばらくは続けてみるつもりです。

また作っている途中でいろいろな情報を目にしてしまい、どんどん自作キーボード沼にはまってしまっているような気がしています…

f:id:hrhg:20170212140959j:plain

ArduinoでUSB接続の分割キーボードを作った話 - 筐体編

筐体は簡易的にFalbaTech製やMechanicalKeyboard製のアクリル板を重ねてネジ止めするものを予定していました。なるべく本体が薄くなるように作ろうとしましたが、アクリル板加工が思ったようにうまく進展しなかったので一旦中断しています。とりあえず今まで試したことについて記載します。

目次

ELECROWのサービスを利用

ELECROWのアクリルカットサービスを利用してみました。作成した当時はアクリルの種類の選択がなく、2mm厚で透明しかありませんでしたが、今はアクリルの厚さや色を選べるようです。
依頼をしたデータ作成は当時はInkscapeはまだ慣れていなくて、LibreOfficeのDrawを使ってPDFで横並びをずらした6x7なテキトーな物理配置で作成してみました。

費用は

  • 1 x 5pcs- Acrylic Laser Cutting Service 20cm Max * 20cm Max $16.55
  • OSC Shipping to JP $16.19

かかった日数は

  • 6/25 発注
  • 6/27 - 6/28 送料・運送会社調整
  • 7/2 発送
  • 7/14 到着

でした。

f:id:hrhg:20170211160014j:plain f:id:hrhg:20170211160034j:plain f:id:hrhg:20170211160047j:plain

仕上がりもきれいで精度もまずますでしたが、キーキャップを引き抜いたときにスイッチが外れないように、裏から100均で買ったホットメルトで固定しました。
余分なところについたり誤ってつけてしまったホットメルトを取るときには、薬局なんかで売っている無水エタノールを綿棒につけて流し込むと、きれいかつ簡単に取ることができます。
側面および底面はアクリル角材やアクリル板を切って両面テープで止めていました。 f:id:hrhg:20170211160117j:plain

キースイッチと底面の間に隙間があったため、キースイッチを押すとアクリル板がたわんだ影響でひび割れが発生してしまうことがわかりました。 f:id:hrhg:20170211160136j:plain

近場のレーザー加工機を使用

キーの配置を他の形状のものを試作したくなりましたが、ELECROWのサービスを使うと費用の内半分が送料を占めてしまったり時間がかかるのが難点だと感じました。
地方ではなかなかレーザー加工サービスを行っているところがありませんでしたが、自分で加工機を操作できることろを利用させてもらいました。 ですが加工機のノウハウが無いため、切り抜きの穴が途中からうまく開かなかったり、それを穴の大きさが若干小さくてヤスリで削ったり、その途中でアクリルが割れてしまったり、なかなか簡単にはできませんでした。

f:id:hrhg:20170212055830j:plain

気軽にいろいろ作ってみるという状態にはならなかったので、6x8のキーマトリクス部のものが1組できたら一旦中断とすることにしました。

f:id:hrhg:20170211153801j:plain

ArduinoでUSB接続の分割キーボードを作った話 - ファームウェア編

f:id:hrhg:20170211154409j:plain

最初はErgoDox用にカスタマイズされたTMKを元に、IOのポートとIOエキスパンダあたりを改造するつもりでした。bootloader関係を調べているうちに、試しにArduinoでちょっと作り始めてみたら、すごく簡単にできそうだったので、Arduino入門もかねてArduino環境で作ってみました。 キーボードとして必要最低限の事しかしておらず、レイヤー機能とかはありません。

目次

プログラム本体

/**
  分割キーボード
  split_keyboard.ino

  Copyright (c) 2017 Hiroshi Higuchi

  Released under the MIT license
  http://opensource.org/licenses/mit-license.php

*/

#include <stdint.h>
#include  <MsTimer2.h>
#include  <Keyboard.h>
#include  <Adafruit_MCP23017.h>

#define     NUM_ROW     8
#define     NUM_COL     6
#define     TIMER_MS    1   // 1msec

uint8_t row_pins[] = { 1, 0, 4, 5, 6, 7, 8, 9};
uint8_t col_pins[] = {10, 16, 14, 15, 18, 19, 20, 21};  // output

#define   NUM_ROW_BASE   sizeof(row_pins)

#define   NUM_COL_BASE   sizeof(col_pins)
#define   NUM_COL_EXP     8
#define   NUM_COL         (NUM_COL_BASE + NUM_COL_EXP)

uint8_t row_tmp[NUM_COL];             // 行単位の仮状態
uint8_t row_st[NUM_COL];              // 行単位の確定状態
volatile uint8_t row_timer[NUM_COL];  // 行単位のタイマー
uint8_t col_index;

Adafruit_MCP23017 mcp;
uint8_t mcp_write_data;
uint8_t mcp_read_data;

#define     MCP_ADRS        1
#define     MCP_PORT_A      0
#define     MCP_PORT_B      1
#define     MCP_ALL_OUTPUT  0x00
#define     MCP_ALL_INPUT   0xff
#define     MCP_ALL_PULLUP  0xff

uint8_t key_table[] = {
  // right side
  K_,    K_,    K_,    K_,    K_,    K_,    K_,    K_,       // 0
  K_,    K_F7 , K_F8,  K_F9,  K_F10, K_F11, K_F12, K_DEL,    // 1
  K_,    K_EQL, K_6,   K_7,   K_8,   K_9,   K_0,   K_BS,     // 2
  K_,    K_CLS, K_Y,   K_U,   K_I,   K_O,   K_P,   K_,       // 3
  K_,    K_BSH, K_H,   K_J,   K_K,   K_L,   K_SCN, K_ENT,    // 4
  K_,    K_INT3,K_N,   K_M,   K_KNM, K_DOT, K_SLH, K_RSFT,   // 5
  K_RSFT,K_SP,  K_RCTL,K_RALT,K_LFT, K_DN,  K_UP,  K_RIT,    // 6
  K_,    K_,    K_,    K_,    K_,    K_,    K_,    K_,       // 7
  // left side
  K_,    K_,    K_,    K_,    K_,    K_,    K_,    K_,       // 0
  K_ESC, K_F1,  K_F2,  K_F3,  K_F4,  K_F5,  K_F6,  K_,       // 1
  K_BQT, K_1,   K_2,   K_3,   K_4,   K_5,   K_HFN, K_,       // 2
  K_TAB, K_Q,   K_W,   K_E,   K_R,   K_T,   K_OPN, K_,       // 3
  K_CPS, K_A,   K_S,   K_D,   K_F,   K_G,   K_SCT, K_,       // 4
  K_LSFT,K_Z,   K_X,   K_C,   K_V,   K_B,   K_INT1,K_,       // 5
  K_,    K_INT2,K_INT5,K_INT4,K_LALT,K_LCTL,K_BS  ,K_LSFT,   // 6
  K_,    K_,    K_,    K_,    K_,    K_,    K_,    K_        // 7
};

#define   INIT_ROW_TIMER  3

uint8_t   col_exp_data;

void timer_1msec() {
  for (uint8_t i = 0; i < NUM_COL; i++) {
    if (0 != row_timer[i]) {
      row_timer[i]--;
    }
  }
}

void setup() {
  // put your setup code here, to run once:

  // シリアルモニタ初期化
  Serial.begin(9600);

  // キーマトリクスの初期化
  col_index = NUM_COL;

  // バッファの初期化
  for (uint8_t i = 0; i < NUM_COL; i++) {
    row_tmp[i] = 0xff;
    row_st[i] = 0xff;
    row_timer[i] = 0;
  }

  // 出力ポート値の初期化
  for (uint8_t i = 0; i < NUM_COL_BASE; i++) {
    digitalWrite(col_pins[i], HIGH);
  }

  // ポート方向の初期化
  Serial.print("row_pins:");
  Serial.println(sizeof(row_pins));
  for (uint8_t i = 0; i < NUM_ROW_BASE; i++) {
    pinMode(row_pins[i], INPUT_PULLUP);
  }
  for (uint8_t i = 0; i < NUM_COL_BASE; i++) {
    pinMode(col_pins[i], OUTPUT);
  }

  // MCP23017の初期化
  mcp.begin(MCP_ADRS);
  col_exp_data = 0xff;
  mcp.writeGPIO(MCP_PORT_A, col_exp_data);
  mcp.pinModeByte(MCP_PORT_A, MCP_ALL_OUTPUT);
  mcp.pullUpByte(MCP_PORT_B, MCP_ALL_PULLUP);
  mcp.pinModeByte(MCP_PORT_B, MCP_ALL_INPUT);

  // HIDの初期化
  Keyboard.begin();

  // タイマーの初期化
  MsTimer2::set(TIMER_MS, timer_1msec);
  MsTimer2::start();
}

void loop() {
  // --- col ---
  if (col_index < (NUM_COL - 1)) {
    col_index++;
  } else {
    col_index = 0;
  }
  if (0) {
    Serial.print("col_index:");
    Serial.println(col_index);
  }
  if (col_index < NUM_COL_BASE) {
    for (uint8_t i = 0; i < NUM_COL_BASE; i++) {
      if (i == col_index) {
        digitalWrite(col_pins[i], LOW);
      } else {
        digitalWrite(col_pins[i], HIGH);
      }
    }
  } else {
    col_exp_data = ~(0x01 << (col_index - NUM_COL_BASE));
    mcp.writeGPIO(MCP_PORT_A, col_exp_data);
    if (0) {
      Serial.print("col_exp_data:");
      Serial.println(col_exp_data);
    }
  }
  // --- row ---
  uint8_t row_data = 0;

  if (col_index < NUM_COL_BASE) {
    for (int8_t i = 0; i < NUM_ROW_BASE; i++) {
      row_data = (row_data << 1) | digitalRead(row_pins[i]);
    }
  } else {
    row_data = mcp.readGPIO(MCP_PORT_B);
  }
  if (0) {
    Serial.print("row_data:");
    Serial.println(row_data);
  }

  if (row_data != row_tmp[col_index]) {
    // 変化があった場合
    row_tmp[col_index] = row_data;
    if (row_tmp[col_index] != row_st[col_index]) {
      // 変化開始の場合
      row_timer[col_index] = INIT_ROW_TIMER;
    } else {
      // 元に戻った場合
      // nop
    }
  } else {
    // 前回と変化がない場合
    if (row_tmp[col_index] != row_st[col_index]) {
      // 変化中(確定状態とは異なる場合)の場合
      if (0 == row_timer[col_index]) {
        // 確定した場合
        uint8_t row_xor = row_tmp[col_index] ^ row_st[col_index];
        // 全ビットの確認
        for (uint8_t i = 0; i < NUM_ROW_BASE; i++) {
          // ビット単位で変化があった場合
          if (0 != (row_xor & 0x01)) {
            uint8_t key_code = key_table[(col_index * NUM_ROW_BASE) + i];
            if (K_ != key_code) {
              if (0 != ((row_tmp[col_index] >> i) & 0x01)) {
                // off
                if (0) {
                  Serial.print("OFF:");
                  Serial.println((col_index * NUM_ROW_BASE) + i);
                }
                Keyboard.releaseEx(key_code);
              } else {
                // on
                if (0) {
                  Serial.print("ON:");
                  Serial.println((col_index * NUM_ROW_BASE) + i);
                }
                Keyboard.pressEx(key_code);
              }
            }
          }
          row_xor = row_xor >> 1;
        }
        row_st[col_index] = row_tmp[col_index];
      }
    } else {
      // 変化していない(確定状態と同じ)場合
      // nop
    }
  }
}

IOエキスパンダライブラリの拡張

I2Cの通信はそれほど早くないので、処理の効率化のためにIOへのアクセスを標準の1bit単位ではなく8bit単位で行えるように拡張しました。こちらの記事を参考に、Adafruit_MCP23017に対して

  • 8ビット単位でのモード設定
  • 8ビット単位でのプルアップ設定
  • 8ビット単位でのポート書込み

のメソッドを追加しました。

Arduino/libraries/Adafruit_MCP23017_Arduino_Library/Adafruit_MCP23017.hへのpublicメソッドの追加

  void pinModeByte(uint8_t p, uint8_t d);
  void pullUpByte(uint8_t p, uint8_t d);
  void writeGPIO(uint8_t p, uint8_t b);

Arduino/libraries/Adafruit_MCP23017_Arduino_Library/Adafruit_MCP23017.cppへのメソッドの追加

void Adafruit_MCP23017::pinModeByte(uint8_t p, uint8_t d){
    writeRegister(p, d);
}

void Adafruit_MCP23017::pullUpByte(uint8_t p, uint8_t d){
    if(p == 0){
        writeRegister(MCP23017_GPPUA, d);
    } else {
        writeRegister(MCP23017_GPPUB, d);
    }
}

void Adafruit_MCP23017::writeGPIO(uint8_t p, uint8_t a){
    Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
    if(p == 0){
        wiresend(MCP23017_GPIOA);
    } else {
        wiresend(MCP23017_GPIOB);
    }
    wiresend(a);
    Wire.endTransmission();
}

キーボードライブラリの拡張

標準のライブラリでは使えるキーコードの範囲が限定されているようでした。日本語配列特有のキー(変換キー等)に対応するために、標準ライブラリを以下のように拡張しました。

USBキーコードはこちらの資料の「10 Keyboard/Keypad Page (0x07)」を参照しました。

  • USB Usage のキーコードを定義
  • 最大値を変更
  • pressおよびreleaseの対応版メソッドの追加

Arduino\Libraries\Keyboard\src\Keyboard.hへの定数の追加 (macの場合 Arduino.app/Contents/Java/libraries/Keyboard/src/Keyboard.h)

/***************************************/
/* define USB HID Usage Table Keyboard */
/***************************************/
#define   K_        0 // Reserved (no event indicated)

#define   K_A       4
#define   K_B       5
#define   K_C       6
#define   K_D       7
#define   K_E       8
#define   K_F       9
#define   K_G       10
#define   K_H       11
#define   K_I       12
#define   K_J       13
#define   K_K       14
#define   K_L       15
#define   K_M       16
#define   K_N       17
#define   K_O       18
#define   K_P       19
#define   K_Q       20
#define   K_R       21
#define   K_S       22
#define   K_T       23
#define   K_U       24
#define   K_V       25
#define   K_W       26
#define   K_X       27
#define   K_Y       28
#define   K_Z       29
#define   K_1       30  // 1 and !
#define   K_2       31  // 2 and @
#define   K_3       32  // 3 and #
#define   K_4       33  // 4 and $
#define   K_5       34  // 5 and %
#define   K_6       35  // 6 and ^
#define   K_7       36  // 7 and &
#define   K_8       37  // 8 and *
#define   K_9       38  // 9 and (
#define   K_0       39  // 0 and )
#define   K_ENT     40  // Return (ENTER)
#define   K_ESC     41  // ESCAPE
#define   K_BS      42  // DELETE (Backspace)
#define   K_TAB     43  // Tab
#define   K_SP      44  // Spacebar
#define   K_HFN     45  // - and _
#define   K_EQL     46  // = and +
#define   K_OPN     47  // [ and {
#define   K_CLS     48  // ] and }
#define   K_BSH     49  // \ and |
#define   K_NUS     50  // Non-US # and ~ (?)
#define   K_SCN     51  // ; and :
#define   K_SCT     52  // ' and "
#define   K_BQT     53  // Grave Accent and Tilder
#define   K_KNM     54  // , and <
#define   K_DOT     55  // . and >
#define   K_SLH     56  // / and ?
#define   K_CPS     57  // Caps Lock
#define   K_F1      58
#define   K_F2      59
#define   K_F3      60
#define   K_F4      61
#define   K_F5      62
#define   K_F6      63
#define   K_F7      64
#define   K_F8      65
#define   K_F9      66
#define   K_F10     67
#define   K_F11     68
#define   K_F12     69
#define   K_PSC     70  // PrintScreen
#define   K_SLK     71  // Scroll Lock
#define   K_PAS     72  // Pauses
#define   K_INS     73  // Insert
#define   K_HOM     74  // Home
#define   K_PUP     75  // PageUp
#define   K_DEL     76  // Delete Forward
#define   K_END     77  // END
#define   K_PDWN    78  // PageDown
#define   K_RIT     79  // RightArrow
#define   K_LFT     80  // LeftArrow
#define   K_DN      81  // DownArrow
#define   K_UP      82  // UpArrow
#define   K_NLK     83  // Num Lock and Clear

#define   K_INT1    135 // *15,28 \ and _
#define   K_INT2    136 // *16 hiragana
#define   K_INT3    137 // *17  \ and |
#define   K_INT4    138 // *18 henkan
#define   K_INT5    139 // *19 muhenkan
#define   K_INT6    140 // *20
#define   K_INT7    141 // *21
#define   K_INT8    142 // *22
#define   K_INT9    143 // *22
#define   K_LNG1    144
#define   K_LNG2    145
#define   K_LNG3    146
#define   K_LNG4    147
#define   K_LNG5    148
#define   K_LNG6    149
#define   K_LNG7    150
#define   K_LNG8    151
#define   K_LNG9    152

#define   K_LCTL    224
#define   K_LSFT    225
#define   K_LALT    226
#define   K_LGUI    227
#define   K_RCTL    228
#define   K_RSFT    229
#define   K_RALT    230
#define   K_RGUI    231

Arduino\Libraries\Keyboard\src\Keyboard.hへのpublicメソッドの追加 (macの場合 Arduino.app/Contents/Java/libraries/Keyboard/src/Keyboard.h)

  size_t pressEx(uint8_t k);
  size_t releaseEx(uint8_t k);

Arduino\Libraries\Keyboard\src\Keyboard.cppへの書換え

//    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x29, (K_LCTL - 1),            //   USAGE_MAXIMUM (Keyboard Application)

Arduino\Libraries\Keyboard\src\Keyboard.cppへのメソッドの追加 (macの場合 Arduino.app/Contents/Java/libraries/Keyboard/src/Keyboard.cpp)

size_t Keyboard_::pressEx(uint8_t k) 
{
  uint8_t i;
  if (k >= K_LCTL){  // it's a modifier key
    _keyReport.modifiers |= (1<<(k-K_LCTL));
    k = 0;
  } else {
    if (!k) {
      setWriteError();
      return 0;
    }
  }
    
  // 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;
}

size_t Keyboard_::releaseEx(uint8_t k) 
{
  uint8_t i;
  if (k >= K_LCTL){  // it's a modifier key
    _keyReport.modifiers &= ~(1<<(k-K_LCTL));
    k = 0;
  } else {
    if (!k) {
      setWriteError();
      return 0;
    }
  }
  
  // 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;
}

2017/05/01 mac用のパスを追記