1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Raspberry Pi Zero 2 W でキーボード入力 ~Bluetooth キーボード入力との統合~

Last updated at Posted at 2025-02-09

はじめに

本記事は、前回の記事 をベースに、Raspberry Pi Zero 2 WUSB Gadget (g_hid) として仮想キーボード化し、

  1. ネットワーク経由 (gRPC + Protobuf) で受信した文字列 をターゲットPCへ“キーボード入力”として送信
  2. Bluetooth キーボードからのキー入力(Modifierキーや複数キー同時押下を含む)もターゲットPCへ送信

同時に実現します。
さらに、Print ScreenScroll LockPause/Break などの特殊キーにも対応し、JIS配列向けとしてほぼ完成形の実装例を紹介します。
参考として、最後にUS配列向けのコード例も追加しています。
また、「Pi 側ではBluetooth キーボードからのキー入力を受け取りたくない(USB HIDへの入力のプロセス専用にする)」 場合の dev.grab() の使い方やプロセス終了時の動作なども解説します。

注: 本記事のサンプルスクリプトでは、Python の evdev ライブラリを使用します。
Raspberry Pi OS には標準でインストールされていない場合が多いため、事前に

sudo apt update && sudo apt install python3-evdev

あるいは

pip3 install evdev

などで導入してください。
また、実際に発生するキーコード (ecodes.KEY_???) はキーボードやOSにより微妙に異なる場合があるため、evtest などで実機確認し、必要に応じてマップを調整してください。


1. 前回の設定が済んでいれば

前回の記事で、

  • USB Gadget (g_hid) 設定: Pi Zero 2 W がターゲットPCから見て「USBキーボード」と認識される状態
  • gRPC サーバ: ネットワークで受け取った文字列 → /dev/hidg0 に書き込む仕組み

が既に整っている場合、本記事の Bluetooth キーボード入力スクリプト を追加するだけで、以下の2つの入力ソースが同時に /dev/hidg0 へ書き込めるようになります。

  1. ネットワーク入力 (gRPC)
  2. Bluetooth キーボード入力 (evdev)

ターゲットPC側から見ると、単一のUSBキーボードが同時に2種類の入力を送信してくるイメージです。
なお、本記事は前提として「BluetoothキーボードがPiに接続済み」である必要があります。


2. システム全体の概要

2.1 構成

以下の図は、システム全体を示しています。

  • A: ネットワーク越しの文字列を受け取り、1文字ずつキーコードに変換
  • B: Bluetooth キーボードの押下情報を evdev で取得
  • D: 各入力ソースを取りまとめ、ターゲットPCへ送るHIDレポートを生成
  • E: /dev/hidg0 に対して HID レポートを書き込み
  • F: ターゲットPCは「USBキーボード」として認識し、キー入力が反映される

2.2 メリット

  1. ネットワーク入力物理キーボード入力 をまとめて扱える
  2. USB OTG 機能のおかげで、ターゲットPC側に専用ドライバは不要(標準的なUSBキーボードとして動作)
  3. Bluetooth キーボードを Pi 側でも使えるし、必要に応じて Pi 側で受け取らず HID 専用にすることもできる

3. システム構築手順の要点

ステップ 内容 補足
1. USB Gadget 設定 (前回記事) /usr/local/bin/setup_hid_keyboard.sh などで Pi を「USBキーボード」に設定 ターゲットPCが Pi を USBキーボードとして認識する
2. gRPC サーバ (前回記事) ネットワークで文字列受信 → 1文字ずつ /dev/hidg0 に書き込む リモート入力をキーボード入力にエミュレート
3. Bluetooth 入力スクリプト evdev でキーイベントを取得 → HID レポート生成 → /dev/hidg0 に送る Modifierキーや複数同時押し、特殊キーにも対応 (本記事)
4. systemd サービス スクリプトを自動起動 Pi のブート時から常時キーボードを監視
5. (任意) dev.grab() Pi 側の入力を「奪って」HID専用にする grab を呼ばなければ Pi OS でも通常のキーボード入力として使え、呼ぶ場合は Pi OS への入力が遮断される
6. 競合対策 (必要に応じて) gRPC と Bluetooth が同時書き込みする場合、Lock やキューを検討 複数プロセスによる同時アクセスが想定される場合は、必ず排他ロック(例: Python の threading.Lock)を導入してください。

