Pebble QEMU Development on uConsole with Physical Controls

의도

기본 생각은 단순하다:

uConsole의 Joystick Keys 와 물리키보드를 이용해 Pebble QEMU Emulator에 직접 버튼/터치/가속도데이터 등을 전송하여 개발/디버깅 하겠다는 것이다.

왼쪽 이미지에 보이는 Pebble앱은 한국어(한글) 모르스부호 연습기 앱인데, 단순히 한글 자모를 출력하는게 아니라 한글 오토마타를 내장해서 모르스부호를 입력하면 한국어 문장이 조립된다. 죽도록 버튼을 누르지 않고 화면 터치만 사용하게 했기 때문에, 제대로 동작하는지 확인하려면 Pebble Time 2 실물기기에서 테스트하는 것이 가장 정확하겠지만, 심각한 배터리 사용이 걱정스러웠다. 실제 기기를 오래쓰려면 이런 개발 과정을 Emulator에서 실행해야하지만, macOS 상태에서 테스트하는 것보다, 벽에 달려 놀고있는 uConsole을 활용하자는 생각이 떠올랐다.

구현 아이디어

GamePad 키를 QEMU로 전송하는 방식은 오래전에 이미 사용했던 방법이 있다. RetroPie Joy2Key 방식이다. 그때는 AI가 없어서 직접 코드를 내가 원하는 방향으로 쓰기위해 시간을 제법 쏟아 부었던 적이 있으나, 지금은 뭐 AI 도움을 받아 거의 막힘이 없다. 구현 제한 사항은, 반드시 페블 앱에서 발생하는 Audio를 QEMU를 통해 들을 수 있어야 한다는 점이다. 그게 아니면, 이딴 건 의미 없다.

삽질로 드러난 아이디어 구현의 걸림돌

① 실제 Touch 처럼 자유롭게 입력을 넣는 건 좀 더 시간이 걸리듯.

  • Touch 인식은 QEMU 앱 소관이라서 외부에서는 Virtual Touch Event Signal 정도만 날릴 수 있다.
  • QEMU 화면이 resize 되면 touch 위치 인식 불능. Pebble Firmware를 수정하거나 페블 앱을 작성할 때 상대크기로 작성해야 한다.
  • Ctrl+마우스 움직임 또는 Ctrl+Drag 등의 키 조합을 가상 이벤트로 날릴 수 밖에 없다.

② 입력 종류마다 다른 방식으로 처리해야 함.

  • QEMU와 통신 방식이 4가지: pypkjs, WebSocket, QMP(unix socket), raw_tcp
  • 포트는 두가지: 포트마다 적절한 시그널을 보내는게 제한된다. 올바르지 않은 채널에 시그널 부어버리면 페블앱을 전환할 때 마다 WebSocket이 죽어버릴 수 있음.

③ pypkjs는 Settings 때문 필수.

  • 따라서 실질적으로 QEMU와 통신수단으로써 남은 선택지는 WebSocket 또는 raw_tcp 채널을 써야 한다.
  • pypkjs process의 생존 여부 + QEMU process 생존여부 + WebSocket server process 생존여부가 조합된 상태에 따라 강제 재 기동 혹은 멈춤을 결정해야 함. 이게 뭥미;;
  • raw_tcp를 사용할 경우 포트 충돌.

④ Audio 품질이 개 구림.

  • PebbleSDK에 포함된 emulator.py 스크립트에 audio 드라이버가 아예 누락됨. 수정해서 써야하는데, 현재로써는 SDK 업데이트마다 배포된 emulator.py 파일을 수정해서 사용해야 한다.
  • 찌그러짐, 늦게 반응, 버퍼 밀림 등의 이상 증상을 보임.
  • Gemini-3.8-Flash High + gpt-5.5 High 조합으로 12시간 넘게 추적 결과 어이없게도 QEMU RTC 모듈의 말도 안되는 시간처리 코드 때문인 것으로 드러남.
  • 같은 맥락으로 Firmware speaker_service.c 에서도 time_ms() 가 개판이라 buffer underrun 및 지터링 발생할 수 밖에 없는 코드로 확인됨.
  • PebbleOS speaker stream과 QEMU pebble-audio 장치의 시작 시퀀스가 맞지 않고, SDL audio driver 대신 PipeWire PulseAudio(pa)로 전환해야 했음.

