Skip to main content

Deterministic Python wrapper around the six ST vendor CLIs (CubeProgrammer, CubeIDE, CubeMX, ST-LINK gdbserver, arm-none-eabi-gdb, STM32_SigningTool_CLI) plus a USB virtual COM port reader. Surfaces a Python library, a `stm32` CLI, and five Claude-Code slash commands.

Project description

embedagents-stm32

STM32 development, by talking to Claude.

STM32 is one of the most widely used 32-bit microcontroller families in the world. ST ships a strong, free toolchain for it — CubeMX to configure a chip, CubeIDE to build and debug, CubeProgrammer to flash it, and a handful more. The tools are capable, but they're a lot to learn, they don't talk to each other, and none was built to be driven by an AI coding agent.

This plugin changes that. It lets you do STM32 development by talking to Claude Code in plain language — "build my project," "flash it to the board," "why is it crashing?" Claude calls ST's tools for you and reports back. You stop juggling six separate applications and just describe what you want done.

The promise: once it's installed, you don't have to remember anything about it. No flags to memorize, no syntax to keep in your head. The five slash commands are there for when you'd rather point at a tool directly — but most of the time you just talk.

Demo

Claude Code building and flashing an STM32 project from one plain-language request

Real session, real hardware: a NUCLEO-L476RG (Cortex-M4), built and flashed end-to-end from one sentence — first try.

The same request in text, plus two more shapes of what a session looks like:

Build & flash:

You: build my project and flash it to my nucleo Claude: (runs stm32 build ~/blinky-demo — resolves the nested CubeIDE project, builds Debug) Build: 0 errors, 0 warnings — BLINKY.elf, ~5.7 KB flash. (runs stm32 prog flash …/Debug/BLINKY.elf) Flashed in ~0.3 s, address taken from the ELF. The LED should be blinking now.

Diagnose a crash:

You: my board keeps crashing — memory bug or interrupt problem? Claude: (runs stm32 debug read-peripheral SCB, read-peripheral NVIC, decode-hardfault, callstack) Neither — every fault register is zero (CFSR=0, HFSR=0) and no IRQs are enabled yet. You're looking at the next boot, not the crash: the MCU was reset mid-fault — most likely a watchdog, or an NVIC_SystemReset() inside a fault handler. Let's catch it live: I'll break in the fault handler before the reset fires.

Read a register:

You: what's RCC->CR right now? Claude: (runs stm32 debug read-peripheral RCC — gdbserver attach, SVD decode, detach) CR = 0x00000063: MSION=1, MSIRDY=1, PLL off — the core is on the 4 MHz MSI reset default, which means your clock configuration never ran.

What just happened in all three: the substrate ran ST's own CLIs and captured their output; Claude read it and decided the next step. No magic, no parsing of vendor prose — the same tools you already trust, driven conversationally.

What Claude can do for you

You ask, Claude does:

  • Configure a chip and generate project code (STM32CubeMX)
  • Build firmware (STM32CubeIDE)
  • Flash, erase, read, and verify device memory (STM32CubeProgrammer)
  • Sign secure binaries for the chips that require it (STM32 Signing Tool)

Claude reaches for these on its own:

  • Read firmware state over the debugger while diagnosing a crash or working a fix (ST-LINK GDB server). For hands-on stepping you'll still want the CubeIDE GUI — this path is built for Claude to read what your firmware is doing, not to replace your debugger.
  • Read the serial port when it needs to see what your firmware is printing, or confirm a board is alive (USB virtual COM port).

The substrate is family-agnostic: if ST's tools support the chip, Claude can drive them against it — from an 8 MHz low-power part up to the latest Cortex-M85 and NPU-equipped silicon.

