Remapに「ファームウェアワークベンチ」機能が追加されました

2025 年 5 月 7 日に、Remap に新しい機能「ファームウェアワークベンチ」が仲間入りしました。Remap は当初から「キーボードを手軽にカスタマイズできるようにする」というようなミッションに基づいていろいろな機能追加を行ってきました。ファームウェアワークベンチは、その中でも一つのマイルストーンではないかな、と思っています。

何を解決するのか

キーボードのファームウェアはいくつか使われていますが、その代表格は「QMK Firmware」です。日本でも多くの自作キーボードキットにて QMK Firmware が採用されています。

QMK Firmware - qmk.fm

Remap に登録されているキーボードのほとんどが、QMK Firmware を採用しています。

QMK Firmware は、オープンソースプロジェクトです。GNU General Public License v2.0 にて説明されたライセンスの範囲内で誰でも利用することができます。QMK Firmware は、主に C 言語で書かれたプログラムで構成されています。つまり、C 言語に基づいてプログラムを書いただけでは、QMK Firmware としては動作しません。その書かれたプログラム、すなわちソースコードをコンパイルやリンクなどのビルド作業を行った後に、ファームウェアとしてマイコンに書き込むことができるファイルを入手することができます。

QMK Firmware をベースにして、目の前にあるキーボード向けのファームウェアを作るためには、QMK Firmware をビルドするための環境をパソコン内に作る必要があるのです。

このビルド環境を作る作業は、プログラミングを行ったことがある人でも、そこそこ面倒かつ複雑な手順となります。おそらく、プロのソフトウェアエンジニアにとっても、そう簡単ではないと想像できます。ましてや、プログラムなんて書いたことがない、という方であれば、ビルド環境と言われても、まったくピンと来ないでしょう。「ただ単に QMK Firmware をビルドしたいだけ」なのに、いくつものソフトウェアの導入が必要になります。基本的には UNIX 環境が求められますので、普段 Windows を使っている方のほとんどが無縁であろう WSL 環境や MSYS2 環境といったものをインストールしなければなりません。

また、プロのソフトウェアエンジニアにとっても、QMK Firmware のビルドのために「今まで培ってきたソフトウェア開発環境に手を入れる」ことになり、あまり良くない気分になる方も少なくはないんじゃないかなと推測します。かといって、Docker などのコンテナ技術で隔離できるとはいえ、また別の手間が生じます。

しかし、QMK Firmware のビルド環境は、とても魅力的です。Remap で提供されている機能だけでは、QMK Firmware が持っている能力のすべてを活かすことができません。QMK Firmware のドキュメント に掲載されている機能群は、目を通すだけでも 1 日では足りないほど、豊富です。これらをすべて使うこと、すなわち QMK Firmware の潜在能力をすべて出し切るには、どうしても自分でプログラミングを行う必要が出てきます。

  • QMK Firmware の機能をフルに使いたい。
  • QMK Firmware のビルド環境は手元に作りたくない。

この 2 つを同時に満たせない状況が今まで続いてきました。ビルド環境さえ何とかなれば、誰でも気軽に QMK Firmware の潜在能力を引き出すことができるようになるはずです。

ファームウェアワークベンチとは

Remap に今回追加された「ファームウェアワークベンチ」機能は、QMK Firmware 開発環境です。ウェブブラウザだけで、以下のことを行うことができます。

  • QMK Firmware をベースにしたファームウェアのソースコードを作成し編集することができる。
  • 書かれたソースコードに基づいてビルドを行って、ファームウェアのバイナリファイルを得ることができる。
  • ビルドされたファームウェアのバイナリファイルを、キーボードのマイコンに直接書き込むことができる。

つまり、皆さんは QMK Firmware のビルド環境を手元のパソコンに準備する必要はありません。Remap がビルド環境を提供します。そして、各キーボード向けに必要となるソースコードを Remap の画面上で作成&編集できます。ビルド作業は、「ビルド」というボタンを押すだけです。うまくビルドできれば、その結果をマイコンに書き出すことも簡単です。

実際に、Lunakey Pico 向けのファームウェアを、ファームウェアワークベンチ機能を使って作ってみましょう。

ファームウェアワークベンチにアクセスする

Remap のトップページに「ファームウェアワークベンチ」と書かれた場所がありますので、そこをクリックします。

