Document QEMU emulation in README and improve tool descriptions
Add QEMU section to README with install instructions and tool reference. Enrich MCP tool docstrings with workflow context, cross-references to related tools, and guidance on when to use each tool — these descriptions are read by the calling LLM to decide tool selection.
This commit is contained in:
parent
740164f582
commit
cb4822a0a4
24
README.md
24
README.md
@ -10,6 +10,7 @@ FastMCP server providing AI-powered ESP32/ESP8266 development workflows through
|
|||||||
- **Production Tools**: Factory programming and batch operations
|
- **Production Tools**: Factory programming and batch operations
|
||||||
- **Middleware System**: Universal CLI tool integration with bidirectional MCP communication
|
- **Middleware System**: Universal CLI tool integration with bidirectional MCP communication
|
||||||
- **ESP-IDF Integration**: Host application support for hardware-free development
|
- **ESP-IDF Integration**: Host application support for hardware-free development
|
||||||
|
- **QEMU Emulation**: Virtual ESP32 devices for testing without physical hardware
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@ -64,6 +65,29 @@ The server implements a component-based architecture with middleware for CLI too
|
|||||||
- `OTAManager`: Over-the-air update workflows
|
- `OTAManager`: Over-the-air update workflows
|
||||||
- `ProductionTools`: Factory programming and quality control
|
- `ProductionTools`: Factory programming and quality control
|
||||||
- `Diagnostics`: Memory dumps and performance profiling
|
- `Diagnostics`: Memory dumps and performance profiling
|
||||||
|
- `QemuManager`: QEMU-based ESP32 emulation with download mode, efuse, and flash support
|
||||||
|
|
||||||
|
## QEMU Emulation
|
||||||
|
|
||||||
|
Run virtual ESP32 devices without physical hardware. Requires [Espressif's QEMU fork](https://github.com/espressif/qemu):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install via ESP-IDF tools
|
||||||
|
source /path/to/esp-idf/export.sh
|
||||||
|
python3 $IDF_PATH/tools/idf_tools.py install qemu-xtensa qemu-riscv32
|
||||||
|
```
|
||||||
|
|
||||||
|
The server auto-detects QEMU binaries from `~/.espressif/tools/`. Once available, five tools are exposed:
|
||||||
|
|
||||||
|
| Tool | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `esp_qemu_start` | Launch a virtual ESP device (supports esp32, esp32s2, esp32s3, esp32c3) |
|
||||||
|
| `esp_qemu_stop` | Stop a running instance |
|
||||||
|
| `esp_qemu_list` | List all running instances |
|
||||||
|
| `esp_qemu_status` | Detailed instance info |
|
||||||
|
| `esp_qemu_flash` | Write firmware to a virtual device's flash |
|
||||||
|
|
||||||
|
Virtual devices appear in `esp_scan_ports` alongside physical hardware, connected via `socket://localhost:<port>`.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|||||||
@ -149,14 +149,30 @@ class QemuManager:
|
|||||||
boot_mode: str = "download",
|
boot_mode: str = "download",
|
||||||
extra_args: list[str] | None = None,
|
extra_args: list[str] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""
|
"""Start a virtual ESP device using QEMU emulation. No physical hardware needed.
|
||||||
Start a QEMU ESP32 emulation instance
|
|
||||||
|
Returns a socket URI (socket://localhost:PORT) that works with all
|
||||||
|
esptool operations: esp_detect_chip, esp_flash_firmware, esp_scan_ports, etc.
|
||||||
|
Virtual devices also appear automatically in esp_scan_ports results.
|
||||||
|
|
||||||
|
Boot modes:
|
||||||
|
- "download" (default): Device starts in serial bootloader. Use this for
|
||||||
|
esptool interactions like flashing, chip identification, and flash reading.
|
||||||
|
- "normal": Device boots from flash and runs firmware. Use this to observe
|
||||||
|
application output after flashing.
|
||||||
|
|
||||||
|
Typical workflow:
|
||||||
|
1. esp_qemu_start (download mode) -> get socket URI
|
||||||
|
2. esp_flash_firmware with socket URI -> flash your firmware
|
||||||
|
3. esp_qemu_stop -> stop the instance
|
||||||
|
4. esp_qemu_start (normal mode, same flash image) -> boot firmware
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
chip_type: Target chip (esp32, esp32s2, esp32s3, esp32c3)
|
chip_type: Target chip (esp32, esp32s2, esp32s3, esp32c3)
|
||||||
flash_image: Path to flash image file (creates blank if not specified)
|
flash_image: Path to existing flash image file. Creates a blank erased
|
||||||
flash_size_mb: Flash size in MB for blank images (default: 4)
|
flash (all 0xFF) if not specified.
|
||||||
tcp_port: TCP port for virtual serial (auto-assigned if not specified)
|
flash_size_mb: Flash size in MB when creating blank images (default: 4)
|
||||||
|
tcp_port: TCP port for virtual serial (auto-assigned from pool if not specified)
|
||||||
boot_mode: "download" for esptool interaction (default), "normal" to boot from flash
|
boot_mode: "download" for esptool interaction (default), "normal" to boot from flash
|
||||||
extra_args: Additional QEMU command-line arguments
|
extra_args: Additional QEMU command-line arguments
|
||||||
"""
|
"""
|
||||||
@ -168,28 +184,34 @@ class QemuManager:
|
|||||||
async def qemu_stop(
|
async def qemu_stop(
|
||||||
context: Context, instance_id: str | None = None
|
context: Context, instance_id: str | None = None
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""
|
"""Stop a running QEMU virtual device. Terminates the QEMU process and frees the TCP port.
|
||||||
Stop a running QEMU instance
|
|
||||||
|
The flash image is preserved on disk, so the instance can be restarted
|
||||||
|
with esp_qemu_start using the same flash_image path (e.g., to switch
|
||||||
|
from download mode to normal boot mode after flashing).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
instance_id: Instance ID to stop (stops all if not specified)
|
instance_id: Instance ID to stop (stops all running instances if not specified)
|
||||||
"""
|
"""
|
||||||
return await self._stop_impl(context, instance_id)
|
return await self._stop_impl(context, instance_id)
|
||||||
|
|
||||||
@self.app.tool("esp_qemu_list")
|
@self.app.tool("esp_qemu_list")
|
||||||
async def qemu_list(context: Context) -> dict[str, Any]:
|
async def qemu_list(context: Context) -> dict[str, Any]:
|
||||||
"""List all QEMU instances with status"""
|
"""List all QEMU virtual device instances with their status, chip type, port, and boot mode.
|
||||||
|
|
||||||
|
Returns running and stopped instances. Use this to find instance IDs
|
||||||
|
for esp_qemu_stop or esp_qemu_status, or to get socket URIs for esptool operations."""
|
||||||
return await self._list_impl(context)
|
return await self._list_impl(context)
|
||||||
|
|
||||||
@self.app.tool("esp_qemu_status")
|
@self.app.tool("esp_qemu_status")
|
||||||
async def qemu_status(
|
async def qemu_status(
|
||||||
context: Context, instance_id: str | None = None
|
context: Context, instance_id: str | None = None
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""
|
"""Get detailed status of a QEMU virtual device including uptime, PID, socket URI,
|
||||||
Get detailed status of a QEMU instance
|
boot mode, and flash/efuse image paths.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
instance_id: Instance to inspect (first running if not specified)
|
instance_id: Instance to inspect (returns first running instance if not specified)
|
||||||
"""
|
"""
|
||||||
return await self._status_impl(context, instance_id)
|
return await self._status_impl(context, instance_id)
|
||||||
|
|
||||||
@ -200,17 +222,21 @@ class QemuManager:
|
|||||||
firmware_path: str,
|
firmware_path: str,
|
||||||
address: str = "0x0",
|
address: str = "0x0",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""
|
"""Write a firmware binary directly into a QEMU instance's flash image file.
|
||||||
Flash a firmware binary to a QEMU instance's flash image
|
|
||||||
|
|
||||||
The instance must be stopped first. This writes the binary at the
|
This is an offline operation — the instance must be stopped first.
|
||||||
given offset into the raw flash image file, then you can restart
|
It patches the raw flash image at the given offset, then you can restart
|
||||||
the instance.
|
with esp_qemu_start in "normal" boot mode to run the firmware.
|
||||||
|
|
||||||
|
For most use cases, prefer using esp_flash_firmware with the instance's
|
||||||
|
socket URI while it's running in download mode — that uses esptool's
|
||||||
|
full flash protocol including verification. Use this tool only when you
|
||||||
|
need direct image manipulation (e.g., pre-loading a merged binary).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
instance_id: Target QEMU instance
|
instance_id: Target QEMU instance (must be stopped)
|
||||||
firmware_path: Path to firmware binary to write
|
firmware_path: Path to firmware binary to write into the flash image
|
||||||
address: Flash address offset (hex string, default: 0x0)
|
address: Flash address offset as hex string (default: "0x0")
|
||||||
"""
|
"""
|
||||||
return await self._flash_impl(context, instance_id, firmware_path, address)
|
return await self._flash_impl(context, instance_id, firmware_path, address)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user