【自作キーボード】OLED表示内容のレシピ&アイディア集

この記事は キーボード #2 Advent Calendar 2021 10日目の記事です。

adventar.org

昨日の記事は、msysyamamotoさんの 2021年の活動振り返り でした。

msysyamamoto.hatenablog.com

キースイッチ沼も深くて楽しいですよね。私のお気に入りはルブったDUROCK L7スイッチです。


自作キーボードといえば、自分好みの配列、自分好みのキースイッチやキーキャップが使えるのが醍醐味ですが、電子工作の技術を活かして、市販のキーボードにはない入出力デバイスを自由に取り付けられるのも大きな特長です。 中でもOLEDは、キーボードに小さなディスプレイを取り付けられるということで、いろいろな情報を表示できそうで夢が広がりますよね!

ですが、OLEDに何を表示させればいいかは中々悩ましいものです。 そこで本記事では、OLEDに表示させるもののネタを可能な限り集めてみました。 一部のネタについてはコード例も載せましたので、よろしければお試しください!

OLEDの有効化方法・表示内容の変更方法

OLEDには実装次第で自由に表示内容を変更できますが、反面その自由度の高さゆえ、現時点ではキーマップなどとは違ってGUIによる設定ができません。 表示内容の変更のためには、自分でQMK Firmwareに実装してビルドする必要があります。 そのため、本記事に書かれている内容を試すためには自分でQMK Firmwareをビルドできる環境が必要です。 まだの方は公式ドキュメントなどを参考に環境を構築してみましょう。

Setting Up Your QMK Environment
QMK 環境の構築

QMK Firmwareのビルド環境が構築できたとして、OLED付きで販売されているキーボードキットであれば、多くの場合はデフォルトでOLEDが光るようになっている(または有効化の方法がビルドガイドに書いてある)と思います。 それ以外の場合は、QMK公式のドキュメントを見て有効化のための設定を書くことになります。

OLED Driver

が、ハードウェアさえ問題なければ、基本的には以下の2ステップで有効化できるはずです。

  • rules.mkの中に、OLED_ENABLE = yesを追加する
  • keymap.cの中に、oled_task_user()関数がなければ追加して、その中にOLEDの表示内容を実装していく

    bool oled_task_user(void) {
        // OLEDに表示する内容を上から実装していく
        oled_write_ln_P(PSTR("Hello, world!"), false);
    
        return false;
    }
    

[f:id:f1p038:20211210000821p:plain:alt=Hello world!]

なお、QMKには時々破壊的変更を伴う更新が入ることがあります。 OLEDに関しても、2021年後半に2つ破壊的変更が入ったため、QMK Firmwareのバージョンが最新でない場合は公式ドキュメント通りに実装してもコンパイルが通らない・通ってもOLEDが光らない場合があります。 特にQMK公式リポジトリではなく、キット作者がForkしたリポジトリを使用している場合、QMK公式に追従しているとは限らないため注意が必要です。

以下が2021年後半にQMK Firmwareに入った破壊的変更です。

  • コンパイルスイッチがOLED_DRIVER_ENABLEからOLED_ENABLEに変わった
  • oled_task_user()関数の戻り値が、voidからboolに変わった

また、OLEDに表示する機能を盛り込んでいくと、次第にファームウェアサイズが肥大化していき、ProMicroの容量と戦う必要が出てきます。 そんなときはLTO(リンク時最適化)を有効にすると、ファームウェアサイズが劇的に軽くなる可能性があります。 rules.mkLTO_ENABLE = yesと書いておきましょう。

レシピ&アイディア集

画像を表示させる

OLEDカスタマイズといえばまずはこれ。 デフォルトファームウェアにオリジナルロゴを設定しているキーボードも多いですね。

OLEDに自分の好きな画像を表示させる方法については、変換サービスも作成されているこちらの記事が分かりやすいです。

HelixのOLED表示を簡単にカスタマイズするサービスを作った - Qiita

なお、OLEDに表示するデータの仕組みについては、この記事でとても詳しく説明されています。 画像だけでなく、フォントを変更したい場合にも参考になると思います。

QMK の OLED 基礎知識 - Qiita

現在のレイヤー [実装例あり]

[f:id:f1p038:20211210002623p:plain]

どのキーも押していなければデフォルトレイヤーと設定している方にとってはそこまで嬉しさはないかもしれませんが、押すたびにレイヤーが切り替わるようにしている方や、私のようにデフォルトレイヤーを切り替えたり、レイヤー切替キー連打でレイヤーロックみたいな機能を実装している方ですと、たまにレイヤー迷子になったときに便利です。

// レイヤーがこのように定義されているものとします
enum layer_names {
    _BASE,
    _RAISE,
    _LOWER,
    _ADJUST
};

