🥔

現状可能なすべての技術でPro MicroにVial-QMKファームウェアを詰め込む

に公開

背景

QMKにおけるSqueezing the most out of AVRの内容だけではPro Micro向けのVialファームウェアを十分に小容量にできないことが多々あるので、これ以外の技術も利用してファームウェアを圧縮する試みです

作業する前に

QMK MSYSでコンパイルコマンドを書く際、末尾に-c -j 0を付けるといい感じになります。

qmk compile -kb cheena/childhood-s-end-v3 -km vial -c -j 0

というような感じです。
-cはclean compileを意味し、それまでにコンパイルしたときに使った作業用のファイルを消して最初からやり直します。
キーボードレベルのファイルを編集した際は自動的にリセットされますが、キーマップレベルのファイル、つまりvial.jsonなどを編集しても自動リセットがかからず、編集前の設定でコンパイルしてしまうことがあります。今回はキーマップレベル含めて大量に作業を行うし、コマンドで書く内容は履歴から呼び出すことが多いはずなのでお祈りとして最初から書いておきます。(CLIで十字キー上を押すと履歴が呼び出せます)
また、clean compileとLTO(高圧縮モード、後述)でかかる時間を-j 0で圧縮します。これは並行処理の数の上限を決めるためのオプションですが、0を指定するとその制限がなくなります。

QMK:Squeezing the most out of AVR解説

ここに記載されている内容はどんなファームウェアであってもだいたい一定の効果があるし、Vial化した時点で代用可能なものが複数あるので、とりあえずここから手を付けます。
なお、QMKがkeyboard.jsonへの設定ファイルの統合を進めているため、rules.mkやconfig.hの一部の設定はkeyboard.jsonに記入できるようになっているようです。
両方に定義した場合はQMK MSYS上でコンパイル中にどちらを優先したか表示するので、それを確認して適宜書き換えてください。

rules.mkの内容

キーボードレベル・キーマップレベル問わず使えるはずです。rules.mkに、

LTO_ENABLE = yes
CONSOLE_ENABLE = no
COMMAND_ENABLE = no
MOUSEKEY_ENABLE = yes
EXTRAKEY_ENABLE = yes
BOOTMAGIC_ENABLE = no
NKRO_ENABLE = no
AUDIO_ENABLE = no

のように記入することでLTO圧縮の有効化と各種機能の無効化が行えます。
LTOを有効化すると圧縮の最適化が行われ、機能削減を行わないままファームウェアの容量が圧縮できます。
consoleとcommandはデバッグで威力を発揮しますが、基本的には不要です。
mouse keyとextra keyはカーソルの制御やスクロール、音量などの操作に使えるので残す方がいいでしょう。
bootmagicは後々vial対応した際にvial側の機能で使えるので必須ではないです。
NKROはバグの温床なので普通に消してOK。そもそもQMKベースのファームウェアはデフォルトで6KROです。
audioは最初から無効化されていたように思いますが、時々デフォルトで有効という記事を見かけるので先制攻撃しておきます。なお、musicはaudioの下位機能に当たるため、audioの機能を維持しながらmusicだけを削除することも可能です。(後述)

なお、info.json Referenceのページを確認すると、keyboard.json内で

"build": {
            "lto":true
        },
"features": {
            "console":false,
            "command":false,
            ...
        },
...

のような形に記述することでも定義できるとされています。

rules.mkに戻ります。

SPACE_CADET_ENABLE = no
GRAVE_ESC_ENABLE = no
MAGIC_ENABLE = no

この3種はrules.mkでしか定義できないようです。
Space Cadetは左右のShiftやCtrlを短く押したときに鍵括弧やエンターなどが入力できるようにするシステムです。要はタップホールドに近いですが、重要なシステムかといわれると微妙なところです。
Grave EscapeはShiftを押しながらEscapeを押すとチルダ、GUIを押しながら(CmdとかWinのこと)Escapeでバッククオートが入力できます。Escape専用のオーバーライドです。
Magic Keycodeは起動すると特定のキーが入れ替わります。
全て割とマジで不要なので無効化します。

config.hの内容

#undef LOCKING_SUPPORT_ENABLE
#undef LOCKING_RESYNC_ENABLE
#define NO_ACTION_ONESHOT

上2つはMX Locking Switchという現在入手困難なキーをサポートするためのものです。#undefでて以後を解除します。(実質トグルスイッチ/オルタネイトスイッチなので、そういうスイッチをキースイッチとして使う際はundefをdefineに書き換えるか、行を丸ごと削除して対応し直しましょう)
最後のOne shotは、同時押しが前提のCtrlやShiftやAltといったキーを、次のキーが押されるまでずっと押された扱いにしておくという機能です。本来は身障者用の機能であり、Windowsの公式機能(固定キー)として左Shiftを5連打すると使えたりします。ゆえに基本的には不要、必要があれば戻す程度の考え方で大丈夫でしょう。

#define NO_ACTION_TAPPING

上記を記入するとタッピングが消えます。流石に厳しいのでやめておきましょう。(これを消すとVialのタッピング/タップホールドも消えたような気がします。未検証ですが)

#define NO_MUSIC_MODE

先述したaudioを維持しながらmusic機能を削除するための方法です。
この場合、rules.mkにも

MUSIC_ENABLE = no

