In this lesson, you will learn how to design robust Finite State Machines (FSMs) that proactively detect and recover from illegal or undefined states to ensure system safety.
The Problem of Undefined States
In complex embedded systems, noise, radiation, or transient power fluctuations can corrupt the registers holding your current state. If your FSM only handles transitions for "valid" states, a bit-flip might land your logic in a "trap" state—a value that doesn't correspond to any defined behavior. Without integrity management, the system may hang indefinitely or perform dangerous, unintended actions.
Implementing Defensive Transitions
To maintain integrity, every FSM must account for the "impossible." We achieve this by utilizing a default case in our state-switching logic. Instead of ignoring unexpected inputs, we treat the default case as an error-handling block that triggers a recovery sequence.
Consider this standard defensive pattern:
typedef enum { IDLE, PROCESSING, ERROR_RECOVERY } SystemState;
SystemState current_state = IDLE;
void update_system(event_t event) {
switch (current_state) {
case IDLE:
if (event == START) current_state = PROCESSING;
break;
case PROCESSING:
if (event == FINISH) current_state = IDLE;
break;
default:
// Recovery: Force system back to a known safe state
current_state = IDLE;
log_error(ILLEGAL_STATE_DETECTED);
break;
}
}
Why This Matters for Reliability
By explicitly defining a default path, you transform a potentially catastrophic "undefined" scenario into a managed recovery process. This pattern works in tandem with your earlier learning on Defensive Input Validation (lesson 22) and Graceful Degradation (lesson 24); the FSM does not just crash, but actively attempts to return the hardware to a deterministic, safe posture.
In safety-critical applications, this is often coupled with a hardware watchdog timer. If the default recovery block is entered, the state machine can also increment a failure counter; if that counter hits a threshold, the system triggers a reset or enters a safe shutdown mode, preventing the propagation of the fault.
Key Takeaway: Never assume the program counter or state register is infallible; always implement a default catch-all branch that forces the FSM back to a known-safe state to prevent undefined execution.