keyball61にOLEDのロゴを変更してみつつ、Caps Lockの状態を表示してみる

Mumbojumbo
Kiaomoto
Kiaomoto

このページではKeyballのファームウェアの書き換え方法を説明します。今回はロゴの変更とCaps Lockの状態表示にチャレンジしました。C言語とか全然わかんねーよと思いながらも、何となく察する事ができた、今日に感謝。

はじめに

note 2024/05/04時点でのファームウェアの書き換え方法です。

前回に続き、ファームウェアをいじります。やりたい事は以下の二つです。

  • スレイブのOLED(USBが刺さってない方)のロゴを別の画像に変える。
  • さらにスレイブのOLEDにCaps Lockの状態を表示する。

今回も素人にはハードル高かったです。。。

ファイルを準備する

ここは割愛します。前回のファイルに追加する想定でいきます。

画像データを用意する

画像ファイルを用意してください。 OLEDは、128×32なのでそこに収まれば良いです。私は適当にこんな画像を用意しました。

次に画像を画像を配列データに変換します。このサイトで変換することができます。

image2cpp

ここからが理解が必要な部分なのですが、OLEDでは横6bit、縦8bitで1fontを表現しています。イメージとしては、このfontを自分で作って、それを文字として並べて一つの画像としてOLEDに表示する感じです。このページが非常に理解しやすいと思うので、読んでみてください。

QMK の OLED 基礎知識 - Qiita
はじめにこれは、私が 2020/12/08 現在で理解している、 QMK での OLED に関するあれこれをまとめたものです私が実際に確認したものなどに限るので、全機能の中の一部にすぎませんこ…

なので、画像のCanvas Sizeを指定するときは、横は6の倍数、縦は8の倍数で、それぞれ、126、32以内で指定する必要があります。今回は横を84、縦を32で指定して、Caps Lock状態を表示する余白を残しました。横14文字、縦4文字を使ってロゴを表示する想定です。2.Image Settingはポチポチして試して下さい。4. Outputの設定は下記の画像通りにして Generate code をクリックして下さい。

keymap.cを編集する

別のファイルを作ってincludeしても良いのですが、面倒だったので 今回は、keymap.c を編集します。そもそも、どこでOLEDの表示を弄っているかというのが分からずにソースを読むことから始めました。CなのかC++なのかの違いも分かってないのですが、何となくコードを読んでいくと、こんなの発見しました。(間違ってるかもですが)簡単に説明すると oledkit_render_logo_user がスレイブのOLEDの表示をしてます。で、 oledkit_render_info_user は keymap.c の方で上書きされており、マスターの OLED に色々な情報が表示される様になってました。具体的にはここです。

で、最終的には oled_task_user の中で、 is_keyboard_master という関数でマスタなのかスレイブなのか判定して呼び分けしてる感じかと思います。

なので、keymap.c で oledkit_render_logo_user をオーバーライドしてしまえば良いという結論に至りました。

  • keyball\lib\oledkit\oledkit.c
__attribute__((weak)) void oledkit_render_logo_user(void) {
    // Require `OLED_FONT_H "keyboards/keyball/lib/logofont/logofont.c"`
    char ch = 0x80;
    for (int y = 0; y < 3; y++) {
        oled_write_P(PSTR("  "), false);
        for (int x = 0; x < 16; x++) {
            oled_write_char(ch++, false);
        }
        oled_advance_page(false);
    }
}

__attribute__((weak)) void oledkit_render_info_user(void) {
    oledkit_render_logo_user();
}

__attribute__((weak)) bool oled_task_user(void) {
    if (is_keyboard_master()) {
        oledkit_render_info_user();
    } else {
        oledkit_render_logo_user();
    }
    return true;
}

今回、 84×32で画像を作ったので、横14文字、縦4文字を使ってロゴを表示しています。分かりやすく絵にしてみると、こんな感じのイメージになります。

ちなみに配列データを分割している理由なのですが、分割しないで表示しようとすると、プログラム側で、どこが文字の折り返しなのかが判断できない為、1行目15列目に2行目1列目の文字が来てしまい思った通りにロゴが表示がされませんでした。

結果、14文字毎に配列データを分割して、一行ずつ表示を行うという方法で解決しました。oled_set_cursor(row, col) は、次の表示開始位置を指定してくれる関数らしく 0,0(指定しないとここからっぽいので最初は指定してない。そして、1行目を0からカウントする模様)で1行目、 0,1 で2行目という感じで14文字づつ表示をしています。

  • Keymap.c(追加した行のみ。たぶん、どこに追加しても動くとは思う)
static const char PROGMEM meat_ball_logo_1[] = {
    0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 
    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 
    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 
    0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 
    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 
    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 
    0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 
    0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 
    0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 
    0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 
    0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
};

static const char PROGMEM meat_ball_logo_2[] = {
    0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 
    0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 
    0x00, 0x00, 0xff, 0xff, 0xff, 0xc1, 
    0xc1, 0xc1, 0xc1, 0x01, 0x00, 0x00, 
    0xc0, 0xff, 0xff, 0x0f, 0x3f, 0xff, 
    0xfe, 0x80, 0x01, 0x01, 0x01, 0xff, 
    0xff, 0xff, 0x03, 0x01, 0x01, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 
    0xff, 0xe3, 0x61, 0xe1, 0xff, 0xff, 
    0x9f, 0x00, 0x00, 0x00, 0xf8, 0xff, 
    0x3f, 0x0f, 0xff, 0xff, 0xf0, 0x00, 
    0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 
    0xff, 0x00, 0x00, 0x00, 0x00, 0x00,  
};

