TL;DR
- Kailh Choc V2スイッチ対応のキーボードを新しく設計しました
- 3Dプリントのインテグレーテッドトッププレートでスイッチを嵩上げして、対応キーキャップを増やしています
- フレキ接続でキーマトリクス基板とマイコン基板を分割して有線版と無線版を作りましたが、組み立てが面倒なのと設計の自由度が下がってしまうのでおすすめしません...
- 無線版のファームウェアにはQMKではなくZMKを使用しました
はじめに
前回Thumbsplit58を設計した時点で理想のキー配列が固まり、エンドゲームに到達したかに思われました。使い心地はほとんど問題なく、実際毎日使っているわけですが、使用しているスイッチがKailh Chocのためキーキャップのバリエーションが少なく、カッコいいキーキャップが発売されても指を咥えて見ていることしかできませんでした...
そこでCherry MX互換のカッコいいキーキャップを嵌めて見た目もエンドゲームなキーボードを目指すべく、Choc V2スイッチを使って新しいキーボードを作りました!
Thumbsplit58 Wired |
Thumbsplit58 BLE |
このとき実験的な試みとして、3Dプリントのインテグレーテッドトッププレートでスイッチを嵩上げすることで、キー押下時のキーキャップとトッププレートのクリアランスを確保し、従来Choc V2スイッチに嵌めるとトッププレートに当ってしまっていたキーキャップも使えるようにしています。また、設計を使い回せるように、フレキ接続でキーマトリクス基板とマイコン基板を分割し、AVRを使った有線版マイコンボードと、nRF52を使った無線版マイコンボードを作りました。
ファームウェアの方も少し実験的な試みとして、無線版のマイコンボードではQMKではなくZMKを使い、BLEの完全無線キーボードとして動作させています。ZMKの設定などはあまり解説しているブログがないので、備忘録的な意味も含めてこのあたりのことをブログに残しておこうと思います。
Kailh Choc V2キースイッチの嵩上げ
Kailh Choc V2スイッチはKailh社製のロープロファイルキースイッチ (Chocシリーズ) の十字ステム版で、キーキャップをはめる部分がCherry MXスイッチ互換になっているため、Cherry MXスイッチ向けのキーキャップをはめることができるようになっています。
ところがChoc V2スイッチにはすべてのCherry MXスイッチ向けのキーキャップをはめることができるわけではありません!AJisai74のビルドガイドによると、CherryプロファイルやGRIDプロファイルはスイッチ自体とキーキャップが干渉してしまうようです。DSA、ADA、OEM、SAなどではキーキャップとスイッチは干渉しないのですが、これらのキーキャップを使った場合でもキー押下時にトッププレート取り付け位置よりもキーキャップが下側に来てしまうので、トッププレートを使うとキー押下時にキーキャップがトッププレートにあたってしまいます。
DSAキーキャップがキー押下時にトッププレートと接触しています |
なので、Choc V2スイッチ対応の自作キーボードキットは、基本的にトッププレートを使用していません。(c.f. AJisai74、Sparrow62)
ところが、トッププレートを使わない場合はPCBがむき出しになってしまうのでデザインが限られてしまうのと、スルーホール部品がある場合には半田付け部などに汚れやホコリが付着しやすくなってしまいます。PCBが直接打鍵の荷重を受けるので、打鍵感や打鍵音にも難点があるかもしれません。
そこで、Choc V2スイッチでトッププレートを使いつつ、キーキャップとトッププレートが干渉しないようにするため、キースイッチを1mm嵩上げするインテグレーテッドトッププレートを3Dプリンタで作りました。
インテグレートトッププレートの断面 |
トッププレートは板厚1mmで、スイッチの爪にかかる0.5mmのリブ部分だけ1mm嵩上げされており、キー押下時にキーキャップがトッププレートに干渉しないようになっています。トッププレートはPCBに接触するため、RGBLED以外の電子部品はすべて裏面に表面実装で実装しました。
嵩上げによってDSAキーキャップがトッププレートと接触しなくなりました |
PA12GBで作り直したトッププレートにスイッチを載せるとこのような感じになります。手持ちのDSAプロファイルのキーキャップでは1mmの嵩上げで接触しなくなりましたが、他のプロファイルの場合は干渉するかもしれません。その場合には、トッププレートを薄くして嵩上げ高さをさらに上げる必要があります。
スイッチをはめた様子 |
嵩上げ部分は0.5mm厚になっており、打鍵の荷重も受けること考えると一般的なFDMの3Dプリンタでは強度を維持するのが難しいかもしれません。今回は高い形状再現性と機械強度を得るべく、DMM.makeのMJF PA12GBで出力しました(※詳細は後述)。まだ1ヶ月程度しか連続使用していませんが、特に破損する様子はありません。
ロジックボードをフレキ接続 (※おすすめしません)
今回キーボードの設計を始めた当初、既に自分は理想のキー配列に到達したと感じていました。そこで、どうせキー配列が変わらないのなら共通で使い回せるキーマトリクスの基板を作っておけば、有線バージョンも無線バージョンにも使えるし、今後新作を作るときにも使い回せるかもしれない!、という考えのもと、基板を「マイコンやコネクタが載ったロジックボード」と「キーマトリクス配線とスイッチソケット、RGBLEDが載ったシールド」に分割しました。
ロジックボードはおなじみのAVRを使った有線接続のロジックボードと、nRF52モジュールを搭載した無線接続ロジックボード(※詳細は後述)を作りました。基板上にはマイコン周りの回路と、TRRSジャック、USB Type-C ハウジング、リセットスイッチが載っており、分割キーボードの背面部分に取り付けて使う想定です。
有線接続ロジックボード (Atmel ATmega32U4) |
無線接続ロジックボード (Minew MS88SF2) |
2種類のロジックボードは、キーマトリクスの配線にスイッチソケットとRGBLEDを実装した共通のシールド基板とフレキケーブルで接続します。ROWとCOL併せて13ピンと、RGBLED用のシリアル1ピン、RGBLEDのVCC/GNDはフレキコネクタの定格電流が低いために3ピンずつ割り当てて、合計20ピンのフレキコネクタを使いました。
フレキケーブルでロジックボードとシールドを接続 |
コネクタには0.5mmピッチのMolex 50348-2000を使っています。ロック付きでフットプリントが小さいため、スイッチを180度回転させなくても行間に配置することができました。
ただし、このコネクタはJLCPCBのパーツリストにはないので、自分でDigiKey等で購入して実装する必要があります。また、Molex純正のフレキがめちゃくちゃ高い (1個400円とかします…) ので、コスパは非常に悪いです…
また、新しいキーボードを作るときには全体的にいろいろ変えたくなるものなので、使い回すことはあまり考えるべきではないのかもしれないです… (次に作ろうと思っているキーボードでは早速互換性を捨てる予定です…)。新しいキーボードを作るときには現状に満足できなくなったときだと思うのですが、その時に互換性を保つことが設計上の制約になってしまって、理想を追求することができずに本末転倒になってしまう、というのが今回共通化を目指してみて得られた知見です。
無線版のマイコンボードの設計
低消費電力で技適が通っている無線キーボード向けのマイコンとなると、現状はほぼBLE Micro Pro (BMP) 一択になってしまっています。Switch ScienceではISP1807を搭載したPro Micro互換のボードも最近発売されましたが、無線キーボードで使う場合には電源LEDを剥がしたり、USB接続時にBATに電流が流れないようにする外付け回路が必要になると思います。
今回はキーボードをなるべく小さくしたかったためにBMPは使わず、技適取得済みのnRF52のモジュールを使用したボードを自分で設計することにしました。ただ、自分はリフロー設備を持っていないため、手ハンダ可能なモジュールを使う必要があります。
nRF52の技適取得済みモジュールはいくつかあるのですが、USB接続可能 (52840 or 52833 or 52820) なもので、かつ手ハンダ可能なものはMinew MS88SF2しかありませんでした。Switch Scienceでもブレイクアウトボードの販売がありますが、モジュール単品の販売はなかったので、AliExpressから購入しました (ちゃんと技適マーク付きのモジュールが届きました)。
ただ、MS88SF2には、VBUSが外に出ていない(モジュール内部でVDDHと接続されている)という大きな問題がひとつあります。これはバッテリー駆動しない場合や、USBからの電源供給のみで動作する場合には問題ないのですが、バッテリー駆動とUSBからの電源供給を切り替える場合には外付け回路が必要になってしまいます…
VBUSとVDDHが切り離されていれば、VDDHは使用せずに電源はVDDから供給しつつUSBの内部モジュールのみをVBUSで駆動することができるのですが、VBUSとVDDHが接続されているために、USB接続時にVDDHに5Vが入って、内部レギュレータの出力がVDDから出てきてしまいます。
回避策はNordicでも同様の質問が上がっている通り、「バッテリー駆動時はVDDに入力、USB接続時にはVDDをバッテリーから切り離してVDDHにVBUSを入れる」という回路を外付けで用意する必要があります。
自分はPch MOSFETを3.3V LDOの後にいれてVBUS接続時にバッテリーを遮断することにしました。VDDHに5Vがかかるとモジュール内部の電源回路がVDDに3.3Vを出力しますが、FETがOFFになっているのでバッテリーに電流は流れません。
USB接続時のバッテリー切り離し回路 |
無線キーボードを動作させるためには何らかのバッテリーが必要になってくるわけですが、前回のキーボードでは塩化チオニルリチウム電池という一次電池を使用していました。この電池は流せる電流が非常に限定されている(~100mA)代わりに、体積あたりの容量が非常に大きく (単三電池型で0.5mA連続放電で2400mAh)、自己放電も少ないのが特徴になっています。
ただ、前回キーボードを実際に毎日運用してみたところ、実測で半年~1年ぐらいしか持たない事がわかりました (前回のブログで計算した理論値より少し短いです)。十分長いといえば長いのですが、それでも電池交換が面倒に感じたので、今回のキーボードは二次電池を搭載して充電できるようにしたいという思いがありました。
今のキーボードの大きさに入れるとなると、リチウムイオン電池は3.7V 500mA程度が限界になります。それだとZMK Power Profilerの計算ではCentral側が1 ~ 2ヶ月ぐらいしか持たない計算になります(※これはDeep Sleepありの場合でこの値のようです。Deep Sleepは現時点ではデフォルト無効になっているのですが、後述のようにZMK_SLEEPで有効にできます)。
3.7V 500mAh のリポバッテリーを搭載 |
そこで、リポ充電回路を載せてUSB接続時に充電できるようにしました。といっても回路としてはMCP73831をリファレンス回路通り使っているだけです。LEDは砲弾型の2色LEDを使っていて、ステータス用にマイコンから青色を駆動、MCP73831から充電ステータス用に赤色を駆動としました。USB接続時にはRGBLEDを点灯させる可能性もあるので、充電電流は100mAとしています。
リポ充電回路 |
自作基板に合わせてZMKの設定をする
自分で設計したnRF52のボードでBluetoothの無線キーボードを作ろうとした場合、QMKを使うのであればsekigon-gonnocさんのqmk_firmwareのフォークのnrf52ブランチを使わせてもらうという選択肢があります(BLE Pro Microの方は"BMPAPI"内部の実装が公開されていないので、自作のボードで動作させることはできません)。
ただ、nrf52ブランチは現在はメンテナンスされていないのと、QMK以外のファームウェアを試してみたかったので、ZMK Firmwareを使用してみました。ZMKはZephyrというRTOSのアプリケーションとして動作するキーボード用のファームウェアで、Permissive License (MIT)なのと、Wireless動作を前提にしているのが特徴になっています。
後発のキーボード用ファームウェアではありますが、精力的に開発が続けられていて、QMKから乗り換える場合でも比較的機能の差異は少なくなってきています(OLED、Mouse Keyやキーマップ書き換えは目下開発中のようです)。
ただ、少し(非常に?)とっつきにくい部分があるのも事実で、ZMKの機能がDevice Tree Bindingとして実装されているために、キーマップ等の設定にDevice Treeの編集が必要になる点です(単純にキーマップを変更するだけならoverlayで良いのですが、自作のボードに対応させようとすると編集するファイルが多くなってけっこう大変です…)。
Device Tree自体はARM Linuxのデバイスドライバの肥大化を防ぐために開発された設定パラメータ格納構造ですが、Zephyrではこのデータ構造をハードウェアごとの設定パラメータを記述するために使用しています。一番の違いはLinuxのDevice Treeはプロパティを格納したツリーでデバイスドライバからは動的に値を参照されるのに対し、ZephyrではDevice Treeをビルド時にマクロ経由でCのソースコードに変換してしまうところでしょうか…。もともとのDevice Treeの思想からは離れてしまうような気がしますが、ZephyrではDevice Treeによる柔軟なハードウェア構成記述を活用して、組み込みデバイスごとの設定を管理しているようです。
ZMKの機能(LayerやMod Tap等々)はDevice Tree Bindingとして実装されていて、基本的にはこれを"zmk,keymap"をcompatible設定したにDevice Treeノード(専用の.keymapファイルに記述します)に書いていくことでキーマップを設定します。ただここで設定したキーマップはビルド時にソースコードに変換されてしまうため、今のところZMKには動的にキーマップを書き換える方法は用意されていません。
また、少しややこしいのが、ZMKではDevice Treeによる設定とは別に、KConfigによる設定というものも存在します。ZephyrではDevice Treeがハードウェアの構成や(その機能を使った場合の)デフォルトの設定値を記述するのに対して、KConfigはアプリケーションが使う機能を取捨選択するのに使うというガイドラインがあるようで、ZMKでもそのガイドラインに習った棲み分けがなされていて、例えばRGBLEDが接続されているピンやSPIの設定はDevice Treeで行った上で、実際にRGBLEDを使用するかどうかはKConfigで設定を行う、といった形です。
今回のキーボードは(基板上は分かれていますが、入れ替えて使用することはできないので)BoardとShieldとして分けずに、マイコン直付けの分割キーボード(=Board)として設定を作成しました。Board内でLeftとRightのDTSを用意するために少し複雑になってしまっていますが、普通に新たなキーボードをZMKで動かすためには、Kconfig.board、Kconfig.defconfig、${board_name}_defconfig、${board_name}.dts、${board_name}.keymap、${board_name}.yaml、のファイルが必要になります (改めて多いですね… ※各ファイルの役割の詳細はZMKのこちらのドキュメントに記載されています)。
また、まだデフォルトで有効にはなっておらずドキュメントにもまだ記載がないのですが、ZMKにはDeep Sleepという機能があり、ZMK_SLEEPのオプションで有効にすることができます。これはその名の通りマイコンをDeep Sleep状態にする機能で、デフォルトでは1440秒(24分)でDeep Sleepします。この状態になると無線機能はすべてオフになり左右間およびホストPCとのBluetooth接続は解除され、消費電力が非常に小さい状態になります。
上記のPRを読む限りではキー押下でスリープから復帰しそうに見えるのですが、自分が試した限りではキーを押してもスリープから復帰してくれませんでした。なので、今のところはリセットスイッチを押してマイコンをリセットすることで対応していて少し不便なのですが、この機能は特にスプリットキーボードでは必ず有効にしておく必要があります。
スプリットキーボードのホスト側はスレーブからのキー入力を受け付けるために定期的にBLEを受信モードで待機する必要があり、キーボードを使用してない間も電力を消費し続けてしまいます。ZMKでは消費電力を予測するZMK Power Profilerというページが用意されていて、ZMKで動くキーボードのバッテリーがどの程度持つのかを手軽に調べることが可能になっています。注意しなければならないのは、この値はDeep Sleepが有効になっている場合のシミュレーション結果になっているので、この値と同等のバッテリー持続時間を得るためにはZMK_SLEEPを有効にしておく必要がある点です。
例えば、今回作成したキーボードは721855サイズの500mAhのバッテリーを搭載していて、PSUはLDO、1日8時間使用するとしてPercentage Asleepを70%とすると、このような結果になりました。
ZMK Power Profiler |
左手(Central)側は右手(Peripheral)側から送られるキー入力を受信する必要があるためマイコンの待機時間が多く、バッテリー持続時間が少なくなっていることがわかります。
実際に毎日使用して運用している限りでは、充電せずに使用し続けて1ヶ月経過時点で残量レポートが40%を切るような形だったので、大体プロファイラーのシミュレーション結果通りになっていると思います。
ZephyrのBluetoothスタック使用と技術基準適合証明(技適)について
Zephyrはnrf52が公式にサポートしているNordicのSoftDeviceではなく、独自のBluetoothスタックを使ってBluetoothを飛ばします。これが無線モジュールの製造業者が工事設計認証を受けた時の検査に使用したBluetoothスタックと異なるものだった場合、製造業者が付与した技適マークが無効になるかもしれない、という懸念があるかもしれません。
Bluetoothスタックの実装によって結果が変わる試験項目にあるのか、技適取得時の何のBluetoothスタックを使ったのか、またそれ以外のBluetoothスタックを使用した場合は技適が無効になるのか、少し調べてみたのですが確かな情報を得ることはできませんでした。
未解決の問題について
今回、キーキャップを嵩上げすることによってDSAキーキャップがトッププレートに接触するのを防ぐことはできたのですが、現状の設計ではスタビライザーを使用できないという問題が残っています。今後の計画としては、インテグレーテッドトッププレートに直接スタビライザーのハウジングを造形して、MXスイッチ向けのスタビライザーのステムが刺さるようにしたいなぁ、と考えています。
今回の基板にはRGBLEDとして、WS2812-2020を使用しています。今までWS2812-2020はWS2812互換だと思っていて、実際QMK(のBitBang)では問題なく使用できていたのですが、ZMKではうまく制御することができず、ランダムにピカピカ光る状態になってしまいました…
ZMKではZephyrに実装されているWS2812のSPIドライバを使用しており、デフォルトの設定だとSPIの駆動周波数が4MHz (250ns / bit)、8bit毎に 0x70 (0b0111'0000) と 0x40 (0b0100'0000) でWS2812のHとLの1ビットを表現しています。よくよくデータシートを読んでみるとWS2812とWS2812-2020ではT0Hが220ns ~ 380nsとシビアで、SPI 4MHzではデータシートの条件を満たしたパルスを作ることはできません。そこで、SPIを8MHzにしてHを 0x7E (0b0111'1000)、Lを 0x60 (0b0110'0000) としてデータシートの条件を満たすようにしました。また、ZMKではWS2812では動くからという理由で、データシートでは250us以上必要なリセットディレイが8usに設定されてしまっている、このコミットをcherry-pickしてリセットディレイもデータシートに合わせました。
ただ、これらの変更を加えても状況は改善せず、現状はRGBLEDをオフにしています…
--
3Dプリントの材質についてのTips
DMM.makeでは3Dプリントに多くの材料を選ぶことができますが、個人的にはMJF PA12GBを非常におすすめします。HP社のMulti Jet Fusionは層ごとにナイロン粉体を選択的に熱で溶かして立体を成形する方式で、複雑な形状の立体を異方性がなく、実用に耐えられる強度で印刷することが可能です。
DMM.makeのPA12GB出力サンプルを手で曲げてみた限りでは、力をかけると1mm厚の部分は弾性変形の範囲を超えて歪みが残ってしまいますが、2mmの部分は全力で力をかけても折ることはできませんでした。PA11、PA12もそこまでPA12GBとは硬さに違いがありませんでしたが、ガラスビーズが添加されていない分少し柔らかく、折れる可能性はより低いと思われます。
PA12GB出力サンプルを手で曲げた図 |
ただ、MJFでも粉体を焼結するときに応力が発生するようで、造形時には歪みが発生します(異方性がないことと、歪みが発生することは別)。PA11で造形を頼んだ際には非常に歪みが出てしまい、まともに使用できないものができてしまったこともありました。
歪みが発生してしまったトッププレート |
PA12GBで印刷し直したトッププレート |
染色に関するTips
DMM.makeでは、100mm以上の物体は色むらが出るため、造形時のオプションでブラックは選べません。PA12GBは無地だと吸水性があるのと、見た目もそこまできれいなわけではないので、何らか色をつけたほうがきれいに見えると思います。
初めは塗装の条件を色々試してみてみたのですが、ナイロンは塗装しづらいのでプライマーが必須なのと、造形物に細かい凹凸があるからなのか、マットで塗装しても変な光沢が出てしましました。もちろん下処理をしたり何回も塗り重ねると良いのかもしれませんが、あまり手軽にできる作業ではありません…
そこで、もう少し手軽に色を付けられる工程として、染色を試してみることにしました。ナイロンは塗装はしにくいですが染色はしやすく、SDN染料というものを使うと簡単に染色できます。自分はこのページを参考に、SDNの説明書より濃い目の10倍希釈した溶液をジップロックに入れて、湯煎して70度に温めた後に造形物を投入、10分後に取り出しました。
ベランダでプラスチックを煮る図 |
SDN染料は非常に匂いが独特でキツいのと、こぼした時に床が黒く染まってしまう可能性があるので、屋外でやるのが良いと思います。もし万が一こぼしてはいけないところで熱い溶液をこぼしてしまった場合は、反応を止めるためにすぐに冷水で流すのが良いと思います(経験談)。
一番左がDMM.makeから届いたままのPA12GB無地、中央が軽く残留粉を落とした後にミッチャクロンマルチ、Mr.サーフェイサー500、アサヒペン水性多用途つや消し黒で塗装したもの、一番右がSDNの黒を10倍希釈したものを70度で湯煎して10分間つけた後に乾燥させたものです。
左からPA12GB無地、プラサフつや消し塗装、SDN10倍10分染色の結果 |
染色だと比較的お手軽にマット黒にできているのがわかると思います。ただし染色では積層痕は完全には消えないので造形時の線が残ってしまったり、少し匂いが残るので鼻を近づけるとSDNの匂いがします(半年たっても匂いは完全には消えませんでした)。また、同時にすべての部品を染色しないと色が変わってしまうという欠点もあります。
この4つのパーツはSDN染料を1本使用し、同一日に10倍希釈70度10分で順番に煮たものなのですが、明るい照明下で並べると違いが分かる程度には仕上がりの色が異なってしまっています。原因はSDN染料の濃度なのか温度/時間条件の少しの違いなのかはわかりませんが、完全に同一の色にしたいパーツがある場合には同時に煮るのが良いと思います。
同一条件で染色しても仕上がりの色は異なる |
ただ、そこまで見た目にこだわらないのであればMJF12GBをマットブラックにする際は染色がお手軽なのでおすすめです。
Minew MS88SF2のBootloaderについて
ZMKのバイナリは直接デバッガを使って焼くこともできるのですが、キーマップを書き換えるたびに一度分解してデバッグ用の端子にアクセスするのは現実的ではありません。そこで、UF2ブートローダーを使用してUSBストレージとしてOSに認識させて、UF2ファームウェアを書き込むようにするのが一般的だと思います。
今回はAdafruitのnRF52ブートローダーのボード設定を追加して、ボードに書き込みました。キーボードをUSBで接続した状態でリセットスイッチを連続して2回押し込むとブートローダーモードになり、認識されたUSBストレージにZMKのUF2ファイルを書き込むことでファームウェアの書き換えが可能になります。