From 5dcacc23ab68d84200ac7c07f3fb3bd5ab7561bc Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 3 Feb 2026 12:11:11 -0700 Subject: [PATCH] Add comprehensive README with ESP32 capabilities and E2E testing guide - Project overview and architecture diagram - ESP32 hardware requirements and capabilities - Full MCP tool reference (connection, classic BT, BLE/GATT, events) - Quick start guide and Claude Code integration - E2E testing instructions with latest results (v5: 76/76 PASS) - Links to detailed documentation --- README.md | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ebdf93 --- /dev/null +++ b/README.md @@ -0,0 +1,221 @@ +# mcbluetooth-esp32 + +ESP32 Bluetooth test harness MCP server — UART-controlled peripheral for automated E2E Bluetooth testing. + +## Overview + +This project turns an ESP32 into a programmable Bluetooth peripheral that can be controlled via MCP (Model Context Protocol). Combined with [mcbluetooth](https://github.com/supported-systems/mcbluetooth) (Linux BlueZ MCP server), it enables fully automated end-to-end Bluetooth testing orchestrated by an LLM. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ LLM (Claude, etc.) │ +│ Orchestrates both MCP servers │ +└─────────────────────────┬───────────────────────────────────────┘ + │ + ┌───────────────┴───────────────┐ + │ │ + ┌───────┴───────┐ ┌───────┴────────┐ + │ mcbluetooth │ │mcbluetooth-esp32│ + │ (bt_* tools)│ │ (esp32_* tools) │ + └───────┬───────┘ └───────┬────────┘ + │ │ + D-Bus/BlueZ Serial/UART + │ │ + ┌───────┴───────┐ ┌───────┴────────┐ + │ Linux Host │◄── Bluetooth ──►│ ESP32 │ + │ (hci0/1) │ (over air) │ (peripheral) │ + └───────────────┘ └────────────────┘ +``` + +## ESP32 Capabilities + +The ESP32 can emulate various Bluetooth devices for testing: + +| Capability | Description | +|------------|-------------| +| **Classic BT Pairing** | All 4 SSP modes: Just Works, Numeric Comparison, Passkey Entry, Legacy PIN | +| **BLE GATT Server** | Dynamic service/characteristic creation at runtime | +| **Device Personas** | Presets for headset, speaker, keyboard, sensor, phone | +| **IO Capabilities** | Configurable: `no_io`, `display_only`, `display_yesno`, `keyboard_only`, `keyboard_display` | +| **Event Reporting** | Real-time events: pair_request, pair_complete, gatt_read, gatt_write, gatt_subscribe | + +### Hardware Requirements + +- **Original ESP32** (ESP32-D0WD or similar) — must have Classic Bluetooth support +- ESP32-S3, C3, H2, S2 will NOT work (no BR/EDR radio) +- USB cable for serial communication + +### Verified Hardware + +| Property | Value | +|----------|-------| +| Chip | ESP32-D0WD-V3 (rev 3.1) | +| Features | Wi-Fi, BT Classic + BLE, Dual Core 240MHz | +| Flash | 4MB | +| Protocol | NDJSON over UART @ 115200 baud | + +## Quick Start + +### 1. Flash the firmware + +```bash +# Install ESP-IDF v5.x first (see docs/hardware-setup.md) +cd firmware +idf.py set-target esp32 +idf.py -p /dev/ttyUSB0 flash +``` + +### 2. Run the MCP server + +```bash +# Install and run +uvx mcbluetooth-esp32 + +# Or with explicit port +ESP32_SERIAL_PORT=/dev/ttyUSB0 uvx mcbluetooth-esp32 +``` + +### 3. Add to Claude Code + +```bash +claude mcp add mcbluetooth-esp32 -- uvx mcbluetooth-esp32 +``` + +### 4. Verify connection + +```python +esp32_connect() # → connected=true, ready=true +esp32_ping() # → {pong: true} +esp32_get_info() # → chip, fw_version, bt_mac +``` + +## MCP Tools + +### Connection & System +| Tool | Description | +|------|-------------| +| `esp32_connect` | Open serial connection to ESP32 | +| `esp32_disconnect` | Close serial connection | +| `esp32_ping` | Verify UART link is alive | +| `esp32_status` | Get current BT/BLE state | +| `esp32_get_info` | Get chip model, firmware version, MAC | +| `esp32_reset` | Reboot the ESP32 | + +### Configuration +| Tool | Description | +|------|-------------| +| `esp32_configure` | Set device name, IO capabilities, PIN | +| `esp32_set_ssp_mode` | Configure SSP pairing mode with optional auto-accept | +| `esp32_load_persona` | Load preset device profile (headset, speaker, etc.) | + +### Classic Bluetooth +| Tool | Description | +|------|-------------| +| `esp32_classic_enable` | Enable BR/EDR radio | +| `esp32_classic_disable` | Disable BR/EDR radio | +| `esp32_classic_set_discoverable` | Make device visible for pairing | +| `esp32_classic_pair_respond` | Accept/reject incoming pairing | + +### BLE / GATT +| Tool | Description | +|------|-------------| +| `esp32_ble_enable` | Enable BLE radio | +| `esp32_ble_disable` | Disable BLE radio | +| `esp32_ble_advertise` | Start/stop BLE advertising | +| `esp32_ble_set_adv_data` | Configure advertisement data | +| `esp32_gatt_add_service` | Create GATT service | +| `esp32_gatt_add_characteristic` | Add characteristic to service | +| `esp32_gatt_set_value` | Set characteristic value | +| `esp32_gatt_notify` | Send notification to subscribers | +| `esp32_gatt_clear` | Remove all GATT services | + +### Events +| Tool | Description | +|------|-------------| +| `esp32_get_events` | Retrieve event history | +| `esp32_wait_event` | Block until specific event occurs | +| `esp32_clear_events` | Clear event history | + +## E2E Testing + +Run automated tests using Claude CLI in headless mode: + +```bash +# Setup test environment +mkdir -p /tmp/bt-e2e-test && cd /tmp/bt-e2e-test +git init + +# Create MCP config with both servers +cat > .mcp.json << 'EOF' +{ + "mcpServers": { + "esp32": { + "type": "stdio", + "command": "uvx", + "args": ["mcbluetooth-esp32"], + "env": {"ESP32_SERIAL_PORT": "/dev/ttyUSB0"} + }, + "bluez": { + "type": "stdio", + "command": "uvx", + "args": ["mcbluetooth"] + } + } +} +EOF + +# Run test suite +PROMPT=$(cat tests/prompts/test-prompt-v5.md) +claude -p "$PROMPT" \ + --mcp-config .mcp.json \ + --allowedTools "mcp__esp32__*,mcp__bluez__*" \ + --output-format json +``` + +### Test Coverage (v5 - 76 tests) + +| Phase | Tests | Coverage | +|-------|-------|----------| +| ESP32 Connection | 1-4 | connect, ping, info, status | +| BlueZ Adapter | 5-8 | list, info, pairable, discoverable | +| Classic BT + SSP | 9-24 | Full pairing workflow with auto-accept | +| BLE GATT Setup | 30-42 | Battery Service, Environmental Sensing | +| HCI Capture | 43-55 | btsnoop capture and analysis | +| GATT Operations | 56-63 | read, write, notify, subscribe | +| Cleanup | 64-76 | Adapter management, reset | + +### Recent Test Results + +| Version | Tests | Result | Duration | +|---------|-------|--------|----------| +| v4 | 71 | 66 PASS, 5 PARTIAL | 8.6 min | +| v5 | 76 | **76/76 PASS (100%)** | 7.6 min | + +## Documentation + +| Document | Description | +|----------|-------------| +| [hardware-setup.md](docs/hardware-setup.md) | ESP-IDF installation, flashing, wiring | +| [protocol-spec.md](docs/protocol-spec.md) | NDJSON UART protocol specification | +| [test-scenarios.md](docs/test-scenarios.md) | Manual E2E test procedures | +| [automated-e2e-testing.md](docs/automated-e2e-testing.md) | Claude CLI headless testing guide | + +## Development + +```bash +# Install dev dependencies +uv sync --all-extras + +# Run unit tests +uv run pytest tests/ -v --ignore=tests/integration + +# Run integration tests (requires ESP32) +ESP32_SERIAL_PORT=/dev/ttyUSB0 uv run pytest tests/integration/ -v + +# Lint +uv run ruff check src/ +``` + +## License + +MIT