の記述が必要になります。
なお、Musicモードを起動するとスイッチのRowとColumnの値に基づいて音階を出力するようになるそうです。
通電時にビープ音を鳴らす、オーディオクリックなどの機能はそのまま使えるはずです。

config.hでキーマップの上限数を制限することができます。

#define LAYER_STATE_8BIT

末尾を8BITまたは16BITとすることで、キーマップの定義に使っている桁数を減らすことができます。
レイヤー数の上限はbit数に準じます。(8BITで8枚、16BITで16枚)
ちなみにデフォルトは32枚です。
レイヤーが全くいらないという場合は

#define NO_ACTION_LAYER

で1枚に固定できます。

Magic機能関連の削除

Magicをrules.mkで排除しても、それ専用に使っている関数が残るので、keymap.cの末尾、};より後にカスタムコードを追加して関数の中身を上書きします。

uint16_t keycode_config(uint16_t keycode) {
    return keycode;
}

uint8_t mod_config(uint8_t mod) {
    return mod;
}

OLED用コードの圧縮

sprintfやsnprintfを使用しなければ1500byteほど空くとのことですが、しっかりしたコードの書き換えが必要になります。ゆえにここでは割愛します。
(そもそもVial用ファームウェアを組んでいるときにOLEDを残すこともそう多くないでしょう)

RGBライティングの個別削除

config.hまたはkeyboard.jsonの中で個別に光らせ方を削除することができます。
リストが長大なので割愛しますが、正直あまり綺麗とは言い難いRGB TestやChristmasは削除することをおすすめします。また、LEDの配置によってはKnightも綺麗に光りません。特に円形配置や縦方向に並べている場合はKnightよりもSnakeの方がいいでしょう。
順番を入れ替えるためのシステムもありますが、当然ながらファームウェアが重くなります。

Vial:Select Vial GUI features (for size)解説

VialはQMKベースのファームウェアの中でも特に強力な機能と巨大なデータ量を持ち、それゆえに一般的にはARMシリーズに搭載することが推奨されています。
しかし、AVRに実装が不可能な訳ではなく、実際私もLainやEndZone34などをVial対応させてきました。
適切な圧縮を行えばそれぞれに特有の機能(LainのレイヤーLED、EndZone34のロゴ入りOLEDなど)も残すことができます。

vial.jsonの編集

vialにはKLEの記法を元にした物理配列のデータを保持する機能があります。
このデータを本来の形が分かる程度にシンプルにしていきます。
角度のついたキーは水平に戻し、できるだけ一か所に寄るように調整しておきます。
また、マルチレイアウトも選択可能にするのではなく、最初から分割した形にしておきます。

rules.mkの内容

VIAL_INSECURE = yes

を記入することでVialのセキュリティモードを無効化することができ、アンロックキーを指定する必要がなくなるのでわずかに軽量化します。
一方でVialのBootloaderを強制起動する機能は使えなくなるので、好みで選んでも大丈夫です。
(マトリクスチェッカー使う時に楽なんだよね…………………)

タップダンス・コンボ・オーバーライドが完全に不要な場合、

TAP_DANCE_ENABLE = no
COMBO_ENABLE = no
KEY_OVERRIDE_ENABLE = no

で削除できます。数を減らすだけの場合は後述のconfig.hで作業を行います。
なお、レイヤーを削除する場合はQMK側の設定で行います。

config.hの内容

#define VIAL_TAP_DANCE_ENTRIES x
#define VIAL_COMBO_ENTRIES x
#define VIAL_KEY_OVERRIDE_ENTRIES x
#define DYNAMIC_KEYMAP_LAYER_COUNT x

により、各要素の登録可能数を減らすことができます。
xに任意の数字を入れてください。

PC側の設定が日本語キーボードで、容量が多少余っているなら、オーバーライドを残しておくのがおすすめです。初期状態では何も入力できないShift+0に任意のキーコードを割り当てられます。

ブートローダー自体の圧縮(強力だが非推奨)

現在一般的に入手可能なATmega32u4のブートローダーはAtmel-DFUまたはCaterinaですが、両方とも少々容量が大きめなのが弱点です。(ちなみにResetとGNDを1回ショートでBootするのがDFU、2回でBootするのがCaterinaです)
keyboard.jsonで

...
"bootloader": "caterina",
...

などといった定義をする部分がありますが、ここでBootloaderを指定したことによりBootloaderがどれだけの容量を使っているのかの空き容量計算を行ったり、書き込み時に齟齬がないように調整しています。
※Pro MicroがBootloaderをフラッシュ領域に配置しているための問題です。本来は32kbの容量があるのに、通常のBootloaderだと4kbほど占有しているために残りの28kbでなんとかやりくりしているわけです。

この問題の対処法として、nanoBootという512bのブートローダーを使用することを考えます。
必要なのはArduino Leonardなど別のAVR MCUで、AVRライターだのISPだのといった名前でよく制作されています。

原理をざっくり説明すると、ArduinoにnanoBootの元プログラムを覚えさせ(これがISP)、それを別のArduino(ターゲット)に書き込むというもので、手軽ではないですが非常に強力なシステムです。
やり方はネットに無限に散らばっています。もしあと1.5kb欲しいなどということになったら試してみてください。

カスタムコードを戻す

色々なものを削除したり圧縮して容量を開けてみたところで、本来入れたかった機能を順番に戻していきます。
順当に入れば万々歳、入らなかったらブートローダー自体を圧縮したりもっと色々やってみましょう。

Discussion