Skip to content

Project Anatomy

Every firmware project has the same shape, whether it lives in src/, src/tests/ or starter-projects/. This page uses src/bmc as the example. For what each board actually does, see Boards.

What You Should Edit

Almost everything in a project directory is generated and will be overwritten. The files that are genuinely yours:

  • Src/*.cpp: your firmware. Picked up by a glob, so new files need no CMake change.
  • Inc/*.hpp: your headers, other than the generated <project>_config.hpp.
  • USER CODE BEGIN / USER CODE END blocks inside CubeMX's main.c and main.h.
  • The .ioc file, edited through the CubeMX GUI.

Everything else regenerates.

File Breakdown

src/bmc/
  CMakeLists.txt            generated by update_cmake_cfg.py
  CMakePresets.json         generated by update_cmake_cfg.py
  bmc.ioc                   CubeMX, edited through the GUI
  startup_stm32g431xx.s     CubeMX
  STM32G431XX_FLASH.ld      CubeMX, referenced by the toolchain file
  cmake/
    gcc-arm-none-eabi.cmake CubeMX, used by every preset
    starm-clang.cmake       CubeMX, unused
  Inc/
    main.h                  CubeMX, USER CODE blocks are yours
    stm32g4xx_hal_conf.h    CubeMX
    stm32g4xx_it.h          CubeMX
    bmc_config.hpp          generated by config_gen.py
    motor.hpp               yours
    err.hpp                 yours
    type.hpp                yours
  Src/
    main.c                  CubeMX, USER CODE blocks are yours
    stm32g4xx_hal_msp.c     CubeMX
    stm32g4xx_it.c          CubeMX
    syscalls.c              CubeMX
    sysmem.c                CubeMX
    system_stm32g4xx.c      CubeMX
    controller.cpp          yours

The five CubeMX .c files plus the startup assembly are compiled by lib/stm32g4, not by the project itself. The project's own target_sources globs Src/*.cpp only.

Two Directory Layouts

Most projects keep sources at Src/ and Inc/. Some CubeMX versions emit Core/Src and Core/Inc instead, and src/tests/serial is the one project in this tree that does. The generator probes for both, so either works, but do not mix them within a project.

The Interface with lib/

A project must set three variables before pulling in the library tree:

set(MX_SRC_DIR   "${CMAKE_SOURCE_DIR}/Src")
set(MX_INC_DIR   "${CMAKE_SOURCE_DIR}/Inc")
set(MX_STARTUP_S "${CMAKE_SOURCE_DIR}/startup_stm32g431xx.s")

add_subdirectory(../../lib fwlib)

lib/stm32g4 fails the configure step if any is missing, and lib/CMakeLists.txt uses their presence to decide whether this is an MCU build at all. lib/stm32g4 then reaches back up and adds sources to the project's own executable target by name, which is why the ordering matters.

Linked Libraries

The bottom of a generated CMakeLists.txt looks like this:

# Add linked libraries
target_link_libraries(${CMAKE_PROJECT_NAME}
    stm32cubemx

    # Add user defined libraries
    stm32
    dbc
    util
)

stm32cubemx is always present. The rest come from the --lib flags passed to scripts/new.sh:

./scripts/new.sh --board NUCLEO-G431RB --src src/example --lib stm32 --lib dbc --lib util

Warning

update_cmake_cfg.py deletes and re-renders CMakeLists.txt, CMakePresets.json and .clangd from scratch. It does not read your existing library list back out of the file. Re-running it without re-passing the full set of --lib flags silently drops the libraries the project was linking, and the failure shows up as a confusing link error later.

Library names are not validated. A typo becomes a missing-target error at build time.

Regenerating a Project

There is no shell wrapper for this. Run the tool directly:

uv run --project tools python tools/scripts/update_cmake_cfg.py \
    --src src/bmc --root . --ctx lib/stm32g4 \
    --lib stm32 --lib dbc --lib util