10  States, Events, and Transitions

reference-architectures
state
machine

10.1 Start With One Allowed Transition

State-machine thinking begins with a guarded move: the device is in one state, an event arrives, and only certain next states are legal. That small rule prevents a system from acting as if every command is valid at every moment.

This is why state machines matter in IoT gateways, actuators, and field devices. Start with one transition you must make safe, such as disconnected to connecting or armed to alarm, then write the event, guard, action, and next state clearly enough to test.

10.2 Overview: Make Device Behavior Explicit

A finite state machine models an IoT device as named modes plus the events that move the device between those modes. Instead of scattering behavior across flags, callbacks, and nested conditionals, the state machine names where the device is, what happened, and what behavior is allowed next.

This chapter focuses on the fundamentals: states, events, transitions, guards, actions, outputs, and fault paths. The goal is not a decorative diagram. The goal is behavior that a developer can implement, a tester can exercise, and an operator can reason about when the device is under stress.

For example, a soil-moisture sensor can be described with states such as SLEEP, SAMPLE, TRANSMIT, WAIT_ACK, and FAULT. A wake timer moves the device from SLEEP to SAMPLE. A valid reading moves it toward TRANSMIT. A radio acknowledgement moves it back to SLEEP. A timeout increments a retry counter, and a failed retry guard moves the device into FAULT with the unsent sample and reason preserved.

The state-machine view also separates facts that are easy to confuse in code. The current state is not the same as the event that just arrived. A guard is not the same as an action. A timeout event is not the same as the retry policy. Keeping those parts separate makes reviews sharper because each transition can be checked for allowed input, blocked input, visible output, and safe cleanup.

Use a state machine when behavior has modes: sleeping, waking, sampling, transmitting, waiting for an acknowledgement, retrying, faulted, or in service. Each mode should have clear event handling and a clear way out.

State-machine route from current state through event, guard, transition action, next state, and observable output.
A state-machine route is reviewable when the current state, event, guard, transition action, next state, and observable output are all named before code is written.

10.2.1 Core Vocabulary

State

A named mode where the device behaves consistently until an event changes it.

Event

Something that happened: a timer, button press, message, sensor result, completion signal, or fault.

Transition

An allowed movement from one state to another after a particular event and guard outcome.

Guard

A condition that must be true before the transition can fire. Good guards are fast, deterministic, and side-effect free.

Action

Work performed on entry, exit, or during a transition, such as starting a timer or recording a fault.

Fault State

An explicit safe mode that preserves useful context and prevents ambiguous half-completed behavior.

10.2.2 What a Basic Trace Answers

Where are we? The current state describes the active behavior, such as SLEEP, SAMPLE, TRANSMIT, or FAULT.
What happened? The event names the input, timer, completion, or failure that must be handled.
Is movement allowed? The guard decides whether the transition is safe under the current context.
What changes next? The action and next state define visible output, resource use, and follow-up behavior.

10.3 Transition Contract

A practical state machine is a contract. It says which events are accepted in each state, which guards must pass, which actions run, and which next state becomes active. The contract should be readable as a transition table before it becomes code.

Transition table showing source state, event, guard, action, next state, and expected output.
A transition table makes the design checkable because each row connects source state, event, guard, action, next state, and expected output.

10.3.1 Transition Table Fields

Field
Question
Good answer
Common failure
Source state
Where does this rule apply?
A named stable mode such as TRANSMIT or SLEEP.
A flag combination that nobody can inspect.
Event
What happened?
A timer, message, sample result, completion, button press, or fault.
A vague condition such as “system changed.”
Guard
When is movement allowed?
A clear side-effect-free condition, such as retry count below its limit.
A guard that also sends messages or changes hardware.
Action
What work happens?
Start a timer, queue a packet, record a fault, or update context.
Long blocking work hidden inside the event handler.
Next state
What behavior is active next?
A named state with documented output and accepted events.
An implied state buried in a variable outside the model.

10.3.2 Worked Contract: Battery Sensor Timeout

