M06 / LESSON 3 OF 5

Start: CP05 · Book chapters 13

Download video · Download captions

Use a larger display to read dense source code and terminal output. The transcript and written lesson are also available below.

M06-L03 — Add input handling and reporting

Run synthetic samples through shared normal/fault policy, then make one bounded reporting change. Start with CP05 in a fresh folder. Alerts, hysteresis, and threshold commands are not implemented at this stage; they belong to the capstone.

From the project root in PowerShell, establish a baseline:

.\scripts\replay.cmd fixtures/normal.csv
$baselineExit = $LASTEXITCODE
.\scripts\build-firmware.cmd
$buildExit = $LASTEXITCODE

The normal fixture's expected CP05 states are FAULT, NORMAL, NORMAL, NORMAL, NORMAL, FAULT, FAULT, NORMAL. A successful replay exits zero even when the intentionally nonfinite input produces FAULT. Preserve the actual firmware result separately; the verified final CP09 Windows cross-build does not substitute for this CP05 stage or your reporting edit.

Trace host/replay.cpp into monitor_policy.cpp and output_format.cpp under the sketch directory. The adapter supplies timestamped values, the policy decides meaning, and the formatter renders JSON. The native and firmware builds use the same shared source files. The sketch itself supplies synthetic values and has no physical sensor driver.

The policy starts FAULT/missing and needs two consecutive valid readings to recover. A recovering sample can contain current numeric data while remaining in FAULT. Missing, nonfinite, or stale input has no current temperature. This distinction makes a useful reporting exercise: expose the existing Snapshot.current flag as a JSON Boolean without changing policy.

Ask for a change only to the status formatter and its reporting description. Add ,"current":%s to the JSON format and the matching v.current ? "true" : "false" argument. Review the complete formatting call and retain its bounds/completion check. Boolean true/false values are unquoted JSON literals; quoted words would be strings.

Run the stale-recovery fixture after the change. At 4000, the reading is still current; at 4001 it is stale. At 5000 and 7000, recovery is incomplete but the new sample is current. Mapping the field to state != FAULT would therefore be wrong.

$lines = .\scripts\replay.cmd fixtures/stale-recovery.csv
$runExit = $LASTEXITCODE
if ($runExit -ne 0) { throw "Replay failed with exit $runExit; inspect its output before parsing records." }
$records = $lines | ForEach-Object { $_ | ConvertFrom-Json }
$records | Where-Object { $_.type -eq 'sample' } |
  Format-Table uptime_ms, state, error, current

Expected current flags are true, true, true, false, true, false, true, true. Check that JSON parsing succeeds and the field is a Boolean. The existing state/error sequence must remain unchanged. Cross-compile the shared-source edit with the same target and retain the actual receipt or precise open dependency.

Inspect the final diff for scope. A changed scheduler, recovery counter, board target, or dependency is unrelated to exposing an existing field. Update the local exercise's output contract because adding a field can affect strict consumers. Subsequent lessons start from their own checkpoints; this optional reporting extension is not silently required by CP06 or CP09.

Resources and completion

Use Chapter 13, CP05 README, shared formatter/policy, and the normal/stale fixtures. Figure references: SS13-01–05. Complete the exercise, then use the key (supplied separately). Host replay is synthetic software evidence; compilation and optional physical validation remain distinct.

Windows book workspace terminal with the actual baseline replay records and firmware build result.
SS13-01 · SS13-01. Actual CP05 baseline: normal and stale synthetic replays and the firmware build complete with exit 0.
Verified Windows book editor displaying the actual before-and-after formatter diff, including the unquoted true or false serialization.
SS13-02 · SS13-02. Actual CP05 formatter diff adds a Boolean current field while retaining the existing state and error fields.
Actual changed CP05 firmware build, its ESP32-S3 target/options and build directory.
SS13-03 · SS13-03. Actual changed CP05 firmware build, its ESP32-S3 target/options and build directory.
Actual changed normal replay: eight synthetic sample records, including recovery and the nonfinite fault.
SS13-04 · SS13-04. Actual changed normal replay: eight synthetic sample records, including recovery and the nonfinite fault.
Actual stale replay: expiry and missing input produce FAULT with current false, followed by recovery.
SS13-05 · SS13-05. Actual stale replay: expiry and missing input produce FAULT with current false, followed by recovery.

Recording transcript

We are ready to run a small firmware behavior and make one reviewable reporting change. Open checkpoint five in a new exercise folder. This version has synthetic input and normal or fault policy. It does not yet have configurable alerts, hysteresis, or threshold commands. A high temperature can remain normal at this stage because those later capstone requirements are deliberately absent.

