Integrate Espressif's QEMU fork for virtual ESP device management: - QemuManager component with 5 MCP tools (start/stop/list/status/flash) - Config auto-detects QEMU binaries from ~/.espressif/tools/ - Supports esp32, esp32s2, esp32s3, esp32c3 chip emulation - Virtual serial over TCP (socket://localhost:PORT) transparent to esptool - Scan integration: QEMU instances appear in esp_scan_ports results - Blank flash images initialized to 0xFF (erased NOR flash state) - 38 unit tests covering lifecycle, port allocation, flash writes
395 lines
14 KiB
Markdown
395 lines
14 KiB
Markdown
# 🔗 Integration Patterns: Arduino MCP ↔ ESPTool MCP
|
|
|
|
## Overview
|
|
|
|
The ESPTool MCP server is designed to complement and enhance the existing Arduino MCP server, creating a unified ESP development ecosystem. Rather than replacing Arduino workflows, it provides specialized ESP capabilities that work seamlessly together.
|
|
|
|
## 🎯 Integration Philosophy
|
|
|
|
### Complementary Architecture
|
|
|
|
```
|
|
┌─────────────────────┐ ┌─────────────────────┐
|
|
│ Arduino MCP │ │ ESPTool MCP │
|
|
│ Server │ │ Server │
|
|
├─────────────────────┤ ├─────────────────────┤
|
|
│ • Sketch Management │ │ • Direct ESP Control│
|
|
│ • Library System │ │ • Advanced Flashing │
|
|
│ • Compilation │ │ • Security Features │
|
|
│ • Basic Upload │ │ • Production Tools │
|
|
│ • Serial Monitor │ │ • OTA Management │
|
|
│ • Cross-Platform │ │ • eFuse Programming │
|
|
└─────────────────────┘ └─────────────────────┘
|
|
│ │
|
|
└─────────┬─────────────────┘
|
|
│
|
|
┌─────────▼─────────┐
|
|
│ Unified ESP │
|
|
│ Development │
|
|
│ Workflow │
|
|
└───────────────────┘
|
|
```
|
|
|
|
## 🏗️ Integration Patterns
|
|
|
|
### 1. **Workflow Handoff Pattern**
|
|
|
|
The Arduino MCP server handles high-level development, while ESPTool MCP handles low-level chip operations:
|
|
|
|
```python
|
|
# Example: Advanced ESP32 Development Workflow
|
|
class UnifiedESPWorkflow:
|
|
"""Orchestrates between Arduino and ESPTool MCP servers"""
|
|
|
|
async def complete_esp_development_cycle(self, project_config: dict):
|
|
"""End-to-end ESP development using both servers"""
|
|
|
|
# Phase 1: Arduino MCP Server - High-level development
|
|
arduino_tasks = [
|
|
"arduino_create_sketch", # Create project structure
|
|
"arduino_install_library", # Install ESP32 libraries
|
|
"arduino_write_sketch", # Write application code
|
|
"arduino_compile_sketch" # Compile to binary
|
|
]
|
|
|
|
# Phase 2: ESPTool MCP Server - Low-level optimization
|
|
esptool_tasks = [
|
|
"esp_detect_chip", # Identify ESP variant
|
|
"esp_firmware_analyze", # Optimize binary
|
|
"esp_partition_create_ota", # Setup OTA partitions
|
|
"esp_flash_firmware", # Advanced flashing
|
|
"esp_security_audit" # Security validation
|
|
]
|
|
|
|
return await self._execute_unified_workflow(arduino_tasks, esptool_tasks)
|
|
```
|
|
|
|
### 2. **Shared Configuration Pattern**
|
|
|
|
Both servers share common configuration for seamless operation:
|
|
|
|
```python
|
|
# config/unified_esp_config.py
|
|
from pathlib import Path
|
|
from dataclasses import dataclass
|
|
from typing import List, Dict
|
|
|
|
@dataclass
|
|
class UnifiedESPConfig:
|
|
"""Shared configuration between Arduino and ESPTool MCP servers"""
|
|
|
|
# Shared paths
|
|
project_roots: List[Path]
|
|
sketch_directory: Path
|
|
libraries_directory: Path
|
|
tools_directory: Path
|
|
|
|
# Arduino MCP specific
|
|
arduino_cli_path: str
|
|
arduino_cores: List[str]
|
|
|
|
# ESPTool MCP specific
|
|
esptool_path: str
|
|
esp_idf_path: Path
|
|
partition_templates: Dict[str, Path]
|
|
|
|
# Shared ESP settings
|
|
default_baud_rate: int = 460800
|
|
connection_timeout: int = 30
|
|
enable_stub_flasher: bool = True
|
|
|
|
@classmethod
|
|
def from_environment(cls) -> 'UnifiedESPConfig':
|
|
"""Initialize from environment variables and MCP roots"""
|
|
return cls(
|
|
project_roots=cls._get_mcp_roots(),
|
|
sketch_directory=Path(os.getenv('MCP_SKETCH_DIR', '~/Arduino')).expanduser(),
|
|
arduino_cli_path=os.getenv('ARDUINO_CLI_PATH', 'arduino-cli'),
|
|
esptool_path=os.getenv('ESPTOOL_PATH', 'esptool'),
|
|
# ... other config
|
|
)
|
|
```
|
|
|
|
### 3. **Cross-Server Communication Pattern**
|
|
|
|
Enable servers to coordinate operations and share information:
|
|
|
|
```python
|
|
# coordination/server_bridge.py
|
|
class MCPServerBridge:
|
|
"""Facilitates communication between Arduino and ESPTool MCP servers"""
|
|
|
|
def __init__(self, arduino_server_url: str, esptool_server_url: str):
|
|
self.arduino_client = MCPClient(arduino_server_url)
|
|
self.esptool_client = MCPClient(esptool_server_url)
|
|
|
|
async def compile_and_flash_esp(self, sketch_path: str, port: str) -> dict:
|
|
"""Coordinate compilation via Arduino MCP and flashing via ESPTool MCP"""
|
|
|
|
# Step 1: Use Arduino MCP to compile
|
|
compile_result = await self.arduino_client.call_tool(
|
|
"arduino_compile_sketch",
|
|
{"sketch_path": sketch_path, "board": "esp32"}
|
|
)
|
|
|
|
if not compile_result["success"]:
|
|
return {"error": "Compilation failed", "details": compile_result}
|
|
|
|
# Step 2: Use ESPTool MCP for advanced flashing
|
|
flash_result = await self.esptool_client.call_tool(
|
|
"esp_flash_firmware",
|
|
{
|
|
"port": port,
|
|
"firmware_files": [{"address": 0x1000, "file": compile_result["binary_path"]}],
|
|
"verify": True,
|
|
"optimize": True
|
|
}
|
|
)
|
|
|
|
return {
|
|
"compile_result": compile_result,
|
|
"flash_result": flash_result,
|
|
"success": flash_result.get("success", False)
|
|
}
|
|
```
|
|
|
|
## 🎨 Natural Language Workflows
|
|
|
|
### Unified Development Commands
|
|
|
|
Claude can seamlessly use both servers to provide comprehensive ESP development:
|
|
|
|
```
|
|
User: "Create an ESP32 weather station with OTA updates"
|
|
|
|
Claude: I'll create a comprehensive ESP32 weather station with OTA capability using both Arduino and ESP-specific tools.
|
|
|
|
# Arduino MCP Server - Application Development
|
|
→ arduino_create_sketch("WeatherStation")
|
|
→ arduino_install_library("DHT sensor library")
|
|
→ arduino_install_library("WiFiManager")
|
|
→ arduino_write_sketch(weather_station_code)
|
|
→ arduino_compile_sketch()
|
|
|
|
# ESPTool MCP Server - Production Setup
|
|
→ esp_detect_chip(port="/dev/ttyUSB0")
|
|
→ esp_partition_create_ota(app_size="1MB")
|
|
→ esp_flash_firmware(with_ota_partitions=True)
|
|
→ esp_security_audit()
|
|
|
|
✓ Weather station deployed with OTA updates enabled
|
|
✓ Secure boot configured for production
|
|
✓ Ready for remote firmware updates
|
|
```
|
|
|
|
### Development vs Production Workflows
|
|
|
|
```
|
|
User: "I'm developing an ESP32 project - I want fast iteration"
|
|
|
|
Claude: I'll set up a development-optimized workflow for rapid testing.
|
|
|
|
# Development Mode - Fast Iteration
|
|
→ arduino_compile_sketch(board="esp32", debug=True)
|
|
→ esp_load_test_firmware(to_ram=True) # No flash wear
|
|
→ serial_monitor(detailed_logging=True)
|
|
|
|
# When ready for testing:
|
|
→ esp_flash_firmware(development_mode=True)
|
|
|
|
User: "Now I'm ready for production deployment"
|
|
|
|
Claude: I'll configure this for production with security and optimization.
|
|
|
|
# Production Mode - Security & Optimization
|
|
→ esp_firmware_analyze(optimize=True)
|
|
→ esp_enable_flash_encryption()
|
|
→ esp_enable_secure_boot()
|
|
→ esp_flash_firmware(production_mode=True)
|
|
→ esp_factory_test_suite()
|
|
|
|
✓ Production deployment complete with security enabled
|
|
```
|
|
|
|
## 🔄 Shared Resource Management
|
|
|
|
### Unified Device Detection
|
|
|
|
```python
|
|
# resources/unified_device_manager.py
|
|
class UnifiedDeviceManager:
|
|
"""Manages ESP devices across both MCP servers"""
|
|
|
|
async def get_all_esp_devices(self) -> List[Dict]:
|
|
"""Combine device information from both servers"""
|
|
|
|
# Get basic device info from Arduino MCP
|
|
arduino_devices = await self._get_arduino_devices()
|
|
|
|
# Get detailed ESP info from ESPTool MCP
|
|
esptool_devices = await self._get_esptool_devices()
|
|
|
|
# Merge information
|
|
unified_devices = []
|
|
for device in arduino_devices:
|
|
if device['board_type'].startswith('esp'):
|
|
esp_details = self._find_esptool_device(device['port'], esptool_devices)
|
|
unified_devices.append({
|
|
**device,
|
|
"esp_details": esp_details,
|
|
"capabilities": self._determine_capabilities(device, esp_details)
|
|
})
|
|
|
|
return unified_devices
|
|
|
|
def _determine_capabilities(self, arduino_info: dict, esp_info: dict) -> List[str]:
|
|
"""Determine what operations are available for this device"""
|
|
capabilities = ["compile", "upload", "monitor"] # Arduino basics
|
|
|
|
if esp_info:
|
|
capabilities.extend([
|
|
"direct_flash", "partition_management", "security_config",
|
|
"ota_support", "factory_programming"
|
|
])
|
|
|
|
return capabilities
|
|
```
|
|
|
|
### Resource Synchronization
|
|
|
|
```python
|
|
# Both servers expose unified resources
|
|
@arduino_mcp.resource("unified://esp_devices")
|
|
@esptool_mcp.resource("unified://esp_devices")
|
|
async def synchronized_esp_devices() -> str:
|
|
"""Synchronized device list across both servers"""
|
|
device_manager = UnifiedDeviceManager()
|
|
devices = await device_manager.get_all_esp_devices()
|
|
return json.dumps(devices, indent=2)
|
|
```
|
|
|
|
## 🚀 Advanced Integration Scenarios
|
|
|
|
### 1. **CI/CD Pipeline Integration**
|
|
|
|
```yaml
|
|
# .github/workflows/esp32-deploy.yml
|
|
name: ESP32 Production Deployment
|
|
|
|
on:
|
|
push:
|
|
tags: ['v*']
|
|
|
|
jobs:
|
|
deploy:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
|
|
- name: Setup ESP Development Environment
|
|
run: |
|
|
# Install both MCP servers
|
|
uvx mcp-arduino
|
|
uvx mcp-esptool-server
|
|
|
|
- name: Compile Firmware
|
|
run: |
|
|
claude mcp call arduino arduino_compile_sketch \
|
|
--sketch-path ./WeatherStation \
|
|
--board esp32 \
|
|
--optimize-size
|
|
|
|
- name: Security Configuration
|
|
run: |
|
|
claude mcp call esptool esp_firmware_analyze \
|
|
--input ./WeatherStation/build/WeatherStation.bin \
|
|
--security-check
|
|
|
|
- name: Factory Programming
|
|
run: |
|
|
claude mcp call esptool esp_factory_program \
|
|
--firmware ./WeatherStation/build/WeatherStation.bin \
|
|
--security-keys ./keys/ \
|
|
--partition-table ./partitions/production.csv
|
|
```
|
|
|
|
### 2. **Development Environment Setup**
|
|
|
|
```python
|
|
# setup/unified_development_environment.py
|
|
class UnifiedESPDevEnvironment:
|
|
"""Sets up complete ESP development environment"""
|
|
|
|
async def setup_project(self, project_name: str, project_type: str):
|
|
"""Initialize new ESP project with both Arduino and ESP tooling"""
|
|
|
|
setup_tasks = {
|
|
"arduino_tasks": [
|
|
("arduino_create_sketch", {"name": project_name}),
|
|
("arduino_install_core", {"core": "esp32"}),
|
|
("arduino_config_init", {})
|
|
],
|
|
"esptool_tasks": [
|
|
("esp_config_init", {}),
|
|
("esp_partition_create_ota", {"project": project_name}),
|
|
("esp_security_template_create", {"level": "development"})
|
|
]
|
|
}
|
|
|
|
# Execute tasks in parallel where possible
|
|
arduino_results = await self._execute_arduino_tasks(setup_tasks["arduino_tasks"])
|
|
esptool_results = await self._execute_esptool_tasks(setup_tasks["esptool_tasks"])
|
|
|
|
return {
|
|
"project_name": project_name,
|
|
"arduino_setup": arduino_results,
|
|
"esptool_setup": esptool_results,
|
|
"ready_for_development": all([arduino_results["success"], esptool_results["success"]])
|
|
}
|
|
```
|
|
|
|
### 3. **Error Recovery and Diagnostics**
|
|
|
|
```python
|
|
# diagnostics/unified_troubleshooting.py
|
|
class UnifiedTroubleshooting:
|
|
"""Cross-server diagnostic and recovery tools"""
|
|
|
|
async def diagnose_esp_issue(self, port: str, symptoms: List[str]) -> dict:
|
|
"""Use both servers to diagnose ESP development issues"""
|
|
|
|
diagnosis = {
|
|
"port": port,
|
|
"symptoms": symptoms,
|
|
"tests_performed": [],
|
|
"recommendations": []
|
|
}
|
|
|
|
# Arduino MCP diagnostics
|
|
arduino_tests = [
|
|
("serial_check_port", {"port": port}),
|
|
("arduino_list_boards", {}),
|
|
("serial_test_connection", {"port": port})
|
|
]
|
|
|
|
# ESPTool MCP diagnostics
|
|
esptool_tests = [
|
|
("esp_detect_chip", {"port": port}),
|
|
("esp_security_audit", {"port": port}),
|
|
("esp_flash_analyze", {"port": port})
|
|
]
|
|
|
|
# Run diagnostics and generate recommendations
|
|
arduino_results = await self._run_arduino_diagnostics(arduino_tests)
|
|
esptool_results = await self._run_esptool_diagnostics(esptool_tests)
|
|
|
|
diagnosis.update({
|
|
"arduino_diagnostics": arduino_results,
|
|
"esptool_diagnostics": esptool_results,
|
|
"recommendations": self._generate_recommendations(arduino_results, esptool_results)
|
|
})
|
|
|
|
return diagnosis
|
|
```
|
|
|
|
This integration design ensures that both MCP servers work together seamlessly, providing Claude with a comprehensive toolkit for ESP development that spans from high-level Arduino sketch development to low-level chip programming and production deployment. |