Raspberry Piを使ってUSBキーボードの配列をremapするめも

Capslock?そいつなら1x年前にCtrlに置き換えたよ

私は日頃、キーボードのCapslockキーをCtrlキーにremapして使用しています。

最近はUSB切り替え器を使っていて、一組のマウスとキーボードをPCやRaspberry Piなど複数の機材で使用する機会も増えました。

機材ごとに都度キー設定するのは面倒だし、やんごとない理由でキー設定できない機材もあるし できればキーボード側でremapさせたいと思っていました。

ここで「自作キーボードつくるよ!」となるのが現在は主流でしょうが、私の場合は10年以上使い続けている今のキーボードに満足しているのでそのまま使用したいところです。

ということで、今回は既存キーボードをremapできるように手を加える方向で考えてみました。

紆余曲折(+オチ)があったのですが、結論だけまとめずに失敗含めてここに残しておこうと思います。

f:id:polaroidoon:20220304185154j:plain

キーボードとPCの間にremapするなにかを挟む

キーボードで押されたキーをremapしたキーに置き換えてPCに伝えることが目標となります。

今回はRaspberry Piを使って、remapさせることにしました。

イメージ
Keyboard --> Raspberry Pi ---(USB OTG)--> USBハブ --> USB切り替え器 --> PCやRaspberry Pi
                       ↑ココ

Raspberry Piに接続したキーボードをremapする…ちょうどピッタリのものを見つけました。

https://github.com/viggofalster/kiri

そこらへんに転がってるRaspberry Piに導入すれば簡単にできそうですね!さくっと作ってみましょう(フラグ)

失敗1 Raspberry Pi Zero Wで作るremapデバイス

自宅に転がっていたRaspberry Pi Zero Wを使って試してみることにしました。

で、さっそくキーボードを接続して…動かない。

そう、Raspberry Pi ZeroのUSBポートは以下の2つなのです。

  • 電源供給専用
  • USBデバイスを接続 / USB OTG(排他)

排他!

USBキーボード繋げたらPCにつなげるポートがありません。

おとなしくRaspberry Pi 4Bを購入することにしました。

失敗2 Raspberry Pi 4B で作ったが、効かないキーがある

Raspberry Pi 4Bが届いたところで作業再開です。

microSDカードはZero Wで設定したものをそのまま使用します。

ケースはThingiverseに公開されていたモデルを3Dプリントしました。 www.thingiverse.com

USBハブの電力ではRaspberry Piが動いてくれないので、信号と電源でケーブルを分けます。

USB-C Data/Power Splitterthepihut.com

Splitterのケースはいい感じに3Dプリントします。

基盤自体のデータはGrabCADで公開されていました。

grabcad.com

f:id:polaroidoon:20220311181059g:plain

あとはconfig.pyを設定して… 動きました。

満足感に浸りながらキーボードを使います。

そして気づきます。

「無変換」「変換」「¥」「\」キーが効かない!

失敗3 キーが効くぞ!(Windowsのみ)

動作確認に使っていたWindows PCで効かないキーのキーコードを確認しました。

www2d.biglobe.ne.jp

たとえば「¥」キーはキーコードが 89

次は kiri.py がキーボードとしてどのキーコードを出してるか確認してみましょう。

aキー

[2022-03-04 14:44:00,247|root|INFO] Writing report to output: 00:00:04:00:00:00:00:00

キー

[2022-03-04 14:44:03,320|root|INFO] Writing report to output: 00:00:c2:8b:00:00:00:00:00

…桁、長くね?

知らないキーコード c2 があるし、桁数変わってますね。

89UTF-8にすると 0xC2 0x89 になる。これが原因ですね。

コード全体を直す気がないので、 応急処置しました。

# git diff kiri.py
diff --git a/kiri.py b/kiri.py
index 9106237..629fb3d 100644
--- a/kiri.py
+++ b/kiri.py
@@ -131,9 +131,9 @@ class Kiri:
             self.update_state()

     def write_report(self, report: str):
-        self.log.debug('Writing report to output: %s', ":".join("{:02x}".format(c) for c in report.encode('utf-8')))
+        self.log.info('Writing report to output: %s', ":".join("{:02x}".format(c) for c in report.encode('latin-1')))
         with open('/dev/hidg0', 'rb+') as fd:
-            fd.write(report.encode())
+            fd.write(report.encode('latin-1'))

     @staticmethod
     def set_bit(value, bit):

無事Windowsで「無変換」「変換」「¥」「\」キーが効くようになりました!

さっそく普段使っているUbuntuのデスクトップマシンに接続してみましょう。

…やっぱり効かない。

解決編 キーが効くぞ!(WindowsUbuntuもOK)

コードの問題は解決したはずなのに効かない。

さきほどはコード kiri.py を疑いましたが、コードが悪いのか切り分けが必要ですね。

コードやキーボードを使わず、Raspberry Pi上で直接 /dev/hidg0 に流し込んでみます。

a

echo -ne "\0\0\x4\0\0\0\0\0" > /dev/hidg0
echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0

\

echo -ne "\0\0\x89\0\0\0\0\0" > /dev/hidg0
echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0

a しか効きませんね。

疑うべきはコードではなく…キーボードとして正しく認識されていない?という点です。

