STM32-MCP

Acesse o STM32CubeIDE e as ferramentas de depuração

Documentação

stm32-mcp

Servidor MCP que permite ao Claude Code compilar, gravar e se comunicar com hardware STM32.

O stm32-mcp é bem específico para a forma como costumo abordar desenvolvimento de hardware, mas provavelmente é útil para outras pessoas também! Ele pode ser adaptado para muitos fluxos de trabalho, mas é focado no meu (stlink-v3 mini, VCP nesse conector, microcontrolador STM32).

Você pode fazer coisas como:

eu: ei, quem está conectado agora?

claude: duas sondas sem nome conectadas a duas PCBs sem nome

eu: ok, pergunte quem são e dê um apelido com base na resposta delas

claude: entendido, você quer apelidar as sondas também? suas placas são 'campainha A' e 'sintetizador B'

eu: sim, coloquei marcador de tinta nessas sondas. chame a da campainha de 'azul' e a do sintetizador de 'vermelho'

claude: pronto. o que vem a seguir?

eu: dê comandos VCP para ambas para que possam conversar entre si, depois faça a campainha convidar o sintetizador para um encontro

claude: pensando... pronto, o sintetizador recusou. há muitos peixes no mar, campainha!

MCP (Model Context Protocol) é um padrão aberto que permite que assistentes de IA como o Claude usem ferramentas externas. Este servidor dá ao Claude a capacidade de compilar seu firmware, gravá-lo em uma placa, conversar com ele via serial e ler memória via SWD. É flexível e conversacional.

[!WARNING] Este servidor dá a uma IA acesso direto ao seu compilador, sonda de depuração e portas seriais. Ele pode gravar firmware, sobrescrever memória e enviar dados arbitrários ao seu hardware. Isso é poderoso e útil, mas não é uma sandbox. Saiba o que está conectado antes de deixá-lo agir.

Pré-requisitos

  • STM32CubeIDE instalado em /Applications/STM32CubeIDE.app (macOS) ou /opt/st/stm32cubeide_* (Linux)
  • Python 3.10+
  • OpenOCD (brew install open-ocd) — para gravação, leitura/escrita de memória e monitoramento ao vivo
  • ferramentas stlink de código aberto (brew install stlink) — para enumeração de sondas
  • ST-Link conectado via USB (para informações de gravação/placa)
  • Porta serial disponível (ST-Link VCP ou adaptador USB-UART)

Instalação

git clone https://github.com/shieldyguy/stm32-mcp.git
cd stm32-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e .

Registrar com o Claude Code

Opção A: CLI

claude mcp add stm32 -- /path/to/stm32-mcp/.venv/bin/python -m stm32_mcp.server

Opção B: Configuração do projeto

Adicione ao .claude/settings.json ou .claude.json do seu projeto:

{
  "mcpServers": {
    "stm32": {
      "command": "/path/to/stm32-mcp/.venv/bin/python",
      "args": ["-m", "stm32_mcp.server"]
    }
  }
}

CLI autônoma

bin/ contém quatro wrappers finos sobre o mesmo código que as ferramentas MCP usam

ComandoUso
stm32-listLista sondas + placas conectadas com apelidos
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 esses comandos com seus usos (gerado automaticamente a partir dos scripts)

Adicione bin/ ao seu PATH:

export PATH="/path/to/stm32-mcp/bin:$PATH"

Apelidos de sondas e apelidos de placas são resolvidos.

As compilações compartilham o bloqueio do workspace headless do CubeIDE do MCP, então um stm32-build/stm32-bf competindo com uma compilação orientada por agente ficará na fila atrás dela.

Ferramentas Disponíveis

Compilação e Gravação

FerramentaDescrição
stm32_buildCompila firmware usando o builder headless do CubeIDE
stm32_flashGrava .elf/.bin/.hex na placa via ST-Link SWD
stm32_build_and_flashCompila + grava em um único passo (o caso de 90%)
stm32_board_infoLê informações do ST-Link/MCU (ID do dispositivo, tamanho da flash, tensão)

Gerenciamento Multi-Placa

FerramentaDescrição
stm32_list_probesMostra todas as placas conectadas com apelidos e IDs de MCU
stm32_set_nicknameNomeia uma placa (por UID do MCU) ou sonda (por SN do ST-Link)

Apelidos de placas seguem o MCU físico (persistem entre trocas de sonda). Apelidos de sondas seguem o hardware ST-Link. Use apelidos em qualquer parâmetro probe em todas as ferramentas.

Comunicação Serial

