概要
1年ほど前に、Pro Micro + mini USB Host Shield(以下、miniUHS)という構成にふつうのUSBキーボードとPCをつなぎ、USB キーボードの仲立ちをする話を書いた(Pro Microにmini USB Hostシールドを付けてUSBキーボードを接続する)。
今回はこのとき作ったスケッチをすこし拡張し、キー配列を変更できるアダプタにしてみた。
対象とするハードウェアは上の写真のように、Pro Micro (+3.3V, 8MHz動作)にminiUHSを組み合わせたものを使う。回路図や作り方などは Pro Micro + mini USB Hostシールドの二階建てバージョン(更新) と、上記のリンクとを参照のこと。
経緯
もともとの記事では、ふつうのUSBキーボードとPCの間に上記のハードウェアを挟んでも、ふつうのUSBキーボードとして使えますよ、というだけの話なのだけど、その後キー配列の入れ替えや同時打鍵処理の実装によって親指シフト化アダプタ(hoboNicolaアダプタ)の実現に至った。
hoboNicolaアダプタやUSBキーボード化したASKeyboardを毎日使っているがとても好調で満足してしまったから、キーボード周りの工作に対するモチベーションも低下しPro Microのことはすっかり忘れていた。
そんな中、ブログ記事を読んでくれた方から最初の記事の内容をちょっと膨らませて、単純なキーの入れ替えができるようなスケッチを作るためのヒントが欲しいというお手紙をいただいた。ご要望は以下の2点。
-
-
英数キーと左Ctrlキーを入れ替えたい。
-
あるいは、英数キーと H を押したらBackspaceキーを押したことにしたい。このとき、英数キーは単独では機能はしなくて結構。
-
項目1はよく聞く話であり、PCだったらキーコードを入れ替えるソフトはいくつもありそうである。ただ、PCでもスマホでもその他のホストでも、余計な手間をかけずに同じように使いたいのです、ということだった。
項目2については、Ctrlキーと英数キーを入れ替えた上で、各エディタやブラウザのキーバインドを揃えていけばいいのだろう、と思った。
アダプタあるいはキーボードの中で、CtrlキーやShiftキーを押したときだけ、一部の配列を変更していく処理は厄介である。Ctrlキーが押されているとき、いったんそれをオフにして文字キー側のコードを出力してその後またCtrlキーをオンにするとか、先にCtrlキーを離したらどうするのかとか、そのとき別のキーも押されたら…、とかややこしい話になる。
やり方の説明の代わりに、以下のような機能をもつスケッチを作ることにした。
-
キーコードを入れ替えるための変換テーブルをもつ。入力コードと出力コードをいずれもHID Usage IDで指定することでキーの配列を変更できる。入力側に英数キー、出力側に左Ctrlキーのコードを書いておけば、キーボードで英数キーを打ってもPCにはCtrlーオンが伝わるようになる。その逆の組み合わせも指定しておけば、キーの位置が入れ替わることになる。
-
アダプタ内部でローカルな修飾キー(Fnキーのようなキー)を定義する。英数キーを念頭に考えたので、このローカルなFnキーを「小指キー」と称することにした。
小指キーを表す内部コードを変換テーブルの出力側に指定することで、例えば英数キーを小指キーに割り付けることができる。小指キーの内部コードはPC側には何も影響しない(出力しないので)。 -
変換テーブルの入力コードに、小指キーがオンであることを条件として追加できる。小指キー + H で左矢印キーとか、小指キー + L で右矢印キーとか指定できる。とうぜんBackspaceやHome, Endなど、キーボードに存在するキーのコードは指定できる。
-
細かいところでは、小指キーの単独ストローク(小指キーを押し、他のキーを押さずに離す)では、変換テーブルの入力側のキーコードも出せること。ただ、編集操作の邪魔になるかもしれないから、単独ストロークでは何も出さないこともできる。
およそこういう要件で作ってみた。
スケッチについて
スケッチ一式については、後ろの方にダウンロードのリンクがあるので、そこから入手してください。
今回も、mini USB Host Shieldを介してUSBキーボードを利用するために USB Host Shield Library 2.0 (以下、UHSライブラリ)を利用する。前回の記事にあるように、UHSライブラリはGitHubに公開されている。また、Arduino IDEにライブラリマネージャを使って組み込むこともできる。
以前の記事内容と重複する部分もあるが、今回のスケッチでUHSライブラリを使ってキーボード入力を得る方法やコード変換のための仕組みなどをしょうしょう。
UHSライブラリからキー入力を受け取る
UHSライブラリを使ってキーボード入力を受け取る際には、ライブラリ内に定義されているKeyboardReportParser クラスを派生し、スケッチ側で各キーの押下/リリース通知 を受けとるためのメソッドを実装してやる、という流儀になっている。今回のスケッチでは、KeyboardEvent クラスがそれにあたる。
このとき、通常のキーと修飾キー(Ctrl, Shift, Alt, GUI(Win)の各キー)とで通知のされ方が異なっている。
通常のキー
OnKeyDown(uint8_t mod, uint8_t key);
キーの押下通知。
mod に修飾キー状態(ビット構成は後述)、keyにHID Usage ID(USBキーコード)。
OnKeyUp(uint8_t mod, uint8_t key);
キーのリリース通知。
引数はOnKeyDown()と同じ。
キーコードの入れ替えを行わないならば、各メソッドから直接report_press() または report_release() を呼び出してホスト側にキー入力の通知を行えばよかったのだけど、今回はコード変換のために用意した process_key() という関数を呼び出すようにした。
なお report_press() および report_release() では、構造体 KeyReport 内に押下中のキーの状態をセットまたはクリアし、その時点での修飾キーの状態をセットしてからキー入力通知用の関数 sendReport() を呼び出す。この呼出しにより、ホストにキー入力が通知される。
なお、修飾キーの押下/リリース時には対応するキーコードの通知はない。
修飾キー
OnControlKeysChanged(uint8_t before, uint8_t after);
修飾キーが変化したことの通知を受ける。beforeに変化前、afterに変化後(現在)の状態。beforeやafterは、UHSライブラリが生成しており、スケッチ内でも現在の状態として変数 modifiers に維持している。
単純に文字、記号、数字のみを受け取るならば、OnKeyDown() / …Up() だけを処理すればいいのだけど、今回のアダプタのようにホストとの仲立ちをするならば、たとえばGUIキー単独でスタートメニューを開いたり、Shiftキー連打でShiftロックするなどがあるから、OnControlKeyChanged() を受けたときも、modifiers を更新したうえでホスト側にキー入力通知(sendReport())を上げてやるようにしている。
修飾キーのコード化
このように修飾キー自体のコードは通知されないから、キーコードの入れ替えには都合が悪い。そのため、OnControlKeyChanged() で受けた修飾キー状態を分解し、内部的に以下のようなキーコードを与えるようにした。
1 2 3 4 5 6 7 8 9 |
// 修飾キーの内部キーコード static const uint8_t HID_LEFT_CONTROL = 0xe0; static const uint8_t HID_LEFT_SHIFT = 0xe1; static const uint8_t HID_LEFT_ALT = 0xe2; static const uint8_t HID_LEFT_GUI = 0xe3; static const uint8_t HID_RIGHT_CONTROL = 0xe4; static const uint8_t HID_RIGHT_SHIFT = 0xe5; static const uint8_t HID_RIGHT_ALT = 0xe6; static const uint8_t HID_RIGHT_GUI = 0xe7; |
OnControlKeyChanged()の引数には、以下のようなビット並びで修飾キーの押下状態 が通知されてくる(押されているキーのビットが1 ) 。上記の内部キーコードと合わせて書くと以下のようになる。
ビット | 内部キーコード | キーの名前 |
0 | 0xe0 | 左Ctrl |
1 | 0xe1 | 左Shift |
2 | 0xe2 | 左Alt |
3 | 0xe3 | 左GUI |
4 | 0xe4 | 右Ctrl |
5 | 0xe5 | 右Shift |
6 | 0xe6 | 右Alt |
7 | 0xe7 | 右GUI |
こんな具合なので、beforeとafterとで変化があるビットの位置を数値化し、0xe0に足し込んでコード生成している。そして生成したキーコードを通常キーと同様に process_key(); に渡してコード変換させている。
コード変換処理
void process_key(uint8_t key, bool pressed, bool pending = false);
-
- key : キーコード(HID Usage ID)。
- pressed : 押下からtrue, リリースなら false。
- pending : 修飾キー処理なら true。
今回の実装では、修飾キーや通常のキー(文字、数字、記号)に変化(押下またはリリース)があったとき process_key() という関数を呼び出しキーコードの変換を(指定されていれば)やらせるようにした。この関数では以下のような処理を行う。
- key が変換テーブルの入力キーと一致していなければ、そのまま出力キーして使う。
- key が変換テーブル内の入力キーと一致すれば、対応する出力キーを使う。小指キー修飾が指定されている場合もある。
- 出力キーが小指キーならば、小指キーの状態を維持する変数( local_mod_state ) を更新する。
- 出力キーが 0 となっているキーは取り捨てる(出力無し)。
- 出力キーが修飾キー( 0xe0~0xe7 )ならば、修飾キーの状態を維持する変数 ( modifiers ) を更新する。
- pending == trueならば、ホスト側にキー入力通知を行わない。modifiers の更新のみを意図したとき true としている。
小指キー
変換アダプタの中だけで有効な修飾キーは、今回のスケッチで以下のように定義した。
1 |
static const uint8_t LOCAL_MODIFIER = 0xf0; |
0xf0 は、UHSライブラリやHIDキーボードからは上がってこないはずのコードである。テーブルの出力側にこのコードが指定されていた場合、pressed ならば local_mod_state = 1; とし、!pressed ならば、local_mod_state = 0;として押下中状態を表現している。
たとえば、Appキーや右側のAltやCtrlキーもローカルな修飾キーとして使うこともできるだろう。今回は、複数の小指キーのサポートはスケッチが長くなるのでやめた。そのうち追加の予定だが、小指で操作するキーばかりではないので名前を変える必要がある。
変換テーブル
変換対象のキーは、以下の構造体 table_entry_t の配列にあらかじめ(スケッチレベルで)指定しておく。
1 2 3 4 |
typedef struct { uint16_t input; uint16_t output; } table_entry_t; |
input にはキーボードから上がってくる変換対象の入力キーを、output には変換後の出力キーを指定する。おのおのの下位8ビットがキーコードで、上位8ビットが小指キー情報などの修飾情報をもつ。今後NICOLA配列をもつhoboNicolaアダプタにも適用していく予定なので、修飾情報には余裕をもたせた。
変換テーブルの実体となる配列は、以下のようにSRAM内にNUM_TABLE_ENTRIES 個の要素をもつように宣言した。
1 2 |
static const int NUM_TABLE_ENTRIES = 50; table_entry_t table[NUM_TABLE_ENTRIES]; |
1要素あたり4バイト占めてしまうので、SRAMサイズが2.5Kbytes (0x100~0xaff) のProMicro (ATmega32u4) ではあまり大きくできないのだけど、200エントリくらいなら問題ないだろう。今回のスケッチでは実装例ということで50エントリにしている。
なお、変換テーブル用の配列をPROGMEM宣言してフラッシュメモリに置いていないのは、スケッチをビルドし直さなくても変換テーブルの内容を外部から更新できるようにするため。今回は話が長くなるので省略した。
今回の変換テーブル例
今回のスケッチでは、小指キーは英数キーとし、小指キーを押しながらDFBHJKLMキーを使うことで最小限のテキスト編集ができるようにしてみた。テキストエディタでもブラウザのフォーム上でも同じように操作できる。ついでにEscキーと半角/全角キーを入れ替えている。
入力 | 出力 | ||
名前 | コード | 名前 | コード |
英数(CapsLock) | HID_CAPSLOCK (0x39) | 小指キー | LOCAL_MODIFIER (0xf0) |
半角/全角 | HID_ZENHAN (0x35) | Esc | HID_ESCAPE (0x29) |
Esc | HID_ESCAPE (0x29) | 半角/全角 | HID_ZENHAN (0x35) |
D (小指) | HID_KEY_D (0x07) | Backspace | HID_BACKSPACE (0x2a) |
F (小指) | HID_KEY_F (0x09) | Page Down | HID_PAGE_DOWN (0x4e) |
B (小指) | HID_KEY_B (0x05) | Page Up | HID_PAGE_UP (0x4b) |
H (小指) | HID_KEY_H (0x0b) | ← | HID_LEFT_ARROW (0x50) |
J (小指) | HID_KEY_J (0x0d) | ↓ | HID_DOWN_ARROW (0x51) |
K (小指) | HID_KEY_K (0x0e) | ↑ | HID_UP_ARROW (0x52) |
L (小指) | HID_KEY_L (0x0f) | → | HID_RIGHT_ARROW (0x4f) |
M (小指) | HID_KEY_M (0x10) | Enter | HID_ENTER (0x28) |
なお、単純に英数キーと左Ctrlキーを入れ替えるだけならば、テーブル内容は以下のようになる。
1 2 3 4 5 |
table_entry_t table[NUM_TABLE_ENTRIES] = { {HID_CAPSLOCK, HID_LEFT_CONTROL}, {HID_LEFT_CONTROL, HID_CAPSLOCK}, { 0, 0 } }; |
テーブルの最後には、入力側が 0 の要素を指定する必要がある。
備考
- 表内の、HID_xxxx という表記は今回のスケッチ内で使っている定義であり一般性はない。
- 入力キーに (小指) と表記していあるキーは、小指キーを押しながら表内のキーを押したときにマッチすることを表しており、スケッチ内では、
1 2 3 |
... {WITH_LOCAL_MODIFIER | HID_KEY_D, HID_BACKSPACE}, ... |
のように表現している。
追記
スケッチ内のテーブルでは、小指キーを単独ストロークしたとき、小指キーの入力側キー(この例では英数キー)のストロークを出力する指定( LOCAL_MODIFIER_OUTPUT )とか、小指キーを押しながら変換テーブルの入力側に無いキーを押したときは何も出力しない ( LOCAL_MODIFIER_MAPPED_ONLY ) などの指定も含んでいる。
今後、たとえば小指+SでCtrl+S を出力するためなどの理由で、出力側の上位8ビットの使い方は変えるかもしれない。
また、今回の変換テーブル例は PROGMEMにおいた test_table1 をRAM上の table にコピーして使っているが、これはいくつかの変換テーブルを試した名残。
ビルド方法
ビルドには、Windows10 用のArduino IDE 1.8.10 (インストーラ版)を使った。長らくご無沙汰していたので最新版に更新したのだけど、いろいろなスケッチは問題なくビルドできた。
ソースの入手と必須項目
ソース一式
ソースファイルは、メインスケッチ( KeyCodeConverter1.ino)と、Arduino標準ライブラリに若干手を加えたHID.cppとHID.hの3本になっており、KeyCodeConverter1.zip として用意した。KeyCodeConverter1.zip はこちらからダウンロードできます。
ビルドする際には、Arduino IDEのスケッチディレクトリにKeyCodeConverter1というフォルタを作り、その中にzipの内容を展開してからArduino IDEを起動してやること。
HIDライブラリへの修正は、PCからのキーボードLEDレポートを処理できるようにしたもので、受け取ったLEDレポートはmini UHSライブラリに接続されたキーボードに渡せるようにしている。
今回のメインスケッチはGPL3でライセンスしています。今回のように、明示的にライセンス種別を示していない場合、ブログトップページに書いてある規約が適用されます。
Arduino IDEで準備しておくこと
以前のものと同様にArduino IDEでビルドする際には以下が必須になる。
- ボードマネージャにSparkFun社のPro Micro用アドオンを追加してあること (以下を参照 : https://learn.sparkfun.com/tutorials/pro-micro–fio-v3-hookup-guide#windows_boardaddon )
- USB Host Shield Library 2.0が導入済みであること。Arduino IDEのライブラリマネージャから追加できるはず。
参考資料
HIDキーボードが出力してくるキーコードについては、usb.org のHID Usage Tables 10/28/2004 Version 1.12 を参考にした。
きょうのまとめ
小指キーによる編集操作は案外いける。昔々ASKeyboardでダイヤモンドカーソルのエディタを使ったいたころ、小指が痛くなったことを思い出す。急にEnterキーが遠く感じるようになったので、小指+MをEnterに割り付けた。
今回のスケッチで変換テーブル内容を変更するためには、スケッチ内のテーブルを変更し、ビルドとフラッシュ書き込みが必要になる。およそ、ArduinoやPro Microを使っている人ならばこれで困らないだろう。ただ、いくつかの変換テーブルを切り替えて使うような場合、PCからテーブル内容を送り込めるような機能があったほうが便利だろう。
次回は今回のスケッチを拡張し、変換テーブル内容をUSBを介して送り込めるようにする予定です。