Audio 문제 원인 파악을 위해 사용한 접근법

아래와 같이 문제를 발생시키는 원인을 특정하거나 좁혀가는 방법을 적용했다.

① 추측 금지

  • Local macOS에 PebbleOS 소스를 clone 받아두고 Pebble Firmware 및 QEMU 동작관련 코드를 확인케 했다.
  • 실제 동작은 Remote uConsole 에서 확인해야 하므로 ssh로 접근하는 데서 많은 토큰 소모가 발생했다.

② 고치라고 요구하는 내용에 더해, 완료시 접하게될 상태를 더 했다

  • “이 요청이 계획에 따라 수행되면 사용자는 다음과 같은 상태를 기대한다” 라는 식으로, 어떤 상태를 기대하는지, 해야하는지를 기술했다. LLM모델이 학습한 방식을 적용한 것이다.
  • 그치만, Gemini 병신은 단정적이고, GPT 고자놈은 뭘 어떻게 해도 멋대로 확장해서 해석한다. 오지게 패고싶다…

③ 문맥 유지를 위해 kontexus-mcp를 사용했다

  • 엄청난 양의 출력을 굳이 문서로 만들 필요없이, LLM이 Chatting 내용을 언제든 검색할 수 있으므로, 토큰을 아끼고 문맥 포화를 최대한 막기위해 세션을 여러번 재시작 하며 이어갔다.
  • LLM 스스로 가설을 나열하고 하나씩 실행하면서 범위를 좁혀갈 때, 문맥포화로 인해 멍청해 지지 않도록 kontexus-mcp가 지속적으로 상태를 유지하고 결과를 기록하여 끊임없이 LLM에게 주입하였다.

그럼에도 불구하고 실제 원인 파악은 인간인 내가 감으로 때려잡았다. LLM ㅅㄲ들은 자꾸만 문제를 확장시키거나 돌아가는데, 어떤 특정 오류 패턴에서 시간 문제인 것 같다는 느낌이 들어 RTC(realtime clock) 코드를 팠던 것이 핵심.

최종 삽질 결과

  • Dynamic Audio Generator 류의 페블 앱을 제외한다면 거의 모든 영역의 페블앱을 uConsole 에서 생성/테스트/디버깅을 할 수 있다.
  • 이 삽질을 하게 만들었던 모르스부호연습기 앱은 안타깝게도 소리가 시작되는 시점에서 음이 갈라져서, 지금은, 실제로 활용을 할 수가 없다.
  • 언젠가 다시 QEMU와 Firmware를 건드릴지 모르겠지만, 어디 격리되서 할 짓이 마땅이 없을 때 해결해볼 꺼리로 남겨둔다.
  • 페블 실제 기기에서는 아무런 문제가 없지만 QEMU에서만 발생하는 문제다.
  • 덕분에 pebble-mcp 도구를 어떻게 만들지에 대해 아이디어가 정리됐다.

pebble-mcp 도구 설계 스케치

  • PYPKJS는 CLI에서 직접 처리하고, QMP 채널로 signal을 날리게, 그리고 도구의 리턴값은 전이된 상태명을 돌리게 하는 방식이 적절할 것 같다.
  • MCP는 앱의 특정 동작을 유도하기 위해 상태전이를 시키는 시그널을 날리는 것으로 족하다.
  • 그리고 --logs 명령 등으로 로그를 파싱하게 하는 것보다 도구 호출의 리턴 값을 전이된 상태명을 전달하면, 상태전이 테이블은 코드에 있으므로 스스로 알아서 테스트를 할 것 이다.

Claude Startups 지원 프로그램에 얽힌 이야기

