This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
FOME (Free Open Motorsports ECU) is an open-source engine control unit firmware for STM32 microcontrollers. It's a fork of rusEFI focused on user experience and stability.
Default to building with 12 threads unless otherwise specified (-j12 etc).
NEVER attempt to build one file manually, always do it via the board build scripts.
Each board+chip combination has its own compile script in firmware/config/boards/<board>/:
# Example: Build for Proteus F7
cd firmware/config/boards/proteus
./compile_proteus_f7.shOutputs are placed in firmware/deliver/:
fome.bin- Complete image (bootloader + firmware) for blank ECUsfome_update.srec- Update image for bootloader flashing
cd unit_tests
make -j$(($(nproc)*1.5))
./build/fome_test
# Run a specific test
./build/fome_test --gtest_filter=TestNameUnit tests use Google Test and run on amd64/aarch64, not on the ECU.
Code generation occurs as part of the normal firmware and unit test builds, via firmware/fome_generated.mk.
If there is a build failure that looks like generated files are missing, try running make clean first. In general, there should be no reason to manually run generated code scripts.
A .clang-format file at the repository root enforces consistent code style:
- Tabs for indentation (width 4)
- K&R brace style
- Left-aligned pointers/references
VS Code integration: Format-on-save is enabled for C/C++ files via ms-vscode.cpptools.
Using the format script:
# Check formatting and show diffs
./format.sh check
# Apply formatting to all files
./format.shThe format.sh script automatically excludes external code (firmware/ext/, unit_tests/googletest/) and generated files. When running check, it displays a unified diff of formatting changes needed, making it easy to see exactly what would change.
Manual formatting:
# Format a single file
clang-format -i <file>Excluded directories: firmware/ext/ and firmware/ChibiOS/ (third-party code) are automatically excluded from formatting via DisableFormat: true markers.
firmware/config/boards/- Hardware configuration and defaults for different ECU hardwarefirmware/config/engines/- Hardware-agnostic configuration for engines (orthogonal to what ECU you run)firmware/controllers/- Core control logicalgo/- Fuel, ignition, and air calculationsactuators/- Control for engine-asynchronous outputs like electonic throttle, idle, AC, boost, VVT, etc.engine_cycle/- Control for engine-synchronous outputs like injection, ignitionsensors/- Input processing (ADC, thermistors, pressure)trigger/- Crank/cam position decoding and synccan/- CAN bus communicationlua/- Runtime scripting
firmware/hw_layer/- Hardware abstraction layerfirmware/ext/libfirmware/- Reusable library codefirmware/util/- Self-contained utilities (no external dependencies)unit_tests/- Google Test suitesimulator/- Windows/Linux firmware simulator. Uses core ECU code, but runs on PC rather than MCU.
- Event-driven execution: Trigger events from crank/cam sensors drive the main control loop
- Angle-based scheduling: Events scheduled by crank angle, not just time
- Configuration-driven: Board and engine parameters externalized; firmware adapts via configuration
- ChibiOS RTOS: Real-time operating system foundation
firmware/fome_config.txtdefines the parameters stored in persistent configuration (both "configuration", ie which pins do what, and the "calibration" or "tune", like the VE table, timing, etc.)- That file is processed by the java tool at
java_tools/configuration_definitionto generate several outputs. It is critical that these match, so that each part of the system can communicate and agree about the in-memory config format.- C/C++ headers in
firmware/generated - Along with
firmware/tunerstudio/tunerstudio.template.ini, generates the ini file used by TunerStudio to communicate with the ECU. All tuner-adjustable parameters MUST appear in this file to be useful.
- C/C++ headers in
firmware/integration/LiveData.yamldefines objects processed by the same tool to be transmitted from the ECU about the current state of the world. For example sensors, output values, and intermediate calculations useful for logging.
These are all automatically regenerated as part of running make, so no direct script invocation is required. Do not attempt to commit any generated files.
- C99 with GNU extensions for C code
- C++20 for firmware code
- No RTTI, no exceptions (
-fno-rtti -fno-exceptions) - LTO enabled by default
Key preprocessor flags that control compilation:
EFI_PROD_CODE=1- Production firmwareEFI_UNIT_TEST=1- Unit test buildEFI_SIMULATOR=1- Simulator build
- Static allocation: Prefer static allocation over dynamic (
new/malloc). Memory is limited and fragmentation must be avoided. - Performance matters: This is a hard real-time application. Fuel and ignition events must fire at precise crank angles. Avoid unnecessary computation in hot paths. Use lower priority threads for expensive computation.
- No exceptions: C++ exceptions are disabled. Use return values or error codes for error handling.
- No RTTI:
dynamic_castandtypeidare unavailable. - Interrupt safety: Be mindful of code that runs in interrupt context vs. thread context. Use appropriate synchronization primitives.
- Stack usage: Keep stack allocations small. Large arrays should be static or global, not local variables.
When you make a user-facing change (new feature, bug fix, breaking change, removal), update firmware/CHANGELOG.md under the ## Unreleased section. See /changelog for details on what counts and how to write entries.
- Supported IDE: Visual Studio Code
- Requires Unix-like OS (Linux, macOS, or Windows WSL)
- All PRs must pass CI gates (firmware builds for all boards, unit tests)
- Wiki: https://wiki.fome.tech/
- Discord: #firmware channel for help