Remap にログインしていない時は、以下のようにログインを促してきます。Google アカウントもしくは GitHub アカウントを使って、ログインを行ってください。

以下のような画面になれば、ファームウェアワークベンチ機能が起動したことになります。

「New Project 1」という名前のプロジェクトが自動的に作られています。

ソースコードを作っていく

では、プロジェクトにソースコードを追加していきましょう。ソースコードの中身は、C 言語と QMK Firmware が提供する関数群を理解していないと、さっぱり何のことだかわからないはずです。ここでは、ソースコードを理解せずに、ひたすらコピペしていきましょう。

最初に、左にある「keyboards/…/」の右にある「+」ボタンを押してください。

表示されたダイアログの「ファイル名」という入力欄に、「config.h」と入力して、「作成」ボタンを押してください。

すると、左に「config.h」ファイルが追加されたのがわかると思います。その「config.h」ファイルをクリックしてください。右側にエディターが表示されます。

右側のエディターに、以下をコピペしてください。

// Copyright 2022 Yoichiro Tanaka (@yoichiro)
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED GP25
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 500U

#define SERIAL_USART_FULL_DUPLEX
#define SERIAL_USART_TX_PIN GP0
#define SERIAL_USART_RX_PIN GP1

//#define SERIAL_DEBUG

#define WS2812_PIO_USE_PIO1

/*
 * Feature disable options
 *  These options are also useful to firmware size reduction.
 */

/* disable debug print */
//#define NO_DEBUG

/* disable print */
//#define NO_PRINT

/* disable action features */
//#define NO_ACTION_LAYER
//#define NO_ACTION_TAPPING
//#define NO_ACTION_ONESHOT

同じ手順で、以下のソースコードを作っていってください。

halconf.h

// Copyright 2022 Yoichiro Tanaka (@yoichiro)
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#define HAL_USE_SIO TRUE

#include_next <halconf.h>

keyboard.json

