An embedded motor control application implementing Field-Oriented Control (FOC) for BLDC and PMSM motors with strict real-time and memory constraints. Built for deterministic execution on resource-constrained microcontrollers.
This project provides a production-ready motor control implementation designed for embedded systems with no heap allocation, deterministic execution, and minimal memory footprint. It demonstrates FOC algorithms with complete hardware integration for ST and TI microcontrollers.
- Field-Oriented Control (FOC): Complete implementation for BLDC and PMSM motors
- Space Vector Modulation (SVM): Efficient PWM generation
- PID Control: Anti-windup implementations for current and velocity loops
- Sensored Control: Encoder and Hall sensor feedback integration
- Auto-Tuning: Resistance and Inductance estimation
- Real-Time Performance: Deterministic execution with no heap allocation
- Zero Heap Allocation: All memory statically allocated at compile-time
- Fixed Memory Footprint: Uses
infra::BoundedVector,infra::BoundedStringinstead of standard containers - Minimal Stack Usage: No recursion, predictable call depths
- Platform Abstraction:
PlatformFactoryinterface with ST and TI implementations
- C++ Simulator: Mathematical models for motor evaluation and curve plotting
- Unit Tests: Comprehensive GoogleTest coverage for control algorithms
- Hardware-in-the-Loop Ready: Mockable interfaces for testability
- CMake 3.24 or later
- Ninja, ccache, and Qt 6 (Core, Network, Widgets) for the host preset — the host build always configures
tools/ - Compatible toolchain for embedded targets (ARM GCC)
- Development board: EK-TM4C1294XL (TI) or STM32F407G-DISC1 (ST)
- Basic understanding of motor control and embedded systems
- Clone the repository with submodules
git clone --recursive https://github.com/embedded-pro/e-foc.git
cd e-foc- Build for host simulation
cmake --preset host
cmake --build --preset host-Debug- Run tests
ctest --preset host- Build for embedded target (example: TI EK-TM4C1294XL)
cmake --preset EK-TM4C1294XL
cmake --build --preset EK-TM4C1294XL-Debuge-foc/
├── core/ # FOC library code (no application entry points)
│ ├── foc/ # Field-Oriented Control
│ │ ├── interfaces/ # FOC vocabulary and contracts (Foc.hpp, Signals.hpp, MotorModel.hpp, LoopTunings.hpp, Execution.hpp, Units.hpp)
│ │ ├── math/ # Generic numerics (FastTrigonometry.hpp, AngleWrap.hpp)
│ │ ├── transforms/ # Clarke/Park transforms and Space Vector Modulation
│ │ ├── selection/ # Heap-free runtime controller selection
│ │ ├── current_loop/ # PID/decoupled-PID/deadbeat/sliding-mode current regulators
│ │ ├── speed_loop/ # PID/LQI/ADRC/two-DOF speed regulators
│ │ ├── cascade/ # Torque/speed/position cascade orchestration and gain design
│ │ └── instantiations/ # Execution wiring (Runner, FocController)
│ ├── platform_abstraction/ # Platform abstraction interfaces and shared adapters
│ │ ├── interfaces/ # Hardware ports in namespace drivers (ThreePhaseInverter, Encoder, HallSensor)
│ │ ├── PlatformFactory.hpp # Abstract factory for peripherals
│ │ ├── AdcPhaseCurrentMeasurement.hpp
│ │ ├── CanBusAdapter.hpp
│ │ └── QuadratureEncoderDecorator.hpp
│ ├── services/ # Application-level services
│ │ ├── alignment/ # Motor alignment and offset detection
│ │ ├── cli/ # Command-line interface service
│ │ ├── electrical_system_ident/ # Resistance/inductance estimation
│ │ ├── mechanical_system_ident/ # Friction and inertia estimation
│ │ └── non_volatile_memory/ # NVM persistence service
│ ├── can/ # FOC motor CAN category (server, client, bridge, wire contract)
│ ├── supervision/ # Runtime plausibility monitors
│ └── state_machine/ # FOC motor lifecycle state machine (Idle/Calibrating/Ready/Enabled/Fault)
├── integration_tests/ # BDD integration tests (Cucumber/Gherkin)
│ ├── features/ # Gherkin .feature files
│ ├── steps/ # Step definitions
│ ├── hooks/ # Before/After scenario hooks
│ ├── main/ # Test executable entry point
│ └── support/ # Fixture, mocks, and bridge
├── targets/ # Application entry points and platform implementations
│ ├── sync_foc_sensored/ # Synchronous FOC with encoder feedback
│ ├── hardware_test/ # Hardware validation application
│ └── platform_implementations/ # Platform-specific PlatformFactory implementations
│ ├── host/ # Host simulation (GoogleTest stubs/mocks)
│ ├── qemu/ # Emulated Cortex-M4 platform for software-in-the-loop tests
│ ├── st/ # STM32 platform implementation
│ └── ti/ # TI Tiva C platform implementation
├── tools/ # Host-side developer tools
│ ├── simulator/ # C++ motor simulators (torque, speed, position control)
│ ├── can_commander/ # CAN bus command interface tool
│ └── hardware_bridge/ # Terminal/CAN/serial bridge client (Qt) for HIL tests
├── infra/ # Infrastructure submodules
│ ├── embedded-infra-lib/ # Bounded containers, build helpers, toolchain CMake
│ ├── numerical-toolbox/ # PID, filters, fixed-point algorithms
│ ├── can-lite/ # CAN protocol library
│ └── hal/ # Hardware abstraction layer (ST, TI)
├── documentation/ # Architecture, design, theory, and requirements docs
└── build/ # Build artifacts (generated, not committed)
- No Heap Allocation: All memory is statically allocated at compile-time
- Bounded Containers: Uses
infra::BoundedVector,infra::BoundedStringinstead of STL containers - Fixed Memory Footprint: Predictable RAM usage for resource-constrained systems
- Deterministic Execution: No dynamic allocation or unbounded loops in control paths
- Minimal Branching: Optimized control loops for consistent timing
- ISR-Safe Design: Interrupt service routines avoid virtual calls and complex operations
- Dependency Injection: Constructor injection for testability and flexibility
- Platform Abstraction: Clean separation between application logic and hardware
- Interface-Driven Design: Pure virtual interfaces enable mocking and testing
The simulator includes a Qt-based GUI for real-time visualization. Since the development environment runs inside a Dev Container, an X server on the host machine is required to display the GUI.
- Install VcXsrv (free) or X410 from the Microsoft Store.
- Launch VcXsrv with the following settings:
- Multiple windows
- Start no client
- Disable access control (checked)
- Open the project in VS Code and reopen in the Dev Container.
- Allow Docker containers to access your X server:
xhost +local:docker
- Open the project in VS Code and reopen in the Dev Container.
Note: The Dev Container sets
DISPLAY=host.docker.internal:0.0to forward GUI windows over TCP. On Linux, if you prefer Unix socket forwarding, you can overrideDISPLAYto:0inside the container and add a bind mount for/tmp/.X11-unix.
Inside the Dev Container terminal:
cmake --preset host
cmake --build --preset host-DebugRun the simulator:
./build/host/bin/Debug/e_foc.tool.simulatorOn launch, a dialog asks which controller to simulate (Torque, Speed, or Position). The chosen controller assembles the corresponding loops; the main window provides tabbed Parameters/Configuration and Phase Signals/RLS Estimates panels around an always-visible SVPWM hexagon.
The three desktop tools render through ui-cpp,
fetched at configure time and pinned in CMakeLists.txt to an exact commit. Consuming a
newer ui-cpp is a deliberate edit to that pin, reviewed like any other change, rather than
something a fresh configure does on its own; changing it re-fetches on the next configure.
To build against a local ui-cpp working tree instead of the pinned commit, which is what makes bisecting across the two repositories possible:
cmake --preset host -DFETCHCONTENT_SOURCE_DIR_UI=/path/to/ui-cpp| Problem | Solution |
|---|---|
cannot open display |
Ensure the X server is running on the host and "Disable access control" is enabled |
| Black window or no rendering | Set LIBGL_ALWAYS_INDIRECT=1 (already configured in the Dev Container) |
| Windows firewall blocks display | Allow VcXsrv (vcxsrv.exe) through the Windows firewall for private networks |
Each release carries:
| Asset | Content |
|---|---|
e_foc-firmware-<board>-<tag>.tar.gz |
.elf and .hex of every application built for the board |
e_foc-firmware-EK-TM4C1294XL-<application>-<tag>.hex |
ready-to-flash images for the EK-TM4C1294XL (sync_foc_sensored, hardware_test) |
e_foc-source-<tag>.tar.gz |
the complete source with every submodule (GitHub's own source archives omit them) |
e_foc-can_commander-*, e_foc-simulator-* |
desktop tools for Linux and Windows (see below) |
eFocDesign.pdf |
the complete design booklet |
e_foc-documentation-<tag>.zip |
the individual design, theory and requirements documents as PDFs |
Configuring a build from the source archive still downloads the dependencies CMake declares with
FetchContent (such as ui-cpp and GoogleTest).
Every release asset carries a build-provenance attestation, signed through Sigstore by the release workflow, which proves the file was built from this repository by GitHub Actions. Check one with the GitHub CLI:
gh attestation verify e_foc-can_commander-<tag>-windows-x86_64-setup.exe --repo embedded-pro/e-focOnce code signing is enabled for the project (see .github/signpath/README.md), the Windows
CAN Commander executable and installer are also Authenticode-signed, so Windows shows the publisher instead of
Unknown publisher.
e-foc CAN Commander commands and monitors the e-foc motor controllers on one CAN bus — up to eight,
each addressed by its node ID. Every controller whose heartbeat is seen gets its own tab (you can also add
one by node ID) with start/stop, control mode, setpoints, loop bandwidths, telemetry and identified
parameters; an overview row keeps every motor's state and faults in view, and one always-visible
EMERGENCY STOP ALL (Esc) reaches every motor.
Each controller needs its own node ID. On the EK-TM4C1294XL, fit the board-ID jumpers on PK0–PK2 (to ground; PK0 is bit 0): with the default configuration a board answers as node 1 + its board ID, so boards jumpered 0–7 become nodes 1–8. Jumpers are read at reset only.
| Adapter | Linux | Windows | Interface name |
|---|---|---|---|
| SocketCAN devices | Yes | — | can0 |
| CANable and other slcan adapters | Yes | Yes | slcan:/dev/ttyACM0, slcan:COM3 |
| candleLight (gs_usb firmware) | Through SocketCAN | Yes, no driver to install | candleLight:1 |
| PEAK PCAN-USB | Through SocketCAN | Yes, with the PEAK driver package | PCAN:1 |
| Kvaser | Through SocketCAN | Yes, with the Kvaser CANlib driver | Kvaser:0 |
On Linux, slcan needs the user in the dialout group (sudo usermod -aG dialout $USER, then log in again).
Download the installer for your platform from the latest release:
| Platform | Asset | Install |
|---|---|---|
| Windows x86-64 | e_foc-can_commander-<tag>-windows-x86_64-setup.exe |
Run it; start from Start → e-foc CAN Commander |
| Debian/Ubuntu x86-64 | e_foc-can_commander-<tag>-linux-x86_64.deb |
sudo apt install ./e_foc-can_commander-<tag>-linux-x86_64.deb |
The .deb is built on Ubuntu 24.04 and needs a distribution of that generation or newer. Portable archives
(.zip, .tar.gz) remain available. On Linux, bring a SocketCAN interface up first, e.g.
sudo ip link set can0 up type can bitrate 1000000. e-foc-can-commander --version prints the installed
version without opening a window (on Windows, run it from a console).
- Every session writes an issue log (UTC timestamps, rotated at 1 MiB, three files) to
~/.local/share/e-foc/CAN Commander/logson Linux or%LOCALAPPDATA%\e-foc\CAN Commander\logson Windows. Help → Open Log Folder opens it. - Help → Report Issue opens a pre-filled GitHub issue with the version, OS and recent log lines for you to review and submit.
- At startup and every 24 hours the tool checks the latest GitHub release and shows a banner when a newer
version exists. Install Update… downloads the installer, checks it against the SHA-256 published with the
release, then installs it and restarts — after Windows asks for permission, or after your password on Linux.
Installing waits until the CAN bus is disconnected. Portable archives get a download link instead. Turn the
checks off with Help → Check for Updates Automatically or
--no-update-check.
cmake --preset host && cmake --build build/host --config Release --target e_foc.tool.can_commander
cpack --config build/host/tools/can_commander/installer/CPackConfig.cmake -C Release -G DEB # Linux
cpack --config build/tools/can_commander/installer/CPackConfig.cmake -C Release -G NSIS # Windows, NSIS on PATHDesign: documentation/design/tool-can-commander.md.
| Document | Description |
|---|---|
| System Architecture | High-level system architecture and component relationships |
| Document | Description |
|---|---|
| FOC Transforms | Clarke and Park transform design |
| FOC Torque Control | Torque (current) control loop design |
| FOC Speed Control | Speed control loop design |
| FOC Position Control | Position control loop design |
| Alignment Service | Motor alignment and offset detection service |
| CLI Service | Command-line interface service |
| Current Offsets Service | Current sensor offset calibration service |
| Dead-Time Compensation Service | Duty correction for the bridge's dead time |
| Cogging Compensation Service | Cogging torque map and current feed-forward |
| Electrical Identification Service | Resistance and inductance estimation service |
| Mechanical Identification Service | Friction and inertia estimation service |
| NVM Service | Non-volatile memory persistence service |
| Integration Testing Design | Fixture design, calibration flow, CAN bridge |
| CAN Commander | Desktop commander, issue log, installers |
| Document | Description |
|---|---|
| State Machine Requirements | Lifecycle state transitions and NVM boot behaviour |
| FOC Torque Controller Requirements | Torque control loop functional requirements |
| CAN-to-State-Machine Integration Requirements | CAN command to state machine mapping requirements |
| Document | Description |
|---|---|
| FOC Theory | Field-Oriented Control theory and control block diagrams |
| Motor Alignment | Motor alignment and offset detection theory |
| Resistance & Inductance Estimation | Parameter estimation theory |
| Friction & Inertia Estimation | Mechanical parameter estimation theory |
| Document | Description |
|---|---|
| Performance Optimization Guide | High-performance embedded code, assembly analysis, cycle budgets |
| Document | Description |
|---|---|
| Copilot / Agent Instructions | Development guidelines, patterns, and constraints |
| embedded-infra-lib | Bounded containers and infrastructure library reference |
| numerical-toolbox | Reusable numerical algorithms (PID, filters, fixed-point) |
This project is licensed under the e-foc Non-Commercial License. Commercial use is not permitted without prior written permission from the project owner. See the LICENSE file for details.
Third-party components and submodules keep their own license terms as documented in their respective directories.