QMKでのレイヤー機能の挙動について

情報追加

以下に記載したmodifierキーがロックされる事象は、QMKのreadmeのPrevent stuck modifiersに記載されていることをみやおかさんから教えていただき、その通りであることを確認しました。

概要

QMKでのレイヤー機能の動作が自分の想定したものとは異なっていたので、その調査結果を解説をしてみます。

動機

レイヤー機能を自前で実装するのが面倒くさかったので、自作の分割型キーボードにQMKを移植してみました。 レイヤーを使った自分の好みの少数キー配列が出来たので、ようやく本格的にレイヤーを使ってみる気になることができました。

しかし、しばらく使っていると一部のmodifierキーがロック(キーを離しているのにそれが認識されない)されてしまうことに気がついたため調べてみることにしました。

調査方法

調査対象のキーボード

自作の分割型にQMKの移植した際の誤りの可能性があると考えたので、QMKへの修正の影響があまり無いはずと考えている以前に作成したplanckモドキを対象としました。 hrhg.hatenablog.com

環境(OS)

Windows8.1/64bitを使用しました。

操作パターン

  1. 対象キーON -> レイヤーキーON -> レイヤーキーOFF -> 対象キーOFF
  2. 対象キーON -> レイヤーキーON -> 対象キーOFF -> レイヤーキーOFF
  3. レイヤーキーON -> 対象キーON -> 対象キーOFF -> レイヤーキーOFF
  4. レイヤーキーON -> 対象キーON -> レイヤーキーOFF -> 対象キーOFF

の4つとしました。対象キーおよびレイヤーキーの定義は後述します。

QMKのソース

2017/07/20時点でのソースをgitで取得して確認しました。

キーマップ

