icon

nazo6.dev

一覧に戻る
2024/4/26 2024/5/22 14 min read

RustとEmbassyでKeyballのファームウェアを作った

この記事はZennにも投稿しています

#はじめに

以前RustでKeyballのファームウェアを書きたい話で、ATMega32U4向けのファームウェアの作成をRustで試みたという話を書きましたが、結論から言うとこれは諦めてProMicro RP2040向けのファームウェアをRustで書くことにしました。

理由は当該記事に既にちらっと書いているのですが

  • USBの謎バグを解決できなかった
  • さすがにメモリサイズが小さすぎる
  • RP2040であればEmbassyが対応している というのが大まかなものになっています。

今回は純正品ではなくAliExpressで互換品を購入したのですが、これは16MBのフラッシュメモリを積んでおりもはや容量の心配はゼロです。 また、Rustの組み込み向け非同期フレームワークEmbassyがRP2040をサポートしており、Rustのasync/await機能を使った開発ができます。 とまあこのようにRP2040搭載ボードを使うことで開発が大幅に楽になりそうだったのでこのような選択となりました。 また、今回購入したボード一つ1000円もせず非常に安価なことも決め手の一つでした。

そんなわけでRP2040向けのファームウェアをちまちま作ってきたのですが、一応キーボードとして使えるぐらいのところまでは持ってこれたので行ってきたことをまとめておこうと思います。

#作ったもの

実際に作ったもののリポジトリはこちらになります。

主に実装した物としては

  • キースキャン
  • マウス
  • OLEDディスプレイ
  • LED(とりあえず自動マウスレイヤで光るだけ)
  • 分割キーボードの通信
  • レイヤシステム(本当に一部だけ)
  • 自動マウスレイヤ などです。

#試し方

使えないことはないレベルにはなりましたがまだ色々とバグがありレイヤ機能がまだ実装途中なので実用はちょっと厳しいかもしれないです。

READMEに書いている通りにelf2uf2-rsをインストールしてProMicroを接続しcargo runをすれば実行できます。分割キーボードとして使いたれば両側に行う必要があります。 通常のProMicroではなくRP2040版ProMicro(かその互換ボード)が必要です。

今のところKeyball61専用です。src/constants.rsを適切に変更すれば恐らく動くと思いますが持っていないので検証はできません。

#このファームウェアのアピールポイント

  • Rust製!

…残念ながら今のところ公式のものよりこちらのファームを使うべき理由があるほど完成はしてません。

しかしながらRustとasync/awaitを用いて書くことでQMKよりはわかりやすいであろうコードになっているので(主観)、1から自作キーボードのファームを作りたいという方には参考になるのではないかと思います。

#苦労話

苦労したところです。

#Duplex Matrix

まずキースキャン部を作り始めたわけですが、その時はマトリックススキャンというものがあるなどとは露知らず、まず思ったのが「あれ?どうやって全キースキャンするんだろう…」ということでした。30キーに対してCOL03、ROW04のピンしかありません。3x4でも全く足りないじゃん。というわけで調べた結果KeyballはDuplex matrixという方法でスキャンを行っているということを知りました。

ただ一度理解してしまえば実装はそこまで難しくはなく、以下のように実装することができました。

#ボード間の通信

Keyballでは、3極のTRSケーブルで分割キーボード間の通信を行います。一つはV+、一つはGNDなので実質通信に使える線は一本だけということになります。また、繋がっているピンも一つだけです。この実装がなかなか大変でした。

自分も調べるまで知らなかったのですが、このようなカスタムの通信機能を実装するために、RP2040にはPIOというものが搭載されています。これは小さいCPUのようなもので、プログラムを書き込むことができます。また、メインのプログラムからアクセスできるキューとGPIOにアクセスできる機能を持ちます。 つまりどういうことかと言うと、送られてきたデータに合わせてGPIOのHigh/LowをカチカチしたりFIFOキューに書き込むようなプログラムをPIOで実行させ、CPUの負荷なくデータ送受信処理を行えるということです。

長々と書いてしまいましたが、このPIOのアセンブリを書いて半二重通信を行うというのがこのファームウェアの制作で一番大変なところでした。QMKのコードなどを参考にしてなんとか実装しましたが、通信が不安定だなと感じる瞬間がたまにあるのでなるべく早く直したいです。

#キーマップ機能