한가닥 가능성을 발견했을 때 Claude를 기반으로 제품을 만드는 창업자들과 함께하세요. Claude Team 1년 무료 이용권, API 크레딧, AI-native 기업 운영에 도움이 되는 툴 특별 혜택, Anthropic Applied AI 팀과의 오피스 아워 등 다양한 혜택을 누리세요. https://claude.com/ko/programs/startups Claude의 Startups 지원 소식을 듣기 전까지는 공허한 마음에 삐끗한…

Audio 사용이 가능한 pebble-qemu-wasm

uConsole에서 삽질하며 수정한 QEMU Audio 코드를 PR 했다. Eric이 바뻐서 테스트해보기나 할지 몰라, 내 브랜치를 만들어서 몇가지 수정하여 연결한다:

structura.lisp

Abstract structura.lisp는 위상기반 구조판독을 프로그램으로 표현하기 위해 만들기 시작한 Common Lisp 패키지이다. Rust로 작성한 Aspectscope-MCP는 Swiss Ephemeris를 기반으로 Natal, Transit, Progress Chart를 계산하고, Aspect와 Aspect Pattern 및 CSF, Celestigram을 탐색하며, 특정 조건이 성립하는 시점을 찾아내등 LLM을 끼고 기본적인 역할을 해왔다. HTTP 방식의 MCP 서버이기 때문에…

speak-mcp on Zeno.app

Zeno.app - 16GB Mac에서 35B 로컬 AI를 돌리는 흥미로운 실험, 그러나 아직은 실험에 가깝다 최근 Icosa Computing이 공개한 Zeno라는 macOS용 로컬 AI를 설치해 보았다. 회사 설명에 따르면 전체 MoE expert를 메모리에 올리지 않고, 사용되지 않는 expert weight는 SSD에 두었다가 필요한 expert만 RAM으로 읽어들이는 방식을 적용하여…

Saffron Ink – VSCode Theme

Gray background: Github Light Theme - Gray 테마를 기반으로 내 취향에 맞게 수정하였다. 원래 테마보다 좀 더 어두운 배경, 강한 텍스트 대비를 주었다. 선택영역 색상 및 커서 색상을 쪼매 튀게. 각 영역의 border-radius 제거. scrollbar 색상을 배경색 대비 3% 어둡게. https://marketplace.visualstudio.com/items?itemName=andrwj.saffron-ink

HERDR multiplexer

Why & What https://herdr.dev/ ('26-SEP-18) HERDR 사용하기로 결정한 이유는 ① Rust로 제작되었다는 것, ② Plugin 생태계가 꽤 성장세로 보여서, ③ TMUX가 채우지 못하는 부분이 있어서 이다. 깔끔하게 hook을 설치/제거할 수 있다는 점은 좋다. 그러나 integration hook은 설치하지 않는다. 불필요하게 모든 턴마다 hook을 걸어 하는 일은…

Obsidian Web Clipper Setting for ChatGPT

ChatGPT 채팅의 내용을 깨끗하게 Obsidian 앱으로 import 하는 설정. 현재로썬 이게 최선... 구분선 위의 빈줄을 한 칸으로 줄일 방법을 찾다 찾다... 그냥 쓰기로 .. {{selectorHtml:[data-testid*="conversation-turn"] |join:"" |remove_html:("img,svg,picture,source,canvas,object,embed,iframe") |remove_attr:("style,alt,aria-label,aria-description,title,srcset") |markdown |replace:"#### 나의 말:":"---\n\n# 질문:" |replace:"/\\n+#### ChatGPT의 말:/g":"---\n\n## 답변:" |trim}}

페블타임2

https://repebble.com/watch 오랬만 작년 9월에 주문한 Pebble Time 2 , 1년만인 2026년 9월 3일 오후 3시에 DHL로 도착. 본체를 꺼낼 때의 그 묵직함... 꼴도 보기 싫은 기본 스트랩! 배터리 충전후 곧바로 스트랩부터 다른 걸로 끼웠다. 치렁 치렁한 USB 케이블 대신 USB-C 타입을 꽂을 수 있는 어댑터…

