Key Mime Pi: Raspberry Pi를 원격 키보드로 바꾸기
최근 버전의 Raspberry Pi는 USB 온더고(USB OTG)를 지원하므로 키보드, USB 메모리, 마이크 같은 USB 장치로 동작하도록 흉내 낼 수 있습니다. 이 기능을 활용해 Raspberry Pi를 가짜 키보드로 바꿔 주는 오픈 소스 웹 앱을 만들었습니다. 이름은 Key Mime Pi입니다.
이 글에서는 Key Mime Pi가 어떻게 작동하는지, 그리고 직접 만드는 방법을 보여 드립니다.
데모
준비물
- USB OTG를 지원하는 Raspberry Pi:
- Raspberry Pi 4(모든 변형 모델)
- Raspberry Pi Zero W
- Raspberry Pi A 및 A+ (확인 필요)
- 이 자료에 따르면 초기 Raspberry Pi도 USB OTG를 지원하지만, 이 장치들은 직접 테스트하지 않았습니다.
- Raspberry Pi OS(Raspbian이라고도 함)
- Stretch 이상
- USB 케이블
- Pi 4용: USB-C-USB-A(수-수)
- Pi Zero W용: Micro-USB-USB-A(수-수)
Raspberry Pi OS Lite 설치
먼저 microSD 카드에 Raspberry Pi OS Lite(이전 이름은 Raspbian)를 설치합니다.

저는 Pi용 microSD 카드에 이미지를 기록할 때 Rufus를 사용하지만, 디스크 전체 이미지를 기록할 수 있는 도구라면 무엇이든 괜찮습니다.
microSD 카드의 부팅 파티션에 ssh라는 이름의 파일을 만들어 SSH 접속을 활성화한 뒤, microSD 카드를 Pi 장치에 삽입합니다. 무선으로 연결하려면 부팅 파티션에 wpa_supplicant.conf 파일도 만들어야 합니다.
Pi 연결
USB 케이블을 Pi의 USB OTG 포트에 연결합니다. Pi 4에서는 USB-C 포트입니다. Pi Zero에서는 “USB”라고 표시된 Micro-USB 포트입니다.


Raspberry Pi 4(왼쪽)는 USB-C 포트에 연결합니다. Raspberry Pi Zero W(오른쪽)는 Micro-USB 데이터 포트에 연결합니다.
USB 케이블의 반대쪽을 키보드로 연결하려는 컴퓨터에 연결합니다. USB 3.0 포트는 더 많은 전력을 출력하므로 더 잘 작동하지만, 제가 테스트한 USB 2.0 포트도 모두 문제없이 작동했습니다.
Pi는 컴퓨터의 USB 포트에서 전원을 공급받아 부팅될 것입니다.
Key Mime Pi 설치
Key Mime Pi를 설치하는 방법은 두 가지입니다. 외부 도구가 필요 없는 평범한 bash를 사용할 수 있습니다. 또는 제가 가장 좋아하는 오픈 소스 구성 관리 도구인 Ansible을 사용해 조금 더 세련되게 설치할 수도 있습니다.
방법 1: 순수 bash 방식
bash 셸에서 다음 명령을 입력해 Pi에 연결하고 USB 장치 에뮬레이션을 설정합니다.
# SSH into your Pi
PI_HOSTNAME="raspberrypi" # Change to your pi's hostname
PI_SSH_USERNAME="pi" # Change to your Pi username
# Connect to the Pi (default password is "raspberry")
ssh "${PI_SSH_USERNAME}@${PI_HOSTNAME}"
# Install pre-requisites
sudo apt-get update && \
sudo apt-get install -y \
git \
python-pip \
python3-venv
# Install Key Mime Pi
git clone https://github.com/mtlynch/key-mime-pi.git
cd key-mime-pi
sudo ./enable-usb-hid
sudo rebootPi가 재부팅될 때까지 기다린 다음, 다시 SSH로 접속해 Key Mime Pi 웹 서버를 시작합니다.
ssh "${PI_SSH_USERNAME}@${PI_HOSTNAME}"
cd key-mime-pi
python3 -m venv venv
. venv/bin/activate
pip install --requirement requirements.txt
PORT=8000 ./app/main.py방법 2: Ansible 방식
Ansible 사용자라면 제 Key Mime Pi Ansible role을 사용해 더 자동화된 방식으로 설치할 수 있습니다. 다음 명령은 Key Mime Pi를 systemd 서비스로 장치에 설치합니다.
PI_HOSTNAME="raspberrypi" # Change to your pi's hostname
PI_SSH_USERNAME="pi" # Change to your Pi username
# Install the Key Mime Pi Ansible role
ansible-galaxy install mtlynch.keymimepi
# Create a minimal Ansible playbook to configure your Pi
echo "- hosts: $PI_HOSTNAME
roles:
- role: mtlynch.keymimepi" > install.yml
# Install all software (default password is "raspberry")
ansible-playbook \
--inventory "$PI_HOSTNAME", \
--user "$PI_SSH_USERNAME" \
--become \
--become-method sudo \
--ask-pass \
install.yml
# Reboot the Pi
ansible \
"$PI_HOSTNAME" \
-m reboot \
--inventory "$PI_HOSTNAME", \
--user "$PI_SSH_USERNAME" \
--ask-pass \
--become \
--become-method sudoKey Mime Pi 사용
설치 스크립트를 실행하면 다음 주소에서 Key Mime Pi를 사용할 수 있습니다.
인터페이스는 다음과 같습니다.

