自作キーボードHanamuguriを作った

この記事ははJLCPCBの提供でお送りします。

JLCPCBとは

jlcpcb.com (↑こちらは日本語版のログインページで、お得なクーポンも配布されています。)

JLCPCBとは、プリント基板製造などで有名な香港の企業です。

日本からでもWebページでポチポチするだけでKiCADなどで作成した基板データの製造を依頼できます。

値段もかなりお手頃で、ホビー電子工作ユーザーの間では広く利用されています。

この記事の作例もJLCPCBに基板を発注して実現しました。

自作キーボード「Hanamuguri」とは

Hanamuguriはinajobの2つ目に設計した自作キーボードです。

いわゆる40%キーボードと呼ばれるレイアウトで、コンパクトながら実用的なキー配列となっています。一般的なパソコンのキーボードとしても利用できますが、どちらかというと、Raspberry Piなどを使って作られたコンパクトな自作モバイル端末を実現するための部品の一つとして利用できるものを目指しています。

前作はALLPCB39というキーボードを作りました。こちらもコンパクトなキー配列のキーボードです。

ALLPCB39はオーソリニアと呼ばれる格子状のキーボードだったのですが、今回のHanamuguriはロウスタッガードと呼ばれる、行ごとに少しずつキーがずれている、いわゆる一般的なキーボードの配列のキーボードと同じ配列を選択しました。 加えて、ALLPCB39はCherry MX互換キーボードスイッチを採用しましたが、HanamuguriはGateronのLowProfileを利用できるようにして、より薄くなりました。

これも含めて以下のような特徴があります

  • 49キー(配列はいわゆる40%キーボード的)
  • 独立カーソルキー
  • 固定用のねじ穴がたくさんある
  • ユニバーサル基板っぽい領域がキーボード上部にある
  • 対応キーボードスイッチ
    • Gateron LowProfile
    • Gateron LowProfile Hot swap
    • Cherry MX(not LowProfile)
  • キーボードスイッチはパネルマウント
  • 128*64 OLED
  • 開発ボードはRP2040-Zero
  • キーボードマトリクス、未使用ピンのための拡張ピンヘッダ

Hanamuguriをいきなり作るのは心配だったので、まず オリジナルのマクロパッドを作ってみた - inajob's blogを使ってキーボードスイッチまわりのフットプリントの使い方に問題がないかを検証しています。そのため、キーの数が多いことを除いては、以前作ったマクロパッドと同じような構成となっています。

RP2040-Zero

今回のキーボードのキモともいえるこのマイコンボード。コアとなるMCUはRP2040で、Raspberry Pi Picoと同じものですが、RP2040-Zeroの方が基板がコンパクトであることや、USB Type-C端子であることに魅力を感じ、こちらを選択しました。

コンパクトな分利用できるGPIOは少ない、、と思いきや裏面に細かいピッチでパッドが並んでいるので、この部分をはんだ付けできればより多くのGPIOを利用できます。(Hanamuguriではこの部分のピンは今後の拡張のために引き出しているだけで、利用していません)

www.waveshare.com

基板の設計

基板の設計と書きましたが、以前のALLPCB39を作った時と同様、Keyboard Layout Editorのデータから基板のデータを半自動的に生成します。

ということで、まずキーレイアウトを作成しました。

keyboard-layout-editorのデータから回路図を作るツールについて、KiCADのバージョンを上げたせいか、以前使ったツールはうまく動かなくなっていたので、今回はKiCADのアドオンであるGitHub - adamws/kicad-kbplacer: KiCad plugin for automatic keyboard's key placement and routingを使いました。

使い方はkicad-kbplacerのREADMEに従いましたが、ちょっとわかりにくかったので、ここでも説明します

まず、キーボードマトリクスの論理回路図を作成します。今回はキーの数が44キーなので、Rowが7ピン、Columが7ピンのマトリクスレイアウトを採用します。結果として49キー利用できるので、キーボードの配列とは別に5つのキーを利用できるように設計することとします。

で、この論理回路図の部品に番号を振ります

annotation機能を実行すれば番号を振れるのですが、その際に以下の画像のように設定します。

おそらくここを変な形で設定しても、ファームウェアでうまいこと設定すれば対応できそうですが、今回はおとなしく指示に従いました。 その後、フットプリントを割り当てます。

Gateron LowProfileのキーのフットプリントは以前作成したものがあるので、これを使います。

次にKiCADの基板エディタで、ネットリストを読み込みます。 読み込んだ直後は以下のようになっています。

さて、ここでこの部品たちをkeyboard-layout-editorで設計したキー配列のように並べるのがkicad-kbplacerの役割です。

