# ESP32 iPhone Agent: 맥 없이 에이전트에게 아이폰 맡기기
# ESP32 iPhone Agent: handing an iPhone to an agent without a Mac

- Site: https://select1.dev/2026-10-13 (English), https://select1.dev/ko/2026-10-13 (Korean)
- Entry: 003
- Published: 2026-10-13 (KST). Launch date: 2026-10-12
- Pick: ESP32 iPhone Agent <https://github.com/onehumanbeing/esp32-iphone-agent> (source: GitHub)
- Verified: ran
- What: ESP32 iPhone Agent 는 비전 모델이 아이폰을 직접 조작하게 하는 오픈소스 프로덕트다. 7달러짜리 ESP32-S3 보드를 아이폰에 꽂아 마우스와 키보드 역할을 맡기고, 화면은 AirPlay 미러링으로 읽는다.
- What (en): ESP32 iPhone Agent is an open-source product that lets a vision model operate an iPhone directly. It uses a $7 ESP32-S3 board as the phone's mouse and keyboard and reads the screen over AirPlay mirroring.
- Why: 오늘의 픽으로 선정한 이유는 맥도 개발자 계정도 없이 에이전트가 아이폰을 화면과 포인터만으로 조작하게 만들어서다.
- Why (en): It is today's pick because it lets an agent operate an iPhone the way a person does, with no Mac and no developer account.
- Verdict: 서버는 작동한다. 명령 네 줄로 설치와 테스트가 끝난다.
- Verdict (en): The server runs. Four commands install and test it.
- Tags: agent, computer-use, iphone, esp32, hardware, ai-built

## Checked against the README

- [pass] 보드에는 로직이 없고, 서버가 TCP 로 한 줄씩 명령을 보내면 보드는 ok 나 err 로 답한다 — 보드를 흉내 낸 TCP 클라이언트를 7300 포트에 접속시키고 /api/raw 로 ping 과 move 10 -5 를 보냄. 클라이언트가 두 줄을 그대로 받았고 서버 로그에 "move 10 -5 -> ok (0 ms)" 가 남음
- [pass] OpenAI 호환 채팅 API 라면 어느 비전 모델이든 사용할 수 있다 — llm.py 가 /chat/completions 에 image_url 데이터 URL 로 보내고, 400 응답에 max_completion_tokens 가 있으면 토큰 인자를 바꿔 재시도. tests/test_llm.py 4개 통과
- [pass] 행동마다 화면이 실제로 바뀌었는지 확인해 모델에 알린다 — vision.changed 가 포인터와 상태 표시줄을 가리고 비교하고, agent._step 이 changed 를 기록. tests/test_vision.py 8개와 test_agent.py 의 반복 감지 테스트 2개 통과

## Measured

- 서버 콜드 스타트(실행부터 /api/status 200 까지): 583 ms (python -m hidagent 를 실행한 뒤 100 ms 간격으로 curl 해 첫 200 응답까지 잰 시간)

## Progress (days after launch; full history https://select1.dev/2026-10-13/progress)

- day 3 (due 2026-10-15) [missed] Progress checks started on 2026.10.16. This date had already passed.
- day 7 (checked 2026-10-19) [active] commits since launch 40, stars 3107, releases 1, contributors 4, last commit 2026-10-18
  - The agent now runs in an iPhone app, with no computer. The board receives the iPhone's screen over AirPlay directly and talks to the app over Bluetooth Low Energy (BLE).
  - OpenRouter can be set as the model provider, with a searchable model list. A failed run shows its cause and reconnects before retrying.
  - v0.1.0: the setup before the redesign is kept as the first tagged release. In that setup, the iPhone mirrors its screen over AirPlay to a Mac or Linux computer.

## 한국어

## 무엇이 문제인가

README 에 따르면 아이폰 자동화는 대부분 XCTest 나 WebDriverAgent 를 거친다. 그 방식에는 네 가지가 필요하다.

- 맥과 Xcode.
- 서명된 도우미 앱.
- 애플 개발자 계정. 무료 계정의 서명은 7일마다 만료된다.
- 접근성 트리. 앱은 이 기능으로 화면의 버튼과 글자를 목록으로 알려 준다. 게임처럼 앱이 목록을 주지 않으면 통하지 않는다.

