Skip to main content
Glama
README.md
# BIP

A desk pet on the [Waveshare ESP32-S3-LCD-1.47B](https://www.waveshare.com/product/esp32-s3-lcd-1.47b.htm): a 172×320 face, one RGB LED, and a USB link a computer can drive. Cursor, Claude, and Codex connect through the built-in MCP server.

The board is ESP32-S3R8, 16 MB flash, 8 MB PSRAM, ST7789, QMI8658 IMU, and an 18-pin header for add-ons. Tilting the board turns the face. The only onboard key is BOOT (GPIO0). Hold BOOT while resetting to enter the ROM download mode.

## Flash

Firmware builds in Docker with ESP-IDF 5.5.1. The image is pulled on first use. Output stays in `.deps/`, which is not part of the source tree.

```bash
bash scripts/build.sh
pip install esptool          # or: python3 -m venv .venv && .venv/bin/pip install esptool
bash scripts/flash.sh /dev/cu.usbmodem1101
```

On Linux the port is usually `/dev/ttyACM0`.

USB is enough. Wi-Fi is optional and only for the UDP state packet:

```bash
python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/python scripts/provision.py --port /dev/cu.usbmodem1101 --ssid "Your2.4GHzSsid"
```

The password is written to the board and is not kept in the repo. The pairing file is `.deps/pet-device.json` (mode 600).

## MCP

Install the bridge once:

```bash
python3 -m venv .venv
.venv/bin/pip install -e .
```

This checkout already has a project config at `.cursor/mcp.json`. Restart Cursor and the `esp32-pet` server is available. Tools:

| Tool | What it does |
| --- | --- |
| `bip_set_state` | Face, gaze, mouth, blink |
| `bip_play_timeline` | Timed face keyframes |
| `bip_speak` | Speak on the Mac with `say` and move the mouth with the audio |
| `bip_get_status` | Port, last face, recent button presses |

Set `PET_USB_PORT` when more than one USB serial device is plugged in.

Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "esp32-pet": {
      "command": "/absolute/path/to/esp32-pet/.venv/bin/python",
      "args": ["-m", "bip_bridge.mcp_server"],
      "cwd": "/absolute/path/to/esp32-pet"
    }
  }
}
```

Codex (`~/.codex/config.toml`):

```toml
[mcp_servers.esp32-pet]
command = "/absolute/path/to/esp32-pet/.venv/bin/python"
args = ["-m", "bip_bridge.mcp_server"]
cwd = "/absolute/path/to/esp32-pet"
```

The server speaks MCP over stdio. It prints protocol messages on stdout and nothing else.

## Voice and the local page

Voice needs ElevenLabs. Copy `.env.example` to `.env` and fill in the key and agent id, then:

```bash
.venv/bin/pip install -e ".[voice]"
pnpm live
pnpm live -- --ptt    # BOOT (GPIO0) is the talk key
pnpm demo             # macOS say, no API key
pnpm dev              # dashboard at http://127.0.0.1:5173
```

`pnpm dev` needs Node 22 and `pnpm install`. The page is a host preview of the face plus buttons for the onboard key and three add-on slots.

## Pins

| Use | GPIO |
| --- | --- |
| LCD MOSI, SCLK, CS, DC, RST, backlight | 45, 40, 42, 41, 39, 46 |
| RGB LED | 38 |
| IMU SDA, SCL | 48, 47 |
| TF card CMD, SCK, D0, D1, D2, D3 | 15, 14, 16, 18, 17, 21 |
| USB D−, D+ | 19, 20 |
| BOOT | 0 |

Free for add-ons: GPIO1, GPIO2, GPIO4–GPIO13. GPIO14–GPIO18 and GPIO21 are the TF slot. Leave them if the card should keep working.

## Add-ons

An add-on is one C file and one line in `firmware/pet/main/addon.c`. `firmware/pet/addons/example_button.c` is a button on GPIO2 that reports button 4 (cycles the LED). It is not compiled until you add it to `SRCS` in `firmware/pet/main/CMakeLists.txt` and register `&example_button_addon`.

`pet_emit_button(n)` reuses the face actions: 1 pet, 2 cycle mood, 3 agent, 4 LED mode. The onboard BOOT key is separate. It always cycles the mood and is reported to the host as button 1.

The host hears one line per press:

```text
PET_BTN button=1 mood=HAPPY led=0 energy=95
```

A JSON line on the same serial port sets the face (`mood`, `gazeX`, `gazeY`, `flags`, `energy`, `title`, `subtitle`). `STATUS` answers with `PET_STATUS` and `PET_IP`.

## Layout

```text
firmware/pet/     ESP-IDF project for this board
bip_bridge/       USB controller and the MCP server
scripts/          build, flash, provision, voice
src/              optional local dashboard
```

`.env`, `.venv`, `.deps`, and `node_modules` stay on your machine.

TDQS

B3.4/5.0

Scored across 4 tools

Disambiguation4/5

Each tool targets a distinguishable action: setting a single state, playing a keyframe timeline, reading status, and speaking text. There is mild overlap since bip_set_state also controls speaking mouth animation while bip_speak synchronizes mouth/gaze/blink, so an agent might hesitate over which to use for mouth motion, but the descriptions clarify the intended split.

Naming Consistency5/5

All four tools use a consistent snake_case pattern with the shared 'bip_' prefix and a verb-led name (set_state, play_timeline, get_status, speak). The convention is predictable and readable across the whole surface.

Tool Count4/5

Four tools is on the lean side but each covers a distinct facet (state, sequence playback, status, speech) of driving a physical pet device. It is slightly thin — no separate stop/reset tool — but nothing feels redundant.

Completeness3/5

The core actions (set expression, play animation, speak, read status) are covered, but there is no way to stop or interrupt a running timeline, reset to a known idle state, or enumerate valid expressions/gazes. These gaps will force agents to improvise when recovering from an animation or discovering valid state values.

Maintenance

ActivityMaintained
ResponsivenessSyncing