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
LVPDB/ Low Voltage Power Distribution Board STM32F446RE
BMS/ Battery Management System STM32F446RE
DASH/ Dashboard STM32F469RE FreeRTOS, FatFS
DART/ DART STM32F446RE
DCU/ Data Control Unit STM32F446RE
PCU/ Powertrain Control Unit STM32F446RE
Sensor_Nodes/ Sensor Nodes STM32F446RE
UART_TEST/ UART/Console Test Board STM32U575ZI Test platform for UART/Console libraries

All boards are fully buildable with CMake.

cmake/                           # Shared toolchain files
  gcc-arm-none-eabi.cmake        # ARM GCC cross-compiler config
common/
  FEB_CAN_Library_SN4/           # CAN library (git submodule)
    gen/                         # Generated C pack/unpack code
    *_messages.py                # Python message definitions
    generate_can.sh              # Generation script
scripts/
  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
.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.).

Add to your shell profile (~/.zshrc or ~/.bashrc):

export CUBE_BUNDLE_PATH=/opt/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

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 7 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, DART, DCU, Sensor_Nodes 512 KB STM32F446RE
DASH 2 MB STM32F469RE

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 7 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:

common/
  FEB_CAN_Library_SN4/      # Git submodule - CAN message definitions
    gen/                    # Generated C code (feb_can.c, feb_can.h)
    *_messages.py           # Python message definitions per board
    generate_can.sh         # Generation script
  FEB_UART_Library/         # Printf/logging with DMA
    Inc/                    # Headers (feb_uart.h, feb_uart_log.h, etc.)
    Src/                    # Implementation (feb_uart.c)
  FEB_Console_Library/      # CLI interface
    Inc/                    # Headers (feb_console.h, feb_console_commands.h)
    Src/                    # Implementation (feb_console.c, feb_console_commands.c)

See FEB_UART_Library/README.md and FEB_Console_Library/README.md for detailed usage documentation.

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

Watchers

Forks

Packages

No packages published

Contributors 11