STM32-MCP
Accede a STM32CubeIDE y herramientas de depuración
Documentación
stm32-mcp
Servidor MCP que permite a Claude Code compilar, flashear y comunicarse con hardware STM32.
stm32-mcp es bastante específico de cómo suelo abordar el desarrollo de hardware, pero probablemente sea útil para otros también. Se podría adaptar a muchos flujos de trabajo, pero está enfocado con láser en el mío (stlink-v3 mini, VCP en ese conector, microcontrolador STM32).
Puedes hacer cosas como:
yo: oye, ¿quién está conectado ahora?
claude: dos sondas sin nombre conectadas a dos PCB sin nombre
yo: vale, pregúntales quiénes son y ponles un apodo según su respuesta
claude: entendido, ¿quieres también poner apodos a las sondas? tus placas son 'doorbell A' y 'synthesizer B'
yo: sí, puse marcador de pintura en esas sondas. llama a la del timbre 'azul' y a la del sintetizador 'rojo'
claude: hecho. ¿qué sigue?
yo: dales a ambas comandos VCP para que puedan hablar entre sí, y luego haz que el timbre le pida una cita al sintetizador
claude: pensando... hecho, el sintetizador rechazó. ¡hay muchos peces en el mar, timbre!
MCP (Model Context Protocol) es un estándar abierto que permite a asistentes de IA como Claude usar herramientas externas. Este servidor le da a Claude la capacidad de compilar tu firmware, flashearlo en una placa, hablar con él por serie y leer memoria vía SWD. Es flexible y conversacional.
[!WARNING] Este servidor le da a una IA acceso directo a tu compilador, sonda de depuración y puertos serie. Puede flashear firmware, sobrescribir memoria y enviar datos arbitrarios a tu hardware. Esto es potente y útil, pero no es un sandbox. Sepa qué está conectado antes de dejarlo actuar.
Requisitos previos
- STM32CubeIDE instalado en
/Applications/STM32CubeIDE.app(macOS) o/opt/st/stm32cubeide_*(Linux) - Python 3.10+
- OpenOCD (
brew install open-ocd) — para flasheo, lectura/escritura de memoria y monitoreo en vivo - herramientas stlink de código abierto (
brew install stlink) — para enumeración de sondas - ST-Link conectado vía USB (para información de placa/flasheo)
- Puerto serie disponible (ST-Link VCP o adaptador USB-UART)
Instalación
git clone https://github.com/shieldyguy/stm32-mcp.git
cd stm32-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
Registrar con Claude Code
Opción A: CLI
claude mcp add stm32 -- /path/to/stm32-mcp/.venv/bin/python -m stm32_mcp.server
Opción B: Configuración del proyecto
Añade a .claude/settings.json o .claude.json de tu proyecto:
{
"mcpServers": {
"stm32": {
"command": "/path/to/stm32-mcp/.venv/bin/python",
"args": ["-m", "stm32_mcp.server"]
}
}
}
CLI de autoservicio
bin/ contiene cuatro envoltorios delgados sobre el mismo código que usan las herramientas MCP
| Comando | Uso |
|---|---|
stm32-list | Lista sondas y placas conectadas con apodos |
stm32-flash | stm32-flash <probe|board> <file.elf> [--noverify] [--noreset] |
stm32-build | stm32-build <project_path> [Debug|Release] [--clean] |
stm32-bf | stm32-bf <project_path> <probe|board> [Debug|Release] [--clean] |
stm32-help | Lista estos comandos con su uso (generado automáticamente desde los scripts) |
Añade bin/ a tu PATH:
export PATH="/path/to/stm32-mcp/bin:$PATH"
Los apodos de sondas y placas se resuelven.
Las compilaciones comparten el bloqueo del espacio de trabajo headless de CubeIDE del MCP, por lo que un stm32-build/stm32-bf que compita con una compilación impulsada por agente se pondrá en cola detrás de ella.
Herramientas disponibles
Compilar y flashear
| Herramienta | Descripción |
|---|---|
stm32_build | Compila firmware usando el compilador headless de CubeIDE |
stm32_flash | Flashea .elf/.bin/.hex a la placa vía ST-Link SWD |
stm32_build_and_flash | Compilar + flashear en un solo paso (el caso del 90%) |
stm32_board_info | Lee información de ST-Link/MCU (ID de dispositivo, tamaño de flash, voltaje) |
Gestión de múltiples placas
| Herramienta | Descripción |
|---|---|
stm32_list_probes | Muestra todas las placas conectadas con apodos e IDs de MCU |
stm32_set_nickname | Nombra una placa (por UID de MCU) o sonda (por SN de ST-Link) |
Los apodos de placas siguen al MCU físico (persisten entre cambios de sonda). Los apodos de sondas siguen al hardware ST-Link. Usa apodos en cualquier parámetro probe en todas las herramientas.
Comunicación serie
| Herramienta | Descripción |
|---|---|
serial_list_ports | Lista puertos serie (marca puertos ST-Link VCP con apodos) |
serial_connect | Abre una conexión serie |
serial_send | Envía datos y lee respuesta |
serial_read | Lee datos serie almacenados en búfer |
serial_disconnect | Cierra una conexión serie |
serial_sequence | Ejecuta secuencias de envío/retardo/memoria de múltiples pasos en una sola llamada |
Depuración y monitoreo
| Herramienta | Descripción |
|---|---|
stm32_read_memory | Lee memoria por dirección o nombre de variable (desde símbolos ELF) |
stm32_write_memory | Escribe memoria por dirección o nombre de variable |
live_memory_start | Inicia monitoreo continuo de memoria en segundo plano vía SWD |
live_memory_read | Lee entradas recientes de una sesión de memoria en vivo |
live_memory_stop | Detiene una sesión de memoria en vivo |
Secuencias de hardware
serial_sequence programa múltiples pasos (envío serie, retardo, captura de webcam y lectura/escritura de memoria SWD) en una sola llamada de herramienta. Los retardos usan un time.sleep() en el hilo ejecutor. Claude no puede cronometrar de manera confiable llamadas individuales de herramienta, por lo que esto permite un control preciso del tiempo de comandos y expectativas.
Tipos de paso
[
{ "send": "SIM_LEFT", "to": "/dev/cu.usbmodem11202" },
{ "delay_ms": 500 },
{
"send": "GET_BLINK_STATE",
"to": "/dev/cu.usbmodem11402",
"expect": "BLINK"
},
{ "capture": true, "label": "post_brake" },
{
"mem_write": true,
"address": "0x48000418",
"value": "0x40",
"probe": "yellow"
},
{ "delay_ms": 1000 },
{
"mem_read": true,
"address": "0x48000400",
"count": 2,
"probe": "yellow",
"label": "gpio_post"
}
]
- Paso de envío:
{send, to, expect?, read_timeout?, line_ending?}—toes la ruta del puerto desdeserial_connect - Paso de retardo:
{delay_ms}—time.sleep()real, no idas y vueltas de llamadas de herramienta - Paso de captura:
{capture: true, label?, device_index?}— PNG guardado en/tmp/stm32-captures/ - Paso de escritura de memoria:
{mem_write: true, address | symbol + elf_path, value, probe, width?} - Paso de lectura de memoria:
{mem_read: true, address | symbol + elf_path, probe, count?, width?, label?}
Notas sobre pasos de memoria:
probeacepta SN de ST-Link, apodo de sonda o apodo de placaaddresses hexadecimal (p. ej."0x48000418"); alternativamente usasymbol+elf_pathpara resolver por nombrewidthes de 8/16/32 bits, por defecto 32 (auto-detectado desde el tamaño del símbolo al usarsymbol)- Cada operación de memoria actualmente lanza un proceso OpenOCD nuevo (sobrecarga de ~decenas de ms por operación), por lo que el tiempo entre operaciones de memoria por debajo de ~50ms es aproximado. Los retardos en sí son precisos.
Parámetros
on_failure:"continue"(por defecto) ejecuta todos los pasos sin importar."stop"aborta en el primer fallo.filter_responses: Cuandotrue, los patronesexpectcoinciden solo con líneas de respuesta VCP con prefijo>(ignora ruido de depuración).
Salida
Step 1 [/dev/cu.usbmodem11202] SEND: SIM_LEFT
Response: >OK:SIM_LEFT
Step 2 DELAY: 500ms
Step 3 [/dev/cu.usbmodem11402] SEND: GET_BLINK_STATE
Response: >BLINK_STATE:BLINK
Expect "BLINK": PASS
Step 4 [yellow] MEM_WRITE: Wrote 0x00000040 to 0x48000418
Step 5 DELAY: 1000ms
Step 6 [yellow] MEM_READ: gpio_post 0x48000400: 0xabffdfff 0x00000080
Summary: 2/2 sends OK, 1/1 assertions PASS, 1/1 mem_writes OK, 1/1 mem_reads OK
Monitoreo de memoria en vivo
Monitorea variables de firmware en tiempo real vía SWD, sin modificar el firmware ni usar serie. OpenOCD se ejecuta como un subproceso persistente y consulta variables a través de su socket TCL integrado.
Iniciar una sesión
live_memory_start(
variables='["blink", "ts"]', # symbol names from ELF
elf_path="/path/to/firmware.elf",
probe="taillight", # board/probe nickname
interval_ms=500 # min 250ms
)
Las variables pueden ser:
- Nombres de símbolo (cadenas):
"blink"— resueltos desde el ELF víaarm-none-eabi-nm - Diccionarios con símbolo + tipo:
{"symbol": "temperature", "type": "float"}— interpreta el valor de 32 bits como IEEE 754 - Diccionarios con dirección cruda:
{"address": "0x20000304", "name": "x", "width": 32}
Leer valores recientes
live_memory_read(session_id="abc123", last_n=10)
Devuelve entradas recientes de un búfer circular en memoria (máx. 100 entradas). El historial completo se escribe en el archivo de salida JSONL.
Formato de salida JSONL
{ "t": 1709830123.456, "elapsed_s": 1.002, "values": { "blink": 65539 } }
Detener una sesión
live_memory_stop(session_id="abc123")
Devuelve estadísticas: duración, recuento de lecturas, recuento de errores, ruta del archivo de salida.
Restricciones
- Una sesión por sonda — esto es una restricción de hardware (conexión SWD única)
- Detener antes de flashear —
live_memorymantiene la conexión SWD;stm32_flashystm32_read/write_memoryfallarán si hay una sesión activa - Puerto TCL 6666 — el predeterminado de OpenOCD. Detén otras instancias de OpenOCD primero si hay conflicto
Valores predeterminados de serie
- Velocidad de baudios: 115200
- Fin de línea: LF (
\n) - Sondeo de lectura: 50ms de espera entre bytes, 200ms de silencio para corte
- Límites de búfer: 4096 bytes máx. de lectura
Desarrollo
Inspector MCP
source .venv/bin/activate
mcp dev src/stm32_mcp/server.py
Pruebas de bucle invertido
Las herramientas serie se pueden probar sin hardware usando el bucle invertido de pyserial:
import serial
ser = serial.serial_for_url("loop://", baudrate=115200, timeout=0.1)
ser.write(b"PING\n")
print(ser.read(100)) # b'PING\n'