Skip to content

Formula-Electric-Berkeley/FEB_FIRMWARE_SN5

Repository files navigation

FEB Firmware SN5

Firmware for the FEB SN5 Formula E vehicle. Each subdirectory corresponds to a board on the car, built around STM32F4 microcontrollers.

Project Structure

Directory Board MCU Notes Docs
BMS/ Battery Management System STM32F446RE FreeRTOS, cell-monitor front-end BMS/README.md
DART/ Fan / Tachometer Controller STM32F042K6Tx Cortex-M0, bare-metal, -Os DART/README.md
DASH/ Driver Dashboard STM32F469NI FreeRTOS, LVGL, SDRAM, FATFS DASH/README.md
DCU/ Data Control Unit STM32F446RE Placeholder (empty user code) DCU/README.md
LVPDB/ Low Voltage Power Distribution STM32F446RE Dual CAN, 7× TPS LVPDB/README.md
PCU/ Powertrain Control Unit STM32F446RE Dual CAN, triple ADC, RMS gateway PCU/README.md
Sensor_Nodes/ Sensor Aggregator STM32F446RE IMU + GPS + WSS; third-party lwgps Sensor_Nodes/README.md
UART/ Serial / Console Bridge STM32F446RE FreeRTOS UART/README.md
UART_TEST/ STM32U5 Validation Fixture STM32U575ZI Cortex-M33, GPDMA, FreeRTOS UART_TEST/README.md

All boards are fully buildable with CMake.

Common Libraries

Shared across boards, under common/. See common/README.md for the library index.

Target Role Docs
feb_io Umbrella: UART + Log + Console + Commands + Version + Time + String_Utils FEB_Serial_Library
feb_can FreeRTOS-safe CAN runtime (dispatcher) FEB_CAN_Library
(submodule) CAN message definitions + code generator FEB_CAN_Library_SN4
feb_tps TPS2482 power-monitor driver FEB_TPS_Library
feb_time 64-bit microsecond monotonic clock FEB_Time_Library
feb_rtos_utils REQUIRE_RTOS_HANDLE fail-fast macro FEB_RTOS_Utils

Repository Layout

cmake/                           # Shared toolchain files
  gcc-arm-none-eabi.cmake        # ARM GCC cross-compiler config
  FEB_Version.cmake              # Per-board build provenance generator
common/                          # Shared libraries (see common/README.md)
  FEB_Serial_Library/            # feb_io umbrella + sub-libraries
  FEB_CAN_Library/               # feb_can runtime dispatcher
  FEB_CAN_Library_SN4/           # CAN message defs (git submodule)
  FEB_TPS_Library/               # feb_tps
  FEB_Time_Library/              # feb_time
  FEB_RTOS_Utils/                # feb_rtos_utils
scripts/                         # Developer scripts (see scripts/README.md)
  setup.sh                       # First-time dev environment setup
  build.sh                       # Build firmware (interactive/batch/release)
  flash.sh                       # Flash firmware to boards
  format.sh                      # Code formatting (--check for CI)
  setup-hooks.sh                 # Install pre-commit hooks
  version.sh                     # Create version tags for releases
  cubemx.sh                      # Generate HAL code from .ioc files
third-party/                     # Vendored external code (lwgps, etc.)
.github/workflows/               # CI/CD pipelines

Cloning the Repository

# Clone with submodules (required for CAN library)
git clone --recursive https://github.com/Formula-Electric-Berkeley/FEB_FIRMWARE_SN5.git

# If already cloned without --recursive:
git submodule update --init --recursive

Getting Started

For first-time setup, run the setup script:

./scripts/setup.sh        # Full setup with initial build
./scripts/setup.sh --quick  # Skip initial build

This script:

  • Verifies toolchain installation (ARM GCC, CMake, Ninja)
  • Initializes git submodules
  • Installs pre-commit hooks
  • Configures CMake
  • Runs an initial build to verify everything works

Prerequisites

Required

  • STM32CubeCLT -- bundles ARM GCC, CMake, Ninja, and STM32_Programmer_CLI