README 의 비교표에는 다른 프로젝트 여덟 개가 있다. ESP32 블루투스 마우스나 HDMI 캡처 보드를 사용하는데, 대부분 맥이나 별도 캡처 장비가 필요하거나 폰에 앱을 설치해야 한다.

## 어떻게 해결했나

ESP32 iPhone Agent 는 사용자와 같은 방법으로만 아이폰을 다룬다. 화면은 AirPlay 미러링으로 읽는다. 아이폰이 컴퓨터의 UxPlay 로 화면을 보내고 GStreamer 가 그것을 JPEG 프레임으로 바꾼다. 입력은 USB HID로 넣는다. 7달러짜리 ESP32-S3 보드가 아이폰 USB 단자에 꽂혀 마우스와 키보드로 인식되고, iOS 의 AssistiveTouch 를 활성화하면 화면에 둥근 포인터가 생긴다. 클릭이 탭이고 드래그가 스와이프다. 보드에는 로직이 없다. Wi-Fi 로 서버에 접속해 move, click, type 같은 한 줄 명령을 받아 실행할 뿐이다. 모델은 OpenAI 호환 비전 모델이면 무엇이든 된다. 계획자가 목표와 화면으로 짧은 계획을 작성하고, 행동자가 새 스크린샷마다 다음 행동을 고른다. 폰에는 아무것도 설치하지 않는다.

## 무엇이 다른가

포인터가 상대 좌표라 서버는 포인터가 어디 있는지 알 수 없다. 그래서 탭 전에 포인터를 12 단위 흔들어 미러링 화면에서 찾아 보정하고, 행동 뒤에는 화면이 실제로 바뀌었는지 픽셀 차이로 확인해 빗나간 탭을 모델에 알린다. 같은 행동이 세 번 반복되면 다시 계획한다. 트레이드오프는 속도와 보이지 않는 화면이다. README 에 따르면 속도는 DGX Spark 두 대로 행동당 약 1.3초이고, 잠금 화면과 Apple Pay, DRM 영상은 미러링되지 않아 에이전트가 볼 수 없다. 키보드 입력은 ASCII 뿐이다.

## 어떻게 만들었나

- 커밋 20개가 2026-10-12 하루에 몰려 있고, 마지막 커밋은 기록 전날 밤이다.
- 그중 16개에 Co-authored-by 트레일러로 Claude Opus 5.5 가 적혀 있다.
- README 의 Quick start 첫 항목은 지시문이고, 사용자가 아니라 코딩 에이전트에게 붙여 넣게 되어 있다. 지시문에는 보드에 펌웨어를 기록하고, UxPlay 와 서버를 설치하고, .env 를 작성하라고 적혀 있다.
- 파이썬 서버는 1,100줄 정도, 펌웨어는 Arduino 스케치 155줄, 테스트는 28개다.

## 실행해 보니

오늘은 서버만 확인했다. 보드도 아이폰도 없어 에이전트가 실제로 탭하는지는 보지 못했다. 서버는 아래 순서로 시험했다.

1. 설치와 테스트. `python3 -m venv` 와 `pip install -e ".[dev]"` 로 18초가 걸렸고, 의존성이 FastAPI, uvicorn, httpx, Pillow 네 개라 가상환경은 91 MB 다. 테스트 28개는 1.45초에 통과했다.
2. 서버 실행. `.env` 에 가짜 모델 주소를 적고 서버를 실행하니 583 ms 만에 웹 UI 가 열렸다. UxPlay 가 없어 화면 수신 스레드는 FileNotFoundError 로 종료됐지만 서버는 그대로 작동했다.
3. 가짜 보드. 보드 프로토콜을 확인하려고 TCP 클라이언트를 만들어 7300 포트에 접속했다. 이 클라이언트는 무엇을 받든 ok 만 답한다. 서버는 그것을 보드로 받아들여 상태가 device true 로 바뀌었고, 웹 UI 의 첫 단계 Plug in the controller 가 초록으로 바뀌었다.
4. 명령 전달. 디버그 전용 /api/raw 로 ping 과 move 10 -5 를 보내자 두 줄이 그대로 클라이언트에 도착했다. README 의 설명대로 보드에는 로직이 없다.
5. 거절 응답. 보정 없이 탭을 요청하면 409 pointer not calibrated, 화면 없이 에이전트를 시작하면 409 no phone screen 으로 거절한다.
6. 스크린샷 비교. 코드에서 비교 방식을 하나 더 확인했다. 포인터와 상태 표시줄을 가리고 나머지 픽셀만 비교하며, 체크 표시 하나 정도의 작은 변화도 여섯 픽셀 이상이면 바뀐 것으로 판단한다.

