This repository is a general hardware and cross-compilation template for VS Code, CMake, Ninja, and script-driven workflows.
The current stage is the native minimum loop only. It validates the command structure, CMake configure/build flow, VS Code task entry points, local configuration files, and documentation before hardware-specific backends are added.
No flashing, serial monitor, debug server, GDB launch, reset, or board-specific embedded support is implemented in this stage.
Implemented now:
python scripts/hw.py doctor
python scripts/hw.py list-boards
python scripts/hw.py configure
python scripts/hw.py build
python scripts/hw.py clean
python scripts/hw.py rebuild
python scripts/hw.py sizePlanned later:
- Cortex-M plus
arm-none-eabibuild output. - OpenOCD flashing with confirmation.
- Serial monitor.
- Debug server and GDB integration.
- ESP32, RP2040/RP2350, RISC-V MCU, and Linux ARM as separate backends.
Run these commands from the project root:
python scripts/hw.py doctor
python scripts/hw.py list-boards
python scripts/hw.py configure
python scripts/hw.py build
python scripts/hw.py sizeconfigure writes .build-config/current.yaml. That file is local machine state and is ignored by Git.
For a non-interactive default configuration:
python scripts/hw.py configure --non-interactiveFor an explicit generator and build type:
python scripts/hw.py configure --build-type Debug --generator Ninja --non-interactiveSupported build types in this stage:
DebugReleaseRelWithDebInfoMinSizeRel
Reserved generator options:
NinjaMinGW MakefilesMSYS MakefilesNMake MakefilesVisual Studio 17 2022
Ninja is the default recommended generator, but it is not the only supported design path. The selected generator is stored in .build-config/current.yaml, and build reads that file.
python scripts/hw.py clean
python scripts/hw.py rebuildclean prints the configured build_dir and only removes that directory under build/. It does not remove source files or the project root.
Use:
python scripts/hw.py doctordoctor checks:
- Python version
- pip
- Python packages:
pyyaml,rich - Git
- CMake
- Ninja
gccg++cl.exemingw32-makemake
The script reports OK, WARN, or FAIL. It does not install tools, modify PATH, or change system settings.
Manual verification commands:
cmake --version
ninja --version
python -m pip --versionThis stage expects these tools to be installed and available in PATH:
- Python 3.10 or newer
- CMake 3.20 or newer
- Ninja, recommended by default
- Git
- A native compiler, such as GCC/G++ or MSVC Build Tools
- Python packages from
requirements.txt
For Python packages, prefer a project-local virtual environment:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txtIf Python packages are missing, run:
python -m pip install -r requirements.txtThe dashboard is a local read-only board workbench. It emphasizes the board itself, hardware specs, key pins, wiring, demo status, operation status, artifacts, and flash/debug configuration. Fixed environment paths and command references are still available, but they are folded under Advanced instead of being main page content.
Commands:
python scripts/hw.py status
python scripts/hw.py dashboard
python scripts/hw.py dashboard --open
python scripts/hw.py dashboard-liveGenerated files:
reports/dashboard/index.html
reports/dashboard/status.json
reports/dashboard/history.json
reports/dashboard/ is a local generated report directory and should not be committed. It is ignored by Git.
The static dashboard contains:
- Summary status bar
- Current Board
- Board Visuals
- Hardware Specs
- Key Pins
- Demo Status
- Wiring / Connection
- Operation Status
- Build Artifacts Summary
- Flash Configuration
- Debug Configuration
- Recent Operations
- collapsed Advanced section with Quick Commands, Environment Details, and System Events
dashboard-live starts a foreground local HTTP server on localhost:8765 by default. It uses Python standard library http.server; it does not start a background service. The page polls status.json every 2 seconds, and the server refreshes status.json on request.
All dashboard/status commands are read-only with respect to firmware and hardware. They do not build, flash, debug, erase, reset, or modify PATH. Copy buttons only copy commands to the clipboard; they do not execute commands.
Status color semantics:
success,ok,ready,supported, andverifiedare green.fail,error, andmissingare red.warnandpendingare yellow.no record,n/a, andunknownare neutral gray. A missing recent flash/debug record is not treated as a warning.
Board images can be configured in a board profile:
images:
board_photo: assets/boards/stm32f103_bluepill/board
pinout: assets/boards/stm32f103_bluepill/pinoutThe paths may also include an explicit extension:
images:
board_photo: assets/boards/stm32f103_bluepill/board.jpg
pinout: assets/boards/stm32f103_bluepill/pinout.pngPlace images under:
assets/boards/stm32f103_bluepill/board.jpg
assets/boards/stm32f103_bluepill/board.png
assets/boards/stm32f103_bluepill/board.webp
assets/boards/stm32f103_bluepill/pinout.png
assets/boards/stm32f103_bluepill/pinout.jpg
assets/boards/stm32f103_bluepill/pinout.webp
Recommended names are board.jpg or board.png for the board photo, and pinout.png or pinout.jpg for the pinout. The dashboard also checks .jpg, .jpeg, .png, .webp, .bmp, .gif, and .svg, including common uppercase variants such as .JPG and .PNG.
If images are missing, the dashboard shows board image not available or pinout image not available and continues normally. The generated status.json records both the configured image path and the resolved file path that was actually used.
When the dashboard is generated, resolved local images are copied into the generated report directory, for example:
reports/dashboard/assets/boards/stm32f103_bluepill/board.jpg
reports/dashboard/assets/boards/stm32f103_bluepill/pinout.png
The original image files are not renamed or modified. The copied files are local report assets and are covered by the reports/dashboard/ Git ignore rule. This makes both dashboard --open and dashboard-live serve images from the same report directory.
Dashboard operation status notes:
- Flash dry-run and real flash are tracked separately.
- Command-line GDB debug and VS Code GUI debug are tracked separately.
dashboard-livestartup may be recorded once, but automaticstatus.jsonrefreshes are not appended to history.- The dashboard summary bar shows board, readiness, build status, flash dry-run status, real flash status, command-line debug status, VS Code GUI debug status, artifact count, and last refresh time.
Board image paths for STM32F103 BluePill:
assets/boards/stm32f103_bluepill/board.jpg
assets/boards/stm32f103_bluepill/board.png
assets/boards/stm32f103_bluepill/board.webp
assets/boards/stm32f103_bluepill/pinout.png
assets/boards/stm32f103_bluepill/pinout.jpg
assets/boards/stm32f103_bluepill/pinout.webp
This stage adds read-only project status views for the terminal and a local static HTML dashboard.
Terminal status:
python scripts/hw.py statusStatic dashboard generation:
python scripts/hw.py dashboard
python scripts/hw.py dashboard --openThe dashboard is generated at:
reports/dashboard/index.html
status and dashboard only read project state. They do not build, flash, debug, erase, reset, modify PATH, or start a background service.
The dashboard can be used to inspect:
- current board
- current build configuration
- board information
- toolchain status
- build artifacts
- flash configuration
- debug configuration
- common commands
reports/dashboard/ is a local generated report directory and is ignored by Git by default.
This stage adds a VS Code graphical debug configuration for stm32f103_bluepill using the Cortex-Debug extension, OpenOCD, ST-Link, and arm-none-eabi-gdb.
Prerequisites:
- Cortex-Debug extension installed:
marus25.cortex-debug - OpenOCD available in PATH
arm-none-eabi-gdbavailable in PATH- ST-Link connected over SWD
stm32f103_bluepillbuilt successfully- The matching firmware has already been flashed with
python scripts/hw.py flash
Recommended setup before starting VS Code debugging:
python scripts/hw.py configure --board stm32f103_bluepill --build-type Debug --generator Ninja --non-interactive
python scripts/hw.py build
python scripts/hw.py size
python scripts/hw.py flash --dry-run
python scripts/hw.py flashThen in VS Code:
- Open Run and Debug.
- Select
HW: Debug STM32F103 BluePill. - Click Start Debugging.
- Set breakpoints, step, continue, inspect variables, and inspect the call stack from the VS Code UI.
The launch configuration uses:
type:cortex-debugservertype:openocdconfigFiles:interface/stlink.cfg,target/stm32f1x.cfggdbPath:arm-none-eabi-gdb- executable:
build/stm32f103_bluepill/Ninja/Debug/stm32f103_bluepill.elf
The launch configuration does not execute load, does not erase Flash, and does not flash firmware. It assumes the board already contains firmware matching the selected .elf. If the flashed firmware and .elf do not match, breakpoints and stepping may behave incorrectly.
Common issues:
- Cortex-Debug extension is not installed.
arm-none-eabi-gdbis not found in PATH.- OpenOCD is not found in PATH.
localhost:3333is occupied by another OpenOCD instance.- ST-Link is busy in another program.
- The ELF path does not exist because the project was not built.
- The build directory is not
Ninja/Debug; update the launch config or rebuild with the documented command. - Current configured board is not
stm32f103_bluepill. - Breakpoints do not hit because the board firmware does not match the ELF.
- GDB may warn about host encoding; this has not affected command-line debugging in the current setup.
A common ST-LINK V2 supports SWD flashing and debugging. It does not provide a normal serial monitor. Serial monitor requires a separate USB-UART adapter or another board-specific serial path and is intentionally not part of this stage.
This stage adds command-line SWD debugging for stm32f103_bluepill using OpenOCD and arm-none-eabi-gdb.
Scope:
- Supported board:
stm32f103_bluepill - Probe: ST-LINK V2 over SWD
- GDB server: OpenOCD on
localhost:3333 - GDB client:
arm-none-eabi-gdb
A common ST-LINK V2 supports SWD flashing and debugging. It is not the same thing as a USB-to-serial adapter, so this stage does not implement serial monitor.
Build and flash the firmware first so the target contains the same program as the .elf symbols:
python scripts/hw.py configure --board stm32f103_bluepill --build-type Debug --generator Ninja --non-interactive
python scripts/hw.py build
python scripts/hw.py size
python scripts/hw.py flash --dry-run
python scripts/hw.py flashUse two terminals for debugging.
Terminal 1:
python scripts/hw.py debug-serverTerminal 2:
python scripts/hw.py debugThe debug command starts GDB with the current .elf file and runs:
target remote localhost:3333
monitor reset halt
break main
continue
It does not execute load, does not erase Flash, and does not flash firmware. Flashing and debugging are intentionally separate operations.
Useful GDB commands:
break main
continue
next
step
info registers
x/16xw 0x08000000
quit
Common issues:
localhost:3333connection failed: startpython scripts/hw.py debug-serverin another terminal first.- ST-Link is busy: close OpenOCD, STM32CubeProgrammer, or other tools using the probe.
- OpenOCD not started: check terminal 1 output.
.elfdoes not match flashed firmware: rebuild and flash again.- Breakpoints do not work: confirm symbols are from the same
.elfand the target halted correctly. - Stepping looks strange: compiler optimization can affect source-level stepping; use
Debugbuilds.
This stage prepares flashing for stm32f103_bluepill only. It uses OpenOCD with ST-Link and keeps the operation guarded by dry-run output and an explicit confirmation prompt.
Current scope:
- Supported board:
stm32f103_bluepill - Flash method: OpenOCD
- Interface config:
interface/stlink.cfg - Target config:
target/stm32f1x.cfg - Artifact:
.elf
Not implemented in this stage:
- serial monitor
- debug server
- GDB debug launch
- standalone reset command
- erase command
- other platform backends
Install OpenOCD manually if needed. The scripts do not install OpenOCD and do not modify PATH. On Windows, prefer non-C-drive locations such as:
E:\Tools\openocdE:\EmbeddedTools\openocdE:\vscode_code\Tools\openocd
After installation, add the directory containing openocd.exe to PATH and verify:
where openocd
openocd --version
python scripts/hw.py doctorBefore flashing, build and inspect the command:
python scripts/hw.py configure --board stm32f103_bluepill --build-type Debug --generator Ninja --non-interactive
python scripts/hw.py build
python scripts/hw.py size
python scripts/hw.py flash --dry-runflash --dry-run only prints the OpenOCD command. It does not connect to hardware and does not write Flash.
After dry-run output is correct and hardware is connected, run:
python scripts/hw.py flashReal flash prints the OpenOCD command, warns that target Flash will be written, and requires typing yes. Any other input cancels the operation.
Common issues:
openocd not found: install OpenOCD and add the directory containingopenocd.exeto PATH.- ST-Link not detected: check USB cable, driver, ST-Link firmware, and device manager.
- target voltage not detected: check target power and SWD wiring.
- access denied: close other tools using ST-Link and check USB permissions.
- wrong target config: this stage uses
target/stm32f1x.cfgfor STM32F103. - BOOT0/BOOT1 or reset state abnormal: return BOOT0/BOOT1 to the expected boot state and retry after reset.
This stage adds a minimum Cortex-M cross-compilation loop for stm32f103_bluepill. It only builds firmware artifacts. It does not flash, monitor serial output, start a debug server, launch GDB, or reset hardware.
Configure and build native regression first:
python scripts/hw.py configure --board native --build-type Debug --generator Ninja --non-interactive
python scripts/hw.py build
python scripts/hw.py sizeConfigure and build STM32F103 BluePill firmware:
python scripts/hw.py configure --board stm32f103_bluepill --build-type Debug --generator Ninja --non-interactive
python scripts/hw.py build
python scripts/hw.py sizeThe STM32F103 BluePill profile uses:
- backend:
cortex_m - toolchain:
arm-none-eabi - startup:
startup/stm32/startup_stm32f103xb.s - linker script:
linker/stm32f103c8t6.ld - CPU flags:
-mcpu=cortex-m3 -mthumb -mfloat-abi=soft
Expected firmware outputs under build/stm32f103_bluepill/Ninja/Debug:
stm32f103_bluepill.elfstm32f103_bluepill.binstm32f103_bluepill.hexstm32f103_bluepill.map
The size command calls arm-none-eabi-size for Cortex-M boards. If the tool is not found, verify that the official Arm GNU Toolchain bin directory is in PATH.
The future Cortex-M stage will require the official Arm GNU Toolchain for Arm Embedded, usually exposed through these commands:
arm-none-eabi-gcc --version
arm-none-eabi-g++ --version
arm-none-eabi-objcopy --version
arm-none-eabi-size --version
arm-none-eabi-gdb --version
python scripts/hw.py doctorThis toolchain is needed for bare-metal ARM Cortex-M cross-compilation. Future board profiles such as STM32F103 BluePill and other Cortex-M targets will depend on it.
Use the vendor official Arm GNU Toolchain as the default recommendation. Do not use the MSYS2 arm-none-eabi package as the default project path. MSYS2 is still useful for native Windows tools such as gcc, cmake, ninja, and make, but the embedded ARM toolchain should come from the official Arm GNU Toolchain unless a later project deliberately chooses otherwise.
If you use reasonix to install or manage the toolchain, select the official Arm GNU Toolchain release and choose a non-C-drive installation path when possible. This repository does not call reasonix automatically, does not install software, and does not modify PATH.
Recommended Windows install roots:
E:\Toolchains\arm-gnu-toolchainE:\Tools\arm-gnu-toolchainE:\EmbeddedTools\arm-gnu-toolchainE:\vscode_code\Tools\arm-gnu-toolchain
After installation, add the actual bin directory to PATH. Depending on the extracted package layout, that may be something like:
E:\Toolchains\arm-gnu-toolchain\bin
or a versioned subdirectory's bin folder. Verify from the same PowerShell session that VS Code will use:
arm-none-eabi-gcc --version
arm-none-eabi-g++ --version
arm-none-eabi-objcopy --version
arm-none-eabi-size --version
arm-none-eabi-gdb --version
python scripts/hw.py doctorDo not commit local installation paths. If a path needs to be recorded, put it in one of these ignored local files:
config/toolchains.local.yamlCMakeUserPresets.json.build-config/current.yaml
config/toolchains.example.yaml documents the expected command names and recommended install roots without storing private machine paths.
The scripts do not install anything automatically. On Windows, prefer installing tools outside the C drive when possible, for example:
E:\Tools\ninjaE:\vscode_code\Tools\ninjaE:\msys64E:\SDKE:\EmbeddedToolsE:\Toolchains
After installing Ninja, add the directory containing ninja.exe to PATH, then verify:
ninja --versionIf an installer defaults to C:\Program Files, C:\Program Files (x86), or C:\Users\<name>\AppData, choose a custom install path on E: when the tool supports it.
MSYS2 can be a practical Windows source for gcc, g++, cmake, ninja, and make. A reasonable install location is:
E:\msys64
Prefer one environment consistently, usually UCRT64 or MINGW64. Avoid mixing tools from different sources in one shell, such as CMake from one installation, GCC from another, and Ninja from a third. Mixed toolchains often cause generator, path, runtime, and compiler-detection failures.
The scripts may detect common E:\msys64\ucrt64\bin or E:\msys64\mingw64\bin locations and print a hint, but they do not modify PATH.
Check these first:
python scripts/hw.py doctor
cmake --version
ninja --versionThen inspect .build-config/current.yaml:
Get-Content .build-config/current.yamlCommon causes:
- Ninja is selected but
ninja.exeis not in PATH. - CMake is not in PATH.
- The selected generator does not match the compiler available in the current shell.
- GCC/G++ or MSVC is not available in the current shell.
- Tools from MSYS2, Visual Studio, standalone CMake, and standalone Ninja are mixed inconsistently.
Commit these files:
scripts/,boards/examples/,boards/templates/toolchains/,cmake/,config/defaults.yaml,config/toolchains.example.yaml.vscode/tasks.json,.vscode/settings.json,.vscode/extensions.jsonCMakeLists.txt,CMakePresets.json,requirements.txt,.gitignore,README.md,AGENTS.md- template source files under
src/andinclude/
Do not commit these files:
build/.build-config/.venv/CMakeUserPresets.jsonconfig/toolchains.local.yaml- generated firmware or binary outputs
Keep private SDK, compiler, and tool paths in ignored local files such as:
CMakeUserPresets.jsonconfig/toolchains.local.yaml.build-config/current.yaml
scripts/hw.pyis the unified command entry.scripts/hwlib/contains reusable command, config, process, CMake, and backend logic.boards/contains board profiles and templates.toolchains/contains CMake toolchain files.cmake/contains reusable CMake modules.config/contains shared defaults and example local configuration..build-config/contains current local selection state and is ignored by Git..vscode/contains lightweight task shortcuts only.
Recommended order:
- Keep the native backend as a regression target.
- Add a minimal Cortex-M backend with
arm-none-eabibuild output only. - Add size, binary, hex, and map generation for embedded targets.
- Add OpenOCD flashing with explicit confirmation and dry-run support.
- Add serial monitoring.
- Add debug server and GDB integration.
- Add ESP-IDF, RP2040/RP2350, RISC-V MCU, and Linux ARM as separate backends rather than forcing every platform into one flow.