keyboard-layout-editorで作成したjsonをkle-serial形式に変換する必要があるので、以下のツールを利用しました

adamws.github.io

その後ツールバーにあるkbplacerをクリックし、設定画面を開きます

kle-serial形式のjsonファイルを指定して、OKをクリックすると以下のように、部品がkeyboard-layout-editorで設定したものと同様の配列になりました。

なお、今回は45キーのレイアウトだったので、余っている5キーについては手動で配置します。

ここまででキーボードスイッチとダイオードについてはレイアウトが決定しました。

その他の部品や基板外形はいつもの回路設計と同様に行います。

RP2040-Zeroのフットプリントは GitHub - crides/kleeb: Collection of Kicad 6.0 symbols, footprints and 3D models useful in keyboard creationを利用しました。

キーボードの上部にユニバーサル基板のような領域を作ってみました。

これを作るにあたってはKiCadのレイアウトエディタ(Pcbnew)で半田付けパッドを自由に追加する方法(viaを利用する) - PCB Design Tutorial - PCBwayで紹介されているviaとソルダーマスクを使用する方法を利用しました。 ただしこの方法を愚直にやるとfreeroutingで自動配線するときにviaをfreeroutingが認識せず、自動配線がユニバーサル基板領域と重なってしまう問題が起きました。

そのため、まずはユニバーサル基板領域を配線禁止エリアとし、自動配線後にviaとソルダーマスクを並べる手法でユニバーサル基板風の基板を作りました。

続いて、トッププレートも作成します。作り方は以前のオリジナルのマクロパッドを作ってみた - inajob's blogと同じです。

基板の発注

発注はもちろんJLCPCBです。

jlcpcb.com (↑こちらは日本語版のログインページで、お得なクーポンも配布されています。)

10cm×10cm以内の基板が激安なのは、今まで紹介してきたとおりですが、今回の基板はそれより大きいです、さて、いくらくらいかかるのか・・?

大きさは、本体基板が 242.6 mm × 127 mm、トッププレートが242.6 mm × 92.2 mmです。 注文後にメールが1つきて「配線が含まれない基板があるけどデータが間違っているのでは?」(英語)と確認されました。こちらはキーボードのトッププレートなので、配線なしで合っているので、問題ないので進めてくださいと返しました。

その後さらにAn additional charge will be applied to your order[JLCPCB] というメールが来て、「細かい穴が多いので追加で$7.5支払ってほしい」と連絡がありました。これも問題ないので追加で支払いを行いました。

具体的にはこのあたりが追加料金となった原因とのことでした。(まぁ、確かに細かい穴が多い)

ということで5セット作ってもらうのに、お値段は下記に加えて追加の$7.5、送料の$13.77がかかりました。(別にもう1種類基板を発注していたので、送料はそれも込みのものとなっています)

部品の発注

Gateronのロープロファイルのキーボードスイッチ、これに対応したホットスワップソケット、RP2040-Zero、128*64のI2C接続のOLED、ダイオード、キーキャップが必要でしたが、今回はこれらの部品は家のストック部品を利用しました。

Gateronのロープロファイルキーボードスイッチの2Uのスタビライザーは家になかったのでAliExpressで購入しました。

今回のミス

スペースキーが横に長い(2.75U)ため、スタビライザーを取り付けられるように溝を付けたのですが、そのフットプリントのサイズが誤っており、そのままでは取り付けられないものとなりました。

やはり、新しい部分があると、そこでミスしますね・・ 今回は取り敢えず、スタビライザーなしで組み立てることとしました。

部品実装

RP2040-Zeroは端面スルーホール部品なので、基板を重ねて実装することが出来るのですが、とりあえず後で着脱が出来るようにピンヘッダ・ピンソケットを介して実装しました。OLEDも同様にピンヘッダ・ピンソケットを介して実装しました。

後はダイオードと、ホットスワップソケットです。単純な部品なのですが、何しろ44個もあるので、実装が大変でした。特にホットスワップソケットは、はんだがパッド面ではなくソケットの溝の方にばかり流れ込んでしまい、一見はんだが乗っているように見えるが、パッドと部品が接続されていないという状態になるケースが多発し、苦労しました。

加えて、とても初歩的なミスですが、ダイオードの向きを一部間違えて実装してしまい、これも修正が面倒でした。

ファームウェア実装

ファームウェアKMKfw | KMKを使いました。 これはCircuitPython上に実装された、自作キーボード向けのファームウェアで、非常に簡単に利用することが出来ました。