```
status 200 after 583 ms
hidagent.device board connected from ('127.0.0.1', 50552) hello power-on 1234
hidagent.device move 10 -5 -> ok (0 ms)
"POST /api/tap HTTP/1.1" 409 Conflict
```

## 확인하지 못한 것

- 에이전트 루프 전체. 보드에 펌웨어를 기록하고 아이폰을 미러링해야 에이전트가 계획을 작성하고 탭할 수 있다. README 의 행동당 1.3초도 측정하지 못했다.
- phone.py 의 보정. 포인터를 흔들어 찾을 때 밝은 배경과 어두운 배경에서 모두 통하는지, AssistiveTouch 의 포인터 속도를 바꾸면 얼마나 어긋나는지 다음에 확인하자.

## 판정

서버는 작동한다. 명령 네 줄로 설치와 테스트가 끝난다. 다만 보드와 아이폰이 없으면 에이전트가 화면을 보지도 탭하지도 못한다. 맥 없이 컴퓨터 한 대와 7달러짜리 보드로 아이폰을 에이전트에게 맡겨 보고 싶고, 행동마다 1초 넘게 기다려도 되는 경우에 써 볼 만하다. 작업 중에 잠금 화면이나 결제 화면을 거쳐야 한다면 이 프로덕트는 적합하지 않다. 그런 화면은 미러링되지 않아 에이전트에게 보이지 않는다.

## 얻어갈 것

- 화면이 버튼과 글자의 목록을 알려 주지 않으면(게임 등), 화면 픽셀과 입력 장치만으로도 자동화할 수 있는지 검토해 보자. 이 프로덕트는 AirPlay 와 USB HID 로 그렇게 했다.
- 프로덕트에 하드웨어가 필요하다면, 하드웨어 없이도 서버가 실행되고 테스트가 통과하는지부터 확인하면 된다. 여기서는 가짜 보드가 ok 만 답해도 프로토콜까지 확인됐다.
- 에이전트 루프에서는 행동마다 화면이 실제로 바뀌었는지 확인하자. 여기서는 여섯 픽셀 기준의 비교가 빗나간 탭을 모델에 알린다.

### 용어

- HID: 마우스와 키보드 같은 입력 장치의 USB 규격. 운영체제가 별도 드라이버 없이 인식한다.
- 코딩 에이전트: 개발자 대신 코드를 읽고 고치고 명령을 실행하는 AI 프로그램. Claude Code, Codex 등이 있다.
- 펌웨어: 기기에 내장돼 하드웨어를 제어하는 프로그램. TV 같은 기기에서는 운영체제를 포함한 시스템 프로그램 전체를 가리킨다.

## English

## The problem

According to the README, most iPhone automation goes through XCTest or WebDriverAgent. That path needs the following.

- A Mac and Xcode.
- A signed helper app.
- An Apple developer account. With a free account, the signature expires every 7 days.
- The accessibility tree, the list of buttons and text an app exposes to automation tools. Apps such as games do not provide one, so the method fails there.

The README's comparison table lists eight other projects that use an ESP32 Bluetooth mouse or an HDMI capture board. Most of them require a Mac, separate capture hardware, or an app installed on the phone.

## How it solves it

ESP32 iPhone Agent handles the iPhone only the way a user does. The iPhone mirrors its screen to UxPlay on the computer over AirPlay. GStreamer converts the mirrored screen into JPEG frames. It sends input over USB HID. The iPhone recognizes a $7 ESP32-S3 board plugged into its USB port as a mouse and keyboard. A round pointer appears on screen when iOS AssistiveTouch is on. A click is a tap and a drag is a swipe. The board holds no logic. It connects to the server over Wi-Fi and executes one-line commands such as move, click and type. The model can be any OpenAI-compatible vision model. A planner writes a short plan from the goal and the screen, and an actor picks the next action from each new screenshot. Nothing is installed on the phone.