Quick start

  1. Install ST's tools — the ones you need, from st.com: CubeProgrammer, CubeIDE, CubeMX, the ST-LINK GDB server, arm-none-eabi-gdb, the Signing Tool. The substrate drives them; it doesn't bundle them.
  2. Install the substrate — the stm32 CLI plus the Claude Code plugin, in one paste (see Install below).
  3. Bring a project — an existing CubeIDE project, a CubeMX .ioc, or just a .bin/.elf to flash. No project yet? Generate one with /stm32project.
  4. Write stm32-project.jsonc — point the substrate at that project (board, build, ELF, IOC). Claude can scaffold it for you (see the one per-project step below).
  5. Attach your board — an ST-LINK probe and a NUCLEO/DISCO for anything that flashes, debugs, or reads the serial port.
  6. Talk"build it and flash my Nucleo." Claude runs the tools and reports back.

Install — 30 seconds

Requirements: Claude Code, Python 3.11+, Git, and ST's STM32 tools — install the ones you need; the substrate drives them, it doesn't bundle them. Linux or Windows (macOS isn't supported yet). An ST-LINK probe and a board for anything that touches hardware.

Step 1: Install on your machine

Open Claude Code and paste this. Claude does the rest.

Install the STM32 substrate: run pipx install embedagents-stm32 (or pip install embedagents-stm32; on an externally-managed Python — most modern Linux — plain pip is refused, so use pipx or a venv) to get the stm32 CLI on your PATH, then register the plugin with claude plugin marketplace add EmbedAgents/stm32-substrate and claude plugin install embedagents-stm32@embedagents. Then ask me which ST tools I have installed (STM32CubeProgrammer, STM32CubeIDE, STM32CubeMX, the ST-LINK_gdbserver, arm-none-eabi-gdb, the STM32_SigningTool_CLI) and write a .claude/stm32-tools.local.jsonc that points at them — copy the structure from the plugin's stm32-tools.local.jsonc.example (don't invent keys; it must conform to the bundled stm32-tools.local.schema.json), then confirm it loads with no schema error by running any stm32 command from the project folder.

That installs the stm32 CLI + embedagents.stm32 library and registers the five /stm32* slash commands. Restart Claude Code if the commands don't show up right away.

On Windows: a bare pip install often resolves to a per-user install, which drops stm32.exe into %APPDATA%\Python\Python3xx\Scripts — a directory that isn't on PATH by default, so stm32 won't be found in a new terminal. The simplest fix is pipx install embedagents-stm32 (or install into a virtualenv); otherwise add that Scripts folder to your user PATH. pip prints the exact path in its "installed in ... which is not on PATH" warning during install.

Prefer to do the plugin half by hand? Run /plugin marketplace add EmbedAgents/stm32-substrate then /plugin install embedagents-stm32@embedagents. Want the whole EmbedAgents tool family in one step? pip install EmbedAgents (a meta-package that pulls embedagents-stm32 and future siblings). Upgrading from a pre-0.3.0 install: pip uninstall stm32-substrate and /plugin uninstall stm32-substrate first — the distribution and plugin were renamed (the stm32 CLI and all slash commands are unchanged).

Step 2: Point it at your ST tools

The substrate finds each tool by environment variable → .claude/stm32-tools.local.jsonc → your PATH, and fails loud — naming the exact key to set — if it can't. Claude can write that file for you in Step 1; the schema lists every key. Set it once and you're done.

Then just talk:

You: build my project and flash it to the Nucleo Claude: (runs stm32 build then stm32 prog flash …, reports back)

The one per-project step you can't skip

Do this once per project — most commands have nothing to act on until you do. Drop a stm32-project.jsonc in your project folder; it tells the substrate your board, build, workspace, ELF, and IOC, and every command reads it. Fill it once and you stop repeating yourself. Only version is strictly required.

{
  "version": 1,
  "project_name": "blinky",
  "board":    { "name": "NUCLEO-L476RG", "mcu": "STM32L476RG" },
  "firmware": { "board": "nucleo-l476rg", "flash_address": "0x08000000" },
  "build": {
    "project_path": "STM32CubeIDE",
    "default_configuration": "Debug",
    "artifact": "Debug/blinky.elf"
  },
  "debug":  { "elf_path": "STM32CubeIDE/Debug/blinky.elf" },
  "cubemx": { "ioc_path": "blinky.ioc" }
}

