The toolchain decision gets made in the first week of a firmware project, usually by whichever engineer downloads something first, and then it stays for the life of the product. Five years later it is why builds only work on one laptop, why the CI server cannot produce a binary, and why a compiler license renewal shows up as a line item nobody can cancel. Here are the realistic options and what each costs you.

The three families of choice

The vendor IDE

STM32CubeIDE, MPLAB X, Code Composer Studio, MCUXpresso — every silicon vendor ships one, usually free and Eclipse-based. The appeal is real: a graphical pin and clock configurator, generated peripheral init code, working debug out of the box, and examples that compile on the first try. For a bring-up week, nothing beats it.

The costs appear later. Projects are described by IDE-specific metadata rather than a portable build file, so headless builds on a CI runner range from awkward to unsupported. Generated and hand-written code interleave, and a regeneration can clobber your edits. And you are married to one vendor's ecosystem, which matters the day a part goes end-of-life.

Plain GCC plus CMake

The arm-none-eabi-gcc toolchain with CMake or Make, a linker script you own, and your editor of choice. It takes a day or two longer to set up. In exchange the build is a text file in version control that works identically on a developer machine and a build server. This is the default recommendation for any product expected to ship and be maintained for years: the setup cost is paid once, the portability pays every week afterward.

A framework that brings its own build system

Zephyr uses CMake with a Kconfig layer and device tree source files, plus the west meta-tool. ESP-IDF wraps CMake with idf.py and its own menuconfig. Both give you a hardware abstraction, a driver model, a network stack, and a configuration system that scales across board variants — and both impose a learning curve measured in weeks. Device tree in particular defeats experienced engineers on first contact.

Choose one when your product needs the stack it provides: Zephyr for portability across silicon vendors, a mature Bluetooth or Thread stack, and long-term support releases; ESP-IDF once you have committed to Espressif silicon. The RTOS side of the decision is in FreeRTOS vs Zephyr — picking Zephyr means picking its build system whether you wanted to or not.

PlatformIO

PlatformIO sits on top as a package manager and build wrapper across hundreds of boards, with decent VS Code integration and a useful CLI for CI. It is excellent for prototyping. For a product going through certification and long maintenance, its opinionated dependency resolution and distance from the real compiler invocation are drawbacks; many teams prototype in PlatformIO and migrate to plain CMake before design freeze.

Comparing the practical consequences

CriterionVendor IDEGCC + CMakeZephyr / ESP-IDF
Time to first blinkHours1–2 days2–5 days
Headless CI buildPoorExcellentExcellent
Portability to another MCULowMediumHigh (Zephyr)
Learning curve for a new hireLowMediumHigh
Long-term maintenance riskVendor-dependentLowCommunity/vendor LTS

Debug probes are part of the toolchain

Three practical tiers. On-board debuggers such as ST-LINK or the Espressif built-in USB-JTAG cost nothing extra and are fine for development. A SEGGER J-Link, at a few hundred dollars, buys reliable flashing, unlimited breakpoints on many parts, and RTT logging that gives printf-speed diagnostics without a UART. A CMSIS-DAP probe such as a DAPLink is the open, low-cost middle ground.

Whatever you pick, insist that the same probe works from the command line — OpenOCD, pyOCD, or J-Link Commander — because that is what your automated test rig and production programming station will use. The broader picture is in embedded debugging tools: JTAG, SWD, and logic analyzers, and the same reasoning extends to hardware-in-the-loop testing once you have more than a handful of units.

Reproducible builds and CI

The test is simple: can a new laptop, given only your repository, produce a binary byte-for-byte identical to the released one? If not, you cannot prove what shipped, and a field failure investigation becomes archaeology.

Getting there means pinning the compiler version explicitly (not "whatever is on PATH"), pinning every dependency by commit hash, eliminating absolute paths and timestamps, and running the whole build in a versioned container. Then make the CI job produce the release artifact — never a developer's machine. Tie it to a version scheme so a device in the field can be matched to its exact source, which is the foundation for firmware version management and for signing images if you are doing OTA updates.

When a commercial compiler is worth paying for

IAR Embedded Workbench and Keil MDK cost roughly $3,000–$6,000 per seat per year. GCC is free and, for most Cortex-M work, produces comparable code. The honest reasons to pay:

  • Certified toolchains. Functional safety work under IEC 61508 or ISO 26262 is far easier with a vendor-qualified compiler and its qualification kit.
  • Code size on a tight part. On constrained flash, commercial compilers sometimes still win by a meaningful margin — worth measuring if you are near the wall described in how much flash and RAM your microcontroller really needs.
  • Legacy code that only builds under one compiler, where porting is not the best use of the quarter.
  • Support with a phone number when a compiler bug blocks a release.

Otherwise the license cost buys less than an extra week of engineering time would.

Hiring and the ten-year view

The toolchain quietly sets your hiring pool. Engineers who know GCC, CMake, and gdb are everywhere. Production Zephyr experience is scarcer and more expensive, though the pool is growing fast. The same applies to your language choice — see Rust vs C for embedded — where tooling maturity now matters more than the language argument.

A reasonable default: prototype in the vendor IDE to get the hardware talking, then move to CMake with pinned GCC and a containerized CI build before design freeze, keeping vendor HAL sources in-tree rather than as IDE-generated output. Adopt Zephyr instead if you need its stack or expect to run the same firmware across MCU families.

Projects House sets up embedded projects so the build is reproducible from day one, CI produces the release artifact, and the client owns every file needed to rebuild the firmware without us. If you are starting a firmware project or inheriting one that only builds on a single machine, describe it through the contact form.