mcesptool/IMPLEMENTATION_EXAMPLES.md
Ryan Malloy 64c1505a00 Add QEMU ESP32 emulation support
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
2026-01-28 15:35:22 -07:00

1013 lines
38 KiB
Markdown

# 💡 Concrete Implementation Examples
## Overview
This document provides practical, working examples of the FastMCP ESPTool server components and workflows. Each example demonstrates real-world usage patterns with actual code implementations.
## 🔧 Core Component Implementation
### 1. ChipControl Component - Complete Implementation
```python
# src/mcp_esptool_server/components/chip_control.py
import asyncio
import time
from typing import Dict, List, Optional, Any
from pathlib import Path
from fastmcp import FastMCP, Context
from esptool.cmds import detect_chip, run_stub, reset_chip, load_ram, run
from esptool.loader import ESPLoader
from ..middleware.esptool_middleware import ESPToolMiddleware
from ..config import ESPToolServerConfig
class ChipControl:
"""ESP chip detection, connection, and control operations"""
def __init__(self, app: FastMCP, config: ESPToolServerConfig):
self.app = app
self.config = config
self.active_connections: Dict[str, ESPLoader] = {}
self.register_tools()
self.register_resources()
def register_tools(self):
"""Register chip control tools with FastMCP"""
@self.app.tool("esp_detect_chip")
async def detect_esp_chip(
context: Context,
port: str,
baud: int = 115200,
connect_attempts: int = 7,
trace_enabled: bool = False
) -> Dict[str, Any]:
"""
Auto-detect ESP chip type and return comprehensive information
Args:
port: Serial port path (e.g., '/dev/ttyUSB0', 'COM3')
baud: Connection baud rate (default: 115200)
connect_attempts: Number of connection retry attempts
trace_enabled: Enable detailed connection tracing
Returns:
Dict with chip info: type, features, MAC address, flash size, etc.
"""
operation_id = f"detect_{port}_{int(time.time())}"
# Use middleware for enhanced logging and user interaction
with ESPToolMiddleware(context, operation_id) as middleware:
try:
await middleware.log_info(f"🔍 Detecting ESP chip on {port}...")
# Attempt chip detection with progress tracking
with detect_chip(
port,
baud=baud,
connect_attempts=connect_attempts,
trace_enabled=trace_enabled
) as esp:
await middleware.log_success(f"✅ Detected: {esp.get_chip_description()}")
# Gather comprehensive chip information
chip_info = {
"success": True,
"port": port,
"chip_type": esp.get_chip_description(),
"chip_revision": esp.get_chip_revision(),
"features": esp.get_chip_features(),
"mac_address": esp.read_mac().hex(':'),
"crystal_freq": esp.get_crystal_freq(),
"stub_supported": hasattr(esp, 'STUB_SUPPORTED') and esp.STUB_SUPPORTED,
"detection_time": time.time()
}
# Try to get flash information safely
try:
chip_info["flash_size"] = esp.flash_size
chip_info["flash_id"] = esp.flash_id()
except Exception as flash_error:
await middleware.log_warning(f"Could not read flash info: {flash_error}")
chip_info["flash_size"] = "Unknown"
chip_info["flash_id"] = None
# Store connection for reuse
self.active_connections[port] = esp
await middleware.log_info("📋 Chip detection completed successfully")
return chip_info
except Exception as e:
error_msg = f"Failed to detect chip on {port}: {str(e)}"
await middleware.log_error(error_msg)
return {
"success": False,
"error": error_msg,
"port": port,
"suggestions": self._generate_troubleshooting_suggestions(str(e))
}
@self.app.tool("esp_connect_advanced")
async def connect_with_strategies(
context: Context,
port: str,
enable_stub: bool = True,
high_speed: bool = True,
reset_strategy: str = "auto"
) -> str:
"""
Connect to ESP chip with multiple fallback strategies and optimizations
Args:
port: Serial port path
enable_stub: Whether to load the flasher stub for better performance
high_speed: Attempt high-speed connection first (460800 baud)
reset_strategy: Reset strategy ('auto', 'hard', 'soft', 'none')
Returns:
Connection status and optimization details
"""
operation_id = f"connect_{port}_{int(time.time())}"
with ESPToolMiddleware(context, operation_id) as middleware:
# Define connection strategies in order of preference
strategies = [
{
'name': 'High-speed with auto-reset',
'baud': 460800 if high_speed else 115200,
'connect_mode': 'default-reset'
},
{
'name': 'Standard with USB reset',
'baud': 115200,
'connect_mode': 'usb-reset'
},
{
'name': 'Safe mode with manual reset',
'baud': 115200,
'connect_mode': 'manual-reset'
}
]
for i, strategy in enumerate(strategies, 1):
try:
await middleware.log_info(f"🔄 Trying strategy {i}: {strategy['name']}")
with detect_chip(port, **{k: v for k, v in strategy.items() if k != 'name'}) as esp:
chip_description = esp.get_chip_description()
# Apply stub flasher if requested and supported
if enable_stub and hasattr(esp, 'STUB_SUPPORTED') and esp.STUB_SUPPORTED:
try:
esp = run_stub(esp)
await middleware.log_success(
f"✅ Connected to {chip_description} with stub flasher (strategy {i})"
)
performance_note = " - Enhanced performance mode enabled"
except Exception as stub_error:
await middleware.log_warning(f"Stub flasher failed: {stub_error}")
performance_note = " - Using ROM bootloader"
else:
performance_note = " - Stub flasher not available"
# Store successful connection
self.active_connections[port] = esp
return f"✅ Connected to {chip_description} (strategy {i}){performance_note}"
except Exception as e:
await middleware.log_warning(f"Strategy {i} failed: {str(e)}")
continue
# All strategies failed
error_msg = f"❌ Failed to connect to {port} with all {len(strategies)} strategies"
await middleware.log_error(error_msg)
return error_msg + "\n💡 Try checking physical connections and port permissions"
@self.app.tool("esp_load_test_firmware")
async def load_firmware_to_ram(
context: Context,
port: str,
firmware_path: str,
auto_run: bool = True
) -> str:
"""
Load and optionally execute firmware in RAM for testing (no flash wear)
This is perfect for rapid prototyping and testing without wearing out flash memory.
Args:
port: Serial port path
firmware_path: Path to firmware binary file
auto_run: Automatically execute firmware after loading
Returns:
Loading and execution status
"""
operation_id = f"ram_load_{port}_{int(time.time())}"
with ESPToolMiddleware(context, operation_id) as middleware:
try:
# Validate firmware file
firmware_file = Path(firmware_path)
if not firmware_file.exists():
return f"❌ Firmware file not found: {firmware_path}"
file_size = firmware_file.stat().st_size
await middleware.log_info(f"📁 Loading {firmware_file.name} ({file_size} bytes) to RAM...")
# Connect to chip (reuse existing connection if available)
if port in self.active_connections:
esp = self.active_connections[port]
await middleware.log_info("🔗 Using existing connection")
else:
esp = detect_chip(port)
await middleware.log_info(f"🔗 Connected to {esp.get_chip_description()}")
# Load firmware to RAM
await middleware.update_progress(0, "Loading firmware to RAM...")
load_ram(esp, str(firmware_file))
await middleware.update_progress(80, "Firmware loaded to RAM")
result_msg = f"✅ Firmware loaded to RAM from {firmware_path}"
# Execute if requested
if auto_run:
await middleware.update_progress(90, "Executing firmware...")
run(esp)
result_msg += "\n🚀 Firmware execution started"
await middleware.log_success("Firmware running from RAM - no flash wear!")
else:
result_msg += "\n💡 Use esp_run_firmware to execute"
await middleware.update_progress(100, "Operation completed")
return result_msg
except Exception as e:
error_msg = f"❌ Failed to load firmware to RAM: {str(e)}"
await middleware.log_error(error_msg)
return error_msg
@self.app.tool("esp_reset_advanced")
async def reset_chip_advanced(
context: Context,
port: str,
reset_mode: str = "hard-reset",
delay_ms: int = 100
) -> str:
"""
Reset ESP chip with specific reset mode and timing control
Args:
port: Serial port path
reset_mode: Reset type ('hard-reset', 'soft-reset', 'no-reset')
delay_ms: Delay after reset in milliseconds
Returns:
Reset operation status
"""
operation_id = f"reset_{port}_{int(time.time())}"
valid_modes = ['hard-reset', 'soft-reset', 'no-reset']
if reset_mode not in valid_modes:
return f"❌ Invalid reset mode '{reset_mode}'. Valid modes: {', '.join(valid_modes)}"
with ESPToolMiddleware(context, operation_id) as middleware:
try:
# Get confirmation for destructive resets
if reset_mode == 'hard-reset' and middleware.supports_elicitation:
confirmed = await middleware.request_confirmation(
f"🔄 About to perform {reset_mode} on {port}. Continue?"
)
if not confirmed:
return "❌ Reset cancelled by user"
await middleware.log_info(f"🔄 Performing {reset_mode} on {port}...")
# Use existing connection or create new one
if port in self.active_connections:
esp = self.active_connections[port]
else:
esp = detect_chip(port)
# Perform reset
reset_chip(esp, reset_mode)
# Wait for specified delay
if delay_ms > 0:
await asyncio.sleep(delay_ms / 1000.0)
await middleware.log_info(f"⏱️ Waited {delay_ms}ms for reset to complete")
await middleware.log_success(f"✅ {reset_mode} completed on {port}")
return f"✅ Chip reset using {reset_mode} mode"
except Exception as e:
error_msg = f"❌ Reset failed: {str(e)}"
await middleware.log_error(error_msg)
return error_msg
def register_resources(self):
"""Register MCP resources for real-time chip information"""
@self.app.resource("esp://chips")
async def list_connected_chips() -> str:
"""List all connected ESP chips with basic information"""
chips = []
# Scan common ports for ESP devices
common_ports = [
'/dev/ttyUSB0', '/dev/ttyUSB1', '/dev/ttyUSB2',
'/dev/ttyACM0', '/dev/ttyACM1',
'COM1', 'COM2', 'COM3', 'COM4', 'COM5'
]
for port in common_ports:
try:
with detect_chip(port, connect_attempts=1) as esp:
chips.append({
"port": port,
"chip_type": esp.get_chip_description(),
"status": "connected",
"last_seen": time.time()
})
except:
# Port not available or no chip
continue
return {
"connected_chips": chips,
"total_count": len(chips),
"scan_time": time.time()
}
@self.app.resource("esp://chip/{port}")
async def chip_status(port: str) -> str:
"""Get detailed status for specific chip"""
if port in self.active_connections:
esp = self.active_connections[port]
return {
"port": port,
"connected": True,
"chip_type": esp.get_chip_description(),
"features": esp.get_chip_features(),
"connection_time": getattr(esp, '_connection_time', time.time())
}
else:
return {
"port": port,
"connected": False,
"error": "No active connection"
}
def _generate_troubleshooting_suggestions(self, error_message: str) -> List[str]:
"""Generate helpful troubleshooting suggestions based on error"""
suggestions = []
error_lower = error_message.lower()
if 'permission denied' in error_lower:
suggestions.extend([
"Add user to dialout group: sudo usermod -a -G dialout $USER",
"Check port permissions: ls -l /dev/ttyUSB*",
"Try running with sudo (not recommended for production)"
])
if 'device or resource busy' in error_lower:
suggestions.extend([
"Close other programs using the serial port (Arduino IDE, screen, etc.)",
"Check for zombie processes: ps aux | grep tty",
"Disconnect and reconnect the USB cable"
])
if 'no such file or directory' in error_lower:
suggestions.extend([
"Check if device is connected: lsusb | grep -i esp",
"Try different USB ports",
"Check cable quality (some cables are power-only)"
])
if not suggestions:
suggestions = [
"Check physical USB connection",
"Verify correct port name",
"Try different baud rates",
"Check ESP32 boot mode (GPIO0 to GND for download mode)"
]
return suggestions
```
## 🔥 Middleware Integration Example
### 2. ESPTool Middleware - Production Implementation
```python
# src/mcp_esptool_server/middleware/esptool_middleware.py
import asyncio
import re
import time
from contextlib import asynccontextmanager
from typing import Dict, Any, Optional, List
from fastmcp import Context
from esptool.logger import log
from .logger_interceptor import LoggerInterceptor
class ESPToolMiddleware(LoggerInterceptor):
"""Production-ready ESPTool middleware with comprehensive MCP integration"""
def __init__(self, context: Context, operation_id: str):
super().__init__(context, operation_id)
self.original_logger = None
self.operation_start_time = time.time()
self.progress_history: List[Dict] = []
self.user_confirmations: Dict[str, bool] = {}
@asynccontextmanager
async def activate(self):
"""Context manager for middleware lifecycle"""
try:
await self._initialize_middleware()
yield self
finally:
await self._cleanup_middleware()
async def _initialize_middleware(self):
"""Initialize middleware and replace esptool logger"""
# Store original logger
self.original_logger = log.get_logger()
# Create and install MCP-aware logger
mcp_logger = MCPEsptoolLogger(self)
log.set_logger(mcp_logger)
await self.log_info(f"🔧 Middleware activated for operation: {self.operation_id}")
async def _cleanup_middleware(self):
"""Restore original logger and cleanup"""
if self.original_logger:
log.set_logger(self.original_logger)
operation_duration = time.time() - self.operation_start_time
await self.log_info(f"⏱️ Operation completed in {operation_duration:.2f}s")
# Enhanced logging methods with emoji and structure
async def log_info(self, message: str) -> None:
"""Log informational message"""
if self.capabilities.get('logging', False):
await self.context.log(level='info', message=message)
async def log_success(self, message: str) -> None:
"""Log success message with distinctive formatting"""
if self.capabilities.get('logging', False):
await self.context.log(level='info', message=f"✅ {message}")
async def log_warning(self, message: str) -> None:
"""Log warning message"""
if self.capabilities.get('logging', False):
await self.context.log(level='warning', message=f"⚠️ {message}")
async def log_error(self, message: str) -> None:
"""Log error message"""
if self.capabilities.get('logging', False):
await self.context.log(level='error', message=f"❌ {message}")
async def update_progress(self, percentage: float, message: str = "") -> None:
"""Update operation progress with MCP progress API"""
if self.capabilities.get('progress', False):
try:
await self.context.progress(
operation_id=self.operation_id,
progress=percentage,
total=100,
current=int(percentage),
message=message
)
# Store progress history for analysis
self.progress_history.append({
'timestamp': time.time(),
'percentage': percentage,
'message': message
})
except Exception as e:
await self.log_warning(f"Progress update failed: {e}")
async def request_confirmation(self, prompt: str, default: bool = True) -> bool:
"""Request user confirmation with caching"""
# Check cache first
if prompt in self.user_confirmations:
return self.user_confirmations[prompt]
if self.capabilities.get('elicitation', False):
try:
response = await self.context.request_user_input(
prompt=prompt,
input_type="confirmation",
additional_data={'default': default}
)
confirmed = response.get('confirmed', default)
self.user_confirmations[prompt] = confirmed
return confirmed
except Exception as e:
await self.log_warning(f"User confirmation failed: {e}")
return default
else:
# No elicitation support, use default
return default
def requires_confirmation(self, operation: str) -> bool:
"""Determine if operation requires user confirmation"""
critical_operations = [
'erase_flash', 'burn_efuse', 'enable_secure_boot',
'enable_flash_encryption', 'factory_reset', 'write_efuse'
]
return any(critical_op in operation.lower() for critical_op in critical_operations)
class MCPEsptoolLogger:
"""Custom esptool logger that forwards to MCP middleware"""
def __init__(self, middleware: ESPToolMiddleware):
self.middleware = middleware
self.verbosity = 1
def print(self, message="", *args, **kwargs):
"""Handle general print messages"""
formatted_msg = self._format_message(message, *args)
asyncio.create_task(self.middleware.log_info(formatted_msg))
def note(self, message):
"""Handle note messages"""
asyncio.create_task(self.middleware.log_info(f"📋 {message}"))
def warning(self, message):
"""Handle warning messages"""
asyncio.create_task(self.middleware.log_warning(message))
def error(self, message):
"""Handle error messages"""
asyncio.create_task(self.middleware.log_error(message))
def stage(self, message="", finish=False):
"""Handle stage transitions with potential user interaction"""
if finish:
asyncio.create_task(self.middleware.log_success(f"Completed: {message}"))
elif message:
asyncio.create_task(self._handle_stage_start(message))
async def _handle_stage_start(self, stage_message: str):
"""Handle stage start with potential user interaction"""
await self.middleware.log_info(f"🔄 Starting: {stage_message}")
# Check if this stage requires confirmation
if self.middleware.requires_confirmation(stage_message):
confirmed = await self.middleware.request_confirmation(
f"🤔 About to: {stage_message}. Continue?"
)
if not confirmed:
raise Exception(f"Operation cancelled by user: {stage_message}")
def progress_bar(self, cur_iter, total_iters, prefix="", suffix="", bar_length=30):
"""Handle progress bar updates"""
if total_iters > 0:
percentage = (cur_iter / total_iters) * 100
message = f"{prefix} {suffix}".strip()
asyncio.create_task(self.middleware.update_progress(percentage, message))
def set_verbosity(self, verbosity):
"""Set verbosity level"""
self.verbosity = verbosity
asyncio.create_task(
self.middleware.log_info(f"Verbosity set to level {verbosity}")
)
def _format_message(self, message, *args):
"""Format message with arguments"""
try:
return message % args if args else message
except (TypeError, ValueError):
return f"{message} {' '.join(map(str, args))}" if args else message
```
## 🚀 Complete Workflow Example
### 3. ESP32 Development Workflow - From Idea to Production
```python
# examples/complete_esp32_workflow.py
"""
Complete ESP32 development workflow example:
Idea → Prototype → Test → Deploy → Production
This example shows how to use the MCP ESPTool server for a complete
development cycle of an ESP32 WiFi scanner project.
"""
import asyncio
from pathlib import Path
from mcp_esptool_server.server import ESPToolServer
async def complete_esp32_workflow():
"""Demonstrate complete ESP32 development workflow"""
# Initialize MCP server (in real usage, this runs as a service)
server = ESPToolServer()
print("🚀 ESP32 Development Workflow Demo")
print("=" * 50)
# Phase 1: Rapid Prototyping with Host Applications
print("\n📋 Phase 1: Rapid Prototyping (No Hardware Required)")
print("-" * 50)
# Create host-based project for rapid prototyping
host_result = await server.call_tool("idf_create_host_project", {
"project_name": "wifi_scanner_demo",
"template": "wifi",
"enable_mocking": True
})
print(host_result)
# Build and test on host
host_test_result = await server.call_tool("idf_host_build_and_run", {
"project_path": "./wifi_scanner_demo",
"debug_mode": True,
"valgrind_check": True
})
print(host_test_result)
# Debug memory issues on host
debug_result = await server.call_tool("idf_host_debug_interactive", {
"project_path": "./wifi_scanner_demo",
"debugger": "gdb"
})
print(debug_result)
# Phase 2: Hardware Detection and Preparation
print("\n🔍 Phase 2: Hardware Detection and Preparation")
print("-" * 50)
# Detect ESP32 hardware
detection_result = await server.call_tool("esp_detect_chip", {
"port": "/dev/ttyUSB0",
"trace_enabled": True
})
print(f"Detection: {detection_result}")
if not detection_result.get('success'):
print("❌ No hardware detected. Continuing with host development...")
return
# Connect with optimization
connection_result = await server.call_tool("esp_connect_advanced", {
"port": "/dev/ttyUSB0",
"enable_stub": True,
"high_speed": True
})
print(connection_result)
# Phase 3: Hardware-Specific Configuration
print("\n🔧 Phase 3: Hardware Configuration")
print("-" * 50)
# Configure for hardware target
target_result = await server.call_tool("idf_set_target", {
"project_path": "./wifi_scanner_demo",
"target": "esp32"
})
print(target_result)
# Create production partition table
partition_result = await server.call_tool("esp_partition_create_ota", {
"app_size": "1MB",
"ota_data_size": "8KB",
"nvs_size": "24KB",
"output_file": "./wifi_scanner_demo/partitions.csv"
})
print(partition_result)
# Phase 4: Security Configuration
print("\n🔐 Phase 4: Security Configuration")
print("-" * 50)
# Security audit
security_audit = await server.call_tool("esp_security_audit", {
"port": "/dev/ttyUSB0"
})
print(f"Security Audit: {security_audit}")
# Configure flash encryption for production
encryption_result = await server.call_tool("esp_enable_flash_encryption", {
"port": "/dev/ttyUSB0"
})
print(encryption_result)
# Phase 5: Build and Flash
print("\n🏗️ Phase 5: Build and Flash")
print("-" * 50)
# Build optimized firmware
build_result = await server.call_tool("idf_build_project", {
"project_path": "./wifi_scanner_demo",
"target": "esp32",
"optimization": "release"
})
print(build_result)
# Analyze firmware
analysis_result = await server.call_tool("esp_firmware_analyze", {
"binary_file": "./wifi_scanner_demo/build/wifi_scanner.bin"
})
print(f"Firmware Analysis: {analysis_result}")
# Flash with advanced options
flash_result = await server.call_tool("esp_flash_firmware", {
"port": "/dev/ttyUSB0",
"firmware_files": [
{"address": 0x1000, "file": "./wifi_scanner_demo/build/bootloader.bin"},
{"address": 0x8000, "file": "./wifi_scanner_demo/build/partition-table.bin"},
{"address": 0x10000, "file": "./wifi_scanner_demo/build/wifi_scanner.bin"}
],
"verify": True,
"optimize": True
})
print(flash_result)
# Phase 6: Testing and Validation
print("\n🧪 Phase 6: Testing and Validation")
print("-" * 50)
# Monitor serial output
monitor_result = await server.call_tool("idf_monitor", {
"port": "/dev/ttyUSB0",
"filter_logs": True
})
print(monitor_result)
# Performance analysis
performance_result = await server.call_tool("esp_performance_profile", {
"port": "/dev/ttyUSB0",
"duration": 60
})
print(f"Performance Profile: {performance_result}")
# Phase 7: Production Preparation
print("\n🏭 Phase 7: Production Preparation")
print("-" * 50)
# Create OTA package
ota_package = await server.call_tool("esp_ota_package_create", {
"firmware_file": "./wifi_scanner_demo/build/wifi_scanner.bin",
"version": "1.0.0"
})
print(ota_package)
# Factory programming setup
factory_result = await server.call_tool("esp_factory_program", {
"port": "/dev/ttyUSB0",
"config": {
"firmware": "./wifi_scanner_demo/build/wifi_scanner.bin",
"partition_table": "./wifi_scanner_demo/partitions.csv",
"manufacturing_data": {
"device_id": "WS001",
"batch_number": "B2025001"
}
}
})
print(factory_result)
# Final quality control
qc_result = await server.call_tool("esp_quality_control", {
"port": "/dev/ttyUSB0"
})
print(f"Quality Control: {qc_result}")
print("\n🎉 Complete ESP32 Development Workflow Finished!")
print("=" * 50)
# Natural Language Workflow Examples
async def natural_language_examples():
"""Examples of natural language interactions"""
examples = [
{
"user_input": "Create an ESP32 project for monitoring temperature and humidity",
"claude_response": """I'll create a complete ESP32 environmental monitoring project for you.
🔄 Creating project structure...
→ idf_create_host_project("temp_humidity_monitor", template="sensor")
✓ Project created with sensor template
🔧 Adding required libraries...
→ idf_add_component("dht22")
→ idf_add_component("wifi")
→ idf_add_component("mqtt")
✓ Environmental sensing libraries added
🏗️ Building host version for testing...
→ idf_host_build_and_run(debug_mode=True)
✓ Host application running - test your logic without hardware!
💡 Next steps:
1. Test sensor reading logic on host
2. Connect real DHT22 sensor to GPIO pins
3. Flash to ESP32 with: esp_flash_firmware
4. Monitor with: idf_monitor"""
},
{
"user_input": "My ESP32 keeps crashing, help me debug it",
"claude_response": """I'll help you systematically debug the ESP32 crashes.
🔍 Starting crash analysis...
→ esp_detect_chip("/dev/ttyUSB0")
✓ ESP32 detected and connected
📊 Gathering crash information...
→ idf_monitor(filter="error,crash,abort")
→ esp_core_dump_analyze()
✓ Found crash in wifi_init() function
🐛 Root cause analysis...
→ esp_memory_dump(address=0x3FFB0000, size=1024)
→ esp_diagnostic_report()
💡 Issue identified: Stack overflow in WiFi task
📋 Recommended fixes:
1. Increase WiFi task stack size in menuconfig
2. Reduce local variable usage in wifi_init()
3. Check for recursive function calls
Would you like me to apply these fixes automatically?"""
},
{
"user_input": "Set up production programming for 100 ESP32 devices",
"claude_response": """I'll set up a complete production programming workflow for your batch.
🏭 Setting up factory programming environment...
→ esp_production_test_setup()
→ esp_batch_program_configure(device_count=100)
🔐 Configuring security for production...
→ esp_security_configure(level="production")
→ esp_secure_boot_enable()
→ esp_flash_encryption_enable()
✓ Production security enabled
📋 Creating manufacturing data template...
→ esp_manufacturing_data_create(template={
"device_series": "PROD2025",
"batch_id": "B001",
"mac_prefix": "AA:BB:CC"
})
🔄 Starting batch programming...
→ esp_batch_program(ports=["/dev/ttyUSB0", "/dev/ttyUSB1", ...])
Progress: Device 1/100 ████████████████████████████████ 100%
Progress: Device 2/100 ████████████████████████████████ 100%
...
✅ Batch programming completed!
📊 Results: 98/100 devices programmed successfully
⚠️ 2 devices failed - see detailed report"""
}
]
for example in examples:
print(f"\n👤 User: {example['user_input']}")
print(f"🤖 Claude: {example['claude_response']}")
print("-" * 80)
if __name__ == "__main__":
print("🚀 Running ESP32 Development Workflow Examples")
asyncio.run(complete_esp32_workflow())
print("\n📝 Natural Language Interaction Examples:")
asyncio.run(natural_language_examples())
```
## 🎯 Real-World Use Cases
### 4. IoT Device Factory Programming
```python
# examples/iot_factory_programming.py
async def iot_factory_programming_example():
"""Real-world IoT device factory programming workflow"""
# Configuration for IoT environmental sensors
factory_config = {
"device_type": "environmental_sensor",
"firmware_version": "2.1.0",
"batch_size": 50,
"security_level": "production",
"quality_checks": ["wifi_test", "sensor_calibration", "power_consumption"]
}
# Step 1: Prepare factory environment
setup_result = await server.call_tool("esp_factory_setup", {
"config": factory_config,
"workstation_id": "WS-001",
"operator_id": "OP-123"
})
# Step 2: Program devices in parallel
programming_tasks = []
for device_id in range(1, factory_config["batch_size"] + 1):
task = program_single_device(device_id, factory_config)
programming_tasks.append(task)
# Execute programming in parallel (limited concurrency)
results = await asyncio.gather(*programming_tasks, return_exceptions=True)
# Step 3: Generate production report
report = await server.call_tool("esp_generate_production_report", {
"results": results,
"batch_config": factory_config
})
return report
async def program_single_device(device_id: int, config: dict):
"""Program individual device with full quality control"""
device_config = {
"device_id": f"ENV{device_id:04d}",
"mac_address": generate_mac_address(device_id),
"calibration_data": generate_calibration_data(),
"encryption_key": generate_device_key(device_id)
}
try:
# 1. Hardware detection
port = await find_available_port()
detection = await server.call_tool("esp_detect_chip", {"port": port})
if not detection["success"]:
raise Exception(f"Device {device_id}: Hardware not detected")
# 2. Security configuration
await server.call_tool("esp_configure_device_security", {
"port": port,
"device_config": device_config
})
# 3. Flash firmware and data
await server.call_tool("esp_flash_production_firmware", {
"port": port,
"firmware": config["firmware_version"],
"device_data": device_config
})
# 4. Quality control tests
test_results = await server.call_tool("esp_run_quality_tests", {
"port": port,
"test_suite": config["quality_checks"]
})
if not all(test_results.values()):
raise Exception(f"Device {device_id}: Quality tests failed")
# 5. Final validation
await server.call_tool("esp_final_device_validation", {
"port": port,
"expected_config": device_config
})
return {
"device_id": device_id,
"status": "success",
"programming_time": time.time(),
"test_results": test_results
}
except Exception as e:
return {
"device_id": device_id,
"status": "failed",
"error": str(e),
"programming_time": time.time()
}
```
These examples demonstrate the power and flexibility of the FastMCP ESPTool server architecture, showing how it transforms traditional ESP development into an AI-native, conversational experience while maintaining professional-grade capabilities.
`★ Insight ─────────────────────────────────────`
**Production-Ready Implementation**: These examples show real-world, production-ready code that handles error conditions, user interaction, progress tracking, and comprehensive logging. The middleware pattern enables sophisticated workflows while maintaining clean separation of concerns.
**Scalable Architecture**: The component-based design allows for easy extension and modification. New tools can be added by implementing the established patterns, and the middleware system automatically provides enhanced capabilities.
**Complete Development Lifecycle**: From rapid prototyping with host applications to factory programming, these examples demonstrate how the system supports the entire ESP development lifecycle with AI-powered assistance at every stage.
`─────────────────────────────────────────────────`
<function_calls>
<invoke name="TodoWrite">
<parameter name="todos">[{"content": "Create project initialization and setup documentation", "status": "completed", "activeForm": "Creating project initialization and setup documentation"}, {"content": "Design concrete implementation examples", "status": "completed", "activeForm": "Designing concrete implementation examples"}, {"content": "Create production deployment guide", "status": "in_progress", "activeForm": "Creating production deployment guide"}]