A battery sensor samples periodically, transmits a packet, and waits for an acknowledgement. If the acknowledgement does not arrive, the machine should retry only while a guard allows it. When the retry guard fails, the machine should enter a fault or recovery state instead of transmitting forever.

Source state: TRANSMIT
Event: acknowledgement_timeout
Guard: retry_count below retry_limit
Action: increment retry_count; queue retransmit; restart acknowledgement timer
Next state: TRANSMIT
Expected output: retry counter increments and no new sample is taken

Source state: TRANSMIT
Event: acknowledgement_timeout
Guard: retry_count reached retry_limit
Action: record communication fault; stop radio; preserve unsent sample metadata
Next state: FAULT
Expected output: fault reason is visible and recovery is explicit

10.3.3 Design Checks Before Coding

Invalid Events

Decide whether an unexpected event is ignored, deferred, rejected, or treated as a fault. Silent fall-through hides behavior.

Timer Paths

Timers should arrive as events. Avoid hidden sleeps or busy waits that block other device behavior.

Output Ownership

Document whether outputs are tied to the current state, to transition actions, or to both. Tests need that boundary.

Retry Limits

Retries need explicit guards and a recovery path. An unbounded retry loop is usually a missing transition.

10.4 Timing, Hierarchy, Fault Paths

State machines fail when the hard parts are left outside the model. In IoT devices, those hard parts are usually asynchronous events, timers, retries, shared resources, and recovery after faults. The internal design should make those pressures visible without turning every flag combination into a separate state.

Event sources feeding a state machine through an event queue, including timer, sensor, radio, button, and fault events.
Interrupts, callbacks, timers, sensor completions, radio messages, and faults should become explicit events before they are evaluated by the state machine.

10.4.1 Asynchronous Event Discipline

Pressure
Risk
Design response
Test focus
Interrupts and callbacks
Behavior runs in an unpredictable order.
Convert each signal into a queued event with a clear timestamp or context.
Different event orders in the same starting state.
Timers
Timeouts become hidden loops or blocking waits.
Model timeout as an event and move long work into states.
Expired, cancelled, restarted, and late timer events.
Shared resources
Radio, sensor, storage, or actuator ownership becomes ambiguous.
Put resource ownership in state entry, exit, or transition actions.
Entry, exit, and cleanup behavior under failure.
Fault recovery
The device enters a half-state after an error.
Define safe outputs, retry limits, recovery triggers, and preserved proof.
Fault entry, reset, manual service, and return-to-known-state paths.
Fault and recovery path showing normal states, timeout, fault state, safe output, retry, manual reset, and return to idle.
Fault handling belongs inside the model. A safe state should define outputs, retained context, retry behavior, and the allowed route back to normal operation.

10.4.2 Hierarchy and Concurrency

A flat state machine is easiest to audit, but it can grow quickly. Use hierarchy when several states share a common parent behavior, such as an OPERATING parent with SAMPLE, TRANSMIT, and WAIT_ACK children. Use separate concurrent machines when radio, sensing, power, and user interface behavior are mostly independent and only need to exchange events at clear boundaries.

Do not use hierarchy to hide unclear behavior. Use it to remove meaningful duplication while keeping event handling, fault paths, and ownership boundaries explicit.

10.4.3 Coverage Targets

Transition Coverage

Each valid transition should be exercised at least once, including its guard outcome and expected output.

Wrong-State Events

Events that arrive in the wrong state should be handled deliberately, not left to default behavior.

Fault Paths

Recovery tests should prove safe outputs, retry limits, preserved context, and return to a known state.

10.5 Summary

State machines keep IoT behavior inspectable by naming states, events, transitions, guards, actions, outputs, and fault paths. A useful model shows what happens in normal operation, when events arrive at awkward times, when guards block movement, and when recovery is required. The practical artifact is a transition contract that can be implemented, tested, and maintained.

10.6 Key Takeaway

A state machine is valuable when it turns hidden control flow into named behavior: where the device is, what event arrived, which transition is allowed, what action runs, and how the system reaches a known state after faults.

10.7 See Also