static const char PROGMEM meat_ball_logo_3[] = {
    0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 
    0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 
    0x00, 0x00, 0xff, 0xff, 0xff, 0x60, 
    0x60, 0x60, 0x60, 0x60, 0x00, 0x78, 
    0x7f, 0x7f, 0x0f, 0x0c, 0x0c, 0x1f, 
    0x7f, 0x7f, 0x70, 0x00, 0x00, 0x3f, 
    0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x3f, 0x7f, 
    0x7f, 0x70, 0x70, 0x70, 0x7f, 0x3f, 
    0x3f, 0x00, 0x40, 0x7f, 0x7f, 0x1f, 
    0x0c, 0x0c, 0x0f, 0x7f, 0x7f, 0x7c, 
    0x00, 0x00, 0xff, 0xff, 0xff, 0xe0, 
    0xe0, 0xe0, 0xc0, 0x00, 0xff, 0xff, 
    0xff, 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 
};

static const char PROGMEM meat_ball_logo_4[] = {
    0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01
};

void oledkit_render_logo_user(void) {
    oled_write_raw_P(meat_ball_logo_1, sizeof(meat_ball_logo_1));
    oled_set_cursor(0, 1);
    oled_write_raw_P(meat_ball_logo_2, sizeof(meat_ball_logo_2));
    oled_set_cursor(0, 2);
    oled_write_raw_P(meat_ball_logo_3, sizeof(meat_ball_logo_3));
    oled_set_cursor(0, 3);
    oled_write_raw_P(meat_ball_logo_4, sizeof(meat_ball_logo_4));

}

こんな感じにOLEDに表示される

コンパイルして書き込んでみるといい感じに新しいロゴが表示されました。(下の方のCapsの部分は、この後説明します)コンパイル方法は前回は前々回の内容を確認してもらえればと思います。

Caps Lockの状態を取得して表示する

上の画像のCapsって表示されてる部分の実装ですね。先にネタバレしましたが。。。。

今回はCapsがOnの時に+、Offの時はーという感じで表示を切り替えて行きたいと思います。まずは、keymap.cに処理を追加していきます。 host_keyboard_led_state で、Caps Lockなどの状態が取得できる模様で、状態に合わせ表示を+/ーで切り替えています。

  • Keymap.c
// この関数はまるっと追加
static void oled_write_host_led_state(void) { 
    const led_t led_state = host_keyboard_led_state();
    oled_write_P(PSTR("Caps:"), false);
    oled_write_P(led_state.caps_lock   ? PSTR("+") : PSTR("-"), false);
}

// この関数は、さっきの実装から少し修正
void oledkit_render_logo_user(void) {
    oled_write_raw_P(meat_ball_logo_1, sizeof(meat_ball_logo_1));
    oled_set_cursor(0, 1);
    oled_write_raw_P(meat_ball_logo_2, sizeof(meat_ball_logo_2));
    oled_set_cursor(0, 2);
    oled_write_raw_P(meat_ball_logo_3, sizeof(meat_ball_logo_3));
    oled_set_cursor(0, 3);
    oled_write_raw_P(meat_ball_logo_4, sizeof(meat_ball_logo_4));
    oled_set_cursor(15, 0); // ここ追加 1行目15列目から表示
    oled_write_host_led_state(); // ここ追加
}

これでウキウキでコンパイルして書き込んでみたのですが、 Caps Lock を幾らキーボードで切り替えても表示が全く変わりませんでした。

で、いろいろと調べたり試したりしたのですが、上記の処理をマスター側に実装すると状態に合わせて表示が切り替わるという事が判明しました。つまるところ、スレイブに状態が渡ってないのかしら?という事で更に調べた結果、ここに辿りつきました。

QMK Firmware Docs
The full documentation of the open-source firmware

This enables syncing of the Host LED status (caps lock, num lock, etc) between both halves of the split keyboard. The main purpose of this feature is to enable support for use of things like OLED display of the Host LED status.

https://docs.qmk.fm/#/feature_split_keyboard?id=data-sync-options

こいつを有効にせねばなりませんね・・・。ということで、confing.hに追加してみましょう。

  • config.h
/*
This is the c configuration file for the keymap

Copyright 2022 @Yowkees
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

//#ifdef RGBLIGHT_ENABLE
//#    define RGBLIGHT_EFFECT_BREATHING
//#    define RGBLIGHT_EFFECT_RAINBOW_MOOD
//#    define RGBLIGHT_EFFECT_RAINBOW_SWIRL
//#    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

#define TAP_CODE_DELAY 5
#define DYNAMIC_KEYMAP_LAYER_COUNT 5
#define SPLIT_LED_STATE_ENABLE // これを追加するだけ

これでスレイブ側にも状態が伝達される様になり、Caps Lockを切り替えると表示も切り替わる様になりました。

さいごに

ここまで辿り着くのにお世話になったページのリンクを紹介します。ありがとうございます!!

QMK で slave 側でも process_record() を発火する
タイトルとURLをコピーしました