Key Mime Pi: 라즈베리 파이를 원격 키보드로 만들기
원문은 Michael Lynch님이 에 게재했습니다. 이 블로그 구독하기
최근 버전의 라즈베리 파이는 USB On-The-Go(USB OTG)를 지원해 키보드, USB 메모리, 마이크 같은 USB 장치인 것처럼 동작할 수 있다. 이 기능을 활용하기 위해 나는 파이를 가짜 키보드로 바꿔주는 오픈소스 웹 앱을 만들었다. 이름은 Key Mime Pi다.
이 글에서는 Key Mime Pi가 어떻게 동작하는지, 그리고 직접 만드는 방법을 소개한다.
데모
준비물
- USB OTG를 지원하는 라즈베리 파이:
- Raspberry Pi 4 (모든 모델)
- Raspberry Pi Zero W
- Raspberry Pi A 및 A+ (검증 필요)
- 이 자료에 따르면 초기 라즈베리 파이도 USB OTG를 지원한다고 하지만, 나는 이 기기들을 직접 테스트해보지 않았다.
- Raspberry Pi OS (구 Raspbian)
- Stretch 또는 그 이후 버전
- USB 케이블
- Pi 4용: USB-C to USB-A (Male/Male)
- Pi Zero W용: Micro-USB to USB-A (Male/Male)
Raspberry Pi OS Lite 설치하기
먼저 microSD 카드에 Raspberry Pi OS Lite(구 Raspbian)를 설치한다.

나는 파이의 microSD 카드를 구울 때 Rufus를 사용하지만, 전체 디스크 이미징 도구라면 어떤 것이든 상관없다.
microSD 카드의 boot 파티션에 ssh라는 이름의 파일을 넣어 SSH 접속을 활성화한 뒤, microSD 카드를 파이 장치에 삽입한다. 무선으로 연결할 경우에는 boot 파티션에 wpa_supplicant.conf 파일을 생성해야 한다.
파이 연결하기
USB 케이블을 파이의 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 포트에서도 문제없이 동작했다.
파이는 컴퓨터의 USB 포트에서 전원을 공급받아 부팅된다.
Key Mime Pi 설치하기
Key Mime Pi를 설치하는 방법은 두 가지다. 별도의 외부 도구 없이 그냥 bash만으로 설치할 수도 있고, 좀 더 세련되게 내가 가장 좋아하는 오픈소스 구성 관리 도구인 Ansible로 설치할 수도 있다.
방법 1: 순수 bash로 설치하기
bash 셸에서 다음 명령어를 입력해 파이에 접속하고 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 reboot파이가 재부팅되도록 한 뒤, 다시 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 롤을 사용할 수 있다. 다음 명령어는 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 웹 인터페이스
그리고 마법처럼, 브라우저에 입력하면 해당 키 입력이 파이에 연결된 기기에 나타난다.

Key Mime Pi를 이용하면 브라우저를 통해 원격 컴퓨터로 키 입력을 전송할 수 있다.
동작 원리
USB 장치 에뮬레이션
여기서 진짜 마법은 리눅스의 USB Human Interface Device(HID) 가젯 드라이버에서 나온다. 이 드라이버 덕분에 사용자 모드 애플리케이션이 마치 USB 장치인 것처럼 운영체제와 상호작용할 수 있다.
key-mime-pi 구성 스크립트는 /dev/hidg0 경로에 파일을 생성한다. 어떤 프로그램이든 이 경로에 읽고 쓸 수 있으며, OS가 데이터를 키보드 신호로 변환한다.
키보드를 흉내 내려면 파이는 USB HID 스펙에 따라 OS와 통신해야 한다. 키코드와 표로 가득한 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키보드는 키 누름을 알리는 것 외에도 키 해제도 알려야 한다. 8바이트가 모두 0인 블록은 눌린 키가 없음을 의미한다.
위의 예시는 한 번에 하나의 키 입력만 전송했지만, 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 코드로 변환한 뒤, 파이의 HID 인터페이스인 /dev/hidg0로 전송한다.
전체 동작 과정은 다음과 같다:
- 사용자가 브라우저에서 키를 누른다.
- 페이지의 JavaScript가 JavaScript 키코드를 파이의 Key Mime Pi 서버로 전송한다.
- Key Mime Pi 서버가 JavaScript 키코드를 그에 대응하는 HID 코드로 변환한다.
- Key Mime Pi 서버가 HID 코드를
/dev/hidg0의 USB 가젯 인터페이스로 전송한다. - 파이의 USB 케이블에 연결된 컴퓨터가 이를 키보드 입력으로 받아 화면에 문자가 나타난다.
전원 문제
내 테스트에서는 컴퓨터의 USB 포트에서도 파이를 구동할 만큼의 전력이 공급됐지만, 시스템 로그에는 저전압 경고가 자주 나타났다:
$ 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)파이는 표준 USB 2.0 및 USB 3.0 포트가 파이의 요구량을 충족할 만큼의 전력을 공급하지 못한다는 사실을 정확히 감지한 것이었다.
| 라즈베리 파이 모델 | 전력 요구량 |
|---|---|
| Pi Zero W | 5 V / 1.2 A |
| Pi 4 | 5 V / 3.0 A |
표준 USB 포트의 출력은 이에 미치지 못한다:
| USB 포트 유형 | 전력 출력 |
|---|---|
| USB 2.0 | 5 V / 0.5 A |
| USB 3.0 | 5 V / 0.9 A |
나는 아직 이 문제의 해결책을 찾고 있는 중이다. 아직 테스트해보지 않은 가능한 해결책은 다음과 같다:
이더넷 포트에서 전원을 끌어오기 위해 PoE HAT 사용AC to microUSB 어댑터를 연결하기 위해 Zero2Go Power Adaptor 사용
업데이트: 이 문제를 해결하기 위해 나는 엔지니어링 업체와 협력해 커스텀 회로 기판을 만들었다. 이 기판은 파이의 USB-C 포트를 두 개로 나눈다. 첫 번째 포트는 USB 전원을 받아 파이에게 3A의 전력을 그대로 공급할 수 있게 하고, 두 번째 포트는 USB 데이터 출력을 담당해 파이가 여전히 USB 키보드를 에뮬레이션할 수 있게 한다.