これはハードウェアの都合で難しいというよりは、同時押しなどのステートフルな処理を実装するのに結構苦労している感じです。 TapとHoldぐらいなら簡単だろと思いきやQMKで言うところのPERMISSIVE_HOLDとかHOLD_ON_OTHER_KEY_PRESSなどを考えると頭が爆発しそうになりQMKの偉大さを実感しました。

#今後の展望

基本的なキーボードの機能の実装は完了したので今後はレイヤ機能などをきちんと作りこんだ後にVial(Remapみたいなやつ)の対応や、Keyball61以外のサポートをやりたいです。

また、EmbassyにはBLE Micro Proに搭載されているnrfプロセッサのサポートもあるので、いつかこちらに移植できたらいいなーと考えています。

#感想

#Rust

Rustでの開発は非常に快適でした。Rustのモダンな言語機能が使えるのはもちろんのこと、コミュニティ全体でなるべくno_stdで利用しようという意識があるのか様々なクレートでstdfeatureを外すことができたり、heaplessなどの様々なno_std用便利クレートもあります。

また、各デバイス向けの抽象化レイヤがうまく機能していると感じました。例えばembedded-halは組み込みデバイス向けの抽象化トレイトを提供しており、Embassyが提供するモジュールも基本的のこのトレイトを実装しているため、embedded-halに対応しているクレートならEmbassyと一緒に使うことも容易です。 さらに、embedded-graphicsという組み込み向けディスプレイの抽象化もあり、Keyballに搭載されているssd1306を扱うためのクレートも対応しています。

#先駆者

今回この開発をしていて感じたのが、思ったよりもRustでキーボード開発してる人が多いということです。 GitHubで検索をかけると基板から自作したキーボードのファームをRustで書いているようなものも数多く見つけられました。 また、rmkrumcakeなど今回自分がしたことの上位互換みたいなことをやっているのを見つけたりして途中で若干やる気を無くしてしまったぐらいには想像よりも開拓されている領域でした。

#Embassy

最後にEmbassyについて。実はEmbassyはつい最近までstable Rustで使えず、crates.ioにも無い状態でした。それが最近embedded-halのv1がリリースされたことやトレイトでの非同期関数のサポートの安定化によりついに最初のバージョンがcrates.ioにリリースされました

このように今は組み込みRustが過渡期にありますが、なるべく互換性を保つためにEmbassyの各モジュールはembedded-halembedded-hal-asyncの両方を実装するといった工夫が成されています。

さて、Embassyの一番のウリはその名前にもある通り、asyncをサポートしていることです。asyncを用いることで非同期コードを直感的に書くことができます。

Rustのasync/await機能は柔軟性を高めるためにエクゼキュータを持っておらずtokioなどの外部ライブラリが必要で、そのために分かりづらいと言われがちなことで有名です(?)。 しかしEmbassyはまさにその柔軟性を生かしたライブラリとなっており、Rustの非同期がヒープすら必要としないのでこのように組み込み環境でも使用できるわけです。

そんなEmbassyを用いた開発体験は非常に良いです。async/awaitを用いることで並列処理を容易に記述することができますし、embassy-timeembassy-syncなど便利なクレートが沢山用意されています。また、embassy-usbにはUSBやUSB HIDの実装が含まれていて非常に便利です。実際、USB関連のコードは今回ほとんど何も書いていません。

さらに、EmbassyのRP2040向けHALのembassy-rpには先述したPIOも含め必要な機能が網羅されており、「あれができない…」みたいなことは今回は特にありませんでした。 (ただ、embassy-syncのMutexの実装が怪しいのかその辺で落ちたりすることは多少ありましたが…)

embassyのasyncコード例として、実際の処理のメインループ部分は以下のようになっています。

どうでしょうか。ハードウェアに近い層を触る時の面倒なあれこれをあまり感じずにわかりやすいコードになっているのではないでしょうか。また、embassy_futuresのjoinやselect、embassy_syncのchannelやsignalを使うことでstd環境に近い開発体験を得ることができています。

#最後に

なんだか自作キーボードの話というよりRustの良さを力説するような記事になってしまいましたが、今回初めてKeyballという自作キーボードを買ってRustを使って自分でファームウェアまで作れたことは非常に楽しい経験になりました。是非みなさんもファームウェア自作にチャレンジしてみてはいかがでしょうか。

