Ryan Malloy 6398a5223a ESP32 Bluetooth test harness MCP server
UART-controlled ESP32 peripheral for automated E2E Bluetooth testing.
Dual-mode (Classic BT + BLE) via Bluedroid on original ESP32.

Firmware (ESP-IDF v5.x, 2511 lines C):
- NDJSON protocol over UART1 (115200 baud)
- System commands: ping, reset, get_info, get_status
- Classic BT: GAP, SPP, all 4 SSP pairing modes
- BLE: GATTS, advertising, GATT service/characteristic management
- 6 device personas: headset, speaker, keyboard, sensor, phone, bare
- Event reporter: thread-safe async event queue to host

Python MCP server (FastMCP, 1626 lines):
- Async serial client with command/response correlation
- Event queue with wait_for pattern matching
- Tools: connection, configure, classic, ble, persona, events
- MCP resources: esp32://status, esp32://events, esp32://personas

Tests: 74 unit tests passing, 5 integration test stubs (skip without hardware)
2026-02-02 15:12:28 -07:00

63 lines
1.7 KiB
Makefile

.PHONY: help flash monitor menuconfig build clean test lint format serve
IDF_PATH ?= $(HOME)/esp/esp-idf
SERIAL_PORT ?= /dev/ttyUSB0
SERIAL_BAUD ?= 115200
FIRMWARE_DIR := firmware
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
# --- Firmware ---
build: ## Build ESP32 firmware
cd $(FIRMWARE_DIR) && idf.py build
flash: ## Flash firmware to ESP32
cd $(FIRMWARE_DIR) && idf.py -p $(SERIAL_PORT) flash
monitor: ## Open serial monitor (ESP-IDF)
cd $(FIRMWARE_DIR) && idf.py -p $(SERIAL_PORT) monitor
flash-monitor: ## Flash and open monitor
cd $(FIRMWARE_DIR) && idf.py -p $(SERIAL_PORT) flash monitor
menuconfig: ## Open ESP-IDF menuconfig
cd $(FIRMWARE_DIR) && idf.py menuconfig
clean-firmware: ## Clean firmware build
cd $(FIRMWARE_DIR) && idf.py fullclean
# --- Python MCP Server ---
serve: ## Run MCP server (for development)
uv run mcbluetooth-esp32
test: ## Run Python tests
uv run pytest tests/ -v
test-unit: ## Run unit tests only (no hardware)
uv run pytest tests/ -v --ignore=tests/integration
test-integration: ## Run integration tests (requires ESP32)
uv run pytest tests/integration/ -v
lint: ## Lint Python code
uv run ruff check src/ tests/
format: ## Format Python code
uv run ruff format src/ tests/
# --- Utilities ---
raw-monitor: ## Raw serial monitor with screen
screen $(SERIAL_PORT) $(SERIAL_BAUD)
ping: ## Quick ping test via Python
uv run python -c "import asyncio; from mcbluetooth_esp32.serial_client import SerialClient; \
async def t(): \
c = SerialClient('$(SERIAL_PORT)'); await c.connect(); \
r = await c.send_command('ping'); print(r); await c.disconnect(); \
asyncio.run(t())"