Raspy Zero WH でキー変換器を作る （その2）

# はじめに

[こちら](https://qiita.com/sushisushiBoo/items/820bfe2d02dea9e65faf)の続き

Raspberry Pi Zero Wに Thinkpad TracPoint Bluetooth Keyboardをつなげて、
RaspiをUSB Keyboardに見せて、キー入力を転送して、最終的には最終的にはキーの入れ替えをします。

# キー入力転送

USBのスキャンコードについては以下参照しました。
[http://www3.airnet.ne.jp/saka/hardware/keyboard/109scode.html](http://www3.airnet.ne.jp/saka/hardware/keyboard/109scode.html)

```keyboard.py 
from evdev import InputDevice, categorize, ecodes

# scancode
scancode = {}
scancode[ecodes.KEY_A] = 0x04
scancode[ecodes.KEY_B] = 0x05
scancode[ecodes.KEY_C] = 0x06
scancode[ecodes.KEY_D] = 0x07
scancode[ecodes.KEY_E] = 0x08
scancode[ecodes.KEY_F] = 0x09
scancode[ecodes.KEY_G] = 0x0a
scancode[ecodes.KEY_H] = 0x0b
scancode[ecodes.KEY_I] = 0x0c
scancode[ecodes.KEY_J] = 0x0d
scancode[ecodes.KEY_K] = 0x0e
scancode[ecodes.KEY_L] = 0x0f
scancode[ecodes.KEY_M] = 0x10
scancode[ecodes.KEY_N] = 0x11
scancode[ecodes.KEY_O] = 0x12
scancode[ecodes.KEY_P] = 0x13
scancode[ecodes.KEY_Q] = 0x14
scancode[ecodes.KEY_R] = 0x15
scancode[ecodes.KEY_S] = 0x16
scancode[ecodes.KEY_T] = 0x17
scancode[ecodes.KEY_U] = 0x18
scancode[ecodes.KEY_V] = 0x19
scancode[ecodes.KEY_W] = 0x1a
scancode[ecodes.KEY_X] = 0x1b
scancode[ecodes.KEY_Y] = 0x1c
scancode[ecodes.KEY_Z] = 0x1d
scancode[ecodes.KEY_1] = 0x1e
scancode[ecodes.KEY_2] = 0x1f
scancode[ecodes.KEY_3] = 0x20
scancode[ecodes.KEY_4] = 0x21
scancode[ecodes.KEY_5] = 0x22
scancode[ecodes.KEY_6] = 0x23
scancode[ecodes.KEY_7] = 0x24
scancode[ecodes.KEY_8] = 0x25
scancode[ecodes.KEY_9] = 0x26
scancode[ecodes.KEY_0] = 0x27
scancode[ecodes.KEY_ENTER] = 0x28
scancode[ecodes.KEY_ESC] = 0x29
scancode[ecodes.KEY_BACKSPACE] = 0x2a
scancode[ecodes.KEY_TAB] = 0x2b
scancode[ecodes.KEY_SPACE] = 0x2c
scancode[ecodes.KEY_MINUS] = 0x2d
scancode[ecodes.KEY_EQUAL] = 0x2e
scancode[ecodes.KEY_LEFTBRACE] = 0x2f
scancode[ecodes.KEY_RIGHTBRACE] = 0x30
scancode[ecodes.KEY_BACKSLASH] = 0x31
scancode[ecodes.KEY_SEMICOLON] = 0x33
scancode[ecodes.KEY_APOSTROPHE] = 0x34
scancode[ecodes.KEY_GRAVE] = 0x35
scancode[ecodes.KEY_COMMA] = 0x36
scancode[ecodes.KEY_DOT] = 0x37
scancode[ecodes.KEY_SLASH] = 0x38
scancode[ecodes.KEY_CAPSLOCK] = 0x39

scancode[ecodes.KEY_SYSRQ] = 0x46

scancode[ecodes.KEY_INSERT] = 0x49
scancode[ecodes.KEY_HOME] = 0x4a
scancode[ecodes.KEY_PAGEUP] = 0x4b
scancode[ecodes.KEY_DELETE] = 0x4c
scancode[ecodes.KEY_END] = 0x4d
scancode[ecodes.KEY_PAGEDOWN] = 0x4e
scancode[ecodes.KEY_RIGHT] = 0x4f
scancode[ecodes.KEY_LEFT] = 0x50
scancode[ecodes.KEY_DOWN] = 0x51
scancode[ecodes.KEY_UP] = 0x52

scancode[ecodes.KEY_RO] = 0x87
scancode[ecodes.KEY_KATAKANAHIRAGANA] = 0x88
scancode[ecodes.KEY_YEN] = 0x89
scancode[ecodes.KEY_HENKAN] = 0x8a
scancode[ecodes.KEY_MUHENKAN] = 0x8b

scancode[ecodes.KEY_LEFTMETA] = 0xe3

left_ctrl = ecodes.KEY_LEFTCTRL # convert ctrl key

def main():
	dev = InputDevice("/dev/input/ThinkPadTracPointKeyboard")
	print(dev)

	shift = 0
	ctrl = 0
	alt = 0
	for event in dev.read_loop():
		if event.type == ecodes.EV_KEY:
			buf = bytearray()
			# shift 
			if event.code == ecodes.KEY_RIGHTSHIFT or event.code == ecodes.KEY_LEFTSHIFT:
				if event.value == 1:
					shift = 1
				elif event.value == 0:
					shift = 0
			elif event.code == ecodes.KEY_RIGHTCTRL or event.code == ecodes.KEY_LEFTCTRL:
				if event.value == 1:
					ctrl = 1
				elif event.value == 0:
					ctrl = 0
			elif event.code == ecodes.KEY_RIGHTALT or event.code == ecodes.KEY_LEFTALT:
				if event.value == 1:
					alt = 1
				elif event.value == 0:
					alt = 0
			else:
				if event.code in scancode:
					if event.value == 1 or event.value == 2:
						code = scancode[event.code]
						modify = ctrl + (shift << 1) + (alt << 2)
						buf.extend([modify, 0, code])
					else:
						buf.extend([0, 0, 0])

					buf.extend([0, 0, 0, 0, 0])
	
					with open("/dev/hidg1", mode="wb") as keyboard:
						keyboard.write(buf)
						keyboard.close()
				else:
					print(categorize(event))

if __name__ == "__main__":
	main()
```
## Fnキーを常時有効にする（できない）

Thinkpad TracPoint Keyboardのファンクションキーはデフォルト機能に割り当たっているので
Fnキーを押しながらでないと有効でないようです。
今の所、解決方法はなさそうです。

# キー変換

## capslockとleft ctrlの入れ替え
capslockで左コントロールキー、左コントロールキーでcapslockになるようにします

## ESCと半角/全角の入替、無変換と変換に半角/全角を割り当て
これはMac的に使いたい好みの問題かな。

```keyboard.py 
from evdev import InputDevice, categorize, ecodes

# scancode
scancode = {}
scancode[ecodes.KEY_A] = 0x04
scancode[ecodes.KEY_B] = 0x05
scancode[ecodes.KEY_C] = 0x06
scancode[ecodes.KEY_D] = 0x07
scancode[ecodes.KEY_E] = 0x08
scancode[ecodes.KEY_F] = 0x09
scancode[ecodes.KEY_G] = 0x0a
scancode[ecodes.KEY_H] = 0x0b
scancode[ecodes.KEY_I] = 0x0c
scancode[ecodes.KEY_J] = 0x0d
scancode[ecodes.KEY_K] = 0x0e
scancode[ecodes.KEY_L] = 0x0f
scancode[ecodes.KEY_M] = 0x10
scancode[ecodes.KEY_N] = 0x11
scancode[ecodes.KEY_O] = 0x12
scancode[ecodes.KEY_P] = 0x13
scancode[ecodes.KEY_Q] = 0x14
scancode[ecodes.KEY_R] = 0x15
scancode[ecodes.KEY_S] = 0x16
scancode[ecodes.KEY_T] = 0x17
scancode[ecodes.KEY_U] = 0x18
scancode[ecodes.KEY_V] = 0x19
scancode[ecodes.KEY_W] = 0x1a
scancode[ecodes.KEY_X] = 0x1b
scancode[ecodes.KEY_Y] = 0x1c
scancode[ecodes.KEY_Z] = 0x1d
scancode[ecodes.KEY_1] = 0x1e
scancode[ecodes.KEY_2] = 0x1f
scancode[ecodes.KEY_3] = 0x20
scancode[ecodes.KEY_4] = 0x21
scancode[ecodes.KEY_5] = 0x22
scancode[ecodes.KEY_6] = 0x23
scancode[ecodes.KEY_7] = 0x24
scancode[ecodes.KEY_8] = 0x25
scancode[ecodes.KEY_9] = 0x26
scancode[ecodes.KEY_0] = 0x27
scancode[ecodes.KEY_ENTER] = 0x28
scancode[ecodes.KEY_ESC] = 0x35 # 0x29 # assing to grave
scancode[ecodes.KEY_BACKSPACE] = 0x2a
scancode[ecodes.KEY_TAB] = 0x2b
scancode[ecodes.KEY_SPACE] = 0x2c
scancode[ecodes.KEY_MINUS] = 0x2d
scancode[ecodes.KEY_EQUAL] = 0x2e
scancode[ecodes.KEY_LEFTBRACE] = 0x2f
scancode[ecodes.KEY_RIGHTBRACE] = 0x30
scancode[ecodes.KEY_BACKSLASH] = 0x31
scancode[ecodes.KEY_SEMICOLON] = 0x33
scancode[ecodes.KEY_APOSTROPHE] = 0x34
scancode[ecodes.KEY_GRAVE] = 0x29 # 0x35 # assign to esc
scancode[ecodes.KEY_COMMA] = 0x36
scancode[ecodes.KEY_DOT] = 0x37
scancode[ecodes.KEY_SLASH] = 0x38
scancode[ecodes.KEY_CAPSLOCK] = 0xE0 # 0x39 # assign to left-ctrl

scancode[ecodes.KEY_SYSRQ] = 0x46

scancode[ecodes.KEY_INSERT] = 0x49
scancode[ecodes.KEY_HOME] = 0x4a
scancode[ecodes.KEY_PAGEUP] = 0x4b
scancode[ecodes.KEY_DELETE] = 0x4c
scancode[ecodes.KEY_END] = 0x4d
scancode[ecodes.KEY_PAGEDOWN] = 0x4e
scancode[ecodes.KEY_RIGHT] = 0x4f
scancode[ecodes.KEY_LEFT] = 0x50
scancode[ecodes.KEY_DOWN] = 0x51
scancode[ecodes.KEY_UP] = 0x52

scancode[ecodes.KEY_RO] = 0x87
scancode[ecodes.KEY_KATAKANAHIRAGANA] = 0x88
scancode[ecodes.KEY_YEN] = 0x89
scancode[ecodes.KEY_HENKAN] = 0x35 # 0x8a
scancode[ecodes.KEY_MUHENKAN] = 0x35 # 0x8b

scancode[ecodes.KEY_LEFTCTRL] = 0x39 # 0xE0 # assign to capslock

scancode[ecodes.KEY_LEFTMETA] = 0xe3

left_ctrl = ecodes.KEY_CAPSLOCK # convert ctrl key

def main():
	dev = InputDevice("/dev/input/ThinkPadTracPointKeyboard")
	print(dev)

	shift = 0
	ctrl = 0
	alt = 0
	for event in dev.read_loop():
		if event.type == ecodes.EV_KEY:
			buf = bytearray()
			# shift 
			if event.code == ecodes.KEY_RIGHTSHIFT or event.code == ecodes.KEY_LEFTSHIFT:
				if event.value == 1:
					shift = 1
				elif event.value == 0:
					shift = 0
			elif event.code == ecodes.KEY_RIGHTCTRL or event.code == left_ctrl:
				if event.value == 1:
					ctrl = 1
				elif event.value == 0:
					ctrl = 0
			elif event.code == ecodes.KEY_RIGHTALT or event.code == ecodes.KEY_LEFTALT:
				if event.value == 1:
					alt = 1
				elif event.value == 0:
					alt = 0
			else:
				if event.code in scancode:
					if event.value == 1 or event.value == 2:
						code = scancode[event.code]
						modify = ctrl + (shift << 1) + (alt << 2)
						buf.extend([modify, 0, code])
					else:
						buf.extend([0, 0, 0])

					buf.extend([0, 0, 0, 0, 0])
	
					with open("/dev/hidg1", mode="wb") as keyboard:
						keyboard.write(buf)
						keyboard.close()
				else:
					print(categorize(event))

if __name__ == "__main__":
	main()
```

# 自動起動を有効にする

## raspiの起動時にUSBキーボードを有効にする

rc.localに/home/pi/hid_test/hid.shを追加

→デバイスの有効になるタイミングが良く無いのかhost側の認識がうまく行かないので、Bluetoothのキーボード認識時に行うことにする

## udev rulesでスクリプトを起動する

うまく行かない。
udevでRUNするものはdaemonのようにずっと起動するものはダメらしい。

ー＞rc.localでスクリプトを起動しておいて、bluetoothキーボード接続をポーリングするようにする

```/etc/udev/rulesd/99-tracpoint-keyboard.rules
ACTION=="add" ATTRS{name}=="ThinkPad Compact Bluetooth Keyboard with TrackPoint Keyboard", RUN+="/home/pi/hid_test/keyboardFoundMessage.sh", SYMLINK+="input/ThinkPadTracPointKeyboard"
ACTION=="add" ATTRS{name}=="ThinkPad Compact Bluetooth Keyboard with TrackPoint Mouse", RUN+="/home/pi/hid_test/mouseFoundMessage.sh", SYMLINK+="input/ThinkPadTracPointKeyboardMouse"
```

```/etc/rc.local
/home/pi/hid_test/mouse.sh
/home/pi/hid_test/keyboard.sh
```

```mouse.sh
#!/bin/sh

DEVICE=/dev/input/ThinkPadTracPointKeyboardMouse
while [ ! -e $DEVICE ]
do
   sleep 1
done

echo Thinkpad Tracpoint Bluetooth Keybord Tracpoint Connected
if [ -e /dev/hidg1 ]; then
   echo HID device already started
else
    /home/pi/hid_test/hid.sh
fi
MOUSE_PID=/var/run/mouse.pid
MOUSE=/home/pi/hid_test/mouse.py
$MOUSE &
echo $! > $MOUSE_PID
```

```keyboard.sh
#!/bin/sh
DEVICE=/dev/input/ThinkPadTracPointKeyboard
while [ ! -e $DEVICE ]
do
   sleep 1
done

echo Thinkpad Tracpoint Bluetooth Keybord Connected
if [  -e /dev/hidg0 ]; then
   echo HID device already started
else
    /home/pi/hid_test/hid.sh
fi
KEYBOARD_PID=/var/run/keyboard.pid
KEYBOARD=/home/pi/hid_test/keyboard.py
$KEYBOARD &
echo $! > $KEYBOARD_PID
```

```mouse.py
#!/usr/bin/env python

from evdev import InputDevice, categorize, ecodes

device = '/dev/input/ThinkPadTracPointKeyboardMouse'
dev = InputDevice(device)
print(dev)

for event in dev.read_loop():
	buf = bytearray()
	if event.type == ecodes.EV_REL:
		if event.code == ecodes.REL_X:
			#print('REL_X:{}'. format(event.value))
			if 0 < event.value:
				buf.extend([0, event.value, 0])
			else:
				buf.extend([0, 0xFF + event.value, 0])
		elif event.code == ecodes.REL_Y:
			#print('REL_Y:{}'.format(event.value))
			if 0 < event.value:
				buf.extend([0, 0, event.value])
			else:
				buf.extend([0, 0, 0xFF + event.value])
		#if event.code == ecodes.REL_WHEEL:
		#print('REL_WHEEL:{}'.format(event.value))
	elif event.type == ecodes.EV_KEY:
		if event.code == ecodes.BTN_LEFT:
			#print('BTN_LEFT:{}'.format(event.value))
			buf.extend([1 * event.value, 0, 0])
		elif event.code == ecodes.BTN_RIGHT:
			#print('BTN_RIGHT:{}'.format(event.value))
			buf.extend([2 * event.value, 0, 0])
		if event.code == ecodes.BTN_MIDDLE:
			#print('BTN_MIDDLE:{}'.format(event.value))
			buf.extend([3 * event.value, 0, 0])

	with open("/dev/hidg0", mode="wb") as mouse:
		mouse.write(buf)
		mouse.close()
```

```keyboard.py
#!/usr/bin/env python
from evdev import InputDevice, categorize, ecodes

# scancode
scancode = {}
scancode[ecodes.KEY_A] = 0x04
scancode[ecodes.KEY_B] = 0x05
scancode[ecodes.KEY_C] = 0x06
scancode[ecodes.KEY_D] = 0x07
scancode[ecodes.KEY_E] = 0x08
scancode[ecodes.KEY_F] = 0x09
scancode[ecodes.KEY_G] = 0x0a
scancode[ecodes.KEY_H] = 0x0b
scancode[ecodes.KEY_I] = 0x0c
scancode[ecodes.KEY_J] = 0x0d
scancode[ecodes.KEY_K] = 0x0e
scancode[ecodes.KEY_L] = 0x0f
scancode[ecodes.KEY_M] = 0x10
scancode[ecodes.KEY_N] = 0x11
scancode[ecodes.KEY_O] = 0x12
scancode[ecodes.KEY_P] = 0x13
scancode[ecodes.KEY_Q] = 0x14
scancode[ecodes.KEY_R] = 0x15
scancode[ecodes.KEY_S] = 0x16
scancode[ecodes.KEY_T] = 0x17
scancode[ecodes.KEY_U] = 0x18
scancode[ecodes.KEY_V] = 0x19
scancode[ecodes.KEY_W] = 0x1a
scancode[ecodes.KEY_X] = 0x1b
scancode[ecodes.KEY_Y] = 0x1c
scancode[ecodes.KEY_Z] = 0x1d
scancode[ecodes.KEY_1] = 0x1e
scancode[ecodes.KEY_2] = 0x1f
scancode[ecodes.KEY_3] = 0x20
scancode[ecodes.KEY_4] = 0x21
scancode[ecodes.KEY_5] = 0x22
scancode[ecodes.KEY_6] = 0x23
scancode[ecodes.KEY_7] = 0x24
scancode[ecodes.KEY_8] = 0x25
scancode[ecodes.KEY_9] = 0x26
scancode[ecodes.KEY_0] = 0x27
scancode[ecodes.KEY_ENTER] = 0x28
scancode[ecodes.KEY_ESC] = 0x35 # 0x29 # assing to grave
scancode[ecodes.KEY_BACKSPACE] = 0x2a
scancode[ecodes.KEY_TAB] = 0x2b
scancode[ecodes.KEY_SPACE] = 0x2c
scancode[ecodes.KEY_MINUS] = 0x2d
scancode[ecodes.KEY_EQUAL] = 0x2e
scancode[ecodes.KEY_LEFTBRACE] = 0x2f
scancode[ecodes.KEY_RIGHTBRACE] = 0x30
scancode[ecodes.KEY_BACKSLASH] = 0x31
scancode[ecodes.KEY_SEMICOLON] = 0x33
scancode[ecodes.KEY_APOSTROPHE] = 0x34
scancode[ecodes.KEY_GRAVE] = 0x29 # 0x35 # assign to esc
scancode[ecodes.KEY_COMMA] = 0x36
scancode[ecodes.KEY_DOT] = 0x37
scancode[ecodes.KEY_SLASH] = 0x38
scancode[ecodes.KEY_CAPSLOCK] = 0xE0 # 0x39 # assign to left-ctrl

scancode[ecodes.KEY_SYSRQ] = 0x46

scancode[ecodes.KEY_INSERT] = 0x49
scancode[ecodes.KEY_HOME] = 0x4a
scancode[ecodes.KEY_PAGEUP] = 0x4b
scancode[ecodes.KEY_DELETE] = 0x4c
scancode[ecodes.KEY_END] = 0x4d
scancode[ecodes.KEY_PAGEDOWN] = 0x4e
scancode[ecodes.KEY_RIGHT] = 0x4f
scancode[ecodes.KEY_LEFT] = 0x50
scancode[ecodes.KEY_DOWN] = 0x51
scancode[ecodes.KEY_UP] = 0x52

scancode[ecodes.KEY_RO] = 0x87
scancode[ecodes.KEY_KATAKANAHIRAGANA] = 0x88
scancode[ecodes.KEY_YEN] = 0x89
scancode[ecodes.KEY_HENKAN] = 0x35 # 0x8a
scancode[ecodes.KEY_MUHENKAN] = 0x35 # 0x8b

scancode[ecodes.KEY_LEFTCTRL] = 0x39 # 0xE0 # assign to capslock

scancode[ecodes.KEY_LEFTMETA] = 0xe3

left_ctrl = ecodes.KEY_CAPSLOCK # convert ctrl key

def main():
	device="/dev/input/ThinkPadTracPointKeyboard"
	dev = InputDevice(device)
	print(dev)

	shift = 0
	ctrl = 0
	alt = 0
	for event in dev.read_loop():
		if event.type == ecodes.EV_KEY:
			buf = bytearray()
			# shift 
			if event.code == ecodes.KEY_RIGHTSHIFT or event.code == ecodes.KEY_LEFTSHIFT:
				if event.value == 1:
					shift = 1
				elif event.value == 0:
					shift = 0
			elif event.code == ecodes.KEY_RIGHTCTRL or event.code == left_ctrl:
				if event.value == 1:
					ctrl = 1
				elif event.value == 0:
					ctrl = 0
			elif event.code == ecodes.KEY_RIGHTALT or event.code == ecodes.KEY_LEFTALT:
				if event.value == 1:
					alt = 1
				elif event.value == 0:
					alt = 0
			else:
				if event.code in scancode:
					if event.value == 1 or event.value == 2:
						code = scancode[event.code]
						modify = ctrl + (shift << 1) + (alt << 2)
						buf.extend([modify, 0, code])
					else:
						buf.extend([0, 0, 0])

					buf.extend([0, 0, 0, 0, 0])
	
					with open("/dev/hidg1", mode="wb") as keyboard:
						keyboard.write(buf)
						keyboard.close()
				else:
					print(categorize(event))

if __name__ == "__main__":
	main()
```

#　今後やりたいこ

tracpoint keyboardはスクロールができるのでマウスにスクロールを追加したい