4. dev.grab() を使って Pi 側への入力を遮断する方法

4.1 デフォルト状態(grab なし)

通常は、Bluetooth キーボードを接続すると、Pi OS としても使え、同時に evdev スクリプトでも読み取れます。
この状態だと、Pi のコンソールやGUI操作にも影響し、キーボード入力として反映されます。

4.2 dev.grab() の動作

evdev の dev.grab() を呼び出すと、

  • そのプロセスが動作中の間、入力デバイスが排他ロック状態になり、Pi OS の標準入力には送られず、USB HID へのみレポートが出力されます。
dev = evdev.InputDevice("/dev/input/event3")
dev.grab()   # これを呼ぶと、Pi OS ではキー入力として認識されなくなります

注意: プロセスが終了するとファイルディスクリプタが閉じられ、
自動的に ungrab (アングラブ) されます。
そのため プロセス終了後は Pi OS が再び入力を受け取れる状態に戻ります

4.2.1 SSH への影響はない

SSH はあくまでネットワーク経由なので、grab しても SSH の動作には影響しません
Pi を操作する際は SSH を利用し、物理キーボードはUSB HID出力専用にする、といった使い分けが可能です。


5. Modifier+複数キー+特殊キー対応の実装例

以下は、Modifierキーや最大6キー同時押し、Print Screen/Scroll Lock/Pause なども含めた Python スクリプトの例です。
grab() を呼ぶかどうかは、コメントアウトの有無で制御できます。

補足:

  • キーコード (ecodes.KEY_???) は実機で evtest/proc/bus/input/devices を使用して確認してください。
  • 環境によっては ecodes.KEY_CARETecodes.KEY_AT の定義が異なる場合があるため、必要に応じてマップを調整してください。
  • また、time.sleep(0.005) の値は環境によって 0.01~0.02 秒程度に増やすと取りこぼしが減る場合があります。必要に応じて調整してください。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
JIS配列の物理キーボード入力 (evdev) を取得し、
/dev/hidg0 に対してHIDレポートを書き込むサンプル。

