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

ComandoUso
stm32-listLista sondas y placas conectadas con apodos
stm32-flashstm32-flash <probe|board> <file.elf> [--noverify] [--noreset]
stm32-buildstm32-build <project_path> [Debug|Release] [--clean]
stm32-bfstm32-bf <project_path> <probe|board> [Debug|Release] [--clean]
stm32-helpLista 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

HerramientaDescripción
stm32_buildCompila firmware usando el compilador headless de CubeIDE
stm32_flashFlashea .elf/.bin/.hex a la placa vía ST-Link SWD
stm32_build_and_flashCompilar + flashear en un solo paso (el caso del 90%)
stm32_board_infoLee información de ST-Link/MCU (ID de dispositivo, tamaño de flash, voltaje)

Gestión de múltiples placas

HerramientaDescripción
stm32_list_probesMuestra todas las placas conectadas con apodos e IDs de MCU
stm32_set_nicknameNombra 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

HerramientaDescripción
serial_list_portsLista puertos serie (marca puertos ST-Link VCP con apodos)
serial_connectAbre una conexión serie
serial_sendEnvía datos y lee respuesta
serial_readLee datos serie almacenados en búfer
serial_disconnectCierra una conexión serie
serial_sequenceEjecuta secuencias de envío/retardo/memoria de múltiples pasos en una sola llamada

Depuración y monitoreo

HerramientaDescripción
stm32_read_memoryLee memoria por dirección o nombre de variable (desde símbolos ELF)
stm32_write_memoryEscribe memoria por dirección o nombre de variable
live_memory_startInicia monitoreo continuo de memoria en segundo plano vía SWD
live_memory_readLee entradas recientes de una sesión de memoria en vivo
live_memory_stopDetiene 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?}to es la ruta del puerto desde serial_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:

  • probe acepta SN de ST-Link, apodo de sonda o apodo de placa
  • address es hexadecimal (p. ej. "0x48000418"); alternativamente usa symbol + elf_path para resolver por nombre
  • width es de 8/16/32 bits, por defecto 32 (auto-detectado desde el tamaño del símbolo al usar symbol)
  • 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: Cuando true, los patrones expect coinciden 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ía arm-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 flashearlive_memory mantiene la conexión SWD; stm32_flash y stm32_read/write_memory fallará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'