ロータリーエンコーダーのキー割り当ての3つの方法
概要
ロータリーエンコーダーをキーボードに搭載すると、通常のキーと同様に押したときの動作を設定できるのに加えて
- つまみを時計回りに回したとき
- つまみを反時計回りに回したとき
のそれぞれの動作を設定できます。この記事では、qmk_firmware でロータリーエンコーダーの回し操作の割り当てをカスタマイズする3通りの方法
- ファームウェアの関数定義の中で割り当てる方法
- VIA/Remapでダミーキーを使って割り当てる方法
- VIA/Remapでエンコーダーマッピングを使って割り当てる方法
を紹介します。
3つの方法の違いとして、最初の方法だと割り当てを変える度にファームウェアをビルドして書き込む必要がありますが、残り2つの方法ではVIA/Remapで動的に割り当ての変更ができるので、ファームウェアをビルドしなおさずに割り当てをいろいろ変えて試すことができます。一方ファームウェアのサイズは後の方が大きくなります。したがって、キー割り当ての変更頻度やファームウェアに搭載する他の機能との兼ね合いでやり方を選んでいただくとよいかなと思います。
3つの方法を説明するにあたっての具体例は cocot46plus で試したものを記載します。
ファームウェアの関数定義の中で割り当てる方法
keymap.c で encoder_update_user()
関数を定義します。
例えばcocot46plusにおいて
- レイヤ0では
encoder_update_kb()
に任せる(EXTRAKEYが有効なら音量アップ・ダウン) - レイヤ1ではホイールアップ・ダウン
- レイヤ2ではホイール右・左
の動作をさせる場合は以下のような実装になります:
bool encoder_update_user(uint8_t index, bool clockwise) {
if (index != 0) {
return true;
}
layer_state_t layer = get_highest_layer(layer_state | default_layer_state);
uint16_t keycode;
if (clockwise) {
switch (layer) {
case 1:
keycode = MS_WHLD;
break;
case 2:
keycode = MS_WHLL;
break;
default:
return true; // encoder_update_kbに任せる
}
} else { // counter clockwise
switch (layer) {
case 1:
keycode = MS_WHLU;
break;
case 2:
keycode = MS_WHLR;
break;
default:
return true; // encoder_update_kbに任せる
}
}
tap_code16_delay(keycode, 10);
return false; // encoder_update_kbの処理をスキップ
}
このサンプルコードだと8bitに収まる通常のキーコードしか渡してないので tap_code16_delay()
じゃなく tap_code_delay()
を使っても構いませんが、Modifier付きのキーコード、例えば G(KC_MINS)
などを渡す場合は tap_code16_delay()
じゃないといけないことに注意して下さい。また keycodes.h でModifier付きのキーコードよりも大きな値を持つキーコード、例えば QK_USER_0 = 0x7E40
などは tap_code16_delay()
に渡しても同じように処理されないことにも注意して下さい。私は気付かずに無駄に時間を溶かしました。
VIA/Remapでダミーキーを使って割り当てる方法
通常のキーに対するVIA/Remapによる割り当て変更の機能を利用する方法です。
via.json に実際には存在しないダミーキーの row, col を定義しておき、エンコーダー操作時にファームウェアでダミーキーのイベントを発行させます。
cocot46plus の場合 keymap.c に以下のように書き、反時計回りに対し (row=4, col=2) の、時計回りに対し (row=4, col=5) のキーイベントを発行します。サンプルコードはcocot46plusのファームウェアの古いバージョンの実装をベースに、新しいqmk_firmwareで動くように改変したものです。
keyevent_t encoder1_ccw = {
.key = (keypos_t){.row = 4, .col = 2},
.pressed = false,
.type = KEY_EVENT
};
keyevent_t encoder1_cw = {
.key = (keypos_t){.row = 4, .col = 5},
.pressed = false,
.type = KEY_EVENT
};
bool encoder_update_user(uint8_t index, bool clockwise) {
if (index != 0) {
return true;
}
if (clockwise) {
encoder1_cw.pressed = true;
encoder1_cw.time = (timer_read() | 1);
action_exec(encoder1_cw);
} else {
encoder1_ccw.pressed = true;
encoder1_ccw.time = (timer_read() | 1);
action_exec(encoder1_ccw);
}
return false;
}
void matrix_scan_user(void) {
if (encoder1_ccw.pressed) {
encoder1_ccw.pressed = false;
encoder1_ccw.time = (timer_read() | 1);
action_exec(encoder1_ccw);
}
if (encoder1_cw.pressed) {
encoder1_cw.pressed = false;
encoder1_cw.time = (timer_read() | 1);
action_exec(encoder1_cw);
}
}
こうすることで、以前の私の記事で「飾り」と書いていたencoder配列の要素が、ただの飾りではなくなります。[1]
via.json としては cocot46plus_via.json を使うと、エンコーダーの押し(click)、時計回り(cw)、反時計回り(ccw)がそれぞれキーとして見えて割り当てをカスタマイズできます。[2]
VIA
VIA(https://usevia.app/)の場合、DESIGNタブの"Load Draft Definition"のところのLoadボタンをクリックしてjsonを読み込みます。
CONFIGUREタブの"Authorize device +" をクリックしてキーボードを接続します。
3つのキーを使ってエンコーダーの割り当てをカスタマイズできます。
Remap
Remapの場合は https://remap-keys.app/configure でキーボードを接続した上で、右側の…をクリックし「キーボード定義ファイルのインポート」でjsonを読み込みます。
すると、VIAと同様に3つのキーを使ってエンコーダーの割り当てをカスタマイズできます。
VIA/Remapでエンコーダーマッピングを使って割り当てる方法
先ほど説明した方法は VIA/Remap がエンコーダーに対応する前に用いられていた方法で、こちらがよりモダンなやり方です。
rules.mk で ENCODER_MAP_ENABLE = yes
とするか、 keyboard.json の "features" に key-value として "encoder_map": true
を入れることで、エンコーダーマッピング (encoder_map
配列) を使ったカスタマイズができます。
cocot46plus の場合、via.json としては cocot46plus_via_rev.json を使います。[2:1]
VIA
VIAの場合、エンコーダーをクリックすると画面の下の方で反時計回り(Rotate Counterclockwise)、時計回り(Rotate Clockwise)、押し(Press Encoder)の操作の割り当てをそれぞれ変更できます。
Remap
Remapの場合、エンコーダーの左下の青色のアイコンをクリックすると、押し(click)→時計回り(cw)→反時計回り(ccw)の順にターゲットが切り替わります。
ターゲットごとに割り当てを変更できます。例えば時計回り(cw)だと以下のようになります。
まとめ
qmk_firmware でロータリーエンコーダーのキー割り当てをカスタマイズする方法を紹介しました。ロータリーエンコーダーは1つで3つの操作を割り当てられる面白いデバイスです。活用していきたいと思います。
-
keymap.cのkeymap配列中のencoder1_cw用ダミーキーの設定が効かない問題がありました。修正は https://github.com/aki27kbd/qmk_firmware/pull/11 ↩︎
-
VIAでcocot46plusのjsonがv3互換でないため読み込めない問題がありました。修正は https://github.com/aki27kbd/cocot46plus/pull/6 ↩︎ ↩︎
Discussion