弊社では業務PCとしてノートPCが支給されますが、外付けキーボードやマウスを利用したい方は追加支給してもらうこともできます。ですが自分好みのキーボード・マウスを利用したい方は、各人の責任で持ち込んで利用することも認められています。
その流れで私は自作したキーボードを業務PCに接続して使っていますが、そのキーボードを作成した際の知見を少しまとめました。
小型試作基板から始まったハーフキーボード作り
いつも基板製造でお世話になっているJLCPCB様は、一度の注文は最低5枚からの注文となりますが、小さいサイズの基板を安価なディスカウント価格で試作してくださいます。
回路のテストを小さいサイズで繰り返し試せるため便利に利用しているのですが、ディスカウントの範囲で実用できる物を作れないかと検討した結果、縦横5キーずつのマクロパッドやテンキーパッドに似たものを作りました。
基板が届き組み立てたところで、ふと通常文字のキーキャップをはめてみたところ、2台あればキーの数も50個になるので通常のキーボードとして使えるのでないか?と思ってしまったところから今回の話が始まります。

半分のキーボードを常用するために
届いた5枚の基板のうち2枚を使って組み立てたキーボードが2台できあがり、左手用と右手用のキーキャップを装着して役割に対応する設定(キーマップ)を書き込みます。
こうして同一回路の基板でそれぞれ対応するキーマップに替えて使うことでキーボード一台分の種類の文字を扱えるようにはなるのですが、このキーマップをそれぞれの個体にはめたキーキャップに対応するものに変えるためにqmk_firmwareでは通常、
といったいくつかの方法があります。
1.の方法ではキーマップの割り当てが電源を切っても永続化する一方で、ファームウェアを修正して入れ替えるたびに都度それぞれビルドして対応するバイナリを書き込む必要があります。
2.の方法ではPCに繋ぎなおしたり電源を入れるたびに設定を変更する必要があります。
3.の方法は上記課題をクリアできますが、ファームウェアを修正すると初期化してしまい、その都度キーマップを作り直すことになります。
この手間を解消するため、2.の方法でありながら電源投入時の初期のレイヤを固定して3.のように扱うことができるようにしたいと思います。
qmk_firmwareのBootMagic機能
話は変わりますが、qmkでファームウェアのバイナリを変更するときには、マイコン基板やキーボード基板のボタンを押して書き込みモードにする方法の他に、書き込みモードに移る内部キーコードと、PCに接続するなどの起動時に特定のキーを押しておくBootMagicという機能があります。
このBootMagicの操作方法は半固定の永続設定の操作方法としてあっているのではないでしょうか?と言うことで調べました。
bootmagicの設定例(keyboard.json)
1 2 3 |
"bootmagic":{ "matrix": [5,0] }, |
qmk_firmwareは背後にqmkの元になったtmkやマイコンの基本API(rp2040ではChibiOS)がありますが、qmkとしてはquantum/main.cのmain()関数がプログラムの根幹になっています。
プログラム実行直後に各種の初期化をおこなった後にメインループを回す構造ですが、初期化の中でBootMagicを判断しているのはquantum/keyboard.c の quantum_init() から bootmagic.c の bootmagic() を呼び出す流れの部分でした。
BootMagicを呼び出す流れの途中の関数には (weak) を宣言している関数があり差し替えることはできますが、通常の処理の流れを書き換えたくないため、 quantum_init() の中でファームウェア開発者に提供され呼び出されている keyboard_post_init_user() を実装します。
config.hで追加した定義
1 2 3 4 5 6 7 |
// 起動時のデフォルトキーマップ変更 #define BOOTMAGIC_L_ROW 2 #define BOOTMAGIC_L_COLUMN 0 #define BOOTMAGIC_R_ROW 3 #define BOOTMAGIC_R_COLUMN 0 #define BOOTMAGIC_N_ROW 4 #define BOOTMAGIC_N_COLUMN 0 |
特定のキーが押されているか確認するにはマトリクスのrow,colを指定して確認しますが、これはキーボードの基板の配線ごとに変わるところです。今回は左端上から2~4個目を押しながらUSBケーブルを接続するとキーマップが変わるように、それらのキーの配線を指定しています。
起動時に押すキーと役割(今回の例

keymap.cで記述する関数定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// 起動時に特殊キーを押しているとデフォルトレイヤを変更する void keyboard_post_init_user(void) { uint8_t row = BOOTMAGIC_N_ROW; uint8_t col = BOOTMAGIC_N_COLUMN; matrix_scan(); if( matrix_get_row(row) & (1 << col)) { set_single_persistent_default_layer(_NPAD); } row = BOOTMAGIC_L_ROW; col = BOOTMAGIC_L_COLUMN; if( matrix_get_row(row) & (1 << col)) { set_single_persistent_default_layer(_LPAD); } row = BOOTMAGIC_R_ROW; col = BOOTMAGIC_R_COLUMN; if( matrix_get_row(row) & (1 << col)) { set_single_persistent_default_layer(_RPAD); } } |
起動時に押していてほしいキーの情報は他の定義と同じように config.h などで定義することとして、一つの動作のキーごとに row(行) と col(列) を BOOTMAGIC_N_ROW, BOOTMAGIC_N_COLUMN などと定義しておき、これらのキーが現在のマトリクスマップの押下状況と比較してキーが押されているかどうかを、例では3回(左手、右手、テンキーの3種)判断しています。
マトリクスの押下判断手段はbootmagic.c の手法に沿った方法でmatrix_get_row()で1行分の状態を取得して、colの位置のビット状態を確認します。
config.h で定義したキーが押されていると判断した場合に、 set_single_persistent_default_layer() を使って現在のデフォルトレイヤを変更すると共に、eepromに書き込んでデフォルトキーマップの変更を永続化しています。
このようにしてキーボードをくみ上げた後にキーキャップで決まる役割、それぞれに応じたキーマップに変更しつつ、その変更をeepromに記録して電源を入れなおしてもその役割を維持できるようになりました。
複数のキーボードを組み合わせて使うことについて

このような経緯を経て、左右2台のキーボードを使うことでキーボード一台分の入力ができるようになりましたが、分割キーボードに比べると下記の違いがあります
- PCに接続するためのUSBポートが2口必要(Hubでも可)
- qmk機能が個々のキーボードに閉じるため、レイヤキーなどの操作が両手に分担できない(左でレイヤキーを押しても右のマップが変わらないなど)
- 同上の意味ですがRGBマトリクスなどエフェクトが左右別になる
- 片側だけでも動作・利用可能、途中で抜き差し可能
- 消費電力はそれぞれ別の上限になるため余裕がある
これらの違いは、キーボードの種類と利用者それぞれの設定状況(キーマップや利用方法など)で受容できるかどうかが決まりますので、その時々の判断が必要となります。
とはいえ、個人的にはこの状況でキーの少なさ以外の不自由を感じずに日常業務で利用できていますので、変わり種の多い自作キーボードのバリエーションの一つとしていかがでしょうか。
おわりに
自作キーボードのファームウェアはソフトウェアと電子回路、機械的な事柄まで幅広い知識が身に着けられるのでソフトウェアエンジニアとして幅を広げるにはいい課題ではないでしょうか。
キーボードと言う毎日使うデバイスですので、活動の結果がQOLに直結する点も素晴らしいと思います。
この記事で興味を持って一人でも多くの仲間が現れることを期待しています。