Many firmware bugs are actually invalid state transitions. Rust helps by making state explicit and hard to misuse.
I model each controller state as an enum variant with transition functions that consume the old state and return the next one. This prevents accidental mutation paths.
For asynchronous events, I queue typed commands and process them in one control loop. That keeps timing behavior predictable and testable.
The payoff is long-term maintainability: adding a new mode forces compiler-visible updates instead of hidden branching side effects.