C - Finite State Machine (FSM) Implementation in C
A Finite State Machine (FSM) is a computational model used to represent the behavior of a system by dividing its operation into a finite number of states. At any given moment, the system exists in one specific state, and it changes from one state to another only when a predefined event or condition occurs. FSMs are widely used in embedded systems, communication protocols, game development, robotics, vending machines, traffic light controllers, and user interface design because they provide a structured and organized way to manage complex program logic.
Instead of writing numerous nested if-else or switch statements throughout a program, an FSM organizes the application's behavior into clearly defined states and transitions. This makes the code easier to understand, maintain, and extend.
Components of a Finite State Machine
An FSM consists of four primary components:
States
A state represents the current condition or mode of operation of a program. Only one state is active at a time.
For example, consider a traffic light system:
-
Red
-
Yellow
-
Green
Each of these represents a separate state.
Events
An event is something that causes the system to move from one state to another.
Examples include:
-
Timer expires
-
Button is pressed
-
Sensor detects an object
-
User logs in
-
Network packet arrives
Transitions
A transition defines how the machine moves from one state to another when an event occurs.
Example:
Current State: Red
Event: Timer expires
Next State: Green
Actions
Actions are operations performed when entering a state, leaving a state, or during a transition.
Example:
Current State: Door Closed
Event: Open Button Pressed
Action:
-
Unlock door
-
Open door
-
Move to Open State
Why Use FSM in C?
C programming is often used in systems where reliability and performance are critical. FSMs help developers:
-
Organize complex program flow.
-
Reduce nested conditional statements.
-
Improve readability.
-
Simplify debugging.
-
Make adding new features easier.
-
Separate logic from implementation.
-
Improve code reusability.
Understanding FSM with a Real-Life Example
Consider an automatic washing machine.
Possible states:
-
Idle
-
Filling Water
-
Washing
-
Rinsing
-
Spinning
-
Completed
Initially:
Idle
When the Start button is pressed:
Idle → Filling Water
When water reaches the required level:
Filling Water → Washing
After washing completes:
Washing → Rinsing
After rinsing:
Rinsing → Spinning
After spinning:
Spinning → Completed
Instead of checking every possible condition repeatedly, the machine simply reacts according to its current state.
Implementing FSM in C
The most common implementation involves:
-
Enumeration for states
-
Enumeration for events
-
Switch-case for state handling
-
Transition logic
Step 1: Define States
typedef enum
{
RED,
GREEN,
YELLOW
} TrafficState;
Step 2: Store Current State
TrafficState currentState = RED;
Step 3: Handle Transitions
void changeState()
{
switch(currentState)
{
case RED:
printf("Red Light\n");
currentState = GREEN;
break;
case GREEN:
printf("Green Light\n");
currentState = YELLOW;
break;
case YELLOW:
printf("Yellow Light\n");
currentState = RED;
break;
}
}
Step 4: Run the Machine
int main()
{
for(int i=0;i<6;i++)
{
changeState();
}
return 0;
}
Output:
Red Light
Green Light
Yellow Light
Red Light
Green Light
Yellow Light
The program keeps cycling through the predefined states.
FSM Using Events
Real systems respond to events rather than simply changing states automatically.
Example:
typedef enum
{
LOCKED,
UNLOCKED
} DoorState;
typedef enum
{
INSERT_KEY,
CLOSE_DOOR
} Event;
State transition function:
void processEvent(Event e)
{
switch(currentState)
{
case LOCKED:
if(e == INSERT_KEY)
currentState = UNLOCKED;
break;
case UNLOCKED:
if(e == CLOSE_DOOR)
currentState = LOCKED;
break;
}
}
Here, transitions occur only when the correct event is received.
FSM Using Transition Tables
Large systems often replace lengthy switch statements with transition tables.
Example table:
| Current State | Event | Next State |
|---|---|---|
| Locked | Key Inserted | Unlocked |
| Unlocked | Door Closed | Locked |
This approach allows transitions to be modified by editing the table rather than changing program logic.
Applications of FSM
Traffic Light Control
States:
-
Red
-
Green
-
Yellow
Transitions occur after timer expiration.
ATM Machine
States:
-
Idle
-
Card Inserted
-
PIN Verification
-
Transaction
-
Cash Dispensed
-
Exit
Each user action moves the machine to another state.
Elevator System
States:
-
Idle
-
Moving Up
-
Moving Down
-
Door Open
-
Door Closed
Button presses and floor sensors trigger transitions.
Login Authentication
States:
-
Waiting
-
Username Entered
-
Password Entered
-
Authenticated
-
Failed
Each user input changes the application's state.
Robot Navigation
States:
-
Searching
-
Moving
-
Avoiding Obstacle
-
Charging
-
Stopped
Sensor readings determine the transitions.
Communication Protocols
Protocols such as TCP and Bluetooth use FSMs internally to manage connection establishment, data transfer, acknowledgments, retransmissions, and connection termination.
Advantages of FSM
A Finite State Machine offers several benefits:
-
Provides a clear structure for program behavior.
-
Simplifies complex decision-making.
-
Improves code readability.
-
Makes maintenance easier by isolating logic into states.
-
Facilitates debugging because each state can be tested independently.
-
Enables easy expansion by adding new states or transitions.
-
Reduces duplicated code.
-
Supports modular and reusable designs.
Limitations of FSM
Despite its usefulness, FSMs also have some limitations:
-
The number of states can grow significantly in very complex systems, making the design harder to manage.
-
Poorly planned state transitions may introduce unexpected behavior.
-
Basic FSMs do not naturally represent parallel or concurrent activities without additional design techniques.
-
Large transition tables can become difficult to maintain if not organized properly.
Best Practices for FSM Implementation in C
When implementing Finite State Machines in C, follow these practices:
-
Use
enumto define states and events instead of integer constants for better readability. -
Keep state-handling code modular by placing each state's logic in separate functions where appropriate.
-
Use descriptive names for states, events, and transitions.
-
Validate events before performing state changes.
-
Document the state diagram before writing code to avoid missing transitions.
-
For large projects, consider using transition tables instead of extensive
switchstatements. -
Log state changes during development to simplify debugging.
-
Design states to perform one well-defined responsibility.
Conclusion
Finite State Machines provide a systematic and efficient way to model program behavior by dividing it into clearly defined states and transitions. In C programming, FSMs are particularly valuable for embedded systems, control systems, communication software, and event-driven applications where predictable behavior is essential. By organizing logic around states and events rather than complex conditional structures, developers can create software that is easier to understand, maintain, test, and expand as project requirements evolve.