## What is different

The pointer uses relative coordinates, so the server cannot know its position. Before a tap, the server nudges the pointer by 12 units and finds it on the mirrored screen. It then corrects the pointer position. After each action, it compares pixels to check whether the screen changed. It tells the model about a missed tap. If the same action repeats three times, the agent makes a new plan. The trade-offs are speed and the screens the agent cannot see. The README reports about 1.3 s per action on two DGX Sparks. The lock screen, Apple Pay and DRM video are not mirrored, so the agent cannot see them. Keyboard input is ASCII only.

## How it was built

- Twenty commits were made on 2026-10-12. The last one came the night before this entry.
- Sixteen of them carry Claude Opus 5.5 in a Co-authored-by trailer.
- The first item in the README's Quick start is a prompt to paste into a coding agent. The prompt tells the agent to flash the board, install UxPlay and the server, and write .env.
- The Python server is about 1,100 lines, and the firmware is a 155-line Arduino sketch. There are 28 tests.

## Running it

Today I checked the server only. With no board and no iPhone, I did not see the agent tap anything.

1. Install and tests. Installation took 18 seconds with `python3 -m venv` and `pip install -e ".[dev]"`. There are four dependencies (FastAPI, uvicorn, httpx and Pillow), and the virtual environment is 91 MB. The 28 tests passed in 1.45 seconds.
2. Starting the server. With a fake model address in `.env`, the server opened its web UI 583 ms after launch. Without UxPlay, the screen-capture thread ended with FileNotFoundError, but the server kept working.
3. A fake board. To check the board protocol, I wrote a TCP client that connects to port 7300 and answers nothing but ok. The server accepted it as the board, and the status changed to device true. The first step in the web UI, Plug in the controller, turned green.
4. Passing commands. I sent ping and move 10 -5 through the debug-only /api/raw. The client received both lines unchanged. As the README says, the board holds no logic.
5. Refusals. The server refuses a tap without calibration (409 pointer not calibrated). It also refuses to start the agent without a screen (409 no phone screen).
6. Screenshot comparison. I also checked how the code compares screenshots. It masks the pointer and the status bar and compares the remaining pixels. It treats a difference of six or more pixels as a change. Even a change as small as one checkmark counts.

```
status 200 after 583 ms
hidagent.device board connected from ('127.0.0.1', 50552) hello power-on 1234
hidagent.device move 10 -5 -> ok (0 ms)
"POST /api/tap HTTP/1.1" 409 Conflict
```

## Not checked

- The full agent loop. The board has to be flashed and an iPhone mirrored before the agent can plan and tap. The README's 1.3 s per action was not measured either.
- The calibration in phone.py. Next time, check whether the nudge method finds the pointer on both light and dark backgrounds. Also check how far the pointer drifts after the AssistiveTouch pointer speed changes.

## Verdict

The server runs. Four commands install and test it. Without the board and a phone, though, the agent can neither see a screen nor tap one. It fits if you want to hand an iPhone to an agent with one computer and a $7 board instead of a Mac. You also need to accept more than a second per action. This product is not the right choice for work that passes through screens that are not mirrored, such as the lock screen or a payment screen.

## What to take away

- If the screens you automate do not expose a list of their buttons and text (games, for example), consider an approach that uses only screen pixels and an input device. This product does it with AirPlay and USB HID.
- If hardware is part of the product, check first that the server runs and the tests pass without it. Here a fake board that only answers "ok" was enough to confirm the protocol.
- Add a step in the agent loop that checks whether the screen changed after each action. Here a comparison with a six-pixel threshold tells the model about a missed tap.

### Terms

- HID: The USB standard for input devices such as mice and keyboards, which operating systems recognize without a separate driver.
- coding agent: An AI program that reads and edits code and runs commands for a developer. Claude Code and Codex are examples.
- firmware: The program built into a device to control its hardware. On devices such as TVs, it means all the system software, including the operating system.