Begin with the baseline, before editing source. From the project root in PowerShell, run the replay command with the normal fixture. Save the actual exit value immediately after the command. The supplied wrapper compiles the host executable and then runs it. If compilation fails or the executable cannot start, diagnose that condition before interpreting a policy result that has not actually been produced.

The normal fixture contains eight events. The expected states begin fault, then normal for the following valid readings. An intentionally nonfinite input later produces fault, and two fresh valid readings complete recovery. This is a successful replay when the process exits zero and the expected sequence occurs. A fault state can be correct application behavior; it does not automatically mean the runner failed.

Read the source path with me. The host adapter interprets fixture rows and supplies timestamps and readings. The shared policy decides whether input is current and whether recovery is complete. The output formatter writes a JSON object for the resulting snapshot. These policy and output source files live inside the sketch directory and are also compiled directly into the host executable.

The Arduino sketch provides a different surrounding environment. It uses the platform clock, generates a short synthetic sequence, and sends formatted output through the serial interface. It does not read a physical BME280. Compiling that sketch is useful evidence for the selected target, but it does not turn replay into an electrical simulation or establish that a board has been uploaded successfully.

Attempt the baseline firmware build with the recorded target and retain its actual result. The final release has a separate verified Windows build, but another checkpoint or an edited source needs its own receipt. Keep the native result and firmware result distinct. A successful host run cannot substitute for a cross-build, and neither result establishes sensor accuracy, wiring, USB operation, or measured device timing.

Now choose one reporting requirement. The snapshot already contains a current-data flag, but the sample JSON does not expose that flag directly. We will add a Boolean field named current. The request is limited to the status formatter and its reporting description. Acquisition, policy, timing, dependencies, and the board target should remain unchanged. This gives us a clear basis for reviewing an assistant's patch.

Notice why the field has a useful meaning. The policy starts in fault with missing input. A first valid sample is fresh, but the two-sample recovery rule keeps the state in fault. That record should therefore have current true and state fault. Availability of data and completion of recovery are different properties. Deriving the new field from whether the state is normal would lose that distinction.

Inspect the complete bounded formatting call. Add a placeholder for the current field before the closing brace, then add the matching argument based on the existing snapshot flag. The strings true and false are used as unquoted JSON literals. If the output places quotation marks around them, the consumer receives strings instead of Boolean values. Keep the existing buffer-completion check after the change.

Review the diff before running it. The new placeholder needs its corresponding argument in the right position. The policy does not need a new global variable, and the synthetic sequence does not need new values to accommodate this report. If the assistant also changes recovery or adds a driver, return to the bounded task. Those edits make the result harder to assess and are unnecessary here.

Run the stale-recovery fixture after rebuilding. Its sample at timestamp one thousand is still current at four thousand because the age is exactly three thousand milliseconds. At four thousand and one, it becomes stale. A valid sample at five thousand starts recovery, a missing event at six thousand interrupts it, and valid samples at seven and eight thousand complete a fresh recovery sequence.

The expected current flags are true, true, true, false, true, false, true, true. Look particularly at five and seven thousand. Those records remain in fault while containing fresh input. They distinguish the correct snapshot mapping from the tempting state-based shortcut. The state and error sequence should be the same as before the reporting change, because the policy was not part of the task.

Use PowerShell's JSON parser to inspect the output rather than relying only on visual alignment. Preserve the replay exit before passing lines through the parser. Convert each line into a record, select sample records, and display timestamp, state, error, and current. A parsing failure matters. Confirm that current is a Boolean value, not merely text that looks like the word true or false.

Adding a field changes the local output contract. Update the exercise documentation so a consumer knows what to expect. A strict parser may reject unknown fields, so do not promise compatibility without checking it. This lesson uses a disposable extension; the next published checkpoint remains its own starting state. You are not required to carry this extra field into every later exercise or alter the capstone acceptance tests.

Cross-compile the edited shared source using the unchanged manifest target and save the actual result. If a required tool is unavailable, identify that missing dependency and leave the check open. Do not replace it with an old binary or an agent assurance. The instructor host check establishes this reporting behavior for synthetic fixtures; a learner run and a firmware build are separate receipts.

Finish by inspecting the final diff and writing a brief evidence note. Name the starting checkpoint, changed files, fixtures, command exits, and any remaining limitation. A successful solution exposes the existing flag accurately while leaving the previous behavior intact. We now have a small change that can be explained from requirement to source to output. The next lesson uses the same discipline when the starting output reveals a failure.

Download transcript

Try it, then check your work

Open this lesson’s exercise · Checkpoints and reference sheets