입력을 기다리는 Key Mime Pi 웹 인터페이스
이제 정말 마법처럼, 브라우저에 입력하면 Pi에 연결된 컴퓨터에 키 입력이 나타납니다.

Key Mime Pi를 사용하면 브라우저를 통해 원격 컴퓨터로 키 입력을 보낼 수 있습니다.
작동 원리
USB 장치 에뮬레이션
여기서 진짜 마법은 Linux의 USB 휴먼 인터페이스 장치(HID) 가젯 드라이버에서 나옵니다. 이 드라이버를 사용하면 사용자 모드 애플리케이션이 USB 장치인 것처럼 운영 체제와 상호작용할 수 있습니다.
key-mime-pi 구성 스크립트는 /dev/hidg0 경로를 만듭니다. 어떤 프로그램이든 이 경로를 읽거나 쓸 수 있고, 운영 체제는 데이터를 키보드 신호로 변환합니다.
키보드를 흉내 내려면 Pi가 USB HID 사양에 따라 운영 체제와 통신해야 합니다. 키 코드와 표가 97페이지나 되는 문서라 읽기가 꽤 고역이지만, 키보드 프로토콜 자체는 놀라울 만큼 간단합니다.
키를 누를 때마다 키보드는 ‘리포트’라고 하는 8바이트 메시지를 보냅니다.
| 바이트 인덱스 | 용도 |
|---|---|
| 0 | 수정 키(Ctrl, Alt, Shift) |
| 1 | 제조업체용 예약 영역 |
| 2 | 키 1 |
| 3 | 키 2 |
| 4 | 키 3 |
| 5 | 키 4 |
| 6 | 키 5 |
| 7 | 키 6 |
“Hi”에 해당하는 키를 보내면 다음과 같습니다.
# H (Right shift + h)
echo -ne "\x20\0\xb\0\0\0\0\0" > /dev/hidg0
# i
echo -ne "\0\0\xc\0\0\0\0\0" > /dev/hidg0
# Release all keys
echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0키를 누르는 신호뿐 아니라 키를 놓았다는 신호도 보내야 합니다. 0으로만 이루어진 8바이트 블록은 활성화된 키가 없다는 뜻입니다.
위 예제에서는 한 번에 하나의 키 입력을 보냈지만, HID 리포트에는 키를 위한 공간이 6개 있습니다. 따라서 서로 다른 키라면 하나의 메시지에 최대 6개의 키 입력을 담아 보낼 수 있습니다.
echo -ne "\0\0\x1a\x0b\x04\x17\x18\x13" > /dev/hidg0 && \
echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0whatupJavaScript를 HID로 변환하기
브라우저 창에 입력하면 JavaScript가 키를 누를 때마다 이벤트를 생성합니다. keycode.info 웹사이트에서 이 기능이 실제로 작동하는 모습을 훌륭하게 확인할 수 있습니다.
JavaScript 키 이벤트에는 키 코드가 포함되지만, HID 키 코드와는 서로 다릅니다. 다행히 둘 사이에는 거의 1:1로 대응하는 관계가 있습니다. JavaScript 키 코드를 HID로 변환하기 위해 다음과 같은 조회 테이블을 만들었습니다.
_JS_TO_HID_KEYCODES = {
3: 0x48, # Pause / Break
8: 0x2a, # Backspace / Delete
9: 0x2b, # Tab
...
65: 0x04, # a
66: 0x05, # b
67: 0x06, # c
68: 0x07, # dKey Mime Pi 서버는 브라우저에서 전송되는 JavaScript 키 코드 이벤트를 수신하고, 이를 HID 코드로 변환한 다음 Pi의 HID 인터페이스인 /dev/hidg0으로 보냅니다.
전체 과정은 다음과 같습니다.
- 사용자가 브라우저에서 키를 누릅니다.
- 페이지의 JavaScript가 JavaScript 키 코드를 Pi의 Key Mime Pi 서버로 보냅니다.
- Key Mime Pi 서버가 JavaScript 키 코드를 이에 대응하는 HID 코드로 변환합니다.
- Key Mime Pi 서버가 HID 코드를 USB 가젯 인터페이스인
/dev/hidg0으로 보냅니다. - Pi의 USB 케이블에 연결된 컴퓨터가 이를 키보드 입력으로 받아 화면에 문자를 표시합니다.
전원 문제
테스트해 보니 컴퓨터의 USB 포트에서 Pi를 구동하기에 충분한 전력이 공급되기는 했지만, 시스템 로그에는 저전압 경고가 자주 나타났습니다.
$ sudo journalctl -xe | grep "Under-voltage"
Jun 05 03:46:05 keymimepi kernel: Under-voltage detected! (0x00050005)
Jun 05 03:48:29 keymimepi kernel: Under-voltage detected! (0x00050005)
Jun 05 03:54:22 keymimepi kernel: Under-voltage detected! (0x00050005)Pi가 표준 USB 2.0 및 USB 3.0 포트에서 Pi의 요구 사항을 충족하기에 충분하지 않은 전력이 공급된다는 사실을 정확히 감지하고 있었던 것입니다.
| Raspberry Pi 모델 | 전원 요구 사항 |
|---|---|
| Pi Zero W | 5V / 1.2A |
| Pi 4 | 5V / 3.0A |
표준 USB 포트로는 부족합니다.
| USB 포트 유형 | 출력 |
|---|---|
| USB 2.0 | 5V / 0.5A |
| USB 3.0 | 5V / 0.9A |
현재도 이 문제의 해결책을 찾고 있습니다. 아직 테스트하지 않은 방법은 다음과 같습니다.
이더넷 포트에서 전력을 끌어오도록 PoE HAT 사용Zero2Go Power Adaptor를 사용해 AC-마이크로USB 어댑터 연결
업데이트: 이 문제를 해결하기 위해 엔지니어링 업체와 함께 맞춤형 회로 기판을 제작했습니다. 이 기판은 Pi의 USB-C 포트를 두 개로 분리합니다. 첫 번째 포트는 USB 전원을 받아 Pi에 최대 3A를 계속 공급할 수 있습니다. 두 번째 포트는 USB 데이터 출력을 받아 Pi가 USB 키보드로 계속 동작할 수 있게 합니다.


TinyPilot Power Connector를 사용하면 USB OTG 기능을 유지하면서 USB-C 포트를 통해 Pi에 3A의 전력을 공급할 수 있습니다.
중요한 점은 이 전원 커넥터의 데이터 포트에는 USB 전원 선이 없다는 것입니다. 따라서 컴퓨터의 전원과 Pi의 전원 사이에 전압 차이가 생겨 원치 않는 역전류가 흐르는 일을 막을 수 있습니다.
문제 해결
드라이버 작동 여부 확인
Key Mime Pi가 대상 컴퓨터로 키 입력을 전달하지 못한다면, 먼저 USB 가젯이 제대로 작동하는지 확인해야 합니다.
다음 명령을 실행해 보세요.
echo -ne "\0\0\xb\0\0\0\0\0" > /dev/hidg0 && \
echo -ne "\0\0\xc\0\0\0\0\0" > /dev/hidg0 && \
echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0모든 것이 정상적으로 작동한다면 Pi가 USB로 연결된 컴퓨터에 다음과 같은 출력이 표시됩니다.
hiHID 인터페이스에 쓰기가 멈추는 경우
Pi Zero W에서 Key Mime Pi를 테스트하던 중 /dev/hidg0에 쓰는 작업이 무기한 멈추는 경우를 겪었습니다. Micro-USB-USB-A 케이블을 다른 것으로 바꾸자 문제가 사라졌습니다. 처음 사용한 케이블이 손상되었거나 전원만 지원하고 데이터 전송은 지원하지 않았던 것 같습니다. /dev/hidg0에 쓰는 작업이 멈춘다면 데이터 전송을 지원하는 USB 케이블을 사용해 보세요(대부분의 USB 케이블은 데이터 전송을 지원합니다).
다음 단계: 화면 출력 통합
원격으로 입력하는 일은 재미있지만, 실용성은 조금 떨어집니다. 시스템에 입력할 때는 일반적으로 출력도 함께 볼 수 있어야 편리합니다.
다음 단계로 대상 컴퓨터의 HDMI 출력을 캡처해 Key Mime Pi의 웹 인터페이스에 통합하려고 합니다. 그러면 헤드리스 서버에 Pi를 연결하고 브라우저에서 가상 콘솔을 사용할 수 있게 됩니다. 본질적으로 저렴하고 개조하기 쉬운 KVM over IP 장치가 되는 셈입니다.
ffplay와 HDMI 익스텐더를 사용한 작동하는 프로토타입은 만들었지만, 아직 모든 기능을 낮은 지연 시간으로 하나의 브라우저 창에 담는 방법을 개발 중입니다.

HDMI 익스텐더를 사용하면 원격 컴퓨터의 모니터 출력을 볼 수 있지만, 아직 모든 기능을 브라우저에 통합하는 작업을 진행 중입니다.
미리 구성된 키트를 원하시나요?
이제 대상 장치의 영상 출력을 캡처하고 키 입력을 보낼 수 있는 작동하는 솔루션을 완성했습니다. 이 모든 작업은 하나의 브라우저 창에서 이루어집니다.

자세한 후속 글은 곧 공개할 예정입니다. 그동안 직접 KVM Pi를 구성하는 데 필요한 모든 것이 포함된 사전 구성 KVM Pi 키트를 예약 주문할 수 있습니다.
소스 코드
Key Mime Pi의 코드는 허용적인 조건의 MIT 라이선스에 따라 완전히 오픈 소스로 공개되어 있습니다.
- key-mime-pi: 키 입력을 Pi의 가상 키보드로 전달하는 웹 서버입니다.
- ansible-role-key-mime-pi: Pi의 USB 가젯 기능을 구성하고 웹 서버를 systemd 서비스로 설치하는 Ansible role입니다.
감사의 말
- raspberrypisig/pizero-usb-hid-keyboard는 제 Pi에 가상 USB HID 장치를 성공적으로 설치할 수 있다는 것을 처음 보여 준 샘플 코드였습니다.
- Fmstrat/diy-ipmi는 이 프로젝트에 영감을 주었고, Pi를 KVM over IP로 작동시킬 수 있다는 사실을 입증했습니다.
- Rafael Medina는 제가 찾은 자료 중 HID 프로토콜을 가장 이해하기 쉽게 설명해 주었습니다.
- USB 가젯 기능을 가능하게 만들어 준 Linux 및 Raspberry Pi OS 개발자들에게 감사드립니다.
글을 무작위로 읽기