KHCの

KHC( https://twitter.com/we_can_panic )が140字を超えるツイートをするところ

KeyBallのファームウェアをアップグレードするぞ

なんかめっちゃ苦労したので記録しておきます
Windows11だからかなー?

目次

1. QMKのインストール

↓の方のを参考にしました

note.com

シンボリックリンクだとうまく認識されなかったからディレクトリを直接移動した

2. カスタマイズ

エクストラキー対応

音量ボタンとかが使えるようになる

rules.mkに下記オプションを追加

EXTRAKEY_ENABLE = yes

それだけだとマイコンの容量が足りず入れられないので、代わりにいくつかのLEDオプションを無効化

config.h

#ifdef RGBLIGHT_ENABLE

#    define RGBLIGHT_EFFECT_RAINBOW_SWIRL
// #    define RGBLIGHT_EFFECT_RAINBOW_MOOD
// #    define RGBLIGHT_EFFECT_BREATHING
// #    define RGBLIGHT_EFFECT_SNAKE
// #    define RGBLIGHT_EFFECT_KNIGHT
#    define RGBLIGHT_EFFECT_CHRISTMAS
// #    define RGBLIGHT_EFFECT_STATIC_GRADIENT
// #    define RGBLIGHT_EFFECT_RGB_TEST
// #    define RGBLIGHT_EFFECT_ALTERNATING
// #    define RGBLIGHT_EFFECT_TWINKLE

#endif

レイヤーを増やす

config.hの下記の数字を書き換え

#define DYNAMIC_KEYMAP_LAYER_COUNT 7

これも↑と同じくLEDをいくつか無効化しないと入らない

なお、keymap.cにデフォルトの4レイヤー分のキーマップが記述してあるが、増やした分のレイヤーを追記する必要はない。Remapで編集可能

スクロールレイヤーを移動

keymap.clayer_state_set_user関数内で設定

デフォルトは3だが最後尾のレイヤーに移したかったので6に変更

layer_state_t layer_state_set_user(layer_state_t state) {
    // Auto enable scroll mode when the highest layer is 6
    keyball_set_scroll_mode(get_highest_layer(state) == 6);
    
    return state;
}

レイヤーのOLED表示

デフォルトのままだと小さくて見づらかったので大きくした

表示例

OLEDに写せる画像は、qmk/keyboards/keyball/lib配下のglcdfont.cに16進数で収められている

コード中で画像のインデックスを指定すると、その範囲内の画像が表示される形式
詳細は以下リンクが分かりやすい

qiita.com

レイヤー表示の素材となる白の四角を新たに追加。コード末尾の配列の一行を書き換える

const unsigned char font[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  ...
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // <- ココ
};

keymap.cの中で画像のインデックスを指定する

今回は複数のイメージを写すので、↓のように配列をいくつか作る

/* Display Current Layer */

#ifdef OLED_ENABLE

#include "lib/oledkit/oledkit.h"

static const char PROGMEM qmk_logo[] = {
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94,
    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4,
    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0x00
};
static const char PROGMEM qmk_logo_connect[] = {
    0x80, 0x10, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x11,
    0xA0, 0x10, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0x11,
    0xC0, 0x10, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0x11, 0x00
};
static const char PROGMEM qmk_1[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0x00
};
static const char PROGMEM qmk_2[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0x00
};
static const char PROGMEM qmk_3[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0x00
};
static const char PROGMEM qmk_4[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0x00
};
static const char PROGMEM qmk_5[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0x20, 0x20, 0xDF, 0x00
};
static const char PROGMEM qmk_6[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0x20, 0x20, 0xDF, 0x00
};

その下に処理を書く

void oledkit_render_info_user(void) {
    // デフォルトで押下したキー、動かしたボールの動きが表示される設定は無効化する
    // keyball_oled_render_keyinfo();
    // keyball_oled_render_ballinfo();

    switch (get_highest_layer(layer_state)) {
        case 0: // デフォルトレイヤーのときはPCとの疎通チェックのために特別な画像を表示
            oled_write_P(qmk_logo_connect, false);
            break;
        case 1:
            oled_write_P(qmk_1, false);
            break;
        case 2:
            oled_write_P(qmk_2, false);
            break;
        case 3:
            oled_write_P(qmk_3, false);
            break;
        case 4:
            oled_write_P(qmk_4, false);
            break;
        case 5:
            oled_write_P(qmk_5, false);
            break;
        case 6:
            oled_write_P(qmk_6, false);
            break;
        default:
            oled_write_P(qmk_logo, false);
            break;
    }
}

#endif

レイヤーロック機能

MOキーとかで遷移したレイヤーを固定する機能

下記リンクの方のを参考にしました

getreuer.info

keymap.cに下記を記載

/* Layer Lock */

enum custom_keycodes {
    KC_LAYER_LOCK = KEYBALL_SAFE_RANGE // 5DBB
};

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    if (!process_layer_lock(keycode, record, KC_LAYER_LOCK)) { return false; }
    return true;
}

これで、KC_LAYER_LOCKというキーを新たに追加できた

固定ロジックの実装は下記のソースで行っている。featuresディレクトリを自分のkeymap.cとかが置いてあるディレクトリの下に作成し、その下に↓の2ファイルを配置

qmk-keymap/features/layer_lock.h at main · getreuer/qmk-keymap · GitHub
qmk-keymap/features/layer_lock.c at main · getreuer/qmk-keymap · GitHub

配置イメージ

keymaps
├ default
└ khc
  ├ features
  │  ├ layer_lock.c
  │  └ layer_lock.h
  ├ config.h
  ├ keymap.c
  └ rules.mk

コードを紐づけるために、下記をrules.mkファイルに追記

SRC += features/layer_lock.c

また、keymap.cの頭に下記を追記

#include "features/layer_lock.h"

この状態でビルドしたファームウェアを入れたKeyBallをRemapに繋ぎ、5DBBを固定したいレイヤー上の任意のキーに配置すれば、固定キーが使えるようになる

固定した状態を解除するにはもう一回同じキーを押せばOK

マウスの微調整

大きく動かせばより早く、小さく動かせばより遅く移動するようになる

追記

CPIやスクロール感度はqmk/keyboards/keyball/lib/keyball/keyball.cで編集可能