Exercise an ESP32 device state machine
Verify normal, timeout, wrong-state, fault, and reset transitions with buttons, LEDs, and structured serial proof.

Gateway Gus: I want every button, timeout, guard, retry, and reset to leave a state transition another engineer can audit.
Predict the reading, then compare it with the measurement.
Wokwi ESP32
Third party ToolOpen the ESP32 editor, paste diagram.json, then paste sketch.ino.
Open Wokwi to paste in the files (new tab)Get the files
Use both prepared files. This is a paste-in setup; saving a project requires a Wokwi account.
sketch.ino
- Use the launch button above to open the ESP32 editor in Wokwi.
- Select the editor’s diagram.json tab and replace all its text with the supplied diagram.json.
- Select the sketch.ino tab, replace all its text with the supplied sketch.ino, then click Start Simulation.
Steps
Step 1
- Do
- In the Wokwi simulation panel, paste the supplied diagram and sketch into Wokwi; identify the three event buttons and four state LEDs.
- You will see
- Input: inspect the running Wokwi circuit and diagram.json. Sample, Ack, and Fault / Reset buttons use GPIO12, 13, and 14. White, yellow, green, and red LEDs represent the four states. Serial Monitor begins with STATE=IDLE retries=0; the white LED is lit.
- Why it matters
- Each event has a named physical control in the simulator. Each state has a distinct LED output. The starting serial line agrees with the white IDLE indicator. This gives later transitions an observable baseline.

Step 1 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 2
- Do
- In the Wokwi simulation panel, inspect the State enum, enterState function, and wrong-state reject function.
- You will see
- Input: inspect State, enterState, and reject in sketch.ino. The enum names IDLE, SAMPLE, TRANSMIT, and FAULT. enterState prints TRANSITION with event, proof, and retries. reject prints EVENT_REJECTED with state and reason=wrong_state.
- Why it matters
- The accepted path records its prior and next states. The rejection path preserves why an event was ignored. The real run begins at STATE=IDLE retries=0. A central transition function keeps the state contract reviewable.

Step 2 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 3
- Do
- Start the simulation and open Serial Monitor at 115200 baud.
- You will see
- Input: start the supplied Wokwi ESP32 simulation. Observed: "ESP32 STATE MACHINE READY". Observed: "STATE=IDLE retries=0". Expected: the controller begins in IDLE with no retries.
- Why it matters
- The two lines establish the starting state. No Sample or Ack event has occurred yet. The zero count is the baseline for later timeouts. The serial line does not by itself measure LED brightness.

Step 3 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 4
- Do
- In the Simulation panel, press the Sample button once; watch Serial Monitor for completion.
- You will see
- Input: hold and release Sample once in Wokwi. Observed: "TRANSITION IDLE + EV_SAMPLE_REQUEST -> SAMPLE proof=sample_timer_started retries=0". Observed: "TRANSITION SAMPLE + EV_SAMPLE_READY -> TRANSMIT proof=message_id=42 retries=0". Expected: sampling completes before transmission begins.
- Why it matters
- The first event starts the sample timer. The second records readiness and message_id=42. Both transitions occurred in this live simulator run. The message identifier is a firmware fixture, not radio traffic.

Step 4 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 5
- Do
- In the Simulation panel, press Ack before the transmit timeout; read Serial Monitor.
- You will see
- Input: hold and release Ack while TRANSMIT is active. Observed: "TRANSITION TRANSMIT + EV_ACK_RECEIVED -> IDLE proof=message_closed retries=0". Expected: a timely Ack closes the message. The returned state is IDLE with retries reset to 0.
- Why it matters
- The Ack is accepted only in TRANSMIT. The proof field names the closure event. The run showed no timeout before this Ack. This is internal state-machine evidence, not a network receipt.

Step 5 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 6
- Do
- In the Simulation panel, press Sample again, leave Ack untouched, and watch three timeouts in Serial Monitor.
- You will see
- Input: press Sample again and leave Ack untouched. Observed: timeout retry lines with retries=1 and retries=2. Observed: "TRANSITION TRANSMIT + EV_TIMEOUT -> FAULT proof=ack_timeout_radio_off retries=3". Expected: the third timeout enters FAULT.
- Why it matters
- The retry count advances one step at a time. The final transition records the fault reason. The live run matched the three-count screenshot. The simulated timer does not prove a real radio switched off.

Step 6 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 7
- Do
- In the Simulation panel, press Ack while in FAULT; read the rejection in Serial Monitor.
- You will see
- Input: press Ack after the third timeout placed the machine in FAULT. Observed: "EVENT_REJECTED state=FAULT event=EV_ACK_RECEIVED reason=wrong_state". Expected: Ack cannot close a message from FAULT. The preceding line in the run was the retries=3 FAULT transition.
- Why it matters
- The event is rejected rather than changing state. The wrong_state reason makes the guard observable. This matches the real Wokwi serial screenshot. The serial rejection does not independently verify the LED state.

Step 7 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 8
- Do
- In the Wokwi simulation panel, press Fault / Reset once while FAULT is active.
- You will see
- Input: press the red Fault / Reset button while FAULT is active. Previous serial line: EVENT_REJECTED state=FAULT for the late Ack. New serial line: TRANSITION FAULT + EV_RESET -> IDLE. The transition records proof=manual_reset_safe and retries=0.
- Why it matters
- The manual reset is accepted from FAULT in this run. The proof field records the recovery reason. The retry count returns from three to zero. The IDLE LED is lit in the real Wokwi capture.

Step 8 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab)
Chapter checks
These questions refer to the chapter’s examples. Use the return links to review their answers.
A sensor alternates between SLEEP, SAMPLE, TRANSMIT, and FAULT. Why is this a good fit for a finite state machine?
Return to the chapter’s knowledge checkA TRANSMIT state retries forever after acknowledgement timeouts. Which transition-table change most directly fixes the design?
Return to the chapter’s knowledge checkA firmware design handles radio callbacks, sensor completions, button presses, and timer expirations. What is the safest state-machine pattern?
Return to the chapter’s knowledge check