FerramentaDescrição
serial_list_portsLista portas seriais (marca portas ST-Link VCP com apelidos)
serial_connectAbre uma conexão serial
serial_sendEnvia dados e lê a resposta
serial_readLê dados seriais em buffer
serial_disconnectFecha uma conexão serial
serial_sequenceExecuta sequências multi-etapa de envio/atraso/memória em uma única chamada

Depuração e Monitoramento

FerramentaDescrição
stm32_read_memoryLê memória por endereço ou nome de variável (dos símbolos ELF)
stm32_write_memoryEscreve memória por endereço ou nome de variável
live_memory_startInicia monitoramento contínuo de memória em segundo plano via SWD
live_memory_readLê entradas recentes de uma sessão de memória ao vivo
live_memory_stopPara uma sessão de memória ao vivo

Sequências de Hardware

serial_sequence agenda múltiplas etapas (envio serial, atraso, captura de webcam e leitura/escrita de memória SWD) em uma única chamada de ferramenta. Atrasos usam um time.sleep() no thread do executor. O Claude não consegue cronometrar chamadas de ferramenta individuais de forma confiável, então isso permite temporização precisa de comandos e expectativas.

Tipos de etapa

[
  { "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"
  }
]
  • Etapa de envio: {send, to, expect?, read_timeout?, line_ending?} — to é o caminho da porta de serial_connect
  • Etapa de atraso: {delay_ms} — time.sleep() real, não idas e voltas de chamadas de ferramenta
  • Etapa de captura: {capture: true, label?, device_index?} — PNG salvo em /tmp/stm32-captures/
  • Etapa de escrita de memória: {mem_write: true, address | symbol + elf_path, value, probe, width?}
  • Etapa de leitura de memória: {mem_read: true, address | symbol + elf_path, probe, count?, width?, label?}

Notas sobre etapas de memória:

  • probe aceita SN do ST-Link, apelido de sonda ou apelido de placa
  • address é hexadecimal (ex.: "0x48000418"); alternativamente, use symbol + elf_path para resolver por nome
  • width é 8/16/32 bits, padrão 32 (detectado automaticamente a partir do tamanho do símbolo ao usar symbol)
  • Cada operação de memória atualmente inicia um novo processo OpenOCD (~dezenas de ms de overhead por operação), então a temporização entre operações de memória abaixo de ~50ms é aproximada. Os atrasos em si são precisos.

Parâmetros

  • on_failure: "continue" (padrão) executa todas as etapas independentemente. "stop" aborta na primeira falha.
  • filter_responses: Quando true, padrões expect correspondem apenas a linhas de resposta VCP prefixadas com > (ignora ruído de depuração).

Saída

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

Monitoramento de Memória ao Vivo

Monitore variáveis de firmware em tempo real via SWD, sem modificar o firmware ou usar serial. O OpenOCD roda como um subprocesso persistente e consulta variáveis através de seu socket TCL integrado.

Iniciar uma sessão

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
)

As variáveis podem ser:

  • Nomes de símbolos (strings): "blink" — resolvidos do ELF via arm-none-eabi-nm
  • Dicionários com símbolo + tipo: {"symbol": "temperature", "type": "float"} — interpreta valor de 32 bits como IEEE 754
  • Dicionários com endereço bruto: {"address": "0x20000304", "name": "x", "width": 32}

Ler valores recentes

live_memory_read(session_id="abc123", last_n=10)

Retorna entradas recentes de um buffer circular em memória (máximo de 100 entradas). O histórico completo é gravado no arquivo de saída JSONL.

Formato de saída JSONL

{ "t": 1709830123.456, "elapsed_s": 1.002, "values": { "blink": 65539 } }

Parar uma sessão

live_memory_stop(session_id="abc123")

Retorna estatísticas: duração, contagem de leituras, contagem de erros, caminho do arquivo de saída.

Restrições

  • Uma sessão por sonda — esta é uma restrição de hardware (conexão SWD única)
  • Pare antes de gravar — live_memory mantém a conexão SWD; stm32_flash e stm32_read/write_memory falharão se uma sessão estiver ativa
  • Porta TCL 6666 — padrão do OpenOCD. Pare outras instâncias do OpenOCD primeiro se houver conflito

Padrões Seriais

  • Taxa de baud: 115200
  • Fim de linha: LF (\n)
  • Polling de leitura: 50ms de pausa entre bytes, 200ms de pausa por silêncio
  • Limites de buffer: máximo de 4096 bytes por leitura

Desenvolvimento

MCP Inspector

source .venv/bin/activate
mcp dev src/stm32_mcp/server.py

Teste de Loopback

As ferramentas seriais podem ser testadas sem hardware usando o loopback do 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'