From ce93070a04a973beedaa2b915abb8fc50d397b7d Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 3 Feb 2026 09:10:36 -0700 Subject: [PATCH] Add README and CLAUDE.md README covers installation, hardware setup, tools, prompts, and cross-server integration with mcnanovna for 3D pattern measurement. CLAUDE.md provides project guidance for Claude Code. --- CLAUDE.md | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 CLAUDE.md create mode 100644 README.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..167ec52 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,102 @@ +# CLAUDE.md + +This file provides guidance to Claude Code when working with code in this repository. + +## What This Is + +mcpositioner is a FastMCP server that gives LLMs direct control of an ESP32 dual-axis antenna positioner over WiFi HTTP. It exposes 5 MCP tools and 3 guided workflow prompts for moving stepper motors, homing axes via StallGuard, and orchestrating 3D antenna radiation pattern measurement in coordination with a VNA MCP server (mcnanovna). + +## Commands + +```bash +# Run the server +uv run mcpositioner # from source +uvx mcpositioner # from installed package +python -m mcpositioner # module invocation + +# Lint and format +ruff check src/ +ruff format src/ + +# Install dependencies +uv sync + +# Build/verify ESP32 firmware (requires PlatformIO) +cd firmware && pio run + +# Add to Claude Code for testing +claude mcp add mcpositioner-local --scope project -- uv run --directory /home/rpm/claude/ham/nanovna/mcpositioner mcpositioner +``` + +There are no tests yet. The project has no README. + +## Architecture + +``` +src/mcpositioner/ + __init__.py Package docstring + __main__.py python -m mcpositioner + server.py FastMCP server -- tool registration, version banner, entry point + positioner.py Async HTTP client for the ESP32 (httpx) + tools.py 5 MCP tool methods on a simple PositionerTools class + prompts.py 3 guided workflow prompts (home, configure, measure_pattern_grid) + +firmware/ PlatformIO ESP32 project for antenna positioner hardware + platformio.ini Build config: espressif32, AccelStepper, TMCStepper, ESPAsyncWebServer + include/config.h Pin assignments, motor constants, TMC2209 UART config + src/main.cpp Dual-axis stepper control, HTTP API, mDNS, StallGuard homing + +hardware/ KiCad 9 schematic for positioner wiring + positioner.kicad_pro Project file (JSON) + positioner.kicad_sch Root schematic -- ESP32 + 2x TMC2209 + motors + power + positioner.kicad_sym Local symbol library (6 custom module symbols) + sym-lib-table Registers local symbol lib with project + generate_kicad.py Python generator for all KiCad files (dev tool) +``` + +### How tools get registered + +`server.py` holds a flat list `_TOOL_METHODS` of method name strings. `create_server()` instantiates one `PositionerTools` object, iterates the list, and wraps each bound method with `FunctionTool.from_function()`. To add a new tool: add a public method to `tools.py` then add its name to `_TOOL_METHODS`. + +### HTTP client (positioner.py) + +The `Positioner` class wraps httpx async calls to the ESP32's HTTP API: +- `GET /status` -- theta/phi position, moving, homed +- `POST /move` -- absolute move to theta/phi +- `POST /move/relative` -- relative offset move +- `POST /home` -- StallGuard sensorless homing +- `POST /stop` -- emergency stop +- `GET /config` -- read motion parameters +- `POST /config` -- update speed, accel, microstepping + +Default host: `positioner.local` (mDNS). Override with `MCPOSITIONER_HOST` env var. + +### Cross-server orchestration + +This server controls physical positioning only. For 3D antenna pattern measurement, the LLM orchestrates both mcpositioner and mcnanovna (VNA MCP server): + +1. `positioner_move` (mcpositioner) -- position antenna at theta/phi +2. `scan` (mcnanovna) -- measure S21 transmission +3. Repeat across theta/phi grid +4. Assemble pattern dict from collected measurements + +The `measure_pattern_grid` prompt provides step-by-step guidance for this workflow. + +### ESP32 firmware (firmware/) + +PlatformIO project targeting ESP32. Controls 2x NEMA 17 steppers via TMC2209 drivers in UART mode. Features: +- AccelStepper for smooth acceleration profiles +- TMCStepper for UART configuration and StallGuard sensorless homing +- ESPAsyncWebServer for the HTTP API +- mDNS advertisement as `positioner.local` +- Pin assignments and motor constants in `include/config.h` + +## Key conventions + +- Date-based versioning: `2026.02.02` in pyproject.toml +- Python 3.11+, ruff at 120-char line length +- `httpx` is a core dependency (not optional -- it IS the transport layer) +- Single `PositionerTools` instance per server lifetime +- No mixin pattern -- flat class with 5 methods (single-concern server) +- Custom exception: `PositionerError` +- Env var: `MCPOSITIONER_HOST` (default `positioner.local`) diff --git a/README.md b/README.md new file mode 100644 index 0000000..7198b93 --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +# mcpositioner + +MCP server for controlling an ESP32 dual-axis antenna positioner over WiFi. + +Drives two NEMA 17 stepper motors via TMC2209 drivers for theta (polar, 0-180°) and phi (azimuth, 0-360°) rotation. Designed for automated antenna radiation pattern measurement in coordination with a VNA. + +## Installation + +```bash +# From PyPI (when published) +uvx mcpositioner + +# From source +uv run mcpositioner +``` + +## Hardware + +- **ESP32** (any DevKit variant) running the firmware in `firmware/` +- **2x TMC2209** stepper drivers in UART mode +- **2x NEMA 17** stepper motors (0.9° or 1.8° step angle) +- **24V power supply** for motors (5V for ESP32) + +See `hardware/` for KiCad schematics showing the wiring. + +### Firmware + +Build and flash with PlatformIO: + +```bash +cd firmware +pio run -t upload +``` + +The ESP32 advertises itself as `positioner.local` via mDNS. Override with the `MCPOSITIONER_HOST` environment variable if needed. + +## MCP Tools + +| Tool | Description | +|------|-------------| +| `positioner_status` | Get theta/phi position, moving state, homed state | +| `positioner_move` | Move to absolute theta/phi with optional wait | +| `positioner_home` | StallGuard sensorless homing on one or both axes | +| `positioner_stop` | Emergency stop all motors | +| `positioner_config` | Get/set speed, acceleration, microstepping | + +## Prompts + +| Prompt | Description | +|--------|-------------| +| `home_positioner` | Guided homing with safety checks | +| `configure_positioner` | Motion parameter tuning guide | +| `measure_pattern_grid` | Cross-server 3D pattern measurement workflow | + +## Cross-Server Pattern Measurement + +This server controls positioning only. For 3D antenna pattern measurement, use together with [mcnanovna](../mcnanovna/) (VNA MCP server): + +1. `positioner_move` (mcpositioner) — position antenna at theta/phi +2. `scan` (mcnanovna) — measure S21 transmission +3. Repeat across theta/phi grid +4. Assemble pattern from collected measurements + +The `measure_pattern_grid` prompt provides step-by-step guidance for this workflow. + +### Adding Both Servers to Claude Code + +```bash +claude mcp add mcpositioner -- uvx mcpositioner +claude mcp add mcnanovna -- uvx mcnanovna +``` + +## Configuration + +| Environment Variable | Default | Description | +|---------------------|---------|-------------| +| `MCPOSITIONER_HOST` | `positioner.local` | ESP32 hostname or IP | + +## HTTP API (ESP32 Firmware) + +The ESP32 exposes these endpoints: + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/status` | GET | Current position, moving, homed | +| `/move` | POST | `{"theta_deg": 90, "phi_deg": 45}` | +| `/move/relative` | POST | `{"d_theta": 5, "d_phi": 10}` | +| `/home` | POST | `{"axis": "both"}` (or "theta"/"phi") | +| `/stop` | POST | Emergency stop | +| `/config` | GET/POST | Motion parameters | + +## License + +MIT