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

No engine, no board, no excuse: the firmware is now proven in emulation on every commit

The bench hardware is still in transit. Even so, on every commit, the real firmware binary boots on an emulated STM32F767 (Renode) and has to prove itself: boot + telemetry, console replying byte-exact, actuators held LOW without sync, 32-2 crank synchronization, and ADC fault injection falling into a safe mode. Red = no merge.

Pistonix’s bench hardware — Nucleo boards, probes, display, tools — is still in transit. The obvious question: how do you know the firmware works if the board hasn’t arrived?

The answer we adopted: emulate the entire microcontroller. We use Renode, an embedded-systems emulator that runs the same ELF that goes to flash — not a “test build”, not a mock of the hardware. The production binary boots on a simulated STM32F767ZI, with virtual USARTs, GPIOs and ADC, and has to behave.

And since this arc, that’s not a side experiment anymore: it’s a CI gate. A red scenario means the commit doesn’t land.

The four scenarios

Every commit that touches the firmware goes through four emulation scenarios:

1. Boot + telemetry. The firmware boots, the heartbeat blinks at the right frequency, and the telemetry serial port emits valid CSV. The basics — but the basics asserted by a machine, not by “it ran on my bench”.

2. The console replying, byte by byte. We inject the bytes of a PING and a GET_INFO into the console USART and assert the exact reply — PONG and INFO, full frame, CRC32 included. If a refactor changes one byte of the protocol without changing the spec, CI flags it before any human notices.

3. The safety invariant: without sync, no actuator turns on. This is the scenario that lets me sleep. Coil and injector pins are watched by emulator “LED testers” that hold the assertion the whole time, not just sample at the end: as long as the ECU hasn’t synchronized with the engine, all 4 actuator pins stay LOW. Always. An out-of-time spark on a high-compression V-twin means kickback, a bent rod, a dead engine — that “never” is now a property verified on every commit, not a code review hope.

4. Synthetic crank + ADC fault injection. A 32-2 signal generator (the Harley trigger wheel pattern) feeds the decoder at 1500 RPM: the ECU has to synchronize within 3 revolutions and — closing the loop with scenario 3 — only start scheduling ignition events after sync. On the ADC, we inject counts into the TPS/MAP/ECT channels and assert the engineering conversion; then we inject a fault and assert the ECU falls into a safe mode instead of continuing to trust a dead sensor.

The bug emulation caught before the hardware did

The best argument for this setup was a bug that would have bitten on the bench. The off-the-shelf ADC driver from our async framework waits for the conversion in an unbounded loop. On a healthy chip, it works. On an ADC that doesn’t answer — hardware fault, wrong clock, floating pin — that loop hangs the task forever, the whole executor chokes, and the watchdog resets the ECU in an infinite loop.

On the bench, that would have been a lost afternoon with a board resetting for no visible reason. In emulation, it surfaced for free. We wrote our own driver with a bounded timeout on every wait: a dead ADC becomes a DTC plus safe mode, not a possessed ECU. Median-of-5 filtering completes the sensor path.

The crank generator becomes a bench tool

A detail I like: the 32-2 signal generator used in the emulation scenario is not throwaway test code. It’s a drift-free no_std module that will run on a second board at the bench as a crank signal simulator once the hardware arrives. The same code that proves the decoder in CI will feed the decoder on the desk. A test tool with dual citizenship.

What emulation does not prove

Technical honesty: emulation doesn’t replace the bench. It doesn’t see the real analog front-end, the VR crank sensor signal conditioning, electrical noise, coil EMI, or fine interrupt timing under real load. All of that remains bench work, and bike work after that.

What changes is what’s left for the bench to discover. Boot, protocol, sync logic, sensor conversion, fault paths, the safety invariant — all of that arrives pre-proven. Day 1 with hardware becomes confirmation, not discovery.

Where this fits

Underneath these scenarios there’s a structural change worth mentioning: the control loop that runs on the microcontroller is the same code that runs in our host-side engine simulator — extracted into a shared core library, with 500 tests and golden traces that guarantee no refactor changes behavior without anyone noticing.

The engine simulator proves the physics. Emulation proves the chip. The bench will prove the real world. Three layers, each catching what the previous one can’t — and the first two already run, automatically, on every commit.

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.