void oled_write_layer_state(void) {
    oled_write_P(PSTR("Layer: "), false);
    switch (get_highest_layer(layer_state | default_layer_state)) {
        case _BASE:                               // 各レイヤーで表示する内容を定義
            oled_write_ln_P(PSTR("Base"), false); // レイヤー表示名はお好みで変更してください
            break;
        case _RAISE:
            oled_write_ln_P(PSTR("Raise"), false);
            break;
        case _LOWER:
            oled_write_ln_P(PSTR("Lower"), false);
            break;
        case _ADJUST:
            oled_write_ln_P(PSTR("Adjust"), false);
            break;
        default:
            oled_write_ln_P(PSTR("Undef"), false);
            break;
    }
}

bool oled_task_user(void) {
    oled_write_layer_state();
    return false;
}

NumLock/CapsLock/ScrollLockの状態 [実装例あり]

f:id:f1p038:20211209235843p:plain

市販キーボードではLEDランプで表示されることが多いですが、自作キーボードではOLEDに表示させることができます。

void oled_write_host_led_state(void) {
    const led_t led_state = host_keyboard_led_state();
    oled_write_P(PSTR("NL:"), false);
    oled_write_P(led_state.num_lock ? PSTR("on") : PSTR("- "), false);
    oled_write_P(PSTR(" CL:"), false);
    oled_write_P(led_state.caps_lock ? PSTR("on") : PSTR("- "), false);
    oled_write_P(PSTR(" SL:"), false);
    oled_write_ln_P(led_state.scroll_lock ? PSTR("on") : PSTR("-"), false);
}

bool oled_task_user(void) {
    oled_write_host_led_state();
    return false;
}

打鍵数 [実装例あり]

f:id:f1p038:20211209235855p:plain

キーボードの電源を入れてからの打鍵数を数えて表示させています。 作業中や作業後に確認して、今日はこれだけ頑張ったんだなーと達成感を感じてはいかがでしょうか?

static unsigned int type_count = 0;
void count_type(void) {
    type_count++;
}

// キーを押す・離すタイミングで呼ばれる関数
// 関数自体なければ関数ごと追加、あれば`#ifdef OLED_ENABLE`内だけ追加する
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
#ifdef OLED_ENABLE
    if (record->event.pressed) {
        count_type();
    }
#endif
    return true;
}

void oled_write_type_count(void) {
    static char type_count_str[7];
    oled_write_P(PSTR("Type count: "), false);
    itoa(type_count, type_count_str, 10);
    oled_write_ln(type_count_str, false);
}

bool oled_task_user(void) {
    oled_write_type_count();
    return false;
}

なお、この打鍵数カウントはキーボードの電源を切るたびにリセットされるので、累計打鍵数(このキーボードでどれだけ打鍵したか)は表示できません。 私はまあいいかと割り切っています。

打鍵数をEEPROMに書き込むなどして永続化しておけば累計打鍵数も表示できそうですが、打鍵のたびに書き込んでいてはEEPROMの寿命がすぐ尽きそうなので、永続化のタイミングを考える必要がありそうです。

uptime [実装例あり]

f:id:f1p038:20211209235907p:plain

キーボードの電源を入れてからの時間がtimer_read32()で取得できるので、それを表示しています。

// write given digit, with leading zero if digit < 10
void oled_write_2digit(unsigned int digit) {
    static char buf[6];
    itoa(digit, buf, 10);
    if (digit < 10) {
        oled_write_char('0', false);
    }
    oled_write(buf, false);
}

void oled_write_uptime(void) {
    static uint32_t uptime_s;
    uptime_s = timer_read32() / 1000;
    oled_write_P(PSTR("Uptime "), false);
    // hour
    oled_write_2digit((uptime_s / 3600) % 60);
    oled_write_char(':', false);
    // minutes
    oled_write_2digit((uptime_s / 60) % 60);
    oled_write_char(':', false);
    // seconds
    oled_write_2digit(uptime_s % 60);
    oled_write_char('\n', false);
}

bool oled_task_user(void) {
    oled_write_uptime();
    return false;
}

なお、現在時刻や日付はQMKでは取得できないため表示できません。 RTC(real-time clock)というパーツとバックアップ用電池をキーボードに搭載すれば、キーボードで時計を持てるので時刻を表示することはできます(そこまでしなくてもキーボードが繋がってるPCで時刻は確認できるでしょ、というツッコミが聞こえてきそうですが)。

キッチンタイマー・ストップウォッチ

時間が測れるので、ストップウォッチやタイマーも実装できます。

圧電スピーカーとOLEDが搭載されたキーボードにタイマー機能を実装して、キーボード上でポモドーロテクニックを回せるようにする、というのも面白そうです。

打鍵速度(WPM) [実装例あり]