Share this article:
一覧に戻る

関連記事

2024/8/23

2024/8/30

#hardware/keyboard/keyball
memo

Keyball消費電力メモ

QMKなどのファームウェアでKeyballの消費電力を測定し、各マイコン及び周辺機器の電力消費を調べた。

Read Article

2021/12/27

2023/10/20

#tech/lang/rust
memo

Rust

気づきとかいろいろ

Read Article

2024/3/23

#tech/lang/rust#hardware/keyboard/keyball
blog

RustでKeyballのファームウェアを書きたい話

KeyballのファームウェアはQMKを使ったC言語のものになっています。ですがやはりRust、使いたいですよね?

Read Article

2023/5/26

#tech/lang/rust#tech/database
memo

RustでSQLからコードを生成するcornucopiaについて

SQLからRustのコードを生成して安全にデータベース操作ができる。恐らくGoのsqlcと同じ感じなんだと思う。

Read Article

2023/11/15

#tech/lang/rust
memo

Rustでジョブキュー的なもの

実行するコマンド(EnqueueかClear)をチャネルで受け取る

Read Article

2025/12/13

2025/12/14

#tech/lang/rust
blog

RustでデスクトップGUI - gpui入門 Part1 (gpuiの仕組み・状態管理の基礎編)

gpui解説記事のPart1。gpuiのレンダリング方法や状態管理について、実際のソースを見ながら詳しく解説します。

Read Article

2023/8/27

#tech/lang/rust
memo

Rustのserde_jsonでエラーの発生箇所を知る方法

serde_jsonではパースエラー発生時にどのプロパティでエラーが発生したのかわからない

Read Article

2023/6/27

#tech/lang/rust
memo

Rustのtargetフォルダを軽くする

cargo-sweepを使う

Read Article

2022/2/13

#tech/lang/rust
memo

Rustアプリにwasmerを埋め込む

dioxusを使ってwebでもdesktopでも動くアプリを作りたい

Read Article

2025/3/29

#tech/lang/rust
memo

Rustアプリのメモリ使用量を調査する

主にstatic領域のメモリ使用量を調査するのに有益。embassyの独立したタスクなどのサイズが見れる。

Read Article

2023/9/10

2023/12/18

#tech/lang/rust
blog

SerdeのDeserializerを実装する(Part1)

Serdeで任意の形式のファイルなどをデシリアライズする際にはDeserializerを書く必要があります。この記事では基本的なDeserializerの書き方を解説します。 正直自分もあまり理解していない部分が多々あるのですが世に出ている情報が少ないので書くことにしました。

Read Article

2023/12/18

2023/12/19

#tech/lang/rust
blog

SerdeのDeserializerを実装する(Part2 JSON編)

この記事はRust Advent Calendar 2023 シリーズ3の19日目の記事です。

Read Article

2024/5/24

#hardware/keyboard#tech/lang/rust
blog

USB HIDキーボードでメディアキーを操作する方法

USB HIDでは0x80がVolume Up、0x81がVolume Downに割り当てられており、さらに0xEDや0xEEでもVolume UpやDownができそうですが、実はこれらは全て動きません(Windowsでは)。

Read Article

2024/3/26

#hardware/keyboard/keyball
memo

keyballのQMKメモ

Keyballで使えそうなQMK機能のメモ

Read Article

2023/9/1

#tech/lang/rust
blog

prisma-client-rust入門

prisma-client-rustはJavascript向けのORMであるprismaをRustから使えるようにしたものです。実はprismaのコア部分はRustで書かれているためこういうものも作りやすかったんじゃないかと思います。

Read Article

2021/12/25

#tech/lang/rust
memo

tauriでWindows上でproductionビルドでのみ画像が表示されない(fetchエラーが発生する)

誰の役にも立たない気がするけどハマったのでメモ

Read Article

2023/11/18

#tech/lang/rust
blog

tokioで作ったサーバーをdockerで起動すると終了が遅くなるときの対処法

axumなどを作ってRustでサーバーを作るとdocker compose stopなどが微妙に遅くてイライラだったのでそれを解決する方法です。

Read Article

2025/4/9

2025/11/5

#tech/lang/rust
blog

「Rustが嫌いです。」の感想

https://zenn.dev/miguel/articles/f052de93fc9980

Read Article

© 2025 nazo6. All rights reserved.