実装したファームウェアはこんな感じです

  • 日本語キー配列(KMKは日本語キー配列もサポートしていました)
  • レイヤーは2つ
    • 通常レイヤー(キートップに刻印されているアルファベット)
    • 数字レイヤー (一般的なキーボードの1行目にあるキーや、一部の記号)
  • OLEDにレイヤーの状態を表示

これだけの記述でカスタマイズできるのはとても便利だと感じました。またCircuitPythonはインタプリタとして動いているので、修正・確認のフィードバックループを素早く回すことが出来たのも良かったです。(その分実行速度は遅いですが、今回の用途ではその遅さが気になることはなかったです)

import board

from kmk.kmk_keyboard import KMKKeyboard
from kmk.keys import KC
from kmk.scanners import DiodeOrientation

# レイヤーを利用する
from kmk.modules.layers import Layers

# 日本語対応
import kmk.extensions.keymap_extras.keymap_jp

# OLED対応
from kmk.extensions.peg_oled_display import Oled,OledDisplayMode,OledReactionType,OledData


keyboard = KMKKeyboard()

# ピンアサイン
keyboard.col_pins = (board.GP11, board.GP12, board.GP13, board.GP14, board.GP15, board.GP26,board.GP27)
keyboard.row_pins = (board.GP4, board.GP5, board.GP6, board.GP7, board.GP8, board.GP9, board.GP10)
keyboard.diode_orientation = DiodeOrientation.COL2ROW

# レイヤー
keyboard.modules.append(Layers())

# OLED用のピンアサイン
keyboard.SCL=board.GP3
keyboard.SDA=board.GP2

# OLEDの設定
oled_ext = Oled(
    OledData(
        corner_one={0:OledReactionType.STATIC,1:["layer"]},
        corner_two={0:OledReactionType.LAYER,1:["1","2","3","4"]},
        corner_three={0:OledReactionType.LAYER,1:["base","raise","lower","adjust"]},
        corner_four={0:OledReactionType.LAYER,1:["qwerty","nums","shifted","leds"]}
        ),
        toDisplay=OledDisplayMode.TXT,flip=False,oWidth=128, oHeight=64)
keyboard.extensions.append(oled_ext)

# よく使うキー
LOWER = KC.MO(1)
_______ = KC.TRNS

# キーマップ
keyboard.keymap = [
        # layer0
        [
            KC.ESC, KC.Q, KC.W, KC.E, KC.R, KC.T, KC.Y, KC.U, KC.I, KC.O, KC.P, KC.BSPC,
            KC.TAB, KC.A, KC.S, KC.D, KC.F, KC.G, KC.H, KC.J, KC.K, KC.L, KC.ENT,
            KC.LSFT, KC.Z, KC.X, KC.C, KC.V, KC.B, KC.N, KC.M, KC.COMM, KC.UP,LOWER,
            KC.LCTRL, KC.LGUI, KC.LALT, LOWER, KC.MINUS, KC.SPC, KC.DOT, KC.LEFT, KC.DOWN, KC.RIGHT,
            KC.N0, KC.N1, KC.N2, KC.N3, KC.N4
            ],
        # layer1
        [
            KC.GRAVE, KC.N1, KC.N2, KC.N3, KC.N4, KC.N5, KC.N6, KC.N7, KC.N8, KC.N9, KC.N0, KC.BSLASH,
            _______, _______, _______, KC.CIRC, KC.JYEN, KC.AT, KC.SCLN, KC.COLN, KC.LBRC, KC.RBRC, _______,
            _______, _______, _______, _______, _______, _______, KC.SLASH, KC.BSLS, _______, _______,LOWER,
            _______, _______, _______, LOWER, LOWER, _______, _______, _______, _______, _______,
            KC.N0, KC.N1, KC.N2, KC.N3, KC.N4
            ],
]

if __name__ == '__main__':
    keyboard.go()

まとめ

自分としては2つ目の自作キーボードを設計・実装しました。 kicad-kbplacerを使った自動配置、ロープロファイルキーボードスイッチ、KMKなど、以前とは違う手法・構成を使い、多くの学びを得ました。

前回のときも感じましたが、自作キーボードは、趣味の電子工作のジャンルの中でもとてもポピュラーなものとなってきており、周辺ツールや文書が潤沢にあるため、非常に入門しやすく、簡単に作り上げることが出来ると感じました。

Hanamuguriは、まだキーボードとして最低限動作するところまでしかできていないので、今後ケースの作成、キーマップの充実、OLEDやカラーLEDの活用、これを組み込んだモバイル端末の作成など、まだまだやりたいことがたくさんあります。

また進展があれば、ブログ記事などで紹介しようと思います。