- Raspberry Pi Zero 2 W (g_hid でUSBキーボード化) などで利用。
- JIS配列を意識したKEYCODE_MAPを定義(ホストOSがJISキーボードとして認識している前提)。
- Modifierキー(Shift,Ctrl,Alt,Meta) や最大6キー同時押しに対応。
- Print Screen / Scroll Lock / Pause 等の特殊キーにも対応。
- CapsLockやNumLock、矢印キー、には未対応。
- 半角/全角キー、変換、無変換、カナなど日本語特有のキーも一部定義例あり(※実機要確認)。
"""

import time
import struct
import evdev
from evdev import ecodes

#=========================================================================
# 1. Modifierキー として扱うキーコード → ビットマスク
#=========================================================================
MODIFIER_MAP = {
    ecodes.KEY_LEFTCTRL:   0x01,
    ecodes.KEY_LEFTSHIFT:  0x02,
    ecodes.KEY_LEFTALT:    0x04,
    ecodes.KEY_LEFTMETA:   0x08,  # Windowsキー or Commandキー
    ecodes.KEY_RIGHTCTRL:  0x10,
    ecodes.KEY_RIGHTSHIFT: 0x20,
    ecodes.KEY_RIGHTALT:   0x40,  # AltGr
    ecodes.KEY_RIGHTMETA:  0x80,
}

#=========================================================================
# 2. 通常キー (JIS配列を想定した例)
#=========================================================================
KEYCODE_MAP = {
    #----------------------------------------------------
    # アルファベット A-Z
    #----------------------------------------------------
    ecodes.KEY_A: 0x04, ecodes.KEY_B: 0x05, ecodes.KEY_C: 0x06,
    ecodes.KEY_D: 0x07, ecodes.KEY_E: 0x08, ecodes.KEY_F: 0x09,
    ecodes.KEY_G: 0x0A, ecodes.KEY_H: 0x0B, ecodes.KEY_I: 0x0C,
    ecodes.KEY_J: 0x0D, ecodes.KEY_K: 0x0E, ecodes.KEY_L: 0x0F,
    ecodes.KEY_M: 0x10, ecodes.KEY_N: 0x11, ecodes.KEY_O: 0x12,
    ecodes.KEY_P: 0x13, ecodes.KEY_Q: 0x14, ecodes.KEY_R: 0x15,
    ecodes.KEY_S: 0x16, ecodes.KEY_T: 0x17, ecodes.KEY_U: 0x18,
    ecodes.KEY_V: 0x19, ecodes.KEY_W: 0x1A, ecodes.KEY_X: 0x1B,
    ecodes.KEY_Y: 0x1C, ecodes.KEY_Z: 0x1D,

    #----------------------------------------------------
    # 数字キー (上段)
    #----------------------------------------------------
    ecodes.KEY_1: 0x1E, ecodes.KEY_2: 0x1F, ecodes.KEY_3: 0x20,
    ecodes.KEY_4: 0x21, ecodes.KEY_5: 0x22, ecodes.KEY_6: 0x23,
    ecodes.KEY_7: 0x24, ecodes.KEY_8: 0x25, ecodes.KEY_9: 0x26,
    ecodes.KEY_0: 0x27,

    #----------------------------------------------------
    # 改行, バックスペース, Esc, Tab, Space
    #----------------------------------------------------
    ecodes.KEY_ENTER:      0x28,
    ecodes.KEY_ESC:        0x29,
    ecodes.KEY_BACKSPACE:  0x2A,
    ecodes.KEY_TAB:        0x2B,
    ecodes.KEY_SPACE:      0x2C,

    #----------------------------------------------------
    # 記号キー (JIS配列版)
    #----------------------------------------------------
    ecodes.KEY_MINUS:      0x2D,  # '-' (SHIFT時に '=')
    ecodes.KEY_CARET:      0x2E,  # '^' (SHIFT時に '~')
    ecodes.KEY_AT:         0x2F,  # '@' (SHIFT時に '`')
    ecodes.KEY_LEFTBRACE:  0x30,  # '[' (SHIFT時に '{')
    ecodes.KEY_YEN:        0x31,  # '¥' (SHIFT時に '|')
    ecodes.KEY_SEMICOLON:  0x33,  # ';' (SHIFT時に ':')
    ecodes.KEY_COLON:      0x34,  # 環境により未定義の場合も
    ecodes.KEY_GRAVE:      0x35,  # '`' など
    ecodes.KEY_COMMA:      0x36,  # ',' (SHIFTで '<')
    ecodes.KEY_DOT:        0x37,  # '.' (SHIFTで '>')
    ecodes.KEY_SLASH:      0x38,  # '/' (SHIFTで '?')

    #----------------------------------------------------
    # ファンクションキー
    #----------------------------------------------------
    ecodes.KEY_F1:  0x3A, ecodes.KEY_F2:  0x3B, ecodes.KEY_F3:  0x3C,
    ecodes.KEY_F4:  0x3D, ecodes.KEY_F5:  0x3E, ecodes.KEY_F6:  0x3F,
    ecodes.KEY_F7:  0x40, ecodes.KEY_F8:  0x41, ecodes.KEY_F9:  0x42,
    ecodes.KEY_F10: 0x43, ecodes.KEY_F11: 0x44, ecodes.KEY_F12: 0x45,

    #----------------------------------------------------
    # Print Screen / Scroll Lock / Pause
    #----------------------------------------------------
    ecodes.KEY_SYSRQ:      0x46,  # PrintScreen
    ecodes.KEY_SCROLLLOCK: 0x47,
    ecodes.KEY_PAUSE:      0x48,

    #----------------------------------------------------
    # 日本語特有キー (変換, 無変換, 全角/半角, カナ 等)
    #----------------------------------------------------
    ecodes.KEY_HENKAN:         0x8A,
    ecodes.KEY_MUHENKAN:       0x8B,
    ecodes.KEY_ZENKAKUHANKAKU: 0x91,
    ecodes.KEY_KATAKANA:       0x92,
}

#==============================================================================
# build_hid_report(modifier_byte, pressed_keys):
#   - modifier_byte: 1バイト (各ModifierキーのビットをORした値)
#   - pressed_keys:  同時押しキーのUsage ID (最大6個まで)
#   → 8バイトのHIDレポート (固定フォーマット) をbytesで返す
#==============================================================================
def build_hid_report(modifier_byte, pressed_keys):
    report = [0]*8
    report[0] = modifier_byte
    report[1] = 0x00

    idx = 2
    for usage_id in list(pressed_keys)[:6]:
        report[idx] = usage_id
        idx += 1

    return struct.pack('8B', *report)

def main():
    # ※注意: 接続されている入力デバイスのパスは環境により異なります。
    # ls /dev/input/event* や evtest でご使用のデバイスを確認してください。
    device_path = "/dev/input/event3"
    dev = evdev.InputDevice(device_path)

    print(f"Using input device: {device_path} ({dev.name})")
    # HID Gadget (ターゲットPCへ送る先)
    hidg_path = "/dev/hidg0"
    print(f"Writing HID reports to: {hidg_path}")

    # dev.grab() を呼ぶと、Pi 側への入力を遮断しUSB HID専用にできます。
    # dev.grab()

    pressed_keys = set()
    modifier_state = 0x00

    with open(hidg_path, "wb") as hid:
        for event in dev.read_loop():
            if event.type == ecodes.EV_KEY:
                # event.value: 0=key up, 1=key down, 2=key repeat
                if event.value not in (0,1):
                    continue
                is_pressed = (event.value == 1)

                if event.code in MODIFIER_MAP:
                    bitmask = MODIFIER_MAP[event.code]
                    if is_pressed:
                        modifier_state |= bitmask
                    else:
                        modifier_state &= ~bitmask
                elif event.code in KEYCODE_MAP:
                    usage_id = KEYCODE_MAP[event.code]
                    if is_pressed:
                        pressed_keys.add(usage_id)
                    else:
                        pressed_keys.discard(usage_id)

                report = build_hid_report(modifier_state, pressed_keys)
                hid.write(report)

                # 環境によっては 0.01~0.02 秒程度に増やすと安定する場合があります
                time.sleep(0.005)

if __name__ == "__main__":
    main()

6. systemd サービスで自動起動

このスクリプトを /usr/local/bin/bluetooth_input.py として保存し、

sudo chmod +x /usr/local/bin/bluetooth_input.py

を付与後、以下のような systemd サービスを作成してください。

sudo vi /etc/systemd/system/bluetooth-input.service
[Unit]
Description=Bluetooth Keyboard Input Service
After=hidgadget.service
Requires=hidgadget.service

[Service]
Type=simple
ExecStart=/usr/local/bin/bluetooth_input.py
Restart=always

[Install]
WantedBy=multi-user.target

次に、以下のコマンドでサービスを有効化・起動します。

sudo systemctl daemon-reload
sudo systemctl enable bluetooth-input.service
sudo systemctl start bluetooth-input.service

再起動(sudo reboot)後、Pi は自動でスクリプトを起動し、Bluetooth キーボード入力が /dev/hidg0 に書き込まれます。


7. gRPC サーバとの統合

前回記事で構築した gRPC サーバ(ネットワーク → /dev/hidg0)を同時に動かすと、以下2つが並行して /dev/hidg0 へ書き込みます。

  1. Bluetooth 入力スクリプト
  2. ネットワーク入力 (gRPC)

ターゲットPCに対しては単一のUSBキーボード入力として届くため、リモート文字列入力と物理キーボード入力が混在して送信されます。

7.1 競合対策

同時アクセスによりHIDレポートが上書きされ、キー入力が混ざる恐れがあります。必要に応じて以下のいずれかを導入する必要があります。(なお、今回は趣味レベルなので、gRPC クライアントから送るときは、必ず手を離してキーボードを押さない、という運用で対策しています。)

  • 別プロセスの場合: multiprocessing.Lock やファイルロック (fcntl) などを利用し、両方のプロセスが共有できる手段でロックを実装する。
  • 同一プロセスにした場合: threading.Lock などで /dev/hidg0 への書き込み区間を保護する。
  • 1プロセス統合: gRPC サーバと Bluetooth 入力を同一プロセス内で管理し、送信を直列化する
  • イベントキュー: 入力イベントをキューに貯め、順番に処理する

注意: 複数プロセスが並行して /dev/hidg0 に書き込む場合は、厳密なロックやキュー管理を行わないと誤動作の原因となります。


8. CPU 負荷は少ない

for event in dev.read_loop(): はブロッキング読み込みのため、キーイベントがないときは待機状態となり、CPU 使用率はほぼ0% です。
高頻度のキー操作があっても、time.sleep(0.005) により書き込みが制御され、CPU 負荷が急激に上がることはありません。


9. プロセス終了時に grab はどうなる?

dev.grab() は該当プロセスが開いている間のみ有効です。プロセス終了時にはファイルディスクリプタが閉じられ、

  • 自動的に ungrab (アングラブ) され、Pi OS が再び通常のキーボード入力として利用できるようになります。

SSH はネットワーク経由のため、この変更による影響はありません。


10. 「Pi 側での入力有無」を比較する表

以下の表は、本スクリプトにおいて dev.grab() を呼ぶ/呼ばないときの違いをまとめたものです。

項目 grab() 呼ばない grab() 呼ぶ
Pi 側でのキー操作 できる(通常のキーボードとして認識) できない(OSが入力を受け取らない)
USB HID 出力 可能(/dev/hidg0 への書き込みは常に行われる) 可能(/dev/hidg0 への書き込みは常に行われる)
Pi 側への影響 Pi のコンソールや GUI でもキー操作が可能 Pi は全くキーを受け取らない
プロセス終了後 変わらず Pi 側は通常利用できる ungrab され、Pi OS が再びキー入力を受け取る (プロセス動作中のみ grab 有効)
SSH への影響 特になし(SSH はネットワーク経由) 特になし(SSH はネットワーク経由)
用途 Pi 自身も操作したい、かつ USB HID に入力を送りたい場合 Pi を完全に操作対象外とし、USB HID 専用キーボードにしたい場合

11. 今後の拡張・注意点

  1. 日本語配列 (JIS) と実機確認
    • CapsLockやNumLock、矢印キー、に現状では未対応です
    • 本記事はJIS配列版として記述していますが、実際のキーボードやOSのレイアウト設定により、円マークや変換キー、無変換キー、カナキーなどのUsage IDが異なる場合があります。evtest 等でキーコードを確認し、必要に応じて調整してください。
  2. 追加の特殊キー・メディアキー
    • 音量、ミュートなどが必要な場合は、KEYCODE_MAP に対応するキーコードを追加してください。
  3. Nキーロールオーバー (NKRO)
    • 8バイトのブートプロトコルは最大6キー同時押しまでの仕様です。Nキー全面対応にはHIDレポート形式の拡張が必要です。
  4. エラーハンドリング
    • デバイス切断時の再接続など、長期間の運用時は例外処理を追加することを推奨します。
  5. US配列版との違い
    • JIS配列版とUS配列版では、特に記号キーのUsage IDが異なります。使用環境に合わせた調整が必要です(後述のUS配列版の例を参照)。

12. まとめ

  1. 前回の記事の設定(USB Gadget + gRPCサーバ)が完了していれば、本記事の Bluetooth 入力スクリプトを追加するだけで動作
    • ネットワーク入力 (gRPC) と Bluetooth 入力 (evdev) が同時に /dev/hidg0 へ書き込み可能
  2. Pi 側で入力を使うかどうかは dev.grab() の呼び出し次第
    • grab() を呼ばなければ、Pi OS 側でも通常のキーボード入力として使えます(併用可能)
    • grab() を呼ぶと、Pi OS への入力が遮断され、USB HID 出力専用になります(SSH は影響なし)
    • プロセス終了時に自動的に ungrab され、再び通常の入力が可能になります
  3. CPU 負荷は低い
    • read_loop() はキー入力がない間はブロッキング待機となり、time.sleep(0.005) により負荷が抑えられます
    • 環境により 0.01~0.02秒 に増やすと文字の取りこぼしが減ることがあります
  4. 競合時の排他制御
    • gRPC と Bluetooth 入力を同時運用する場合は、Lock や1プロセス統合などで衝突を防ぐ(趣味の範囲を超える場合は必須)
  5. JIS配列版のベースとしては、そこそこ完成しているが未対応のキーがある
    • Modifier+複数キー+特殊キー (PrintScreen/ScrollLock/Pause) に対応済み
    • CapsLockやNumLock、矢印キー、に未対応です
    • 日本語配列やメディアキー、NKRO対応などは用途に合わせて追加可能です

これにより、Raspberry Pi Zero 2 W は“USB キーボード”として多彩な入力ソースを統合でき、Pi 側での使用可否(grab)も柔軟に切り替えられます。


13. 参考リンク


参考 US配列の場合のPythonスクリプト例

#!/usr/bin/env python3
import evdev
import struct
import time
from evdev import ecodes

"""
Modifier(Shift,Ctrl,Alt,Meta) + 複数キー同時押し(最大6キー)対応のサンプル。
/dev/hidg0 へ8バイトのHIDレポートを送信し、ターゲットPCがキーボード入力として認識。
こちらはUS配列のキーコードマッピング例です。
Print Screen, Scroll Lock, Pause/Break にも対応。
"""

# 1) Modifier キーの evdev code → HID modifier ビット対応表
MODIFIER_MAP = {
    ecodes.KEY_LEFTCTRL:   0x01,  # 左Ctrl
    ecodes.KEY_LEFTSHIFT:  0x02,  # 左Shift
    ecodes.KEY_LEFTALT:    0x04,  # 左Alt
    ecodes.KEY_LEFTMETA:   0x08,  # 左Meta(Win)
    ecodes.KEY_RIGHTCTRL:  0x10,  # 右Ctrl
    ecodes.KEY_RIGHTSHIFT: 0x20,  # 右Shift
    ecodes.KEY_RIGHTALT:   0x40,  # 右Alt(AltGr)
    ecodes.KEY_RIGHTMETA:  0x80,  # 右Meta
}

# 2) 通常キーの evdev code → HID Usage ID 対応表(US配列)
KEYCODE_MAP = {
    # 文字キー (アルファベット)
    ecodes.KEY_A: 0x04, ecodes.KEY_B: 0x05, ecodes.KEY_C: 0x06,
    ecodes.KEY_D: 0x07, ecodes.KEY_E: 0x08, ecodes.KEY_F: 0x09,
    ecodes.KEY_G: 0x0A, ecodes.KEY_H: 0x0B, ecodes.KEY_I: 0x0C,
    ecodes.KEY_J: 0x0D, ecodes.KEY_K: 0x0E, ecodes.KEY_L: 0x0F,
    ecodes.KEY_M: 0x10, ecodes.KEY_N: 0x11, ecodes.KEY_O: 0x12,
    ecodes.KEY_P: 0x13, ecodes.KEY_Q: 0x14, ecodes.KEY_R: 0x15,
    ecodes.KEY_S: 0x16, ecodes.KEY_T: 0x17, ecodes.KEY_U: 0x18,
    ecodes.KEY_V: 0x19, ecodes.KEY_W: 0x1A, ecodes.KEY_X: 0x1B,
    ecodes.KEY_Y: 0x1C, ecodes.KEY_Z: 0x1D,

    # 数字キー (上段)
    ecodes.KEY_1: 0x1E, ecodes.KEY_2: 0x1F, ecodes.KEY_3: 0x20,
    ecodes.KEY_4: 0x21, ecodes.KEY_5: 0x22, ecodes.KEY_6: 0x23,
    ecodes.KEY_7: 0x24, ecodes.KEY_8: 0x25, ecodes.KEY_9: 0x26,
    ecodes.KEY_0: 0x27,

    # 記号・特殊キー
    ecodes.KEY_ENTER:     0x28,
    ecodes.KEY_ESC:       0x29,
    ecodes.KEY_BACKSPACE: 0x2A,
    ecodes.KEY_TAB:       0x2B,
    ecodes.KEY_SPACE:     0x2C,
    ecodes.KEY_MINUS:     0x2D,  # '-' or '_'
    ecodes.KEY_EQUAL:     0x2E,  # '=' or '+'
    ecodes.KEY_LEFTBRACE: 0x2F,  # '['
    ecodes.KEY_RIGHTBRACE:0x30,  # ']'
    ecodes.KEY_BACKSLASH: 0x31,  # '\'
    ecodes.KEY_SEMICOLON: 0x33,  # ';'
    ecodes.KEY_APOSTROPHE:0x34,  # '\''
    ecodes.KEY_GRAVE:     0x35,  # '`'
    ecodes.KEY_COMMA:     0x36,  # ','
    ecodes.KEY_DOT:       0x37,  # '.'
    ecodes.KEY_SLASH:     0x38,  # '/'

    # ファンクションキー
    ecodes.KEY_F1:  0x3A, ecodes.KEY_F2:  0x3B, ecodes.KEY_F3:  0x3C,
    ecodes.KEY_F4:  0x3D, ecodes.KEY_F5:  0x3E, ecodes.KEY_F6:  0x3F,
    ecodes.KEY_F7:  0x40, ecodes.KEY_F8:  0x41, ecodes.KEY_F9:  0x42,
    ecodes.KEY_F10: 0x43, ecodes.KEY_F11: 0x44, ecodes.KEY_F12: 0x45,

    # PrintScreen, ScrollLock, Pause
    ecodes.KEY_SYSRQ:      0x46,  # PrintScreen
    ecodes.KEY_SCROLLLOCK: 0x47,
    ecodes.KEY_PAUSE:      0x48,

}

def build_hid_report(modifier_byte, pressed_keys):
    report = [0]*8
    report[0] = modifier_byte
    report[1] = 0x00
    idx = 2
    for usage_id in list(pressed_keys)[:6]:
        report[idx] = usage_id
        idx += 1
    return struct.pack('8B', *report)

def main():
    device_path = "/dev/input/event3"
    dev = evdev.InputDevice(device_path)
    # dev.grab()  # Pi側への入力を遮断したい場合は有効化

    print(f"Bluetooth keyboard device: {device_path}")
    pressed_keys = set()
    modifier_state = 0x00

    with open("/dev/hidg0", "wb") as hid:
        for event in dev.read_loop():
            if event.type == ecodes.EV_KEY:
                if event.value not in (0,1):
                    continue
                pressed = (event.value == 1)

                if event.code in MODIFIER_MAP:
                    bitmask = MODIFIER_MAP[event.code]
                    if pressed:
                        modifier_state |= bitmask
                    else:
                        modifier_state &= ~bitmask
                elif event.code in KEYCODE_MAP:
                    usage_id = KEYCODE_MAP[event.code]
                    if pressed:
                        pressed_keys.add(usage_id)
                    else:
                        pressed_keys.discard(usage_id)

                report = build_hid_report(modifier_state, pressed_keys)
                hid.write(report)
                # 同様に 0.005秒を 0.01~0.02 に調整可能
                time.sleep(0.005)

if __name__ == "__main__":
    main()

以上

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?