Optional

  • STM32CubeMX -- for modifying .ioc peripheral configurations
  • Python 3 -- only needed to regenerate CAN message definitions; dependencies are managed automatically (see CAN Library)
  • clang-format -- for code formatting (enforced in CI)

Environment Setup

Set CUBE_BUNDLE_PATH to your STM32CubeCLT install directory. This is required for VSCode IntelliSense to find the cross-compiler's system headers (stdio.h, etc.).

./scripts/setup.sh configures this automatically on both macOS and Windows Git Bash — it globs the install directory, detects your shell (zsh vs bash), and appends the export to the right profile file. If you prefer to set it manually, add to your shell profile:

# macOS (zsh is the default since Catalina — use ~/.zshrc)
export CUBE_BUNDLE_PATH=/opt/ST/STM32CubeCLT_1.19.0   # or wherever the installer placed it

# Windows Git Bash (use ~/.bashrc)
export CUBE_BUNDLE_PATH=/c/ST/STM32CubeCLT_1.19.0

Then restart your terminal (and VSCode, if open).

Verify Your Setup

arm-none-eabi-gcc --version
cmake --version    # 3.22+
ninja --version
echo $CUBE_BUNDLE_PATH   # should print your CubeCLT path

Windows Development

The repository scripts are bash-based and work with Git Bash (included with Git for Windows).

Quick Setup:

  1. Install Git for Windows -- includes Git Bash terminal
  2. Install STM32CubeCLT to default location (C:\ST\STM32CubeCLT)
  3. Run setup script in Git Bash:
    ./scripts/setup.sh
    The script will detect STM32CubeCLT and offer to configure your PATH automatically.

Manual Setup (if needed):

If the automatic setup doesn't work, manually add to ~/.bashrc:

# STM32CubeCLT tools (adjust path if installed elsewhere)
export PATH="/c/ST/STM32CubeCLT/GNU-tools-for-STM32/bin:$PATH"
export PATH="/c/ST/STM32CubeCLT/CMake/bin:$PATH"
export PATH="/c/ST/STM32CubeCLT/Ninja/bin:$PATH"
export PATH="/c/ST/STM32CubeCLT/STM32CubeProgrammer/bin:$PATH"
export CUBE_BUNDLE_PATH="/c/ST/STM32CubeCLT"

If ~/.bash_profile doesn't exist, create it to source ~/.bashrc:

# ~/.bash_profile
if [ -f "$HOME/.bashrc" ]; then
    source "$HOME/.bashrc"
fi

Then restart Git Bash or run source ~/.bashrc.

Verify tools are found:

ninja --version
arm-none-eabi-gcc --version
cmake --version

Alternative: Use WSL2 with Ubuntu for a native Linux development experience on Windows.

STM32CubeMX Code Generation

Generate HAL code from .ioc files without opening the STM32CubeMX GUI:

./scripts/cubemx.sh                     # Interactive menu
./scripts/cubemx.sh -g -b BMS           # Generate code for BMS
./scripts/cubemx.sh -i -b LVPDB         # Inspect LVPDB configuration
./scripts/cubemx.sh -a -g               # Generate code for all boards
./scripts/cubemx.sh --list-boards       # List all boards with .ioc status

The script can:

  • Generate code (-g): Run STM32CubeMX headlessly to regenerate HAL code
  • Inspect (-i): View MCU, clock, and peripheral configuration
  • Show pins (--show-pins): Display GPIO pin assignments
  • Show peripherals (--show-peripherals): List enabled peripherals

Note: Code generation requires STM32CubeMX. Inspection commands work without it by parsing .ioc files directly.

Code Formatting

The project uses clang-format with an LLVM-based style (2-space indent, 120-char lines, Allman braces). The .clang-format file in the repo root is automatically detected by editors and tools.

Format on Save (VSCode)

  1. Install the C/C++ extension (Microsoft) or clang-format extension
  2. Add to your .vscode/settings.json (workspace or user):
    {
      "editor.formatOnSave": true,
      "[c]": {
        "editor.defaultFormatter": "ms-vscode.cpptools"
      },
      "[cpp]": {
        "editor.defaultFormatter": "ms-vscode.cpptools"
      }
    }

Format Script

Format all user code across all boards:

