Goでキーボードファームウェアを作る (2)自設計キーボードのファームウェアを作る

Go言語を使ってキーボードファームウェアを作ってみました。
(厳密には「TinyGoおよびtinygo-keyboardを使って」、ですけど)
思っていたより簡単だったので、その簡単さを記事にまとめます。

今回は、自設計キーボードのファームウェアを作ります。

目次


0. おことわり

この記事は2023年8月時点の情報に基づいています。
そのため、あなたがこの記事を読んでいる時点では、古い情報になっている可能性があります。

また、一個人である私が試した結果をまとめた記事になっています。
そのため、この記事の内容が正しいという保証はどこにもありません。
あくまで一つの事例として参照するにとどめてください。

1. 環境について

前回の記事を参照してください。

2. コードを書く

まずは自設計キーボード用にディレクトリを作ります。

bash
1
2
$ cd ~/tinygo-keyboard
$ mkdir ./targets/teihai70h/

作ったディレクトリに、Vial用のjson vial.json をコピーしておきます。

bash
1
2
$ ls ./targets/teihai70h/
vial.json

Vial用jsonの作り方については、Vial公式サイトなどを参照してください。

この vial.json から def.go を生成するため、コマンドを実行します。

bash
1
2
3
4
$ go run ./cmd/gen-def/main.go ./targets/teihai70h/vial.json
go: downloading github.com/itchio/lzma v0.0.0-20190703113020-d3e24e3e3d49
$ ls ./targets/teihai70h/
def.go vial.json

def.go ができました。

次に main.go を作ります。
一から作るやり方がわからないので、既存の targets/fric10key/main.go をコピーして必要な個所を修正することにしました。

main.go(変更前)
1
2
func main() {
usb.Product = "fric10key-0.1.0"

main() 関数内で usb.Product にキーボードの名前とバージョンを入れるみたいです。
ここを書き換えます。

main.go(変更後)
1
2
func main() {
usb.Product = "Teihai70H-0.1.0"

run() 関数内でcolとrowのピンを指定するようです。

main.go(変更前)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func run() error {
d := keyboard.New()

colPins := []machine.Pin{
machine.D9,
(略)
machine.D6,
}

rowPins := []machine.Pin{
machine.D0,
(略)
machine.D10,
}

ここを適切に書き換えます。

書き換えるにあたって、ピン名は “GPIOxx” ではなく “Dxx” で指定しないといけないのか?と思い、他のファイルを少し見てみました。
/usr/local/lib/tinygo/src/machine/board_waveshare-rp2040-zero.go を見るとD6とかにはGPIO6を代入しているだけのようなので、GPIOxxでもDxxでもどっちでもいいのかもしれません。
まあDxxで指定した方が記述が短くて済むので、そうします。

今回ファームウェアを作るキーボード(Teihai70H)は、7行10列で↓こんな感じの回路です。

この回路に合わせて、colPinsが7個、rowPinsが10個、の配列になるよう書き換えます。

main.go(変更後)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
colPins := []machine.Pin{
machine.D28,
machine.D27,
machine.D26,
machine.D15,
machine.D14,
machine.D13,
machine.D12,
}

rowPins := []machine.Pin{
machine.D1,
machine.D3,
machine.D4,
machine.D5,
machine.D6,
machine.D11,
machine.D10,
machine.D9,
machine.D8,
machine.D7,
}

引き続きrun()関数です。
d.AddMatrixKeyboard 関数でキーマップを定義するっぽい?ので、ここもいい感じに書き換えます。
日本語配列用のキーコードは、tinygo-keyboardリポジトリの keycodes/jp/keycodes.go に定義されているようなので、そちらを参照してキーマップを定義します。

main.go(変更後)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
d.AddMatrixKeyboard(colPins, rowPins, [][]keyboard.Keycode{
{
jp.KeyEsc, jp.Key1, jp.Key2, jp.Key3, jp.Key4, jp.Key5, jp.Key6,
jp.KeyTab, jp.KeyQ, jp.KeyW, jp.KeyE, jp.KeyR, jp.KeyT, jp.KeyY,
jp.KeyLeftCtrl, jp.KeyA, jp.KeyS, jp.KeyD, jp.KeyF, jp.KeyG, jp.KeyH,
jp.KeyLeftShift,jp.KeyZ, jp.KeyX, jp.KeyC, jp.KeyV, jp.KeyB, jp.KeyN,
jp.KeyMod1, jp.KeyHankaku, jp.KeyWindows, jp.KeyLeftAlt, jp.KeyMuhenkan, jp.KeySpace, jp.KeySpace,
jp.Key7, jp.Key8, jp.Key9, jp.Key0, jp.KeyMinus, jp.KeyHat, jp.KeyBackslash,
jp.KeyU, jp.KeyI, jp.KeyO, jp.KeyP, jp.KeyAt, jp.KeyLeftBrace,jp.KeyBackspace,
jp.KeyJ, jp.KeyK, jp.KeyL, jp.KeySemicolon,jp.KeyColon, jp.KeyRightBrace,jp.KeyEnter,
jp.KeyM, jp.KeyComma, jp.KeyPeriod, jp.KeySlash, jp.KeyBackslash2,jp.KeyUp, jp.KeyRightShift,
jp.KeyHenkan, jp.KeyHiragana, jp.KeyLeftAlt, jp.KeyMod1, jp.KeyLeft, jp.KeyDown, jp.KeyRight,
},
})

3. ビルドする

これで main.go はできたはずなので、ビルドします。

bash
1
2
3
$ tinygo build -o teihai70h.uf2 --target waveshare-rp2040-zero --size short ./targets/teihai70h/
code data bss | flash ram
74200 4 6112 | 74204 6116

ビルドが通ったようです。

4. 動作確認

出力されたファイル teihai70h.uf2 を、キーボードに取り付けたRP2040-Zeroに書き込みます。
書き込みが終わったら、Vial Web をWebブラウザで開きます。

「Start Vial」を押すと、HIDデバイスへの接続を求めるウィンドウが開きます。

usb.Product に入れていた値が表示されるはずなので、それを選んで「接続」をクリックします。

少し待つと、キーマップを編集できそうな画面が開きます。

とりあえず、作ったファームウェアがVialから認識できていることが確認できました。
あとは各キーを押したときに正しく文字が入力されるかも確認します(ちゃんと入力できました)。

5. おわりに

tinygo-keyboard、かなりお手軽にファームウェアが作れました。
お手軽すぎて、Goのコードを書いた気がしませんw
Goの文法やお作法がわからない状態で始めましたが、そんな状態からでもちょっと調べればなんとかなるのではないかと思います。
QMK/VIA以外の選択肢として、tinygo-keyboardアリだと思います。

6. 参考資料