The post below is in Portuguese. We will translate critical announcements as the project gains an English-speaking audience.

ECU firmware running in CI — 100% testable without hardware

The safety-critical logic of the ECU firmware (trigger, fueling, ignition, knock, safety) is now a Rust no_std workspace that runs entirely under cargo test in CI. Zero hardware required to prove invariants.

The Proteus F7 hasn’t arrived yet (importing takes time), but that didn’t have to stop what’s doable: practically all of the safety-critical part of the firmware is pure math over tables and states, and pure math can be proven with cargo test in CI.

Result: ecu-firmware/core became a no_std crate that composes the ECU modules and runs 100+ unit + integration tests in CI on every push, with no board needed.

What’s covered today

  • 32-2 trigger decoder — Lost → Searching → Synced state machine, detects the wheel’s missing-tooth gap by the longest period between edges, syncs in 1-2 revolutions.
  • 1D and 2D tables with bilinear interpolation — base of every map (VE, target AFR, advance, dwell, injector dead-time, ECT multiplier, knock threshold by RPM).
  • Sensor conversions — NTC (ECT/IAT), MAP, TPS, Bosch LSU 4.9 lambda.
  • PID controller — derivative-on-measurement + anti-windup (integral clamp). Used in AFR closed-loop and idle/boost.
  • Speed-density fuelingm_air = (MAP × V_cyl × VE) / (R × T_K), cold-start enrichment via ECT table, after-start linear decay, acceleration enrichment, injector dead-time compensated by Vbat.
  • Ignition — RPM × MAP advance with safety clamp, dwell calculated by Vbat with upper bound to avoid frying the coil.
  • Knock detector — per-cylinder window with variable threshold by RPM band. Suggests spark advance retard on the next cycle.
  • Safety state machine — over-rev and over-temp with hysteresis (warning → limp → cutoff), never returns from cutoff without a manual event.
  • Cooperative watchdog — each subsystem (trigger, fueling, ignition, knock, safety) registers its own deadline. If any stops pinging, the supervisor detects it and trips fail-safe.
  • CAN protocol — encode/decode of the 15 IDs in docs/can-protocol.md v0.1, with versioning embedded in byte 0.

Why this matters

Safety-critical work in solo dev with no embedded-senior code review is a recipe for disaster if you only discover bugs with the ECU in hand. Trigger decoder with a bug detects wrong sync → injector pulses off TDC → engine doesn’t start (or worse, fires the wrong cylinder and slams a piston into a valve). PID with broken anti-windup → permanent boost overshoot.

Each module is tested in CI against what’s supposed to happen (TDC aligned in the decoder, AFR converging in the PI, limp activating before cutoff). When the Proteus arrives, I won’t be debugging map logic on a bench with an oscilloscope — I’ll be validating the driver (which is purely hardware-side), not the algorithm.

CI

GitHub Actions workflow ecu-firmware.yml runs on every push under ecu-firmware/: cargo fmt --check, cargo clippy -- -D warnings (no warnings, no unwrap in production — clippy enforces), cargo test. All on ubuntu-latest, no cross-compile yet — the no_std core compiles on host for tests; cross to thumbv7em-none-eabihf will come with the Proteus.

What’s missing

Drivers (Embassy STM32 HAL, USB CDC console, ADC, timers for injection / ignition scheduling), USB bootloader for flashing via Tuner, adapter harness for the original Delphi ECM. All of that depends on hardware in hand. The logic doesn’t.

Waitlist

Be the first to know.

Pistonix is under active development. Sign up for product updates, early basemaps, invites to ride at Milwaukee Garage Drag Racing events, and priority on the first batch of Pistonix Forge.

  • Monthly roadmap and firmware updates
  • Invite to pre-sale tuning sessions
  • Priority on the initial Pistonix Forge batch
  • Early access to per-engine basemaps

By signing up you agree to receive emails from Pistonix. We do not share your email. You can unsubscribe at any time.