## Launches the same day

- PS2 web recomp <https://github.com/snuri00/ps2-web-recomp> (Show HN, 239): PS2 게임을 WebAssembly 로 정적 재컴파일해 브라우저에서 실행한다. 스레드에서는 코드를 AI 로 만들었는지를 두고 논쟁이 많다.
- Atticus <https://github.com/SaarthurR/atticus-mac> (Show HN, 38): 음성 비서. 맥에서 단축키를 누르고 말하면 창 정리와 파일 찾기를 해 준다. Claude Code 의 구독으로 작동해 API 키가 필요 없다.
- Plimsoll <https://github.com/plimsollmark/plimsoll> (Show HN, 15): Go 데몬. 에이전트의 코드를 격리해 실행하고 결과마다 격리 등급을 적어 준다. 등급이 모자라면 실행하지 않는다.
- Skills Hub <https://github.com/qufei1993/skills-hub> (Show HN, 10): 데스크톱 앱. 에이전트 스킬을 한 번 설치하면 코딩 도구 48개에 동기화한다.
- LLMxRay <https://github.com/LogneBudo/llmxray> (Show HN, 10): 오픈소스 관측 도구. 로컬 LLM 의 토큰 속도, 캐시 적중, 비용을 실시간으로 보여 준다.
- new-term-radar <https://github.com/zhuyansen/new-term-radar> (GitHub, 500): 레이더. AI 이름이 X 와 Hugging Face, GitHub 에 처음 나타나면 키워드 도구보다 먼저 잡아 매일 보고서로 만든다.
- esp32-iphone-agent <https://github.com/onehumanbeing/esp32-iphone-agent> (GitHub, 473): 7달러짜리 ESP32-S3 보드와 AirPlay 미러링으로 비전 모델이 실제 아이폰을 조작하게 한다. 맥도 개발자 계정도 필요 없다.
- juliobsz/skills <https://github.com/juliobsz/skills> (GitHub, 168): 에이전트 스킬 모음. 한 줄 지시로 수상작 수준의 웹사이트를 만들게 한다.
- Google EmbeddingGemma 2 <https://www.producthunt.com/products/embeddinggemma-2> (Product Hunt, 229): 멀티모달 임베딩 모델. 구글이 가중치를 공개했고 가볍다.
- ngram 2.0 <https://www.producthunt.com/products/ngram> (Product Hunt, 210): 영상 제작 도구의 2.0 판. 설명 영상, 화면 녹화, AI 아바타 영상을 만든다.
- 해줘 로컬 (Haejoe Local) <https://disquiet.io/products/haejoe-local> (Disquiet): 윈도우 앱. AI 구독료를 이미 지불하고 있다면 그 구독으로 PC 의 프로젝트를 맡기고, 조직도처럼 일을 나눈다.
- Blabee <https://disquiet.io/products/blabee> (Disquiet): 작은 맥 패널. Codex 작업 결과를 확인하고 다음 지시를 같은 세션으로 보낸다.

## Getting started

- Steps: git clone https://github.com/onehumanbeing/esp32-iphone-agent ; cd esp32-iphone-agent/server && python3 -m venv .venv && .venv/bin/pip install -e . ; cp .env.example .env && set -a && . ./.env && set +a && .venv/bin/python -m hidagent
- Needs: Python 3.10 이상, XIAO ESP32S3 보드(약 7달러)와 arduino-cli, ESP32 코어 3.x, UxPlay(AirPlay 수신기)와 GStreamer, OpenAI 호환 비전 모델 API. 클라우드 API 또는 자체 운영 서버, 아이폰의 AssistiveTouch 설정과 2.4 GHz Wi-Fi
- Cost: MIT 라이선스, 무료. 비용은 보드 약 7달러와 비전 모델 API 사용료다. README 에 따르면 개발에는 로컬 모델을 NVIDIA DGX Spark 두 대로 운영했다.
- Note: 보드에는 hardware/flash.sh 로 펌웨어를 기록하고, 서버는 http://<컴퓨터 IP>:8890 에서 웹 UI 를 연다. 오늘은 보드와 아이폰 없이 서버만 실행했다.