planck/keymaps/ab/keymap.c を元に、LOWERレイヤーの左下隅のキーコードをBLから、OSが認識できるKC_Aに変更しました。しかし変更した意味は無かったかもしれません。

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_QWERTY] = { /* QWERTY */
    {KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,    KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,    KC_BSPC},
    {KC_LCTL, KC_A,    KC_S,    KC_D,    KC_F,    KC_G,    KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_ENT},
    {KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, SFT_ENT},
    {KC_LCTL, KC_ESC,  KC_LGUI, KC_LALT, LOWER,   KC_SPC,  KC_SPC,  RAISE,   KC_LEFT, KC_DOWN, KC_UP,   KC_RGHT}
},
[_LOWER] = { /* LOWER */
    {KC_1,    KC_2,    KC_3,    KC_4,    KC_5,    KC_6,    KC_7,    KC_8,    KC_9,    KC_0,    KC_MINS, KC_EQL},
    {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_LPRN, KC_RPRN, KC_LCBR, KC_RCBR, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
    {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_LBRC, KC_RBRC, KC_QUOT, KC_DQT,  KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
/*  {BL,      ZM_NRM,  ZM_IN,   ZM_OUT,  KC_TRNS, KC_PGDN, KC_PGDN, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS} */
    {KC_A,    ZM_NRM,  ZM_IN,   ZM_OUT,  KC_TRNS, KC_PGDN, KC_PGDN, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS}
},
[_RAISE] = { /* RAISE */
    {KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12},
    {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_LCBR, KC_LCBR, KC_BSLS, KC_TRNS},
    {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_BSLS, KC_PIPE, KC_GRV,  KC_TILD, KC_LBRC, KC_LBRC, KC_TRNS, KC_TRNS},
    {RESET,   KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_PGUP, KC_PGUP, KC_TRNS, EM_UNDO, KC_VOLD, KC_VOLU, KC_MUTE}
},
[_CUSTOM] = { /* CUSTOM */
    {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
    {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
    {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, MOB,     KC_TRNS, CUS1,    CUS2,    KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
    {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS}
}
};

レイヤーキー

LOWERを使用しました。

対象キー

  1. modifierその1として、Aキーの左のキー:レイヤーの有無によらずキーコードが同じ
  2. modifierその2として、左下隅のキー:レイヤーの有無によってキーコードが異なる
  3. 文字キーとして、Qキー:レイヤーの有無によってキーコードが異なる

の3つとしました。

補助ツール

Keymillというツールを使い、OSがどのようにキーのON/OFFを認識しているかもあわせて観測してみました。

f:id:hrhg:20170722041247p:plain

調査結果

操作パターンと対象キーの組み合わせのうち、b2とd2の場合に対象キーがロックされてしまうようでした。 対象キーをON/OFFするとロックが解除されるようでした。

分析

操作パターンをkeymillで観測してみると、下図のようなタイミングになっているようでした。 f:id:hrhg:20170722041137p:plain

この図の中でb1の場合mod_onのキーコード(KC_LCTL)とlayer_offのキーコード(KC_TRNS:元のレイヤーと同じコード)は同一ですが、 b2の場合mod_onのキーコード(KC_LCTL)とlayer_offのキーコード(KC_A)が異なるため(朱字の部分)、mod_onのキーOFFが認識されないと思われます。d1,d2の場合も同様です。

また実質的に問題にはならないかもしれませんが、文字キーとの組み合わせでレイヤキーがONになるタイミングで文字キーがOFFと認識されるところも気になりました(b3, d3)。

modifierと文字キーの挙動が異なるのは tmk_core/common/action_leyer.c の layer_state_set() でロックを回避するために clear_mods() を呼んでいないからだと思われます。

いっぽう自分の想定した各パターンのタイミングは下図になります。 f:id:hrhg:20170722041206p:plain

異なる点は

  • 対象キーのOFF時には、ON時のレイヤーと同じキーコードを伝達する
  • modifierと文字キーとの挙動は同一にし、対象キーのON/OFFのタイミングでのみキーの変化を伝達する

になります。

対応

試にQMKの変更をやってみようとも思いましたが、

  • 上記のように方式を変えるため、ちょっと変える程度ではなさそう
  • 他にも影響がある可能性があるかもしれないので手間取りそう
  • print debugを行う方法がよくわからなかった

などという言い訳により手をつけていません。

自分の当初の目的としては分割型のキーボードのレイヤーの動作を意図するようにしたかったので、重い腰をあげて以前に作ったarduinoでの実装に自分の想定するタイミングのレイヤー機能を追加して、自分で納得する動きを確認しました。

現在のところQMKの変更は試みていませんが一応ここまで調べてみましたので、このような形で情報を公開してみることにしました。

所感

QMK(というかTMKでの処理でしょうか )は世界的に使われている実績があると思いますが、個人的には致命的な問題ととらえています。

しかし特に問題視されている情報が見当たらないので、何か自分が勘違いをしているのか、普通は問題が起こるような指運びをしないのか 不思議に思っているところです。

人さまのご意見をお手柔らかに伺えれば幸いです。

→ 冒頭に追記したように、全に私の確認不足のようでした。

planckモドキを作った話

f:id:hrhg:20170719054358j:plain

概要

QMKの検証用途で、やっつけでplanckモドキを作ってみました。

回路

オリジナルのplanckの回路図 github.com を参考にして、コントローラはProMicro(互換機)を使い、オリジナルのGPIOのうち使用できないポートがあるのでその辺を変更しました。キー部分が2つに分割されていますが単に流用しただけですので、planckを作るということにおいては意味はありません。
またキーマトリクス以外のLEDやスピーカー等については考慮していません。

2017/07/22 変更:キーマトリクスのIOポートがスピーカーのIOポートと被っているので、キーマトリクスのポートをPC6からPD7に変更しました。

f:id:hrhg:20170722023558p:plain

下記の写真では 5x10配列のものと兼用するように一部修正をしてあります。 f:id:hrhg:20170719054414j:plain f:id:hrhg:20170719052029j:plain

QMK

回路図に合わせてポート定義を変更しました。

2017/07/22 変更:キーマトリクスのIOポートがスピーカーのIOポートと被っているので、キーマトリクスのポートをPC6からPD7に変更しました。

planck/config.h

// original planck
//#define MATRIX_ROW_PINS { D0, D5, B5, B6 }
//#define MATRIX_COL_PINS { F1, F0, B0, C7, F4, F5, F6, F7, D4, D6, B4, D7 }

// planck modoki (4x12)
#define MATRIX_ROW_PINS { B4, B5, F4, F5 }
//#define MATRIX_COL_PINS { C6, D4, D0, D1, D2, D3, F6, F7, B1, B3, B2, B6 }
#define MATRIX_COL_PINS { D7, D4, D0, D1, D2, D3, F6, F7, B1, B3, B2, B6 }

// small (5x10)
//#define MATRIX_ROW_PINS { B4, B5, F4, F5, D3 }
//#define MATRIX_COL_PINS { C6, D4, D0, D1, D2, F6, F7, B1, B3, B2 }

5x10配列等にする場合は、追加でキーマップ等の修正が必要になります。

配列の異なるキーボードを同時に使う

大変有用に参考させていただきました。

ref:
配列の異なるキーボードを同時に使う方法 - forPCActionGamer Wiki*

WindowsでMacのUSB Etherを使う

Boot CampのドライバのAsixのが使えるそうです。
WiFiが頻繁に途切れていたので新規に買うところでしたが、流用できたので助かりました。キーボードとかトラックパッドとかのドライバも入っているようでした。

ref:
Windows 10 のマシンでApple純正のUSBEthernetアダプタを使う方法 | Exception

薄型キースイッチの話

f:id:hrhg:20170410062808j:plain

経緯

もともと腱鞘炎で手首を痛めていて、なるべく手首の負担がかからないようにパームレストを使ったり、薄型のキーボードを使っていました。キーボードを自作するにあたり極力筐体が薄くなるように作ってみましたが、希望する薄さにはできなかったためCherryMX以外のキースイッチがないか探していました。 またキースイッチの構造自体にはそれほどこだわりがないので、メンブレンとかパンタグラフのものを作れないかとも思っていました。

いろいろ情報を探していると、CherryMLというキースイッチがあることを知りました。 CHERRY: Innovation at Your Fingertips – ML Series

キースイッチ単体で扱っているところが見当たらなかったので、MLを使っているキーボードをeBayであたりで買ってキースイッチを取り外すことを検討していました。 ところが、CherryMLの事を調べていたら、同じような薄型のキースイッチがあることを知りました。

www.youtube.com

またDESKTHORITYで入手している情報もがあり、Kailh製ということを知りました。

deskthority.net

入手

ダメもとで、直接Kailhに入手方法について問い合わせてみたところ、こちらの会社やプロダクトについて事前に教えて欲しいとのことでした。個人で作っている等のことを返答すると、2種類のキースイッチのデータシートを教えてもらうことができました。
データシートを確認し、2種類のうち薄いのサンプルを入手できることになりました。 この時点ではまだキーキャップの存在は確認できていませんでした。

PG1350矮轴|Products|KAILH Micro Switch Keyswitch Switch Encoder Push button switch Kaihua Electronics Co., Ltd.
この記事を書いている時点で、kailhのサイトにつながらないようです。
ホスト名(www)をつけたらつながるようになりました。
現在メンテナンスアップグレード中だそうです。

Cherry MXとの互換性・比較

キーキャップへの接合形状はMX用と異なるので、MXのキーキャップは流用できないようでした。MLの形状に近いですが、同一ではないようです。
フレームのサイズはMXとほぼ一致しました。 f:id:hrhg:20170410062820j:plain

厚さの違いはこんな感じ。 f:id:hrhg:20170410062818j:plain

フットプリントが異なるのでMX用の基板は流用不可のようです。LEDのためと思われる穴が開いていました。 f:id:hrhg:20170410072547j:plain

同一の形状で

  • no sound, no hand feeling (赤軸)
  • no sound, with hand feeling (茶軸)
  • click sound, with hand feeling (白軸)

の3タイプがあり、荷重はMLの赤軸よりちょっと重い感じでした。

ヲチモノにも同類のキースイッチを使ったと思われる試作?の記事があるようでした。 watchmono.com watchmono.com watchmono.com

また、Tokyo Mechanical Keyboard Meetup vol.2にも持って行ってみました。

www.reddit.com

購入

サンプルを確認した結果、良さそうなので5台分の試作としてとりあえず500個発注することにしました。
価格は情報がひとり歩きすると良くないと思うので公開しませんが、高くはありませんでした。支払いはpaypalで行うことができました。

DESKTHORITYを見るとキーキャップも存在するようでしたので確認したところ、サンプルが2種類入手できるようなので一緒に入手しました。 文字ないものが欲しかったのですが、ブランクのものが製造されるのは1か月後になるとのことで今回の入手はできませんでした。
他に専用のLED関連もあるようです。

その他

とりあえず当面はこのスイッチを使ってみる予定です。

f:id:hrhg:20170523055207p:plain

このキースイッチが広まって、単価が安くなり、荷重バリエーションも増え、キーキャップの互換品も流通するようになることを希望します。

追記 (2017/07/15)

キーキャップについて、形状は下図の左と中および右の2種類あることを確認しています。また左のものは2色成型で文字を表していて、中のものは表面塗装で文字を表しています。右は表面全塗装です。
f:id:hrhg:20170715034429j:plain

ついでに、さらに薄型の下図左のキースイッチも開発したそうです。用途としてはノートパソコン用だそうで、キーキャップは互換性がありますが、フットプリントやフレームサイズの互換性は無いようでした。
f:id:hrhg:20170715042232j:plain

Arduino Leonardoでシリアルポートを無効にする

Arduinoを使ってUSBキーボードを作っているところですが、当然ながらPCに対してHIDの他にUSBシリアルとして認識してしまうことに気づきました。対策としてArduinoのcoreを書き換えることで、とりあえずはUSBシリアルを無効にできることをArduino Leonardoで確認することができました。同じMPU(MEGA32U4)を搭載している機種で共通して出来ると思われます。

注意:以下を実行すると以降はUSBを使った書き込みができなくなりますので、USBシリアルを有効にするためにICSP経由でブートローダーが上書き出来ることを確認してから行うことをお勧めします。

USBシリアルを無効にする対策事例がないかなと調べてみますと forum.arduino.cc というそのまま解決出来そうな情報が見つかりましたが、使っていたArduinoの環境とは一致しないようでした。githubの情報を調べてみると、2年前の2015年2月にUSBシリアルを無効に出来ないように変更されているようでした。 github.com

ということで、

  • 現在のソースに上記で加えられた修正を元に戻す
  • USBCore.cppの_updateLUFAbootLoaderにtrueを代入しているあたりも同様にifdefする
  • USBDesc.hのUSB_ENABLED CDC_ENABLEDのdefineをコメントアウトする

ことでUSBシリアルを無効にすることが出来るようになりました。確認したArduinoのバージョンは1.8.2です。
USBシリアルを無効に出来なくした理由は見当たりませんでした。

基板を使わない手はんだで、キースイッチを交換できるようにしてみた話

常々手軽に物理的なキー配置を変えることができないか考えていたところ、riv_mkさんの riv-mk.hateblo.jp のエントリーに影響されて、基板を使わない手はんだで、キースイッチを交換できるようにする実験をしてみました。

キースイッチを加工

f:id:hrhg:20170412015208j:plain 左が加工前、右がスイッチ導通の足にダイオードとリード線を付けた加工後です。リード線はダイオードの足を切断した余りです。基板に固定させるプラスチックの足を削除したのは薄型にすること目的とした加工です。

スイッチはKailhの薄型キースイッチを使っています(このスイッチについてのエントリーはまだ作成中です)。

ソケットを作成

f:id:hrhg:20170412015232j:plain 丸ピンICソケット akizukidenshi.com から金属部分を取り出し、基板に入る分の足を切断します。この工程が結構手間がかかった気がしました。

ソケットにワイヤーをはんだ付け

f:id:hrhg:20170412015312j:plain 取り出したソケットの金属部にワイヤーをはんだ付けします。実際の作業はキースイッチにソケットをつけた状態でワイヤーをはんだ付けしました。

今回は被覆付きのワイヤーをワイヤーストリッパで被覆に切れ込みを入れ、被覆を引っ張って1mm程度金属部を露出させ、予備はんだをする方法でやってみました。

ソケット付きケーブルをキースイッチにはめる

f:id:hrhg:20170412015339j:plain あらかじめキースイッチをフレームに固定しておき、ダイオードおよびリード線の足にソケットをはめ込みます。

f:id:hrhg:20170412015406j:plain ソケットにしているので、当然後から手で外すことができます。上から4行目を外してみた場合です。

所感

実際にやってみて、手間がかかる割に本当に便利なのかわかりません..
せめてソケットが単体で入手できればかなり楽になると感じました。

その他

kinesisのキースイッチを交換されている記事がありましたのでご紹介します。 squidapache.hatenadiary.com

kinesisはフレキシブル基板を使っているようです。