Firmware ECU rodando em CI — 100% testável sem hardware
A lógica safety-critical do firmware da ECU (trigger, fueling, ignition, knock, safety) virou um workspace Rust no_std que roda inteiro em cargo test no CI. Zero hardware necessário pra provar invariantes.
A Proteus F7 ainda não chegou (importação leva tempo), mas isso não
precisava parar o que dá pra fazer: praticamente toda a parte
safety-critical do firmware é matemática pura sobre tabelas e estados,
e matemática pura tem como ser provada em cargo test no CI.
Resultado: o ecu-firmware/core virou um crate no_std que compõe os
módulos da ECU e roda 100+ testes unitários e de integração no CI a
cada push, sem precisar de placa.
O que está coberto hoje
- Trigger decoder 32-2 — máquina de estados Lost → Searching → Synced, detecta a “lacuna” da roda fônica pelo período mais longo entre flancos, sincroniza em 1-2 voltas.
- Tabelas 1D e 2D com interpolação bilinear — base de todos os mapas (VE, AFR alvo, avanço, dwell, dead-time de injetor, multiplicador de ECT, knock threshold por RPM).
- Conversões de sensor — NTC (ECT/IAT), MAP, TPS, lambda Bosch LSU 4.9.
- Controlador PID — derivada-no-medido + anti-windup (clamp da integral). Usado em closed-loop de AFR e idle/boost.
- Fueling speed-density —
m_air = (MAP × V_cyl × VE) / (R × T_K), cold-start enrichment via tabela ECT, after-start decay linear, acceleration enrichment, dead-time de injetor compensado por Vbat. - Ignition — avanço por RPM × MAP com clamp de safety, dwell calculado por Vbat com upper bound pra não fritar bobina.
- Knock detector — janela por cilindro com threshold variável por faixa de RPM. Sugere retard de spark advance no próximo ciclo.
- Safety state machine — over-rev e over-temp com histerese (warning → limp → cutoff), nunca volta de cutoff sem evento manual.
- Cooperative watchdog — cada subsistema (trigger, fueling, ignition, knock, safety) registra seu próprio deadline. Se algum deixar de pingar, o supervisor detecta e dispara fail-safe.
- CAN protocol — encode/decode dos 15 IDs do
docs/can-protocol.mdv0.1, com versionamento embutido no byte 0.
Por que isso importa
Safety-critical em solo dev sem code review de outro embarcado sênior é receita pra desastre se você só descobre os bugs com a ECU na mão. Trigger decoder com bug detecta sincronia errada → injetor pulsa fora do TDC → motor não dá partida (ou pior, dá no cilindro errado e bate piston em válvula). PID com anti-windup quebrado → boost overshoot permanente.
Cada módulo é testado em CI contra o que tem que acontecer (TDC alinhado no decoder, AFR convergindo no PI, limp ativando antes do cutoff). Quando a Proteus chegar, não vou estar debugando lógica de mapa numa bancada com osciloscópio — vou estar validando o driver (o que é puramente hardware-side), não o algoritmo.
CI
GitHub Actions workflow ecu-firmware.yml roda a cada push em ecu-firmware/:
cargo fmt --check, cargo clippy -- -D warnings (sem warnings, sem
unwrap em produção — clippy enforça), cargo test. Tudo em
ubuntu-latest, sem cross-compilação ainda — o no_std core compila
no host pra testes; cross pra thumbv7em-none-eabihf virá com a Proteus.
O que falta
Drivers (HAL Embassy STM32, USB CDC console, ADC, timers para scheduling de injeção/ignição), bootloader USB pro flash via Tuner, adapter harness pra ECM Delphi original. Tudo isso depende de hardware em mão. A lógica não.