⌨️

オリジナルDIYキーボードでQMK firmwareを使ってロータリーエンコーダとNeoPixelを制御する方法

2023/12/04に公開

前回の記事でボクのDIYキーボードキット用のQMK firmwareの作成方法について説明したけど、ロータリーエンコーダ(以下、エンコーダ)とNeoPixelの制御については、触れていなかったから、QMK firmwareでエンコーダとNeoPixelを制御する方法について説明するよ。

https://zenn.dev/nananauno/articles/99a896a5c6baa8

前回の記事とこのページの内容を読むことで、DIYキーボードキットの全機能を使用したQMK firmwareを作成することができるよ。このページでは、以下の機能の設定方法について説明しているよ:

  • エンコーダ
  • NeoPixel
  • レイヤー
  • COMBOキー

DIYキーボードキットはBOOTHで発売中なので、まだ持っていない場合はぜひ購入してね!

BOOTHの商品ページ
DIY kit 01 - Keyboard XIAO RP2040

対象読者

  • DIYキーボードキットを手っ取り早くマクロパッドにしたいみんな
  • RP2040用にQMKを使ったファームウェアの作り方を知りたいみんな

環境

このページで説明してる内容は、前回の記事で説明した環境と同じだよ。

  • QMK firmware
    環境の構築方法は前回の記事を読んでね。
  • DIY kit 01 - Keyboard XIAO RP2040
  • Windows/Mac
    MacはSonomaでも動作することを確認済みだよ。

エンコーダの設定

まずは、qmk firmwareでエンコーダーの設定をする方法を説明するよ。エンコーダの処理方法には2種類あって、1つ目がキーマップを使う方法。2つ目がコールバックを使う方法。ここではキーマップを使う方法について説明しているよ。

公式の説明はこちらを参照してね。
https://docs.qmk.fm/#/feature_encoders

まずは、作成中のキーボードのディレクトリーに移動してね。例えば、ボクの場合だと:

cd ~/qmk_firmware/keyboards/nanana_diy

エンコーダの設定のために編集するファイルは以下の3つだよ:

  • nanana_diy/info.json
  • nanana_diy/rules.mk
  • nanana_diy/keymap/default/keymap.c

info.json

まずはキーボードの設定を編集するよ。info.jsonを開いて以下の2箇所を追加してね。

  1. featuresにeccoder: trueを追加
  2. TOP階層にencoderを追加

encoder: true

featuresというキーにencoder: trueを追加することで、このキーボードでエンコーダ機能を有効にすることができるよ。

    "features": {
        ..
        "encoder": true
    },

encoder

jsonファイルのTOP階層にencoderというキーを追加して、そこにエンコーダのピン割り当てを定義するよ。割り当て例は以下を参考にしてみてね。この例ではDIYキーボードキットのEnc1とEnc2のピンをそれぞれ割り当てているよ。

pin_a, pin_bはそれぞれエンコーダのA,B端子を指しているよ。resolutionはエンコーダの1クリックあたりに出力されるパルスの数を指定するよ。

   "encoder": {
        "rotary": [
                { "pin_a": "GP4", "pin_b": "GP3", "resolution": 2 },
                { "pin_a": "GP1", "pin_b": "GP2", "resolution": 2 }
        ]
    },

全体

修正後のinfo.jsonは以下の通りだよ。

{
    "manufacturer": "NANANA",
    "keyboard_name": "nanana_diy",
    "maintainer": "nananauno",
    "bootloader": "rp2040",
    "diode_direction": "COL2ROW",
    "features": {
        "bootmagic": true,
        "command": false,
        "console": false,
        "extrakey": true,
        "mousekey": true,
        "nkro": true,
        "encoder": true
    },
    "matrix_pins": {
                "direct": [
                        ["GP26","GP27","GP28","GP29"]
                ]
    },
    "encoder": {
        "rotary": [
                { "pin_a": "GP4", "pin_b": "GP3", "resolution": 2 },
                { "pin_a": "GP1", "pin_b": "GP2", "resolution": 2 }
        ]
    },
    "processor": "RP2040",
    "url": "",
    "usb": {
        "device_version": "1.0.0",
        "pid": "0x2326",
        "vid": "0xFEED"
    },
    "layouts": {
        "LAYOUT": {
            "layout": [
                {"label": "GP26", "matrix": [0, 0], "x": 0, "y": 0},
                {"label": "GP27", "matrix": [0, 1], "x": 1, "y": 0},
                {"label": "GP28", "matrix": [0, 2], "x": 2, "y": 0},
                {"label": "GP29", "matrix": [0, 3], "x": 3, "y": 0},
            ]
        }
    }
}