./scripts/format.sh           # Format all Core/User/ files
./scripts/format.sh --check   # Check only (CI mode, exits 1 if changes needed)

Pre-commit Hooks

The repository includes pre-commit hooks that run automatically before each commit:

./scripts/setup-hooks.sh          # Install hooks
./scripts/setup-hooks.sh --remove # Remove hooks

Hooks include:

  • clang-format - Formats C code in Core/User/
  • cppcheck - Static analysis for warnings and portability issues
  • CAN validation - Verifies generated CAN files match definitions
  • Trailing whitespace and end-of-file fixers

To run hooks manually:

pre-commit run --all-files     # Run all hooks on all files
pre-commit run clang-format    # Run specific hook
git commit --no-verify         # Skip hooks (use sparingly)

Build

All builds are driven from the project root.

Using the Build Script (Recommended)

./scripts/build.sh                # Interactive menu
./scripts/build.sh -a             # Build all boards (Debug)
./scripts/build.sh -b LVPDB       # Build specific board
./scripts/build.sh -b LVPDB -b PCU  # Build multiple boards
./scripts/build.sh -r             # Build in Release mode
./scripts/build.sh -c             # Clean and rebuild
./scripts/build.sh -l             # Loop mode (build multiple boards interactively)

The script validates prerequisites, shows build progress, and displays Flash/RAM usage summary.

Using CMake Presets

# Configure (Debug) -- configures all boards
cmake --preset Debug

# Build a specific board
cmake --build build/Debug --target LVPDB

Manual CMake

cmake -S . -B build/Debug -G Ninja \
  -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_TOOLCHAIN_FILE=cmake/gcc-arm-none-eabi.cmake

cmake --build build/Debug --target LVPDB

Building a Specific Board

Use --target to build a single board:

cmake --build build/Debug --target BMS

Build all boards at once by omitting --target:

cmake --build build/Debug

In VSCode with the CMake Tools extension, select the target from the Build Target dropdown in the status bar.

Build Outputs

After a successful build, outputs are in build/Debug/<BOARD>/:

  • <BOARD>.elf -- ELF executable (for debugging)
  • <BOARD>.bin -- Raw binary (for flashing)
  • <BOARD>.hex -- Intel HEX (for flashing)
  • <BOARD>.map -- Linker map file

Flashing

Using Pre-built Firmware (Easiest)

Download the latest firmware zip from GitHub Releases:

  1. Download FEB_Firmware_latest.zip (or a versioned release)
  2. Extract the zip
  3. Run:
    chmod +x flash.sh
    ./flash.sh
  4. Select the board you want to flash from the interactive menu

The zip contains firmware for all boards organized in subdirectories, plus the flash script which auto-detects available firmware.

Using the Flash Script (For Development)

The scripts/flash.sh script provides an easy interface for flashing boards with prerequisite checking and interactive selection:

./scripts/flash.sh                    # Interactive menu (shows build timestamps)
./scripts/flash.sh -b LVPDB           # Flash specific board
./scripts/flash.sh -f firmware.elf    # Flash specific file
./scripts/flash.sh -l                 # Loop mode (flash multiple boards)
./scripts/flash.sh --list-probes      # List connected programmers
./scripts/flash.sh -h                 # Show help

Note: If you downloaded flash.sh from a GitHub release, make it executable first: chmod +x flash.sh

The script checks for STM32CubeCLT installation and provides platform-specific setup instructions if needed. If the firmware hasn't been built yet, it offers to build it for you.

Using STM32_Programmer_CLI Directly

STM32_Programmer_CLI --connect port=swd --download build/Debug/LVPDB/LVPDB.elf -hardRst -rst --start

Using VSCode

If you have the STM32 VSCode extension installed, use the CubeProg: Flash project (SWD) task (defined in LVPDB/.vscode/tasks.json).

CAN Library

The CAN message library is a git submodule located at common/FEB_CAN_Library_SN4/. It provides auto-generated C pack/unpack functions from Python message definitions. The generated files are committed to the submodule, so you do not need to regenerate them to build.

Regenerating CAN Code