こんな例もあります。HIDクラスのディスクリプタの内容は正しいのでしょうか?

jtakao.web.fc2.com

キーボードを直接つないだときと、Raspberry Piをつないだときで比較してみました。

まず、直接キーボードを接続した場合。

$ lsusb
Bus 003 Device 006: ID 04d9:2011 Holtek Semiconductor, Inc. Keyboard [Diatec Filco Majestouch 1]

$ sudo usbhid-dump -e descriptor -a 003:006
003:006:000:DESCRIPTOR         1646550972.361890
 05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01
 75 01 95 08 81 02 95 01 75 08 81 01 95 03 75 01
 05 08 19 01 29 03 91 02 95 05 75 01 91 01 95 06
 75 08 26 FF 00 05 07 19 00 29 91 81 00 C0

内容を見やすくしてくれるサービスがありました。

eleccelerator.com

0x03, 0x06, 0x00, 0x05, 0x01,  // Unknown (bTag: 0x00, bType: 0x00)
0x09, 0x06,        // Usage (0x06)
0xA1, 0x01,        // Collection (Application)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0xE0,        //   Usage Minimum (0xE0)
0x29, 0xE7,        //   Usage Maximum (0xE7)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x01,        //   Logical Maximum (1)
0x75, 0x01,        //   Report Size (1)
0x95, 0x08,        //   Report Count (8)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //   Report Count (1)
0x75, 0x08,        //   Report Size (8)
0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x03,        //   Report Count (3)
0x75, 0x01,        //   Report Size (1)
0x05, 0x08,        //   Usage Page (LEDs)
0x19, 0x01,        //   Usage Minimum (Num Lock)
0x29, 0x03,        //   Usage Maximum (Scroll Lock)
0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x05,        //   Report Count (5)
0x75, 0x01,        //   Report Size (1)
0x91, 0x01,        //   Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x06,        //   Report Count (6)
0x75, 0x08,        //   Report Size (8)
0x26, 0xFF, 0x00,  //   Logical Maximum (255)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0x00,        //   Usage Minimum (0x00)
0x29, 0x91,        //   Usage Maximum (0x91)
0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              // End Collection

// 65 bytes

次にRaspberry Piです。

$ lsusb
Bus 003 Device 005: ID 1d6b:0104 Linux Foundation Multifunction Composite Gadget

$ sudo usbhid-dump -e descriptor -a 003:005
003:005:000:DESCRIPTOR         1646550112.843986
 05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01
 75 01 95 08 81 02 95 01 75 08 81 03 95 05 75 01
 05 08 19 01 29 05 91 02 95 01 75 03 91 03 95 06
 75 08 15 00 25 65 05 07 19 00 29 E7 81 00 C0
0x03, 0x05, 0x00, 0x05, 0x01,  // Unknown (bTag: 0x00, bType: 0x00)
0x09, 0x06,        // Usage (0x06)
0xA1, 0x01,        // Collection (Application)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0xE0,        //   Usage Minimum (0xE0)
0x29, 0xE7,        //   Usage Maximum (0xE7)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x01,        //   Logical Maximum (1)
0x75, 0x01,        //   Report Size (1)
0x95, 0x08,        //   Report Count (8)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //   Report Count (1)
0x75, 0x08,        //   Report Size (8)
0x81, 0x03,        //   Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x05,        //   Report Count (5)
0x75, 0x01,        //   Report Size (1)
0x05, 0x08,        //   Usage Page (LEDs)
0x19, 0x01,        //   Usage Minimum (Num Lock)
0x29, 0x05,        //   Usage Maximum (Kana)
0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x01,        //   Report Count (1)
0x75, 0x03,        //   Report Size (3)
0x91, 0x03,        //   Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x06,        //   Report Count (6)
0x75, 0x08,        //   Report Size (8)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x65,        //   Logical Maximum (101)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0x00,        //   Usage Minimum (0x00)
0x29, 0xE7,        //   Usage Maximum (0xE7)
0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              // End Collection

// 66 bytes

Logical Maximum の値が違いますね。

キーボード

0x26, 0xFF, 0x00,  //   Logical Maximum (255)

Raspberry Pi

0x25, 0x65,        //   Logical Maximum (101)

細かな違いを分析するのも面倒なので キーボードと同じディスクリプタを喋らせてしまいましょう。

kiri_usb を修正します。

# git diff kiri_usb
diff --git a/kiri_usb b/kiri_usb
index 848c52b..8b2f2c8 100755
--- a/kiri_usb
+++ b/kiri_usb
@@ -22,7 +22,7 @@ echo 1 > functions/hid.usb0/subclass
 echo 8 > functions/hid.usb0/report_length

 # usb descriptor equivalent to keybrd.hid seen with https://www.usb.org/document-library/hid-descriptor-tool
-echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.usb0/report_desc
+echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x01\\x95\\x03\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x03\\x91\\x02\\x95\\x05\\x75\\x01\\x91\\x01\\x95\\x06\\x75\\x08\\x26\\xff\\x00\\x05\\x07\\x19\\x00\\x29\\x91\\x81\\x00\\xc0 > functions/hid.usb0/report_desc

 ln -s functions/hid.usb0 configs/c.1/
 # End functions

ついにUbuntuでもキーが使えるようになりました!

結論

これ買えばよかった…(あとから教えていただきました)

booth.pm