TinyPilot Power Connector를 사용하면 USB OTG 기능을 잃지 않으면서도 USB-C 포트를 통해 파이에 3A의 전력을 공급할 수 있다.
중요한 점은 이 전원 커넥터의 데이터 포트에는 USB 전원 라인이 없다는 것이다. 덕분에 컴퓨터의 전원과 파이의 전원 사이의 전압 차이로 인해 원치 않는 역전류가 발생하는 것을 방지할 수 있다.
문제 해결
드라이버가 동작하는지 확인하기
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모든 것이 정상이라면 파이의 USB에 연결된 기기에서 다음과 같은 출력이 보여야 한다:
hiHID 인터페이스 쓰기가 멈출 때
Pi Zero W에서 Key Mime Pi를 테스트하는 동안 /dev/hidg0에 대한 쓰기가 무기한 멈추는 경우를 겪었다. 다른 Micro-USB to USB-A 케이블로 바꿔 보니 문제가 사라졌다. 첫 번째 케이블이 손상됐거나 데이터 전송을 지원하지 않고 전력만 공급하는 케이블이었던 것 같다. /dev/hidg0에 대한 쓰기가 멈춘다면 데이터 전송을 지원하는 USB 케이블을 사용해 보라(대부분의 USB 케이블은 데이터 전송을 지원한다).
다음 단계: 디스플레이 출력 임베딩
원격으로 타이핑하는 것은 재미있지만, 다소 비실용적이다. 시스템에 입력할 때는 일반적으로 출력 화면도 함께 보는 것이 도움이 된다.
다음 단계는 대상 컴퓨터의 HDMI 출력을 캡처해 Key Mime Pi 웹 인터페이스에 임베드하는 것이다. 그렇게 하면 파이를 헤드리스 서버에 연결하고 브라우저에서 가상 콘솔을 사용할 수 있게 된다. 이는 본질적으로 저렴하고 해킹 가능한 KVM over IP 장치가 될 것이다.
나는 ffplay와 HDMI 익스텐더를 이용한 작동하는 프로토타입을 가지고 있지만, 여전히 모든 것을 낮은 지연 시간으로 하나의 브라우저 창에 넣는 솔루션을 개발 중이다.

HDMI 익스텐더를 이용해 원격 기기의 모니터 출력을 볼 수 있지만, 아직 모든 것을 브라우저에 통합하는 작업 중이다.
미리 구성된 키트가 필요하신가요?
이제 대상 장치의 비디오 출력을 캡처하고 브라우저 창 안에서 키 입력도 전송할 수 있는 동작하는 솔루션을 갖추게 되었다.

자세한 후속 글은 곧 공개될 예정이지만, 그전에 KVM Pi를 직접 만드는 데 필요한 모든 것이 포함된 미리 구성된 KVM Pi 키트를 사전 주문할 수 있다:
소스 코드
Key Mime Pi 코드는 관대한 MIT 라이선스 하에 완전히 오픈소스로 공개되어 있다:
- key-mime-pi: 파이의 가상 키보드로 키 입력을 전달하는 웹 서버.
- ansible-role-key-mime-pi: 파이의 USB 가젯 기능을 구성하고 웹 서버를 systemd 서비스로 설치하기 위한 Ansible 롤.
감사의 말
- raspberrypisig/pizero-usb-hid-keyboard는 내 파이에서 가상 USB HID 장치를 성공적으로 설치한 첫 번째 샘플 코드였다.
- Fmstrat/diy-ipmi는 이 프로젝트에 영감을 주었으며, 파이를 KVM over IP로 동작하게 만들 수 있음을 증명했다.
- Rafael Medina는 내가 찾은 HID 프로토콜에 대한 가장 읽기 쉬운 설명을 제공했다.
- USB 가젯 기능을 가능하게 해준 리눅스 및 Raspberry Pi OS 개발자들에게 감사드린다.
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기