最終更新日: 2016年3月11日
キーボード変換機を作る
Arduino Leonardoはキーボード、マウスといったHIDとして簡単に使う事ができるようになっています。これとUSB Host Shieldを使って、US配列のキーボードをJIS配列のキーボードに変換する変換機を作ってみました。
注:Arduino LLC(https://www.arduino.cc)よりリリースされている Arduino IDE Ver1.66 以降、USBデバイスに関するインプリメントが大きく変更されおり、本ドキュメントはそのままでは現状と異なりますのでご注意ください。このページからダウンロードできるスケッチやこのドキュメントはそれより前のバージョンのIDEを前提としており、現在のIDEではそのまま使用できません。
また、Arduino SRL(http://www.arduino.org)がリリースしているIDEでは検証していません。
なぜ?
どうでもいい事だけど…
きっかけは会社で通常使用するPCがノートPCに置き換わった事。自分は自宅でUS配列のキーボードを使っているので会社でもUS配列で使いたい。会社でデスクトップ機を使っていた時はレジストリを書き換えてUS配列キーボードを接続して使ってました。しかしノートPCは本体のキーボードがJIS配列であり、レジストリを書き換えると本体キーボードが使えなくなるので不便。ということで、ノートPCをデスクトップで使用する時はUS配列キーボードを用意し、それをJIS配列に変換してノートPCに接続…という魂胆です。
USB Host Shieldについて
キーボードを接続するためにはUSB Host Shieldが必用ですが、Circuits@Homeの商品を購入しました。国内ではUSB Host ShieldはSparkFunの物のほうが入手が容易だと思いますが、Circuits@HomeのものはSPI信号がICSP端子に接続するようになっているので、Arduino LeonardoのようにATMega328以外のデバイスを使用しているArduinoにも使えます。
また、SparkFunのものは接続するUSBデバイスの電源はArduinoに接続された外部電源から作られるようになっていますが(それはそれである意味メリットですが)、Circuits@HomeのものはArduinoの5V電源がそのまま使われるようになっています。なので(接続するUSBデバイスの消費電力によりますが)Arduino本体に外部電源を接続せずにUSBバスパワーで動かすことができます。
SparkFunのものでも、SPI信号を適切に配線すればLeonardoでも使用できるのではないかと思われます。
ライブラリ
USB Host ShieldのライブラリはCircuits@Homeのページからリンクされているgithubからダウンロードして使いました。かなり頻繁に更新されているようなので今後もそのまま使えるかどうか不明です。
Arduino側の書き換え
ArduinoのキーボードのAPIは「文字を送信する」といったような比較的高レベルのものですが、今回はUSBで実際に送信するバイト列をそのまま扱ったほうが便利です。しかしそのための関数はそのままでは使えません。というこでこれを書き換えます。
なお、この部分はArduino IDE Ver1.5.1を対象としています。
まずはHIDのAPIを編集。フィアルはhardware/aruduino/avr/cores/arduino/USBAPI.h。
この中に定義されているKeyboard_クラスの中のsendReportメソッドがprivateで宣言されています。これを使いたいので、この定義をpublicに移動させます。
class Keyboard_ : public Print { private: KeyReport _keyReport; public: Keyboard_(void); void begin(void); void end(void); void sendReport(KeyReport* keys); ←この行をpublicに移動させる virtual size_t write(uint8_t k); virtual size_t press(uint8_t k); virtual size_t release(uint8_t k); virtual void releaseAll(void); }; extern Keyboard_ Keyboard;
動作
まず、これらの動作のアイデアとして、MTF2012に出展されたキーボード変換機「はかどるん」での処理を参考にさせて頂きました。
基本的な動作としては、まずキー入力の情報を受け取ると、例えばCapsLockをControlに入れ替えるなど、キー単位の変換をします。そのためにShiftやControl等のモディファイヤキーのビットマップ情報を内部的なキーコードに変換し、キーの入れ替え処理の後にビットマップ情報に戻しています。
この処理の中で、とりあえずCapsLockと左Commandを左Controlに入れ替えてます。
また、テンキー部が押された場合、自動的にNumLockが押さるようにしています。キーが全て離された時には再度NumLockを押すようにしています。つまりテンキーで入力している瞬間だけNumLock状態になるようにしてます。これにより、外付けキーボードでテンキーが押されたら数字が入力され、ノートPC本体のキーボードを使う時にはNumLockが外れているという状態になり、便利です。
その後、USキーボードからJISキーボードのコードに変換しています。これはUSとJISとで各キャラクターのShiftキーの状態が異なるため完璧な変換は不可能です。とりあえず実用的に問題ないような処理を行ってます。具体的には、シフトキーが押され、かつキーコード変換を要するキーが押された場合、その後にシフトキーが先に離されたら、さらにシフト以外のキーが離されるまで待つようにしています。これにより、例えばUSキーボードで「@」(Shift+2)が入力され、2より先にShiftが離された場合に「2」が入力されてしまうことを抑制しています。
USB Host Shieldのライブラリは「必用なクラスのサブクラスを定義してUSBのイベント発生時に呼び出される仮想関数をサブクラスで定義する」という形で実装されています。イベント駆動型のプログラミングでは便利ですが、上記のような「キーが離されるまで待つ」という処理の実装には面倒です。そこで、仮想関数内では状態の変化だけを記録し、実際の処理は仮想関数の外で行うことで、イベント駆動型の処理とポーリング型の処理と両方使えるようにしました。
その他の動作として、キーボード変換のプログラマブルな実装、マクロなどが考えられますが、ArduinoがそもそもIDEさえインストールしておけば簡単にスケッチそのものを書き換えることができるので、それでいいかな、と思ってます。
が、ある程度の機能拡張を想定し、イベント駆動型のフレームを入れており、現段階では無用な処理がソース内に入ってます。
さらに不具合
このままでは、JISキーボードの「¥」に相当するキーのコードが送信されないという事がわかりました。これはまずい。どうやらそのままでは送信できるコードが限定されているようです。ということで以下のように書き換える事により送信されるようになりました。
ファイルはhardware/aruduino/avr/cores/arduino/HID.cpp、これの101行目です。
(98行目から) 0x95, 0x06, // REPORT_COUNT (6) 0x75, 0x08, // REPORT_SIZE (8) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x65, // LOGICAL_MAXIMUM (101) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) 0x29, 0xE7, // USAGE_MAXIMUM (Keyboard Application) 0x81, 0x00, // INPUT (Data,Ary,Abs) 0xc0, // END_COLLECTION
USAGE_MAXIMUMが0x65になっているので、これをUSBで定義されているUsage Tableの最大値であるE7に書き換えました。これで無事に¥も送信されるようになりました。LOGICAL MAXIMUMも書き換えるべきなのかな?
とりあえずこれで基本的な機能はOK、USキーボードがそのまま使えます。