Bare-metal engine management firmware written in Zig.
zigEFI takes a different approach to ECU firmware. Instead of runtime configuration through GUI tuning software, your engine tune lives in code — version-controlled, diffable, and validated at compile time.
Projects like rusEFI and Speeduino have done incredible work making open-source engine management accessible. zigEFI builds on that foundation with a focus on the software engineer's workflow:
-
Config as code. Fuel maps, ignition tables, sensor calibrations, and engine parameters are defined as Zig comptime structs. Change your target AFR at 3000 RPM and full load? Edit a number, rebuild, flash. Review a tune change?
git diff. -
Compile-time validation. Invalid sensor calibrations, out-of-range table values, and misconfigured peripherals are caught by the compiler — not discovered at 6000 RPM.
-
Feature selection, not feature flags. Pick what your engine needs and the rest doesn't exist in your binary. No dead code paths for features you'll never use. No runtime branches for hardware you don't have.
-
Minimal and auditable. The firmware is small enough to read and understand. Every instruction between power-on and your first injector pulse is visible and intentional. No RTOS, no HAL library, no code generators — just Zig and registers.
# Build for Black Pill dev board (default)
make
# Build for uaEFI production board
make BOARD=uaefi
# Build for uaEFI PRO (STM32F767)
make BOARD=uaefi_pro
# Build smallest binary
make release
# Flash via ST-Link
make flash
# See available boards
make list-boards
# Check firmware size
make sizeRequires Zig 0.15.x and st-flash (from stlink) for flashing.
zigEFI/
build.zig Build system with board selection
Makefile Convenience targets
linker/ Linker scripts per MCU
docs/ Project documentation
src/
main.zig Application entry point
start.zig Vector table and startup code
hal/ Hardware abstraction (GPIO, clocks, MMIO)
board/
blackpill/ STM32F401 dev board config + pinout docs
uaefi/ rusEFI uaEFI production board config (STM32F407)
uaefi_pro/ rusEFI uaEFI PRO board config (STM32F767)
engine/
config.zig Engine configuration schema and default tunes
Your tune is a const EngineConfig struct in src/engine/config.zig. Everything is evaluated at compile time.
pub const sbf_tbi_302 = EngineConfig{
.name = "SBF 302 TBI",
.cylinders = 8,
.displacement_cc = 4942,
.rev_limit = 5500,
.injector = .{
.flow_cc_per_min = 325,
.num_injectors = 4,
.simultaneous = true,
.dead_time_us = .{ 1500, 1350, 1200, ... },
},
.ve_table = .{ ... }, // 16x16 volumetric efficiency (RPM x MAP)
.afr_table = .{ ... }, // 16x16 target air-fuel ratio
.warmup_enrichment_pct = .{ 80, 70, 60, ... },
// ...
};Change a value, rebuild, flash. The compiler ensures every field is present and every value is in range.
zigEFI supports a progression of ignition control strategies — pick the one that matches your hardware. Set ignition.mode in your engine config.
| Mode | What it does | Hardware needed |
|---|---|---|
.disabled |
Distributor handles all timing. ECU does fuel only. | Stock distributor with mechanical/vacuum advance |
.distributor |
ECU controls timing via advance table. Single ignition output, distributor routes spark. Replaces the factory ignition module. | Locked-out distributor + VR pickup wired to ECU |
.coil_on_plug |
Full ECU-controlled ignition with individual coil outputs. (Not yet implemented.) | Crank trigger wheel + cam sync sensor + coil pack/COPs |
Most builds start with .disabled for fuel tuning, then move to .distributor once the fuel map is dialed in. This is a natural progression — each mode builds on the previous one, and the VR input does double duty for RPM and position reference.
.ignition = .{
.mode = .distributor,
.advance_table = .{ ... }, // 16x16 degrees BTDC (RPM x MAP)
.dwell_us = 3000, // coil charge time
.distributor_ref_angle = 10, // VR pickup angle with advance locked out
// ...
},See docs/IGNITION.md for wiring diagrams, timing math, and the coil-on-plug architecture.
zigEFI is designed to be portable across MCU architectures. Board-specific pin maps, clock configurations, and HAL implementations are selected at build time with zero runtime overhead — Zig's comptime generics make the abstraction free.
| Board | MCU | Core | Clock | Flash | Target Use |
|---|---|---|---|---|---|
blackpill |
STM32F401CCU6 | Cortex-M4F | 84 MHz | 256 KB | Development and bench testing |
uaefi |
STM32F407VGT6 | Cortex-M4F | 168 MHz | 1 MB | Production ECU (rusEFI uaEFI) |
uaefi_pro |
STM32F767VIT6 | Cortex-M7F | 216 MHz | 2 MB | Production ECU — PRO variant, 2x CAN, 512 KB SRAM |
| Board | MCU | Core | Clock | Notes |
|---|---|---|---|---|
esp32s3 |
ESP32-S3 | Dual Xtensa LX7 | 240 MHz | WiFi/BLE for wireless tuning and telemetry |
arduino_due |
ATSAM3X8E | Cortex-M3 | 84 MHz | Easiest non-STM32 port, 2x CAN built in |
teensy40 |
i.MX RT1062 | Cortex-M7 | 600 MHz | 1 MB SRAM, 3x CAN FD, double-precision FPU |
See docs/BOARDS.md for details, platform implementation checklists, and instructions on adding new boards.
Early development. The build system, startup code, HAL, and board abstraction are in place. The core EFI control loop (speed-density fuel calculation, sensor reading, injector scheduling) is next.
Standing on the shoulders of:
- rusEFI — comprehensive open-source ECU firmware and hardware. The uaEFI board is our primary hardware target.
- Speeduino — accessible Arduino-based engine management that proved open-source EFI is practical for everyone.
Both projects provide excellent references for EFI algorithms and hardware design. zigEFI wouldn't exist without their work.