⌨️

Kermiteのファームウェアの作り方(Arduino 概要編)

2024/02/23に公開

yahiroと申します。Kermiteという自作キーボードを簡単に使えるようにするソフトウェアを作っています。よろしくお願いします。

Kermiteとは

自作キーボードを簡単に運用できるようにするために作ったエコシステムです。2020年ごろから開発を行っており、昨年夏にようやくリリースしました。最初は自分のキーボード専用のファームウェアとして作っていたものですが、その後スケールが広がっていき、Webアプリやデータ管理用のサーバを統合したプラットフォームという形態で公開しました。私(@yahiro120)がファームウェアとWebアプリを作っています。サーバの実装はReo(@reoreo125)がやってくれています。
https://kermite.org/

ファームウェアまわりの課題

Kermiteのコンセプトとして、なるべく手間をかけずに自作キーボードのソフトウェアまわりを受け持ってくれるものが欲しい。というのがありました。このためにGUIで設定を行いファームウェアを生成する機能を提供していました。しかしこれが定型的なキーボードにしか対応できず、自由に拡張するのが難しいという課題がありました。カスタムファームウェアという、コードを書いてファームウェアを作れる仕様もありますが、環境構築のハードルが高くあまりユーザにおすすめできるものではありませんでした。

Arduino対応の動機

自由にソースコードを書いてファームウェアを作りたい、でも環境構築とかで苦しむのは辛い、ということで何か良い方法がないかと考えていたところ、ArduinoやPlatformIOなどのプラットフォームに乗せてファームウェアを作れるようにすれば良いのではないかというアイデアが浮かびました。

これらの開発環境では、ビルド環境の構築やライブラリの依存関係の解決などの面倒なところを、ユーザが自分でやらなくてもツールが裏でやってくれるため、ユーザはアプリケーションコードに専念することができます。また、OSSで多数のライブラリが提供されているため、これを組み合わせればキーボードの周辺モジュールの対応も簡単にできそうです。

さらに、異なるMCUへの移植対応も簡単になるというメリットもあります。特にUSBまわりの実装で複数のMCUをサポートしているライブラリを使えば、MCUが違っていても共通のコードを利用できるので、移植が容易になります。

全体の構成

ということで、Kermiteのファームウェアの実装をArduinoのライブラリの形態にまとめ、またArduinoでRP2040の開発を行うための環境の整備を行っています。USBまわりの実装をArduinoのライブラリに合わせて作り替えるのに苦労しましたが、現在簡単なキーボードのファームウェアが作れるところまでの実装ができました。

以下のリポジトリでライブラリを公開しています。

https://github.com/kermite-org/KermiteCore_Arduino

開発環境全体の構成は、以下のようなものになっています。

図では上にいくほどユーザアプリケーション寄り、下にいくほどインフラ寄りです。一番上にユーザアプリケーションがあります。

KermiteCore_Arduinoがコアライブラリで、キー入力のロジックやUSBの出力を機能を持っています。このライブラリに各キーのキーインデクスと押されているかどうかの状態を与えると、内部でレイヤロジックが動いて、USB Keyboardの機能でキーコードが送り出されます。Webアプリとの通信やキーマッピングの管理もこのライブラリが内部で受け持ちます。

これとは別に、keyboard_peripheral_modulesというキーボードの周辺機能をまとめたライブラリを作りました。ボード上のLEDを共通のコードで扱えるようにしたり、キーマトリクスのスキャンを行うモジュールを提供しています。

RP2040をArduinoで使うために、arduino-picoというフレームワークを使っています。earlephilhower版というものが広く使われていますが、そのままでは複数のUSB HIDデバイスに対応できなかったので、これをフォークして機能を追加したものを作りました。

これらをArduinoの開発環境上で構成してファームウェアを作ります。

コード例

1つのキーを持つ最小構成のファームウェアのコード例です。

#include <KermiteCore.h>

KermiteCore kermite;
int pinButton = 6;
int count = 0;

void setup() {
  pinMode(pinButton, INPUT_PULLUP);
  kermite.setKeyboardName("mykeeb");
  kermite.begin();
}

void loop() {
  if (count % 10 == 0) {
    bool pressed = digitalRead(pinButton) == LOW;
    kermite.feedKeyState(0, pressed);
  }
  kermite.processUpdate();
  count++;
  delay(1);
}

コアライブラリの機能がKermiteCoreというクラスにまとめてあります。上のコードでは、kermiteという変数名でこれのインスタンスを作っています。
kermite.setKeyboardName(キーボード名)でUSBデバイスの表示名を設定します。
kermite.begin()で各機能を初期化してUSBキーボードとしての動作を開始します。
kermite.feedKeyState(キー番号, キー状態)というメソッドで各キーの状態を流しこむと、内部でそれが処理されて、USBでキーコードが出力されます。
kermite.processUpdate()を1ms程度の周期で呼んでもらう想定の作りになっています。これは厳密にこの周期で呼ぶ必要はなくて、キーコードの出力がその分遅くなるだけなので、たまに10ms程度遅れたりしても問題ないです。

今後の予定

実際にArduinoでKermiteのファームウェアを作る手順を、別の記事で詳しく解説する予定です。しばらくお待ちください。

Discussion