build.workspace is the Eclipse workspace CubeIDE uses for headless builds. Leave it unset unless you have a reason not to: by default the substrate uses a private, per-project workspace under your user cache (~/.cache / %LOCALAPPDATA%), outside the project tree, and rebuilds it each build so it always reflects your on-disk project. If you do set it, the path must be outside the project directory (an in-tree value is rejected — CubeIDE's headless import refuses a project that contains its own workspace), and you then own it — see the workspace gotcha under Troubleshooting.

How you tell a command which project or file to use

  1. You name it — attach a file, or put a path in your request.
  2. The folder's stm32-project.jsonc — in the folder you name, or the one Claude is running in.
  3. Otherwise it stops and asks you, with a template to fill — it never scans and guesses (no "pick the only .elf").

A specific ELF or IOC follows the same order: your explicit arg → the descriptor field (debug.elf_path, cubemx.ioc_path, build.artifact) → a loud error.

(Vendor-tool paths resolve separately — see Step 2. Device, board, and peripheral names in your prompts are illustrative; ground truth is CubeMX's database and the SVD files.)

Usage

You mostly just talk, like in Step 1 — "flash the build to my Nucleo and reset it" and Claude runs the tools. The five slash commands stay available when you'd rather point at one directly:

Command What it does ST tool behind it
/stm32project Configure a chip and generate project code STM32CubeMX
/stm32build Build your firmware STM32CubeIDE
/stm32prog Flash, erase, read/verify memory, sign secure binaries STM32CubeProgrammer + Signing Tool
/stm32debug Read firmware state over the debugger during a fix ST-LINK GDB server
/stm32agent Read the serial port and run cross-tool flows VCP reader

Each surface — the library, the stm32 CLI, and the slash commands — maps to the same operations, so anything you can ask for in chat you can also script.

As a Python library

from embedagents.stm32.context import SubstrateContext
from embedagents.stm32.cubeprogrammer import CubeProgrammer

ctx = SubstrateContext.from_environment()
prog = CubeProgrammer(ctx)
banner = prog.connect()
print(banner.device_name, banner.flash_size_kb)

Safety

Destructive operations are gated, not silent. Mass erase, flashing a .bin to an inferred address, and option-byte / RDP writes all require an explicit confirmation (confirm_destructive=True in the library, a --confirm-… flag on the CLI). The substrate captures tool output and outcomes — it doesn't second-guess them — and surfaces failures as structured errors with an actionable hint rather than a raw traceback.

Uninstall

Remove the plugin and the package — nothing else is left behind.

# Remove the Claude Code plugin + its marketplace entry
claude plugin uninstall embedagents-stm32
claude plugin marketplace remove embedagents

# Uninstall the Python package / `stm32` CLI
pip uninstall embedagents-stm32      # or: pipx uninstall embedagents-stm32

The marketplace is named embedagents (the name it registered under when you ran claude plugin marketplace add EmbedAgents/stm32-substrate), not stm32. If you installed the stm32 CLI with pipx — the route the install section recommends for Windows, and the one a PEP 668 "externally-managed" Python on Linux forces — remove it with pipx uninstall embedagents-stm32; a plain pip uninstall won't find a pipx-managed package.

If you created one, delete your .claude/stm32-tools.local.jsonc. The substrate leaves nothing else on your machine — no caches, no dotfiles, no daemons.

Troubleshooting

  • ConfigurationError: … not found — the substrate couldn't locate a tool. The error names the exact env var / JSON key to set. Point it at the tool in .claude/stm32-tools.local.jsonc, or export the named variable (e.g. STM32_PROGRAMMER_CLI).
  • A build/flash fails with a schema-validation error, or a tool you configured still reports "not configured" — check your .claude/stm32-tools.local.jsonc for correctness: it must conform to the bundled schema (compare against stm32-tools.local.jsonc.example; remove any unrecognized keys the error names). The substrate uses the nearest such file searching upward from your project dir, so a stale or invalid one in a parent directory can shadow a correct one. Run any stm32 command from the project folder to see the exact validation error.
  • The /stm32* commands don't show up — restart Claude Code, then check claude plugin list. Re-run Step 1 if the plugin isn't listed.
  • macOS is not supported — v1 runs on Linux and Windows only; macOS is planned based on demand.
  • Probe not found / target-connect errors — check the ST-LINK cable and board power, and make sure nothing else holds the probe. Only one debug client can own the SWD probe at a time, so close the CubeIDE GUI debugger or any other running gdbserver first.
  • A destructive operation was refused — that's the safety gate working. Re-run with confirm_destructive=True (library) or the matching --confirm-… flag (CLI).
  • Build fails after you deleted folders like Drivers/ or Doc/ from the project, and you set your own build.workspace — those folders are linked-resource containers CubeIDE materializes; deleting them while reusing a persistent workspace would make CubeIDE silently strip them from .project. The substrate detects this and refuses to build rather than corrupt the project. Either restore the deleted folders, or delete your build.workspace directory so the next build re-imports the project cleanly. (The default, substrate-managed workspace handles this for you — it rebuilds each build — so this only applies when you configure build.workspace yourself.)
  • .cproject.substrate-backup-<timestamp> files pile up next to your project — that's intentional, not a leak. Whenever a build modifies your .cproject (changing compiler/linker options or symbols, or applying a preset), the substrate first saves a timestamped copy of the original. It could delete these automatically, but keeps them by design: they're a zero-cost safety net that lets you roll back to a known-good .cproject if an edit ever goes wrong — and in practice they have been the only way to recover a project after its .cproject was damaged. They're plain copies; delete the .cproject.substrate-backup-* files whenever you like (the newest is the most recent pre-edit state).
  • Schema validation failed at startup — fix the reported field in your config. For a one-off debug bypass, set STM32_SUBSTRATE_SKIP_SCHEMA_VALIDATION=1 (it warns loudly).

Privacy & Telemetry

Nothing is sent anywhere, ever. The substrate has no telemetry, no analytics, no crash reporting, no usage tracking, and no phone-home — none. It makes no network calls at all.

It runs entirely on your machine: it shells out to ST's local vendor CLIs and reads your serial port, and that's the whole story. No account and no API key are needed to use it. Its only dependencies are jsonschema and pyserial, neither of which contacts a server.

The only things that ever touch the network are tools you already run and control — ST's own installers (when you download them) and Claude Code itself (your conversation with Claude, under Anthropic's terms). The substrate adds zero network surface of its own.

License

MIT © 2026 EmbedAgents

Disclaimer

This project is an independent, community-driven tool and is not an official release by STMicroelectronics. STM32 is a registered trademark of STMicroelectronics International N.V. This software is provided free of charge for educational and development purposes, and its use of the trademark is strictly descriptive to help developers identify hardware compatibility.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

embedagents_stm32-0.3.1.tar.gz (186.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

embedagents_stm32-0.3.1-py3-none-any.whl (207.5 kB view details)

Uploaded Python 3

File details

Details for the file embedagents_stm32-0.3.1.tar.gz.

File metadata

  • Download URL: embedagents_stm32-0.3.1.tar.gz
  • Upload date:
  • Size: 186.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for embedagents_stm32-0.3.1.tar.gz
Algorithm Hash digest
SHA256 edce3ac964037c7c1a8ed7794c49fa547956266f84b0454156b3e79f5470c5d1
MD5 fc754eb3d89467e9c814fd1b77d7a887
BLAKE2b-256 6ea0dcca419f9fdc5bf6121a9ed376868d0d3a4c94f3c84e1b16255ba0265cef

See more details on using hashes here.

Provenance

The following attestation bundles were made for embedagents_stm32-0.3.1.tar.gz:

Publisher: release.yml on EmbedAgents/stm32-substrate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file embedagents_stm32-0.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for embedagents_stm32-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 80884ca729b09ddb5a4e253b8830a29b6b2649608b43d0aa5415c46bf98ee55b
MD5 a929d1e80c3d5a7eb70956bbd8877ec2
BLAKE2b-256 2d41537ed53b3c5c3ae9b39165fedc5a64318cdde630be9cd4f6ed357efcad00

See more details on using hashes here.

Provenance

The following attestation bundles were made for embedagents_stm32-0.3.1-py3-none-any.whl:

Publisher: release.yml on EmbedAgents/stm32-substrate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page