f:id:f1p038:20211209235927p:plain

QMKの機能で打鍵速度(WPM = words per minutes)が測定できるので、それを表示しています。

docs.qmk.fm

なお、この機能で取得できるWPMは、英単語の平均長さを5文字と仮定して、1分間の打鍵速度を5で割った値となります。 この割る数は変更できるので、1に設定すれば「1分間の打鍵速度」そのものも取れそうですが、取得できるWPMの最大値が255なのですぐにカンストしてしまいます。 「ちゃんと分間打鍵速度を表示したい」という方は、QMK本体の実装に手を入れてみてもいいかもしれません。

  • rules.mk

    WPM_ENABLE = yes
    
  • keymap.c

    void oled_write_wpm(void) {
        static char wpm[5];
        oled_write_P(PSTR("WPM: "), false);
        itoa(get_current_wpm(), wpm, 10);
        oled_write_ln(wpm, false);
    }
    
    bool oled_task_user(void) {
        oled_write_wpm();
        return false;
    }
    

気温・湿度・気圧表示

f:id:f1p038:20211210001424p:plain

電子工作スキルが求められますが、キーボードに環境センサを取り付けて、読みだした値を表示することも可能です。 OLEDはI2Cで接続されているので、例えば気温・湿度・気圧が測れるBME280など、I2Cで接続できるセンサであれば追加でピンを消費せずに実装できて便利です。 もちろん空いているADC対応ピンでアナログ値を読み出したりするのもよさそうです。

私の知る範囲では、らてさん作のPistachioProが、気温・湿度・気圧センサを搭載し、OLEDに表示しています。

booth.pm

デバッグ情報

Corneキーボードのデフォルトファームウェアには、最後に押したキーの内容と位置を表示する機能が実装されています。

github.com

これはキーボードのユーザー向けというよりは、QMKのkeymap.c魔改造したい、C言語でゴリゴリと機能を実装したい人にとってデバッグ用にあると嬉しい機能だと思いました。 押したキーの情報に限らず、キーボード上にデバッグ情報を出してprintデバッグできるのは開発者にとって便利です。

電卓モード

以前「PCに繋いでテンキーとしても使える電卓」が発売されたというニュースを見た記憶がありますが、今も普通に売ってるようですね。

www.amazon.co.jp

自作キーボードでもQMKに実装してあげれば、キーボード内で計算した結果をOLEDに表示する電卓モードが実現できそうです。 さらに計算結果をそのままPCに入力できるようにしても面白いかも。

ゲーム

youtu.be

ボタン、画面、CPUが揃っているので、キーボードは実質携帯ゲーム機。

OLED上でPongやテトリスを遊べるよう実装した例が見つかりました。

その他のアイディア

思いついてしまったけど、現実的に実装できるかどうかも分からないネタを供養します。

シェル

最近のほぼ週刊キーボードニュースで、あったらいいなとコメントされていた気がします。 Raspberry Pi Zeroでキーボードを作れば実現できそう。

REPL

REPL(Read-Eval-Print Loop)とは、プログラムを打ち込んだらその場で実行して結果を表示してくれるツールです。シェルからの連想。 ProMicro + QMK 上で実装するのは容量的にも辛そうですが、Raspberry Pi PicoなどでKMK Firmwareを使用すれば、CircuitPythonのインタプリタが付いてくるので実現できそうです。

PCから受信した情報を表示する

恐らくみんなが欲しがっている機能。 キーボードを接続しているPCから情報が得られれば、表示できる情報がぐっと増えますからね。 PCローカルだけでなくネットの情報も利用できますし。

ただ問題は、どうやってキーボードに情報を伝えるか。 VIAやRemapではRAW HIDを使用してキーマップを送り込んでいるようなので、同じ仕組みでできるような気がしますが、できたとしてもPC側でも送信用ソフトの実装・起動が必要なのでそれなりにハードルは高そうです。 残念ながらアドベントカレンダーの記事には間に合いませんでしたが、ちょっと研究してみたいですね。

終わりに

OLEDに表示できそうな内容を、見つかった & 思いついた限り挙げてみました。 OLED搭載キーボードをお持ちの方は、この記事を参考に気になったネタを表示して遊んでいただけたら嬉しいです。

この記事を書いたキーボード

自設計キーボード yacc46 v1.0

  • キースイッチ: MOMOKA Flamingo Switch (スプリングを MX Progressive 78P/72P/68Pに交換して変荷重にしたもの)
  • キーキャップ: DROP + OBLOTZKY SA OBLIVION V2
  • OLEDの表示内容:
    • QMKのロゴ
    • 現在のレイヤー
    • NumLock/CapsLock/ScrollLockの状態
    • 打鍵数
    • WPM

f:id:f1p038:20211209235942p:plain