rules.mk

キーボードのディレクトリの直下にあるrules.mkに以下の1行を追加して、エンコーダのキーマップを使用することを定義しておくよ。

ENCODER_MAP_ENABLE = yes

keymap.c

次に、キーマップを定義するために、キーマップのディレクトリへ移動してね。

cd keymap/default

keymap.cを編集する前に、前回の記事で作成したkeymap.jsonは今回は使用しないから、ファイル名をkeymap.json.oldなどに変更してqmkから使えないようにしておいてね。 keymap.jsonでエンコーダを割り当てる方法が見つからなくて、今回はkeymap.cで割り当てる方法を説明しているよ。もし、keymap.jsonを使ったやり方を知っていたらぜひ教えてね。

keymap.cを開いて以下のように編集してね。

#include QMK_KEYBOARD_H

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    [0] = LAYOUT(
        KC_A,   KC_B,   KC_C,   KC_D
    )
};

#if defined(ENCODER_MAP_ENABLE)
const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
    [0] =   { ENCODER_CCW_CW(KC_PAGE_UP, KC_PAGE_DOWN), ENCODER_CCW_CW(KC_VOLD, KC_VOLU)  }
    //                  Encoder 1                                     Encoder 2
};
#endif

このコードを少し解説しておくと、

const uint16_t PROGMEM keymaps
keymapsは前回の記事で説明したkeymap.jsonを使ったキーの割り当てをC言語のコードで定義し直しているよ。Key1, Key2, Key3, Key4を押したときに、PCへ入力されるキーコードを定義しているよ。例えば、KC_Aはaという文字がPCへ入力されるよ。[0]は0番目のレイヤーのキー割り当てであるということを示しているよ。

const uint16_t PROGMEM encoder_map
encoder_mapがエンコーダのキー割り当てを定義している部分になるよ。ENCODER_CCW_CW(KC_PAGE_UP, KC_PAGE_DOWN)の部分が1つ目のエンコーダEnc1に対するキー割り当てで、ENCODER_CCW_CWの1つ目の値がエンコーダが時計回りに回転した時に入力されるキーコード、2つ目の値がエンコーダが反時計回りに回転した時に入力されるキーコードになっているよ。そして、ENCODER_CCW_CW(KC_VOLD, KC_VOLU)の部分が2つ目のエンコーダEnc2に対するキー割り当てになっているよ。

動作確認

ここまででエンコーダの設定が完了したから、一度ファームウェアを作成してXIAO RP2040に書き込んでみてね。

qmk complile
qmk flash

XIAO RP2040にファームウェアが正常に書き込めたら、PC上でChromeなどのWebブラウザを開いて、キーボードのエンコーダを回転させてみてね。Enc1を回すとブラウザのページがスクロールできるはずだよ。また、Enc2を回すとPCの音量が変わることを確認してね。

NeoPixelの設定

ここでは、DIYキーボードキットに搭載された4つのNeoPixelを点灯させるための設定について説明するよ。最終的には色々なエフェクトを使用するんだけど、ここでは動作確認のためにRAINBOWというエフェクトのみを使用してNeoPixelを点灯させてみるよ。

https://docs.qmk.fm/#/feature_rgblight

NeoPixelの設定のために編集するファイルは以下の2つだよ:

  • nanana_diy/rules.mk
  • nanana_diy/config.h

rules.mk

キーボードのディレクトリの直下にあるrules.mkに以下の2行を追加して、RGB Lightを使用することを定義しておくよ。また、qmkではDIYキーボードキットで使用しているRP2040向けのPIOドライバーも用意されていて、WS2812_DRIVERでvendorを指定することで、PIOドライバーを有効にすることができるよ。

RGBLIGHT_ENABLE = yes
WS2812_DRIVER = vendor

config.h

次に、キーボードのディレクトリの直下にあるconfig.hに以下の定義を追加するよ。DIYキーボードキットで使用しているNeoPixelのモジュールはSK6812MINI-Eだけど、WS2812_xxxxの定義がそのまま使用できるよ。

// For NeoPixel LEDs
#define WS2812_DI_PIN GP0
#define WS2812_TIMING 1250
#define WS2812_T1H 650
#define WS2812_T0H 350
#define WS2812_TRST_US 100

#define RGBLED_NUM 4

#define RGBLIGHT_LIMIT_VAL 200
#define RGBLIGHT_DISABLE_KEYCODES
#define RGBLIGHT_EFFECT_RAINBOW_MOOD
#define RGBLIGHT_DEFAULT_MODE RGBLIGHT_MODE_RAINBOW_MOOD

WS2812_xxxxはNeoPixelのモジュールに関する設定で、SK6812MINI-Eのデータシートを参考に設定するよ。各定義の説明は以下の通りだよ:

WS2812_DI_PIN
NeoPixelのDINが接続されたGPIOピンの番号を指定するよ。DIYキーボードキットのNeoPixelはGP0に接続されているよ。

WS2812_TIMING
SK6812MINI-EのTH+TLの時間を指定するよ。初期値の1250ns=800kHzでオッケー。

WS2812_T1H
SK6812MINI-Eで1を表す波形のHIGH継続時間を指定するよ。データシート上では標準が640nsだけど、下記の注釈の仕様があるから、650nsに設定しているよ。

WS2812_T0H
SK6812MINI-Eで0を表す波形のHIGH継続時間を指定するよ。データシート上では標準が320nsだけど、下記の注釈の仕様があるから、350nsに設定しているよ。

WS2812_TRST_US
SK6812MINI-EでリセットのためのLOW継続時間を指定するよ。データシート上の最小80usより少し長い100usに設定しているよ。

RGBLED_NUM
NeoPixelの数を指定するよ。DIYキーボードキットには4つのNeoPixelが搭載されているから、4を指定しているよ。

RGBLIGHT_LIMIT_VAL
NeoPixelの明るさを制限するための定義だよ。0-255で設定するよ。最大輝度で使用するとNeoPixelの寿命にも影響する可能性があるから、ここでは最大輝度を200に設定しているよ。

RGBLIGHT_DISABLE_KEYCODES
qmkからNeoPixelのエフェクトや色を変更する方法として、config.hで事前に定義する方法と、NeoPixel用に割り当てられたキーコードを使用する方法があるよ。キーコードで変更した場合、変更後の値がRP2040のフラッシュに記憶されて、RP2040の電源を入れ直しても前回のNeoPixelの状態を維持できるようになっているよ。ただ、この値が残っていると、config.hを変更してもその通りにNeoPixelが点灯しない原因になるから、ここでは、キーコードを無効にすることで、config.hで意図した通りにNeoPixelを点灯させるようにしているよ。

RGBLIGHT_EFFECT_RAINBOW_MOOD
RAINBOWのエフェクトを使用するための定義だよ。

RGBLIGHT_DEFAULT_MODE
NeoPixelのエフェクトの初期値としてRAINBOWを指定しているよ。

動作確認

ここまででNeoPixelの設定が完了したから、もう一度ファームウェアを作成してXIAO RP2040に書き込んでみてね。

qmk complile
qmk flash

XIAO RP2040にファームウェアが正常に書き込めたら、DIYキーボードキットに搭載された4つのNeoPixelが色々な色に変化していることを確認してね。

もう少し実用的にしてみよう

エンコーダとNeoPixelが設定通りに動作していることが確認できたかな?エンコーダとNeoPixelを制御するための基本的な設定はこれで完了だけど、実際にキーボードとして便利に使用するには、もう少し工夫した方が良さそうだね。

ここまでの設定だと、NeoPixelはRAINBOWエフェクトしか使えないから、ここからは、DIYキーボードキット上のキー操作で色を変えたり、エフェクトを変えたりできるように設定を変更してみるよ。

レイヤー

qmkはレイヤーという概念があって、レイヤーごとにキーマップが作成できて、レイヤーを切り替えることで、別のキーマップに素早く切り替えることができるよ。qmkでは最大32枚のレイヤーを作成することができるよ。

qmkのキーマップとレイヤーの説明は以下に詳しく書いてあるよ。
https://docs.qmk.fm/#/keymap?id=keymap-and-layers

作りたいもの

もう少し実用的なキーボードにするために、DIYキーボードキットに搭載された2つのエンコーダと4つのNeoPixelをそれぞれ以下のように制御するよ。ここでは、2枚のレイヤーにそれぞれキーを割り当てて、1枚目のレイヤーではPCへのキー入力に、2枚目のレイヤーではNeoPixelの設定変更をできるようにしてみるよ。

Layer0

  • Enc1: 時計回り=PageDown、反時計回り=PageUp
  • Enc2: 時計回り=Vol up、反時計回り=Vol down
  • Key1: 文字A入力
  • Key2: 文字B入力
  • Key3: 文字C入力
  • Key4: 文字D入力
  • A+B: 押している間のみLayer1へ切り替え

Layer1(NeoPixelの設定変更)

  • Enc1: 時計回り=Hueの値を増加、反時計回り=Hueの値を減少
  • Enc2: 時計回り=Val(Brightness)の値を増加、反時計回り=Valの値を減少
  • Key1: 無効
  • Key2: 無効
  • Key3: 前のLEDエフェクト
  • Key4: 次のLEDエフェクト

LEDのエフェクトは、qmkに用意されている以下のエフェクトを使用するよ。

  • RGB Static Light
  • RGB Breathing
  • RGB Rainbow Mood
  • RGB Rainbow Swirl

レイヤーの設定

まずは2枚のレイヤーを作成して、それぞれにキーを割り当てる方法を説明するよ。

レイヤーの設定のために編集するファイルは以下の1つだよ:

  • nanana_diy/keymap/default/keymap.c

keymap.c

keymap.cを開いて、以下のように修正するよ。

#include QMK_KEYBOARD_H

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    [0] = LAYOUT(
        KC_A,   KC_B,   KC_C,   KC_D
    ),
    [1] = LAYOUT(
        KC_NO, KC_NO, RGB_MODE_REVERSE, RGB_MODE_FORWARD
    )
};

#if defined(ENCODER_MAP_ENABLE)
const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
    [0] =   { ENCODER_CCW_CW(KC_PAGE_UP, KC_PAGE_DOWN), ENCODER_CCW_CW(KC_VOLD, KC_VOLU)  },
    [1] =   { ENCODER_CCW_CW(RGB_HUD, RGB_HUI), ENCODER_CCW_CW(RGB_VAD, RGB_VAI)  }
    //                  Encoder 1                                     Encoder 2
};
#endif

1枚だけのレイヤーの場合、[0]=LAYOUT(..)のみがあったと思うけど、[1]=LAYOUT(..)を定義することで2枚目のレイヤーを追加することができるよ。ここではkaymapとencoder_mapそれぞれに[1]=LAYOUT(..)を追加して、2枚目のレイヤーにキーを割り当てているよ。

keymapの2枚目のレイヤーでは、Key3を押した時に前のLEDエフェクトへ変更、Key4を押した時に次のLEDエフェクトへ変更するためのキーを割り当てているよ。2枚目のレイヤーに切り替わっている間はKey1とKey2は押しっぱなしになっているから、Key1とKey2は無効にしているよ。

[1] = LAYOUT(
        KC_NO, KC_NO, RGB_MODE_REVERSE, RGB_MODE_FORWARD
    )

encoder_mapの2枚目のレイヤーでは、Enc1が反時計回りの時にRGBのHueを減少、時計回りの時にRGBのHueを増加、Enc2が反時計回りの時にRGBのVal(Brightness)を減少、時計回りの時にRGBのValを増加させるようにキーを割り当てているよ。

[1] =   { ENCODER_CCW_CW(RGB_HUD, RGB_HUI), ENCODER_CCW_CW(RGB_VAD, RGB_VAI)  }

ここまででレイヤーの設定は完了だよ。

COMBOキーの設定

次にCOMBOキーの設定をするよ。COMBOキーというのは、2つ以上のキーの組み合わせることで、キーに割り当てられているキーとはまた別のキーを入力するための機能だよ。

先ほどのレイヤーの設定で2枚目のレイヤーを定義したけど、この2枚目のレイヤーに切り替えるためのキーが必要だよね。レイヤー切り替え専用のキーがあればベストだけど、DIYキーボードキットのようなマクロパッドはキーの数が少ないから、特定の2つのキーを押すことで、レイヤーを切り替えられるようにするよ。

https://docs.qmk.fm/#/feature_combo

COMBOキーの設定のために編集するファイルは以下の1つだよ:

  • nanana_diy/rules.mk
  • nanana_diy/keymap/default/keymap.c

rules.mk

