USBガジェットモードになれないシングルボードコンピューターでもKVMをDIYしたい!
はじめに
というわけで、前回の続きです。
ohayoyogi.hatenablog.com
ぼくはTinkerBoardが大好きなんですが(色とか)、TinkerBoardの唯一の弱点はmicroSD端子をUSBホストとして使うことができず、ガジェットモードにもなれないという点です。
今回はそんな欠点を補うべく、Raspberry Pi Picoで知られるRP2040を使用して、マウスキーボードの操作を行えるようにしてみようという内容になります。
目論見
もくろみも何も、pikvm公式が提供している方法です。
Pico HID (USB, PS/2) - PiKVM Handbook
その名も 「pico HID」 。HIDとして振舞えないデバイス用に CDC, UART, SPI などの通信でHIDとして振舞っているRP2040を制御してしまおうという内容です。
単純に発想がすごい。ちなみに Arduino HID というのもあるらしい。
やったこと
ひとまず、お手軽なUART通信で、RP2040経由でキーをタイプしてみるというのをやってみます。(基本的にビルドするだけ)
普通にRaspberry Pi Picoを使ったのでは面白くないので、Raspberry Pi Picoと同様RP2040を搭載していて小型の「RP2040-Zero」を使ってみます。
RP2040-Zeroという小さいRaspberry Pi Picoもどき
ohayoyogi.hatenablog.com
自分はAliExpressで買いましたが、Amazonでも売ってるみたい
手順
というわけで早速、手順を紹介します。使用するソースは以下。 kvmdリポジトリの hid/pico というフォルダになります。
https://github.com/pikvm/kvmd/tree/master/hid/pico
開発環境としては、今回は手軽に試したかったので「GitHub codespaces」を使ってみました。手元にビルド用の環境を持っていないときに重宝しますね
結構Makefileが親切に書いてあるので、以下の手順程度でビルドできます。
sudo apt install cmake gcc-arm-none-eabi build-essential
cd ~/dev/kvmd/hid/pico
make
依存ツールを入れて、makeするだけ。すごいぜ
今回は本家 Raspberry Pi Pico ではなく RP2040-Zero というボードを使用するため、ピンの割り当てを変更しました。オプションで変えられるようにとかはなっていないので、ソースコードを直接いじります。
使えるピンなどはボードのピン配置のドキュメントを眺めたり、pico-sdkのヘッダを参照するといいのかなと思います。
https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_gpio/include/hardware/gpio.h
今回は「UART1」を使用する&使いやすそうなピン位置ということで、「ピン4(UART1_TX)」「ピン5(UART1_RX)」を使用することにしました。
というわけで、まずは hid/pico/src/ph_com_uart.c をいじります。
#define _RX_PIN 5
#define _TX_PIN 4
としてUARTにpin4, 5を使用するようにします。
またSPIで制御するかUARTで制御するかは排他となっていて、SPI制御を無効(UART制御を有効)にするには22ピンをLOWにしなければならないのですが、RP2040-Zeroでは22ピンへのアクセスが悪いので、(クソ修正ですが)HIGHで無効になるようにします。
https://github.com/pikvm/kvmd/blob/b3d12910395ec9785b1d86a46c902885b107718b/hid/pico/src/ph_com.c#L57
_use_spi = !gpio_get(_USE_SPI_PIN);
また、4ピン 5ピン はすでに _PS2_SET_MOUSE_PIN _BRIDGE_MODE_PIN で使用されているので、ここら辺をいじります。
https://github.com/pikvm/kvmd/blob/b3d12910395ec9785b1d86a46c902885b107718b/hid/pico/src/ph_outputs.c#L34-L38
それぞれ 19ピン 18ピン を使用するようにしました。
RP2040-Zeroで(とりあえず)使用できるようにするための変更はこのあたり。ここを使用するボードに応じて変更できると便利かなと思いますが・・・
変更したら make でビルドしてできあがった hid.uf2 を RP2040-Zero に転送して書き込み。
RP2040-Zeroから20ピン 21ピンを引っ張り出すのが楽か、ビルドしなおすのが楽かはみなさんの判断に任せます。
配線
変更したピンアサインの通りにRP2040-ZeroとUARTアダプタを配線しました。
RP2040-Zeroを配線
UART-USBアダプタももちろんAliExpressで購入しました。3.3Vでも5Vでも使えるみたいなので便利な商品です。Amazonでも結構安く売ってるけどこんなに個数いらない・・・
試しにUARTで制御してみた
できあがったpico(RP2040-Zero) hidに、実際にUARTで入力してキーを打ってみるコードを書きました。
import serial
def crc16(data : bytearray, offset , length):
polinom = 0xA001
crc = 0xFFFF
for i in range(0, length):
crc ^= data[offset + i]
for j in range(0,8):
if (crc & 0x0001) == 0:
crc = crc >> 1
else:
crc = crc >> 1
crc ^= polinom
return bytearray([(crc & 0xFF00) >> 8, crc & 0xFF])
def create_keypress_bytes(key, state):
send_bytes = [0x33, 0x11, key, state, 0xff, 0xff]
crc16_bytes = crc16(send_bytes, 0, 6)
send_bytes += crc16_bytes
return send_bytes
serial_port = serial.Serial(port='COM4', baudrate=115200)
serial_port.write(create_keypress_bytes(1,1))
print(serial_port.read(8).hex())
serial_port.write(create_keypress_bytes(1,0))
print(serial_port.read(8).hex())
create_keypress_bytes の第一引数の値を変えることでキーを変えることができます。今回は a(1) を打ちました。
→ 打てました(完)
終わりに
久しぶりのC言語だったのでいじり方思い出したりするのに時間がかかりましたが、ほぼほぼコードを修正せずとも動かせました。
これを作った後に発見したんですが、「UARTで制御してHIDでキー入力する」ことについては CH9329 という製品がすでにあるようですね。
kvmdとはUART経由の制御方法が異なるので、そのまま利用することはできないと思いますが、同じようなことは結構考えられているようですね。
次はTinkerBoardからこれを経由してパソコンをネットワーク越しに操作する、というのをやってみたいと思います。