{
    "keyboard_name": "Lunakey Pico",
    "manufacturer": "yoichiro",
    "maintainer": "Yoichiro Tanaka",
    "bootloader": "rp2040",
    "diode_direction": "COL2ROW",
    "features": {
        "bootmagic": false,
        "mousekey": true,
        "extrakey": true,
        "console": false,
        "command": false,
        "nkro": false,
        "rgblight": true
    },
    "matrix_pins": {
        "cols": ["GP21", "GP20", "GP19", "GP18", "GP17", "GP16"],
        "rows": ["GP12", "GP13", "GP14", "GP15"]
    },
    "processor": "RP2040",
    "usb": {
        "device_version": "3.0.0",
        "pid": "0x0003",
        "vid": "0x5954"
    },
    "rgblight": {
        "led_count": 12,
        "led_map": [0, 1, 2, 3, 4, 5, 11, 10, 9, 8, 7, 6],
        "sleep": true,
        "split": true,
        "split_count": [6, 6],
        "animations": {
            "alternating": true,
            "breathing": true,
            "christmas": true,
            "knight": true,
            "rainbow_mood": true,
            "rainbow_swirl": true,
            "rgb_test": false,
            "snake": true,
            "static_gradient": true,
            "twinkle": true
        },
        "hue_steps": 8,
        "saturation_steps": 8,
        "brightness_steps": 8,
        "max_brightness": 255
    },
    "split": {
        "enabled": true,
        "serial": {
            "driver": "vendor"
        }
    },
    "ws2812": {
        "pin": "GP6",
        "driver": "vendor"
    },
    "layout_aliases": {
        "LAYOUT": "LAYOUT_split_3x6_4"
    },
    "layouts": {
        "LAYOUT_split_3x6_4": {
            "layout": [
                {"matrix": [0, 0], "x": 0, "y": 1.5},
                {"matrix": [0, 1], "x": 1, "y": 1.25},
                {"matrix": [0, 2], "x": 2, "y": 0.5},
                {"matrix": [0, 3], "x": 3, "y": 0},
                {"matrix": [0, 4], "x": 4, "y": 0.25},
                {"matrix": [0, 5], "x": 5, "y": 0.5},

                {"matrix": [4, 5], "x": 8.75, "y": 0.5},
                {"matrix": [4, 4], "x": 9.75, "y": 0.25},
                {"matrix": [4, 3], "x": 10.75, "y": 0},
                {"matrix": [4, 2], "x": 11.75, "y": 0.5},
                {"matrix": [4, 1], "x": 12.75, "y": 1.25},
                {"matrix": [4, 0], "x": 13.75, "y": 1.5},

                {"matrix": [1, 0], "x": 0, "y": 2.5},
                {"matrix": [1, 1], "x": 1, "y": 2.25},
                {"matrix": [1, 2], "x": 2, "y": 1.5},
                {"matrix": [1, 3], "x": 3, "y": 1},
                {"matrix": [1, 4], "x": 4, "y": 1.25},
                {"matrix": [1, 5], "x": 5, "y": 1.5},

                {"matrix": [5, 5], "x": 8.75, "y": 1.5},
                {"matrix": [5, 4], "x": 9.75, "y": 1.25},
                {"matrix": [5, 3], "x": 10.75, "y": 1},
                {"matrix": [5, 2], "x": 11.75, "y": 1.5},
                {"matrix": [5, 1], "x": 12.75, "y": 2.25},
                {"matrix": [5, 0], "x": 13.75, "y": 2.5},

                {"matrix": [2, 0], "x": 0, "y": 3.5},
                {"matrix": [2, 1], "x": 1, "y": 3.25},
                {"matrix": [2, 2], "x": 2, "y": 2.5},
                {"matrix": [2, 3], "x": 3, "y": 2},
                {"matrix": [2, 4], "x": 4, "y": 2.25},
                {"matrix": [2, 5], "x": 5, "y": 2.5},

                {"matrix": [6, 5], "x": 8.75, "y": 2.5},
                {"matrix": [6, 4], "x": 9.75, "y": 2.25},
                {"matrix": [6, 3], "x": 10.75, "y": 2},
                {"matrix": [6, 2], "x": 11.75, "y": 2.5},
                {"matrix": [6, 1], "x": 12.75, "y": 3.25},
                {"matrix": [6, 0], "x": 13.75, "y": 3.5},

                {"matrix": [3, 2], "x": 3, "y": 4},
                {"matrix": [3, 3], "x": 4, "y": 3.75},
                {"matrix": [3, 4], "x": 5, "y": 3.75},
                {"matrix": [3, 5], "x": 6, "y": 4},

                {"matrix": [7, 5], "x": 7.75, "y": 4},
                {"matrix": [7, 4], "x": 8.75, "y": 3.75},
                {"matrix": [7, 3], "x": 9.75, "y": 3.75},
                {"matrix": [7, 2], "x": 10.75, "y": 4}
            ]
        }
    }
}

mcuconf.h

// Copyright 2022 Yoichiro Tanaka (@yoichiro)
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include_next <mcuconf.h>

#undef RP_SIO_USE_UART0
#define RP_SIO_USE_UART0 TRUE
#undef RP_SIO_USE_UART1
#define RP_SIO_USE_UART1 FALSE

次に、「keyboards/…/keymaps/remap/」と書かれた場所の右にある「+」ボタンを押して、ファイルを追加していきます。ここでは、「keymap.c」という名前のファイルを一つ追加します。

// Copyright 2022 Yoichiro Tanaka (@yoichiro)
// SPDX-License-Identifier: GPL-2.0-or-later

#include QMK_KEYBOARD_H

enum layer_number {
  _QWERTY = 0,
  _LOWER,
  _RAISE,
  _ADJUST,
};

#define LOWER MO(_LOWER)
#define RAISE MO(_RAISE)

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {

  [_QWERTY] = LAYOUT_split_3x6_4(
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,                      KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,    KC_BSPC,
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   KC_LCTL, KC_A,    KC_S,    KC_D,    KC_F,    KC_G,                      KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_QUOT,
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,                      KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT,
//+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
                              KC_LGUI, KC_LALT, LOWER,   KC_SPC,  KC_ENT,  RAISE,   KC_RALT, KC_RGUI
//+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
  ),

  [_LOWER] = LAYOUT_split_3x6_4(
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   KC_ESC,  KC_1,    KC_2,    KC_3,    KC_4,    KC_5,                      KC_6,    KC_7,    KC_8,    KC_9,    KC_0,    KC_BSPC,
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   KC_LCTL, _______, _______, _______, _______, _______,                   KC_LEFT, KC_DOWN, KC_UP,   KC_RIGHT,_______, _______,
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   KC_LSFT, _______, _______, _______, _______, _______,                   _______, _______, _______, _______, _______, _______,
//+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
                              KC_LGUI, KC_LALT, LOWER,   KC_SPC,  KC_ENT,  RAISE,   KC_RALT, KC_RGUI
//+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
  ),

  [_RAISE] = LAYOUT_split_3x6_4(
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   KC_ESC,  KC_EXLM, KC_AT,   KC_HASH, KC_DLR,  KC_PERC,                   KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_BSPC,
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   KC_LCTL, _______, _______, _______, _______, _______,                   KC_MINS, KC_EQL,  KC_LCBR, KC_RCBR, KC_PIPE, KC_GRV,
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   KC_LSFT, KC_TILD, _______, _______, _______, _______,                   KC_UNDS, KC_PLUS, KC_LBRC, KC_RBRC, KC_BSLS, KC_TILD,
//+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
                              KC_LGUI, KC_LALT, LOWER,   KC_SPC,  KC_ENT,  RAISE,   KC_RALT, KC_RGUI
//+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
  ),

  [_ADJUST] = LAYOUT_split_3x6_4(
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   QK_BOOT, _______, _______, _______, _______, _______,                   KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   UG_TOGG, UG_HUEU, UG_SATU, UG_VALU, _______, _______,                   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,
//+--------+--------+--------+--------+--------+--------+                 +--------+--------+--------+--------+--------+--------+
   UG_NEXT, UG_HUED, UG_SATD, UG_VALD, _______, _______,                   _______, _______, _______, _______, _______, _______,
//+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
                              KC_LGUI, KC_LALT, LOWER,   KC_SPC,  KC_ENT,  RAISE,   KC_RALT, KC_RGUI
//+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
  )
};

layer_state_t layer_state_set_user(layer_state_t state) {
  return update_tri_layer_state(state, _LOWER, _RAISE, _ADJUST);
}

これでソースコードの準備は整いました。

ビルドする

いよいよファームウェアのビルドを行ってみましょう。右上にある「ビルド」ボタンを押します。

左下の「ビルドタスク」という場所に、ビルドタスクが一つ追加されます。

ビルドには少し時間がかかりますので、待ちましょう。うまくビルドできれば、自動的に緑のアイコンに変化します。そのビルドタスクを選択すると、右にビルドログが表示されます。

マイコンに書き込む

ビルドがうまくいったときは、ビルドタスクの右に、ファームウェア書き込みボタンが表示されます。

ファームウェア書き込みボタンを押すと、Remap でおなじみのファームウェア書き込みダイアログが表示されます。

あとは、マイコンに書き込みを行うことで、ビルドしたファームウェアがマイコン内で動き出すはずです。

無料ビルドは 3 回まで

ファームウェアワークベンチ機能では、ビルドを無料で 3 回行うことができます。もっとビルドしたい方は、10 回ビルドパッケージを税込みで $1.65 で購入することができます。ビルド回数の残りがなくなると、「ビルド」ボタンが黄色くなります。その状態で「ビルド」ボタンを押すと、購入のためのダイアログが表示されますので、書かれた指示に従って購入を進めてください。

収益は Remap の運用費として使用したいと考えています。ご理解いただけますと助かります。

まとめ

今まで何度も「QMK Firmware のビルド環境が作れません」といった声を聞いてきました。Remap で何とかその課題を解決したく、ファームウェアワークベンチ機能を開発してみました。今後は、ファームウェアワークベンチ機能内でもっと手軽にソースコードを作れるようにするなどの機能拡張を行うことで、より便利にしていきたいと考えています。ぜひ今後にご期待ください。また、一緒に Remap を開発したいと思ってくれる方々も募集中ですので、声をかけていただければと思います。

では、ファームウェアワークベンチ機能共々、今後も Remap をご活用ください。

このエントリーをはてなブックマークに追加

関連記事

Firebase FunctionsデプロイでArtifact Registryのクリーンアップポリシー設定エラーに遭遇した話

Web Serial APIでブラウザからシリアルデバイスを操作する

ウェブブラウザとハードウェアの関係

macOSやLinuxからWindowsに移行したら快適になった話

「エンジニアチームの生産性の高め方」という書籍が出版されました