rules.mkを開いて以下の1行を追加するよ。この1行を追加することで、qmkでCOMBOキーが有効になるよ。

COMBO_ENABLE = yes

keymap.c

keymap.cを開いて、以下のコードを追加するよ。

const uint16_t PROGMEM test_combo1[] = {KC_A, KC_B, COMBO_END};
combo_t key_combos[] = {
    COMBO(test_combo1, MO(1))
};

コードを少し説明すると、
const uint16_t PROGMEM test_combo1
test_combo1では、キーの組み合わせを定義しているよ。AとBが押された時にLayer1へ切り替えたいから、KC_A, KC_Bを指定しているよ。COMBO_ENDはシーケンスの終わりを示すために必ず最後に必要だよ。

combo_t key_combos
key_combosでは、COMBOキーのリストにキーの組み合わせを追加しているよ。今回はCOMBOキーはA+Bの1つしかないから、COMBO(test_combo1, MO(1))を1つだけ追加しているよ。MO(1)はCOMBOキーの組み合わせでキーが押されている間だけLayer1へ切り替えるという意味だよ。

MO(x)以外にもたくさん切り替え方法があるから、詳しくはこちらを参考にしてみてね。
https://docs.qmk.fm/#/feature_layers

NeoPixelのエフェクト追加

最後にNeoPixelにRAINBOW以外のエフェクトを追加するよ。

NeoPixelのエフェクト追加のために編集するファイルは以下の1つだよ:

  • nanana_diy/config.h

config.h

config.hを開いて、NeoPixelのエフェクトを定義している部分を以下のように修正してね。今回はキーコードでNeoPixelのエフェクトを切り替えるからRGBLIGHT_DISABLE_KEYCODESはコメントアウトしておいてね。以下のように修正することで、RAINBOWに加えて、BREATHINGとRAINBOW_SWIRLのエフェクトも使えるようになるよ。

//#define RGBLIGHT_DISABLE_KEYCODES
#define RGBLIGHT_EFFECT_BREATHING
#define RGBLIGHT_EFFECT_RAINBOW_MOOD
#define RGBLIGHT_EFFECT_RAINBOW_SWIRL

動作確認

ここまででレイヤーの設定、COMBOキーの設定、NeoPixelのエフェクト追加が完了したよ。最後にファームウェアを作成してXIAO RP2040に書き込んで動作確認をするよ。

qmk complile
qmk flash

XIAO RP2040にファームを書き込めたら、Key1とKey2を同時に押して、レイヤーが切り替わるかどうか確認してみてね。Key1とKey2を押しながら、エンコーダを回したり、Key3やKey4を押してNeoPixelのエフェクトを切り替えたりしてみてね。

NeoPixelのエフェクトはエフェクトによっては複数のアニメーションパターンがあって、今回使用した3つのエフェクトは以下のようになっているから、別のエフェクトに切り替えるためには、キーを何回か押す必要があるよ。

RGBLIGHT_MODE_STATIC_LIGHT
RGBLIGHT_MODE_BREATHING
RGBLIGHT_MODE_BREATHING+1
RGBLIGHT_MODE_BREATHING+2
RGBLIGHT_MODE_BREATHING+3
RGBLIGHT_MODE_RAINBOW_MOOD
RGBLIGHT_MODE_RAINBOW_MOOD+1
RGBLIGHT_MODE_RAINBOW_MOOD+2
RGBLIGHT_MODE_RAINBOW_SWIRL
RGBLIGHT_MODE_RAINBOW_SWIRL+1
RGBLIGHT_MODE_RAINBOW_SWIRL+2
RGBLIGHT_MODE_RAINBOW_SWIRL+3
RGBLIGHT_MODE_RAINBOW_SWIRL+4
RGBLIGHT_MODE_RAINBOW_SWIRL+5

まとめ

QMK firmwareを使用してエンコーダとNeoPixelを制御する方法について説明したよ。また、レイヤーとCOMBOキーを使用することで、DIYキーボードキット上のキー操作でキーマップを切り替えてNeoPixelのエフェクトを変更する方法についても説明したよ。

前回の記事と今回の内容でDIYキーボードキットに搭載された全ての機能を使用したqmkのファームウェアを作成することができるよ。qmkには他にも色々な機能があるから、色んなことを試して、オリジナルのキーボードを作ってみてね。

じゃあ、またね!

Discussion