After modifying Python message definitions in common/FEB_CAN_Library_SN4/*_messages.py:

cd common/FEB_CAN_Library_SN4
./generate_can.sh

The script automatically manages a Python virtual environment and installs the correct version of cantools.

Useful Commands

Command Description
./generate_can.sh Regenerate all CAN files
./generate_can.sh --list List all messages with frame IDs
./generate_can.sh --ids Show frame ID allocation map
./generate_can.sh --check CI mode: verify files are up to date

Updating the Submodule

If upstream CAN library changes:

git submodule update --remote common/FEB_CAN_Library_SN4

See common/FEB_CAN_Library_SN4/README.md for detailed documentation on adding new CAN messages.

CI/CD

GitHub Actions runs on pushes and pull requests to main:

Workflow Trigger Description
Build (build.yml) Push/PR to main Matrix build of all 9 boards. Skips boards missing CMakeLists.txt or Core/.
Code Quality (quality.yml) Push/PR to main clang-format on Core/User/ files, cppcheck static analysis.
CAN Validation (can-validate.yml) Push/PR to main Checks submodule is up-to-date with upstream, validates generated files match definitions.
Firmware Size (size.yml) Push/PR to main Tracks Flash/RAM usage per board. Warns at 90%, fails at 98%.
Security (security.yml) Push/PR to main, weekly CodeQL security analysis + TruffleHog secret scanning.
Latest Release (latest-release.yml) Push to main Auto-updates "latest" pre-release with current firmware binaries.
Tagged Release (release.yml) Tag v* Builds Release binaries, creates versioned GitHub Release.

Flash Size Limits

Board Flash Limit MCU
BMS, PCU, LVPDB, DCU, Sensor_Nodes, UART 512 KB STM32F446RE
DART 32 KB STM32F042K6Tx
DASH 2 MB STM32F469RE
UART_TEST 2 MB STM32U575ZI

Releases

Downloading Firmware

Pre-built firmware binaries are available from GitHub Releases:

  • Latest Build: The latest pre-release is automatically updated on every push to main. Download from:

    https://github.com/Formula-Electric-Berkeley/FEB_FIRMWARE_SN5/releases/tag/latest
    
  • Versioned Releases: Stable releases are tagged with version numbers (e.g., v1.0.0).

Each release includes:

  • .elf, .bin, and .hex files for all 9 boards
  • FEB_Firmware_{version}.zip - Complete firmware package with bundled flash.sh
  • SHA256SUMS.txt - Checksums for individual firmware files
  • .zip.sha256 - Checksum for the release archive

Verifying Downloads

Verify integrity of downloaded files using the SHA256 checksums:

# Verify the zip archive
sha256sum -c FEB_Firmware_latest.zip.sha256

# Verify individual firmware files
sha256sum -c SHA256SUMS.txt

Creating a Versioned Release

Use the version script to create and push a new version tag:

./scripts/version.sh              # Auto-increment patch (v1.0.0 → v1.0.1)
./scripts/version.sh patch        # Same as above
./scripts/version.sh minor        # Bump minor (v1.0.1 → v1.1.0)
./scripts/version.sh major        # Bump major (v1.1.0 → v2.0.0)
./scripts/version.sh 2.5.0        # Set explicit version (→ v2.5.0)

This automatically:

  1. Creates a git tag with the new version
  2. Pushes the tag to GitHub
  3. Triggers the Tagged Release workflow
  4. Creates a GitHub Release with all firmware binaries

Code Organization

Shared Code

The common/ directory contains shared code across all boards. Start at common/README.md for the full library index. Each library has its own README; see the Common Libraries table above for quick links.

Board Directory Structure

Within each board directory:

Core/
  Inc/          # CubeMX-generated headers (do not edit manually)
  Src/          # CubeMX-generated source (do not edit manually)
  User/
    Inc/        # Your headers
    Src/        # Your source code
Drivers/
  STM32F4xx_HAL_Driver/   # HAL library (included in repo)
  CMSIS/                  # CMSIS library (included in repo)

All custom application code goes in Core/User/. CubeMX-generated files in Core/Inc/ and Core/Src/ should only be modified through STM32CubeMX to avoid losing changes on regeneration.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors