9 States, Events, and Transitions
9.1 Start With One Allowed Transition
Reject the Unlock Command in the Wrong State
Picture a cabinet controller receiving an unlock command while its door is already open. A finite state machine means a model with named states and allowed moves between them.
Name the starting state, event, guard, action, and next state. Test a normal move, a blocked move, a repeated event, and an event arriving out of order.
Keep the starting and ending states, guard result, action, time, and software version. This proves the named transitions on one build, not every timing fault; the deeper sections cover diagrams, tables, hierarchy, persistence, and recovery.
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.
9.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.
Overview: Make Device Behavior Explicit needs more than a verbal rule; it needs a visible account of how 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. Figure 9.1 supplies that account before the chapter asks for a design choice.
Use State now as the entry point to Figure 9.1; then compare it with NEXT and the outcome at state, log, packet, cleanup. The labels expose the mechanism behind 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. Read this result back into Overview: Make Device Behavior Explicit as the condition the design must preserve.
9.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.
9.2.2 What a Basic Trace Answers
9.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 Contract is easier to reason about once its roles are separated. Figure 9.2 performs that separation while showing how a transition table makes the design checkable because each row connects source state, event, guard, action, next state, and expected output.
In Figure 9.2, Event sets the initial condition, range ok names the governing step, and fault reason exposes the output or proof. Those labels explain how a transition table makes the design checkable because each row connects source state, event, guard, action, next state, and expected output. Thus Transition Contract becomes part of the running system argument, not an isolated diagram tour.
9.3.1 Transition Table Fields
9.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
9.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.
9.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.
Asynchronous sources should not execute hidden state changes in whichever callback happens to run first. Inspect Figure 9.3 to see how the design converts those sources into ordered, testable inputs.
In Figure 9.3, Timer, Sensor, Radio, and Fault each produce a named event rather than performing transition work directly. The Event queue makes those inputs ordered and visible, and the State machine handles one event at a time. That serialization is the connection from asynchronous hardware pressure to deterministic guards, actions, and replayable tests. It also gives a failure trace an unambiguous event order instead of leaving reviewers to reconstruct callback timing.
9.4.1 Asynchronous Event Discipline
Once events are serialized, fault handling still needs an explicit route back to known behaviour. Inspect Figure 9.4 to check that failure changes both state and outputs rather than setting an invisible flag. This matters before defining retries because a retry is safe only when the failed work, retained context, and actuator condition are known.
The diagram in Figure 9.4 routes a timeout or fault from WAIT into FAULT, where Safe output and record reason, stop work define the immediate response. Only manual reset or verified recovery returns the machine to a known state. That path makes recovery evidence part of the transition contract and closes the timing discussion with a testable failure outcome. Tests can now assert both the FAULT entry outputs and the conditions that permit IDLE to resume.
9.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.
9.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.
9.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.
9.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.
9.7 See Also
Applying state machines across devices, gateways, and services
Hands-on implementation practice for event queues, guards, timers, and recovery
Production Architecture Management
Operational ownership and readiness checks for deployed systems
