손목 컨트롤러 따라 하기
손목시계 하나로 PC 의 발표 슬라이드를 넘기고, 앱을 열고, 화상 회의 마이크를 끕니다. 코딩을 몰라도 이 순서대로 하면 됩니다. PC 에 따로 설치할 프로그램은 없습니다(바로 가기 키 설정만 한 번).
무엇을 만드나
시계가 PC 에 블루투스 키보드·마우스로 연결됩니다. 시계 화면을 누르거나 손목을 튕기면 그에 맞는 키(→, ←, 마침표 등)를 PC 로 보냅니다. 화면 4개를 옆으로 밀어 바꿉니다.
PowerPoint·Google Slides·Zoom·Teams·Google Meet 에서 시험했습니다. Windows 기준으로 설명하고, macOS 도 대부분 동작합니다(실행 모드는 Spotlight 로 엽니다).
준비물
- Waveshare ESP32-C6-Touch-AMOLED-2.06 시계. 배터리 포함판을 권장합니다(배터리가 없으면 USB 를 꽂아야만 켜집니다).
- USB-C 데이터 케이블. 충전 전용 케이블은 PC 가 시계를 알아보지 못합니다.
- Windows 10/11 PC + Chrome 또는 Edge(설치 페이지가 이 두 브라우저에서만 됩니다). 블루투스가 있어야 합니다(노트북은 대부분 있음).
- 인터넷 연결(설치할 때만).
주의: 이름이 비슷한 ESP32-S3-Touch-AMOLED-2.06 은 다른 제품입니다. 이 펌웨어는 C6 판에만 맞습니다. 상자·판매 페이지에서 'C6' 를 꼭 확인하세요. 1단계 명령에서 Chip is ESP32-C6 이라고 나와야 합니다.
1원래 펌웨어 백업 — 꼭 먼저
새 시계에는 원래 프로그램(펌웨어)이 들어 있습니다. 설치하면 지워지므로, 되돌릴 수 있게 통째로 파일로 받아 둡니다. 한 번만 하면 됩니다(약 5분).
① Python 과 esptool 설치
- python.org 에서 Python 3.12 이상을 내려받아 설치합니다. 설치 첫 화면 아래의 'Add python.exe to PATH' 를 꼭 체크합니다.
- 시작 메뉴에서 명령 프롬프트(또는 PowerShell)를 열고 아래를 붙여 넣고 Enter.
pip install esptool
② 포트 번호 확인
- 시계를 USB 로 PC 에 연결합니다.
- 시작 버튼 오른쪽 클릭 → 장치 관리자 → 포트(COM & LPT) 에서 새로 생긴
COM번호를 봅니다(예:COM7). 아래 명령의COM7을 내 번호로 바꿉니다. - 안 보이면 케이블을 바꾸거나, BOOT(위 버튼)를 누른 채 USB 를 꽂아 다운로드 모드로 들어갑니다.
③ 칩 확인 → 백업
esptool --chip esp32c6 -p COM7 flash-id
Chip is ESP32-C6, Detected flash size: 16MB 가 보이면 맞는 시계입니다.
esptool --chip esp32c6 -p COM7 -b 460800 read-flash 0 ALL original_backup.bin
16MB 를 읽는 데 1~2분 걸립니다. 명령 창을 연 폴더(보통 C:\Users\내이름)에 original_backup.bin 이 생깁니다.
certutil -hashfile original_backup.bin SHA256
나온 긴 글자(해시)를 메모해 두면 나중에 파일이 망가지지 않았는지 확인할 수 있습니다.
백업 파일은 USB 메모리나 클라우드에 나만 볼 수 있게 보관하세요. 원래 펌웨어의 Wi-Fi 설정 같은 개인 정보가 들어 있을 수 있어 남에게 주거나 인터넷에 올리면 안 됩니다.
되돌리는 방법은 아래 문제 해결 → 원래 펌웨어로 되돌리기에 있습니다. 같은 명령은 설치 페이지 2단계에도 복사 버튼과 함께 있습니다.
2펌웨어 설치
브라우저에서 바로 설치합니다(ESP Web Tools). 시계를 USB 로 연결한 채 PC 의 Chrome 또는 Edge로 시계에 설치 페이지를 엽니다.
- '원래 펌웨어를 백업했습니다' 를 체크합니다(체크해야 설치 버튼이 켜집니다).
- 버전을 고릅니다. 맨 위가 최신입니다.
- 시계에 설치 → 창에서 시계 포트(
COM7등)를 고르고 연결. - 설치 창에서 지우기(Erase device) 를 물으면 아래 표대로 고르고 설치를 누릅니다. 약 1분, 끝날 때까지 USB 를 뽑지 않습니다.
- 끝나면 시계가 다시 켜지며 시계 화면이 나옵니다.
| 상황 | 지우기(Erase) | 결과 |
|---|---|---|
| 처음 설치 | 선택한다 | 원래 펌웨어의 설정 영역과 섞이지 않게 깨끗이 지우고 설치 |
| 업데이트 | 선택하지 않는다 | PC 페어링·시계 설정이 그대로 남아 다시 연결할 필요 없음 |
| 원래 펌웨어로 되돌린 뒤 다시 설치 | 선택한다 | 처음 설치와 같음 |
설치 페이지의 버전 목록에 '예시' 라고만 나오면 아직 공개 버전이 올라오기 전입니다. 그때는 직접 고치기의 '개발판 설치' 방법(esptool 명령)을 쓰거나 공개 버전을 기다립니다.
3블루투스 연결(페어링)
- 시계가 켜져 있으면 PC 를 찾는 중입니다(연결 대기 화면).
- PC: 설정 → 블루투스 및 장치 → 장치 추가 → 블루투스.
- 목록에서 손목 컨트롤러 XXXX 를 누릅니다.
XXXX는 시계마다 다른 네 글자(예:2D3A)라 여러 대를 구분할 수 있습니다. 비밀번호(PIN)는 묻지 않습니다. - '연결됨' 이 뜨고 시계 위쪽 상태줄의 블루투스 표시가 켜지면 끝입니다. 다음부터는 시계를 켜면 자동으로 다시 연결됩니다.
다시 연결이 안 될 때(시계에 'PC에서 이 장치를 제거하고 다시 추가하세요' 가 뜨거나 상태줄 블루투스가 노란색): PC 설정의 장치 목록에서 손목 컨트롤러 XXXX → 장치 제거 를 누른 뒤, 위 2~3번을 다시 합니다. 펌웨어를 지우고(Erase) 설치했을 때도 이렇게 해야 합니다.
4시계 시간 맞추기
시계 화면의 시각은 PC 시각을 받아 맞춥니다. 처음 한 번만 하면 전원을 꺼도 유지됩니다(배터리를 뺐다면 다시).
- 시계를 USB 로 연결하고 설치 페이지 6번 시계 시간 맞추기 를 누릅니다.
- 포트를 고르고 연결하면 끝입니다. 시계가 잠깐 다시 켜질 수 있습니다(정상).
시간대는 한국(UTC+9)이 기본입니다.
5PC 바로 가기 키 만들기(실행 모드용)
실행 모드의 타일은 Ctrl+Alt+1~6 키를 보냅니다. PC 가 이 키를 받고 앱을 열도록, PC 마다 한 번 바로 가기를 만듭니다. 관리자 권한은 필요 없습니다.
- wrist-shortcuts.ps1 내려받기
- 내려받은 파일을 오른쪽 클릭 → PowerShell 에서 실행.
- 실행이 막히면 내려받은 폴더(보통 '다운로드')에서 PowerShell 을 열고 아래 한 줄을 실행합니다.
powershell -ExecutionPolicy Bypass -File .\wrist-shortcuts.ps1 - 앱마다
만듦또는건너뜀이 나옵니다. 건너뜀 = 그 앱이 설치되어 있지 않음(설치 후 다시 실행하면 됩니다). - 그래도 키가 안 먹으면 로그아웃 후 다시 로그인합니다.
| 키 | 앱 |
|---|---|
| Ctrl+Alt+1 | Claude |
| Ctrl+Alt+2 | 카카오톡 |
| Ctrl+Alt+3 | 크롬 |
| Ctrl+Alt+4 | 파워포인트 |
| Ctrl+Alt+5 | 엑셀 |
| Ctrl+Alt+6 | 한글 |
스크립트는 내 시작 메뉴에 손목 컨트롤러 폴더를 만들고 바로 가기 6개를 넣을 뿐입니다. 지우려면 그 폴더를 지우면 됩니다.
사용법 표
버튼(시계 오른쪽 면)
| 버튼 | 누르는 법 | 하는 일 |
|---|---|---|
| 위 BOOT | 짧게 | 시계 화면으로. 이미 시계 화면이면 바로 전 화면으로 돌아감(키는 보내지 않음) |
| 위 BOOT | 2초 누름 | 잠금 켜기/끄기 — 잠금 중에는 터치·손목 튕김이 키를 보내지 않음(주머니 속 오작동 방지) |
| 아래 PWR | 짧게 | 화면 켜기/끄기 |
| 아래 PWR | 3초 누름 | 전원 끄기 확인 '전원을 끌까요?' — 손을 뗀 뒤 [끄기](또는 PWR 짧게)를 누르면 꺼지고, [취소]·BOOT 짧게·10초 기다림이면 취소. 켤 때는 PWR 를 1초쯤 길게(정확한 시간은 확인 화면에 표시) |
| 아래 PWR | 10초 넘게 | 시계가 멈췄을 때의 강제 종료(전원 회로가 함). 펌웨어가 이 시간을 10초로 늘려 둔다 |
화면 터치
| 동작 | 방법 | 하는 일 |
|---|---|---|
| 탭 | 살짝 눌렀다 뗌(0.6초 안에, 손가락을 거의 움직이지 않고) | 타일·영역의 키를 보냄. 손을 뗄 때 보내므로, 잘못 눌렀으면 손가락을 밀어서 떼면 취소 |
| 암전 | 발표 화면 가운데를 0.36초 누름 | 슬라이드 쇼 화면을 검게(마침표 .). 한 번 더 하면 다시 보임 |
| 길게 | 회의 타일을 0.6초 누름 | 키를 보내지 않고 시계 표시만 PC 상태에 맞춤(실제로는 이미 꺼져 있을 때) |
| 옆으로 밀기 | 좌우로 쓱 밂 | 화면 바꾸기: 시계 → 발표 → 실행 → 회의. 슬라이드 쇼 중에는 실수 방지로 더 길게 밀어야 함 |
| 위에서 당기기 | 화면 맨 위 가장자리에서 아래로 당김 | 발표: 쇼 시작/종료 시트. 다른 화면: 시계 설정(감도·OS·이름 등) |
| 위로 쓸기 | 실행 화면에서 위로 | 실행 2쪽(화면 캡처·잠금·바탕 화면·화면 전환·음소거) |
손목 튕김
- 기본은 꺼져 있습니다. 시계 설정 화면의 손목 튕김 줄을 켜면 동작합니다(전원을 꺼도 기억). 꺼져 있으면 다음 장은 오른쪽 탭, 이전 장은 왼쪽 탭입니다.
- 발표 화면에서만 동작합니다. 시계·실행·회의 화면이나 잠금(BOOT 2초) 중에는 무시합니다.
- 손목을 바깥으로 튕기든 안쪽으로 튕기든 모두 '다음 장'입니다(이전 장은 왼쪽 탭).
- 시계는 손목의 가속도(순간적으로 확 움직이는 힘)로 튕김을 알아봅니다. 세게 할 필요 없이 손목을 가볍게, 빠르게 툭 스냅하면 충분합니다. 걷기·팔 흔들기·천천히 손목 돌리기처럼 느린 움직임은 무시합니다.
- 한 번 넘긴 뒤 0.8초 동안은 다시 넘기지 않습니다(튕긴 손목이 되돌아오는 움직임으로 두 장 넘어가지 않게).
- 너무 잘 먹거나 안 먹으면
shared/gesture.json의flick.accel.peakG(기본 1.0 g)를 바꿉니다 — 올리면 더 세게 튕겨야 하고, 내리면 약한 튕김도 잡습니다. 시뮬레이터 설정 → gesture.json 의 그래프에서 점선을 끌어 보며 맞출 수 있습니다.
그 밖에
- 아무것도 하지 않으면 20초 뒤 화면이 꺼집니다. 화면 터치·BOOT·손목 들기로 다시 켜집니다.
- 에어마우스(BOOT 를 누른 채 손목을 움직여 마우스 포인터 움직이기)는 기본으로 꺼져 있습니다. 쓰려면 시계 설정에서 '에어마우스' 를 켭니다.
문제 해결
실행 화면에서 카카오톡(또는 다른 앱)이 안 열려요
5단계 PC 바로 가기 키를 그 PC 에서 했는지 확인합니다. PC 마다 한 번 해야 합니다. 스크립트에 '건너뜀' 이 나왔다면 그 앱이 설치되어 있지 않은 것입니다. 했는데도 안 되면 로그아웃 후 다시 로그인합니다.
한글 입력 상태에서 암전이 안 돼요
이 펌웨어는 암전에 B 대신 마침표 . 를 보내서 한/영 상태와 상관없이 됩니다(B 는 한글 상태에서 'ㅠ' 로 입력됨). 안 되면 ① 펌웨어가 최신인지, ② 슬라이드 쇼가 실행 중이고 그 창이 맨 앞에 있는지 확인합니다. 편집 화면에서는 마침표가 글자로 입력됩니다.
시계에 '연결 대기' 만 나오고 연결이 안 돼요
- PC 블루투스가 켜져 있는지 봅니다.
- PC 의 장치 목록에 '손목 컨트롤러 XXXX' 가 이미 있으면 장치 제거 후 다시 추가합니다(3단계).
- 다른 PC 와 이미 연결되어 있으면 그 PC 의 블루투스를 끕니다. 시계는 한 번에 PC 한 대와 연결됩니다.
- PWR 를 3초 눌러 '전원을 끌까요?' 에서 [끄기] → PWR 를 길게 눌러 다시 켭니다(화면이 멈췄으면 PWR 를 10초 넘게 눌러 강제로 끕니다).
PC 에 배터리가 늘 50% 로 나와요
배터리가 없는 판(또는 배터리 연결이 빠진 상태)이면 시계가 배터리 양을 읽지 못해 50% 로 알립니다. 배터리 포함판에서 배터리 커넥터가 제대로 꽂혀 있는지 확인합니다. USB 를 꽂고 쓰면 동작에는 문제가 없습니다.
PWR 를 오래 눌렀더니 '전원을 끌까요?' 가 떠요 / 꺼졌어요
정상입니다. PWR 를 3초 누르면 확인 화면이 뜨고, [끄기] 를 누르면 꺼집니다. 확인 화면을 무시하고 10초 넘게 계속 누르면 시계 전원 회로가 강제로 끕니다. PWR 를 길게 누르면 다시 켜집니다. 잠금은 BOOT(위 버튼) 2초입니다.
손목 튕김이 안 먹거나 너무 잘 먹어요
발표 화면인지, 잠금(BOOT 2초)이 켜져 있지 않은지 봅니다. 튕김은 짧고 빠르게(가볍게 툭) 해야 잡히고, 한 번 넘긴 뒤 0.8초 안의 튕김은 무시합니다. 감도는 저장소의 shared/gesture.json 의 flick.accel.peakG(가속도 임계값, g)를 고쳐 바꿀 수 있습니다 — 안 먹으면 낮추고, 너무 잘 먹으면 올립니다(직접 고치기).
원래 펌웨어로 되돌리고 싶어요
- 1단계에서 받은
original_backup.bin이 있는 폴더에서 명령 창을 엽니다. - 시계를 USB 로 연결하고(안 되면 BOOT 를 누른 채 꽂기) 아래를 실행합니다. 쓰고 나서 자동으로 검사합니다.
esptool --chip esp32c6 -p COM7 -b 460800 write-flash 0x0 original_backup.bin - PWR 로 껐다 켭니다. PC 의 '손목 컨트롤러 XXXX' 는 장치 제거합니다.
백업이 없으면 Waveshare 공식 저장소(ESP32-C6-Touch-AMOLED-2.06)의 공장 펌웨어로 같은 명령을 씁니다(파일 이름만 바꿈).
설치 페이지에서 '시계에 설치' 버튼이 회색이에요
① 백업 체크를 켰는지, ② 데스크톱 Chrome·Edge 인지(휴대폰·Safari·Firefox 는 안 됨), ③ 버전을 골랐는지 확인합니다. 버전 목록이 '예시' 뿐이면 아직 공개 버전이 없는 것입니다.
직접 고치기(개발자용)
키 배치나 판정값은 코드가 아니라 JSON 파일 두 개에 있습니다. 시뮬레이터와 시계 펌웨어가 같은 파일을 읽어서, 시뮬레이터에서 먼저 시험해 볼 수 있습니다.
저장소 구조
| 폴더 | 내용 |
|---|---|
shared/ | keymap.json(모드·앱별 단축키, 장치 이름) · gesture.json(튕김·터치·버튼·전원 판정값) · test-vectors/(입력 → 기대 출력) |
sim/ | 이 웹앱(Vite + TypeScript): 시뮬레이터 · 설치 페이지 · 이 안내서 |
firmware/ | 시계 펌웨어(ESP-IDF 5.5 + NimBLE + LVGL 9). logic/ 은 하드웨어와 무관한 판정 로직(C++) |
tools/ | JSON → C 헤더 생성기, 글꼴 생성기 |
sim/public/pc-setup/ | PC 바로 가기 키 스크립트 |
고치는 순서
shared/keymap.json또는shared/gesture.json을 고칩니다. 시뮬레이터의 설정(톱니바퀴) 화면에서 편집하고 'JSON 내보내기' 로 받아도 됩니다.- 시뮬레이터로 먼저 시험합니다. 시계 화면을 마우스로 누르면 가상 PC 와 HID 로그에 어떤 키가 갔는지 보입니다. 녹화된 손목 움직임(CSV)도 재생할 수 있습니다.
cd sim npm install npm run dev - 펌웨어용 헤더를 다시 만들고 테스트합니다.
python tools/gen_keymap_header.py cd sim && npm test - GitHub 에 push 하면 GitHub Actions 가 테스트 → 펌웨어 빌드를 합니다(내 PC 에 ESP-IDF 가 없어도 됨). 포크한 저장소의 Actions 탭에서
ci워크플로를 수동 실행(Run workflow)하면 개발판.bin이 아티팩트로 나옵니다. - 개발판 설치: 아티팩트 압축을 풀고 그 폴더에서 세 파일을 나눠 씁니다(페어링 유지). 명령은 함께 든
FLASH.txt에 있습니다.
처음 설치라면esptool --chip esp32c6 -p COM7 -b 460800 write-flash 0x0 wrist-controller-버전-bootloader.bin 0x8000 wrist-controller-버전-partition-table.bin 0x10000 wrist-controller-버전-app.bin…-merged-full-erase.bin을 쓰거나 먼저esptool --chip esp32c6 -p COM7 erase-flash를 합니다.
테스트 벡터
shared/test-vectors/*.json 은 '이런 입력이면 이런 키가 나가야 한다' 는 묶음입니다. 시뮬레이터(TypeScript, npm test)와 펌웨어 로직(C++, firmware/test)이 같은 벡터를 모두 통과해야 CI 가 통과합니다. 판정값을 바꿨는데 벡터가 깨지면, 의도한 변화인지 확인하고 벡터를 고칩니다(node sim/scripts/gen-test-vectors.mjs).
자세한 설명: 저장소의 README.md, sim/ARCHITECTURE.md, docs/research/(근거 자료와 출처).
이 페이지는 한 번 열면 인터넷 없이도 다시 열 수 있습니다. 소스는 MIT 라이선스입니다. 사용한 글꼴·라이브러리는 저장소의 THIRD_PARTY.md 에 있습니다.