LLM Wiki

흥미로워 보였지만 내가 원하는 기능은 없다 LLM Wiki 앱이 사용하는 프로젝트의 루트에 생성된 schema.md, purpose.md 파일들에 추가 설정 없이, AspectScope를 사용하여 분석한 데이터를 Ingest 시킨 결과를 봤더니, 고개가 절래절래 저어졌다. 검증 또는 확인이 필요하다는 문서가 하나의 분석 문서당 적게는 3개, 많게는 8개까지 만들어졌는데 해당 문서를…

command-run MCP

TURA 프로젝트에서 영감 받음 https://github.com/Tura-AI/tura Build agent that uses 80% less token and delivers better results. 핵심 아이디어는 rg, find, sed, git, cargo test 같은 shell command 호출을 개별 단위가 아닌 묶음으로 처리하게 해서 잦은 도구호출로 인한 토큰을 아끼자는 것이다. TURA의 TUI가 멋지긴 한데 당장…

또 한번의 난관을 넘는다

솔직히 어제 좀 기분이 더러웠다. 이런 처짐은 내가 뭔가의 바닥을 꿰뚫어 보고 그 본질을 깨닫고 실망했을 때의 반응이다. functional-thinking-mcp 의 두 도구(tool)를 kontexus-mcp로 편입한 것은 태스크 관리를 맡았던 workgraph-memory-mcp 와의 상호작용이 CLAUDE.md, AGENTS.md 와 같은 규정에 의해 조절되고 있어서 MCP 쪽에서 해결하려는 목적 때문이기도 하지만,…

Harness 체계의 빈틈을 채워 줄 Kontexus-MCP

Kontexus-MCP는 Rust로 포팅된 WGM+FTM 이다 Harness의 빈 틈 Harness의 구조적 전제는 하나이다: 에이전트는 턴 단위로 존재하고, 세션이 끝나면 사라진다. 그 전제 위에서 harness는 실행 중인 것을 잘 다룬다. 컨텍스트를 유지하고, 도구를 붙이고, 병렬로 펼치고, 훅으로 개입 지점을 준다. 그러나 아래와 같은 틈이 있다: 빈자리턴 사이…

제한의 역공

앱의 흐름진행을 오로지 상태테이블에 기반하여 흐름을 결정하고 이어가는 앱KONTEXUS-STUDIO을 만드는 프로젝트를 하고 있다. 이 앱이 필요한 가장 큰 이유는, 이런 방식의 진행을 정말이지 오랬동안 꿈꿔왔기 때문이다. 상태전이 테이블에 의한 앱의 진행은 까다롭지만 거의 모든 최악으로 복잡한 앱이라도 만들 수 있는 근본적인 방법이라고 생각한다. 이 앱은…

Pico-8 Game: Gadget_v2

WordPress Page를 쓰지 않고, 일반 Post 게시물에서 React/Flutter로 만든 페이지/앱을 임베딩하여 마치 WrodPress 게시물의 일부처럼 보이게하고 싶었다. 페이지나 포스트 하나 하나가 다른 앱을 보는 것인가 착각할 정도로 독립된 영역에서 동작하되, 업데이트와 관리가 가능해야한다는 요구조건을 만족시키는 방법을 실험하기 위한 게시물이다. 겉으로는 단순히 WASM으로 변환된 Pico8 게임이긴…

WP 방어

십수년간 업데이트 해오던 내 자료;; 홀라당 날려먹고 백업도 없어서 블로그를 내 팽겨친채 있다가, 다시 운영하려 할 때 걸림돌이 보안 문제였다.  워드프레스 사이트를 운영해본 사람들은 얼마나 공격질을 해대는지 이해할 것이다. 블로그를 내팽겨친 건 그 보안문제를 해결할 마땅한 방법이 없었기도 하거니와 시간내서 그 정도를 만들고 싶지도 않았기…