Independent learning for embedded-systems engineersHardware · Firmware · Software
TEA-403WORKED SYSTEM SOLUTIONINTERFACE AND RECOVERY

Specify the boundary.
Prove the recovery.

One possible answer to TEA-402: a measurable contract from physical acquisition to an honest host display, including interrupted delivery and restart.

Worked answer · 3 October 2026 · Read after completing TEA-402.

The limits below are a proposed teaching specification. The test results are expected outcomes; no hardware tests have been executed.

01 / REQUIREMENTS

Replace vague words with acceptance rules.

The original note conflated measurement, delivery and persistence. These additional requirements refine the TEA-401 baseline; its acquisition, warning, fault and history requirements still apply. Host means the gateway and its local display / durable database.

IDProposed requirement and owner
IF-001Acquisition / host: represent temperature as signed integer millidegrees Celsius. Value, validity, sample identity, acquisition time and configuration revision form one atomic observation.
IF-002Both endpoints: accept only complete version-1 frames meeting the size, schema and fragment-timeout rules below. Invalid frames must not change measurement state or acknowledge storage.
IF-003Node / host: distinguish permanent device identity, power-loss-safe non-reused boot identity, link session, sample sequence and stored-record sequence. Never order different boots by sequence alone.
IF-004Host: only a valid, healthy, correctly correlated SNAP response with a bounded acquisition age can establish Live. History and acknowledgements cannot do so. Age above 90,000 ms is Stale.
IF-005Host storage / node: acknowledge a summary only after durable commitment. Duplicate delivery of the same record produces one stored record. The node retains an unacknowledged record subject to the declared retention / overflow policy.
IF-006Communications: bound acknowledgement waits, retries and total radio activity. Acquisition, sensor diagnostics and local warning deadlines must continue during link failure and backlog replay.
IF-007Hardware / firmware: after reset, validate committed storage, allocate a new boot identity and qualify three new acquisitions before Healthy. Incomplete writes and pre-reset samples cannot qualify recovery.
IF-008Host: start Unknown after a host restart. Establish a new session and an eligible snapshot before Live. Reject delayed traffic from previous sessions.
IF-009Node / host: report history gaps and overflow explicitly, and process a fresh snapshot before backlog replay. Never let backlog delay the required 60 s live reporting opportunity.
IF-010System owner: log enough information to correlate supply / reset, acquisition, message delivery, host commitment and display state on an identified hardware / firmware / host configuration.
02 / EXAMPLE CONTRACT

Define what travels and what it means.

This is a small application protocol over the case-study BLE connection. Lower-layer packets may fragment an application message. Link-layer success is not an application acknowledgement. Assume authenticated, integrity-protected transport for the exercise; session identifiers provide correlation, not authentication.

ConcernAgreed behaviour
Encoding / framingOne compact UTF-8 JSON object per frame, terminated by one LF byte. No literal line breaks inside a frame. Maximum 1,024 bytes including LF. Reassemble fragments only within the current link session; a frame must finish within 250 ms of its first byte. On timeout, overlength or loss of framing, close the application session, clear buffers and reopen before accepting more data.
Schema / compatibilityRequire v:1, a listed message type, all its listed fields with correct types, and no duplicate or unknown keys. Reject unsupported versions, malformed UTF-8 / JSON, non-finite values and out-of-range integers. A complete but invalid frame is discarded and diagnosed; it neither refreshes Live nor receives a storage ACK. Repeated errors remain subject to the radio budget.
Numeric representationtemp_mC is an integer from −40,000 to 125,000, or null when invalid. 2,500 means 2.500 °C; zero is a valid value. This transport range and 0.001 °C encoding resolution do not extend the case’s demonstrated accuracy claim—there is no demonstrated claim yet—or its specified ±0.5 °C target over 0–10 °C.
Time / ordersampled_ms means conversion completion on the node monotonic clock; prepared_ms is that clock when the response is constructed. Never replace acquisition time with transmission time. Both are non-negative integers ≤2⁵³−1 and refer to the same boot. Sequences, counters and configuration revisions are integers 0…2³²−1. Before a sample / record sequence would wrap, allocate a new boot epoch through the same durable mechanism and requalify measurement; never silently reuse an identity.
Device / boot / sessiondevice is the provisioned identifier. boot is a 16-character hexadecimal identity allocated without reuse before publishing new observations. The host opens each connection with a non-reused 32-character hexadecimal link identity. A HELLO binds that session to the device and current boot. Allow 500 ms for HELLO after OPEN, then abandon that attempt and defer / retry within the same budget. A configuration revision change requires a new HELLO binding before the host accepts live data. If identity allocation fails, measurement publication remains unavailable; a random default or zero identifier is not an acceptable fallback.
Health / warninghealth is INITIALISING, HEALTHY or SENSOR_FAULT. valid:true requires HEALTHY, a completed new conversion and an in-range value. Otherwise valid:false, temp_mC:null and a reason are required. Reason is NONE, STARTUP, ACQUISITION_FAILED or RECOVERING. A valid observation must use NONE; an invalid one must use one of the other reasons. A failed attempt invalidates publication immediately, even before the three-failure sensor-fault indication. Warning is HIGH, CLEAR or UNKNOWN and is independent of link state. UNKNOWN applies on boot until qualified; invalid data cannot clear HIGH.
Live opportunityAfter HELLO, and every 60 s while the session is available, the host issues a POLL. The node returns one coherent SNAP of its current acquisition state. A response received more than 500 ms after the host issued that POLL cannot establish Live. Retire that poll identity. At most one poll is outstanding; a new poll uses a new ID. After an initialising response, the next scheduled opportunity is within 60 s; this example does not promise immediate remote recovery after the third local sample.
Record ACK / retriesOne SUMMARY may be awaiting an application ACK. Start its 500 ms ACK timer after the complete frame is handed to transport. On timeout, retain the record; wait at least 1,000 ms before another attempt, using a new tx ID and the same immutable record content. Allow at most three attempts for that record per fixed 60 s window, and only while the total radio budget permits. Late / mismatched ACKs do not remove records.
Radio / scheduling budgetKeep the original normal-use 200 ms/minute radio allowance as a design target. For the fault exercise, impose an assumed 600 ms radio-active cap per 60 s window, including reconnects, live traffic and retries. Reserve available budget for the next live opportunity before admitting backlog or retries. Whichever limit is reached first defers more traffic; failure to meet a scheduled reporting opportunity is diagnosed, never hidden as a successful report. Do not restart the allowance merely because a link reconnects. Repeated resets must not create unlimited retry bursts. Validate achievable payload, timing and energy on the actual platform; this fault allowance does not prove the 12-month battery target.
History / overflowCommitted summaries are immutable. The key is (device, record.boot, record.seq); do not replace the record’s original boot with the retransmitting node’s boot. Preserve at least 24 h of completed summaries. An identical duplicate is acknowledged as DUPLICATE after checking the stored content; different content under the same key is a diagnosed identity conflict with no success ACK. On ring overflow, drop the oldest complete record and expose the dropped count / oldest retained key in HELLO. Commit that gap metadata with the eviction and preserve it across resets; a saturated dropped counter stays saturated and the gap indication remains visible.

On receipt of a correctly correlated invalid SNAP, the host immediately shows measurement unavailable, while optionally retaining a clearly labelled last-good value with its original age. With only a link failure, an already accepted observation continues ageing; a separate disconnected indicator prevents the display implying ongoing acquisition. Above 90 s, it becomes Stale. Host refresh checks run at least every 100 ms, so the changed state must be visible within 100 ms of crossing the threshold.

RFC 8259 is the reference for JSON syntax and interoperability. LF framing, size limits, strict schema, identifiers and recovery rules here are this exercise’s own design choices.

03 / MESSAGE SET

A transport callback is not proof of stored data.

MessageRequired fields, in any JSON key order
OPEN · host to nodev, type, device, link. Establish the new session. Clear old partial frames and retire old transaction identities at both ends.
HELLO · node to hostv, type, device, link, boot, config_rev, health, reset_cause, dropped_records, oldest_record. reset_cause is POWER_ON, BROWNOUT, WATCHDOG, SOFTWARE or UNKNOWN. oldest_record is null or an object with boot, seq. A nonzero dropped count makes a history-gap indication visible.
POLL · host to nodev, type, link, poll. The host records monotonic issue time for this non-reused poll ID in the session. Poll ID exhaustion requires a new link session.
SNAP · node to hostv, type, device, link, boot, poll, sample_seq, sampled_ms, prepared_ms, config_rev, health, valid, reason, temp_mC, warning. sample_seq counts acquisition attempts, including failures. Before the first completed attempt, it and sampled_ms are null. The host checks HELLO binding and configuration, active poll, types, consistency and freshness before updating the display. SNAP has no storage ACK and is not a durable-history promise.
SUMMARY · node to hostv, type, device, link, boot, tx, record. tx is a non-reused attempt ID within the session; open a new session before it would wrap. record contains boot, seq, start_ms, end_ms, config_rev, expected, valid_count, min_mC, max_mC, mean_mC, last_mC, partial. Windows are half-open, [start, end); normal windows are 60,000 ms. Count invalid / missing samples in expected but not valid_count. Partial windows and any configuration boundary are explicit, not merged silently.
ACK · host to nodev, type, device, link, tx, record_boot, record_seq, result. Result is STORED or DUPLICATE. All identifiers must match the outstanding attempt. The success condition is a durable host record plus durable deduplication identity, committed atomically, not a receive buffer or a cloud-forwarding attempt.

For a summary, require 0 ≤ valid_count ≤ expected. With no valid samples all statistics are null. Otherwise use only valid samples; round mean to the nearest millidegree, ties away from zero. Retain the last valid value, and require min ≤ mean ≤ max and min ≤ last ≤ max. A configuration change starts a new partial window. In the exercise’s uninterrupted baseline, configuration remains revision 3.

Each object below is shown on one line as it is sent, followed by LF. The identifiers are illustrative, not credentials.

{"v":1,"type":"SNAP","device":"TEA-NODE-01","link":"0000000000000000000000000000000a","boot":"000000000000002a","poll":9,"sample_seq":60,"sampled_ms":60000,"prepared_ms":60020,"config_rev":3,"health":"HEALTHY","valid":true,"reason":"NONE","temp_mC":2500,"warning":"CLEAR"}
{"v":1,"type":"SUMMARY","device":"TEA-NODE-01","link":"0000000000000000000000000000000a","boot":"000000000000002a","tx":12,"record":{"boot":"000000000000002a","seq":1,"start_ms":0,"end_ms":60000,"config_rev":3,"expected":60,"valid_count":57,"min_mC":2400,"max_mC":2600,"mean_mC":2500,"last_mC":2500,"partial":false}}
{"v":1,"type":"ACK","device":"TEA-NODE-01","link":"0000000000000000000000000000000a","tx":12,"record_boot":"000000000000002a","record_seq":1,"result":"STORED"}

Why 57 of 60? The first three scheduled attempts occurred during start-up qualification, so the summary truthfully records incomplete valid coverage even though the completed minute window is not partial. A schema-valid summary is historical evidence; it cannot set the live display.

After a valid ACK, the node may mark the record delivered. If that marker is lost during a node reset, resend the same committed record; host deduplication resolves it. Do not delete a record when the radio merely reports transmission complete.

04 / FRESHNESS

Bound age without pretending the clocks agree.

The node and host do not subtract their absolute clocks from each other. The node calculates age when it constructs the response. The host measures the complete POLL / SNAP interval on its own monotonic clock; this conservatively covers the return transit and any waiting after construction.

A = (prepared_ms − sampled_ms) + R + U + E

R = host elapsed time from issuing the matching POLL to receiving its complete SNAP.
U = 10 ms assumed combined clock-rate / quantisation allowance over the freshness interval.
E = host elapsed time since that SNAP was received.

Accept only if timestamps are consistent, the same boot is bound by HELLO, R ≤500 ms, health is HEALTHY, validity is true, and the sample identity is newer than the last accepted sample in that boot. Duplicates do not restart the age calculation. With no established bound, label the observation unavailable / historical; do not silently use receipt time.

For the example, node age is 20 ms, R is 120 ms and U is 10 ms. Age at receipt is therefore 150 ms. At E =89,850 ms the age is exactly 90,000 ms and has not yet crossed the stale threshold. At E =89,851 ms the age is 90,001 ms: Stale, displayed within the next 100 ms refresh cycle. An older-than-limit response never establishes Live in the first place.

The uncertainty allowance is a teaching assumption to verify against actual clock tolerances and sleep behaviour. The host must continue to age data through its own sleep; if it cannot establish elapsed time on resume, return to Unknown. UTC corrections, time zones and NTP jumps must not make an old measurement younger.

Limit: a plausible but false timestamp generated by defective firmware can defeat the host’s calculation. Integration tests must observe real conversions and atomic publication; the host cannot infer physical truth from well-formed fields alone.

05 / RECOVERY MODEL

Measurement recovery and delivery recovery are separate.

Two independent state machines. Measurement begins Initialising, becomes Healthy after three new valid acquisitions, and enters Sensor fault after three failed acquisitions. Recovery returns through Initialising. Delivery moves from Offline to Synchronising to Online; exhausted time or radio budgets lead to Deferred, which retries through Synchronising. Power loss restarts measurement; link loss returns delivery to Offline. Host state depends separately on accepted snapshot validity and age.
Events and guards are defined below. A link transition never resets measurement qualification, acquisition deadlines or local warning logic.
State / eventRequired action and guard
Any node state → power lossSupervision holds outputs in their defined unpowered / reset condition. No final transmission is assumed. On usable power, validate storage and allocate a new boot before Initialising. If those prerequisites fail, remain unavailable; do not enter Healthy.
Initialising → HealthyThree consecutive new, valid acquisitions after boot or recovery. A failed attempt resets this success counter. The third acquisition completes qualification; publication can become valid at that point. The record example conservatively excludes all three qualification samples from its summary statistics.
Healthy → degraded acquisition → Sensor faultAt the first failed acquisition, mark publication invalid and enter Initialising with reason RECOVERING. Three consecutive failures assert SENSOR_FAULT within 3.2 s. A successful attempt resets the consecutive-failure counter, but three consecutive successes are required for Healthy. Do not confuse invalid data with a cleared temperature warning.
Sensor fault → InitialisingAt most one bounded sensor-recovery action per minute, then attempt fresh acquisition. A continuing failure returns to Sensor fault. A physical short may require service; an I²C retry cannot repair it.
Offline → Synchronising → OnlineClear old partial frames and transactions; exchange OPEN / HELLO, check version and boot, then attempt a fresh POLL / SNAP before sending history. Online means the session is ready, not that the sensor is healthy. An initialising or invalid SNAP cannot establish Live.
Online → DeferredAfter the retry limit or radio-active cap, retain unacknowledged history and wait until the next eligible 60 s window. Resetting the link cannot reset this budget. If a session is still usable, revalidate its binding and obtain a new snapshot before resuming history; otherwise reopen it.
Any delivery state → link lossGo Offline, cancel pending waits and discard partial frames. Keep local acquisition, warning and committed storage running. Host ages its last accepted observation and shows link loss separately.
Host reset / new node bootHost starts Unknown. A new node boot invalidates the old live claim and requires new qualification and a correlated snapshot. Earlier committed records remain historical under their original identities.
06 / FAULT ANSWERS

What changes in each trace?

A. Power disappears part-way through a frame

The host accepts nothing from a partial frame. It clears the buffer on disconnect or after the 250 ms fragment limit. The node’s previously committed minute record is still pending because there is no matching application ACK. After reboot, the node reports a new boot and its reset cause, qualifies measurement, and sends the pending record with its original record boot / sequence. A reset cause supports the investigation but does not prove why the rail collapsed; correlate rail, reset and radio activity.

B. The acknowledgement is lost

There is no distributed guarantee that both parties know commitment occurred at the same instant. The node waits 500 ms, retains the record and retries within its budget. The host finds the same key and identical immutable content, returns DUPLICATE, and does not create another row. This is retry with idempotent storage, not a promise of exactly-once radio delivery. If the host restarts after commitment, its durable deduplication identity must still exist.

C. Old data arrives in new traffic

Failed acquisition immediately invalidates the atomic publication. Repackaging a last-good sample cannot create a new sample sequence or acquisition time. A sample already 120,000 ms old, with R =100 ms and U =10 ms, has age at least 120,110 ms and cannot establish Live. History goes to the history store only. If the producer fabricates a new timestamp, a physical conversion marker / acquisition log compared with the outgoing message exposes the defect.

D. Either endpoint restarts

The node gets a new boot identity, so sequence zero in the new boot cannot be confused with old sequence 900. A delayed ACK from the previous link is ignored. The host’s new session begins Unknown even if its database holds a last-good value; a new eligible SNAP is needed. If the node cannot prove identity allocation or storage consistency, fail as unavailable rather than emitting a default healthy state.

Cross-discipline decision: an energy-storage or rail-impedance problem may need hardware change. Missing timeout, identity or atomic publication rules need firmware and host changes. A correct parser cannot compensate for a false acquisition timestamp; a larger capacitor cannot define what an ACK means.

07 / INTEGRATION VERIFICATION

Test at the point where the claim can be false.

Baseline: identified board / enclosure and power source, firmware and host builds, protocol v1, configuration revision 3, a controllable sensor input and a durable host store. Use a fault-injecting link proxy or equivalent test fixture plus rail / reset capture. Every test below is a planned procedure with an expected result; execution status: Not executed.

Test / traceSetup and stimulusExpected resultEvidence
INT-01
IF-001 / 002
Healthy source. Send 2,500 mC, 0 mC and −1,000 mC; then a value outside −40,000…125,000 and an invalid observation carrying null.Show 2.500 °C, 0.000 °C and −1.000 °C with valid status; formatting may round consistently. Reject the out-of-range message without changing Live. Validly encoded invalid status shows unavailable, never numeric zero.Frames, parser decisions and captured display state. This test does not verify sensor accuracy.
INT-02 · A
IF-002 / 005 / 007
Commit one local summary, start its transmission, cut power after part of the frame. Repeat at several byte offsets, then restore power.No partial record and no storage ACK. Buffer discarded on link loss or by 250 ms. Previously committed record recovered with the same record key; new node boot; host stores at most one copy after replay. Uncommitted current work may be lost and must not be invented.Supply / reset waveform, byte trace, local commit scan and host rows before / after.
INT-03 · B
IF-005 / 006
Let the host commit a summary but drop its ACK. Restore the link before the next permitted retry.Node retains the record after the 500 ms timeout. Retry waits at least 1,000 ms and uses a new tx ID. Same record key / content creates no second row; matching DUPLICATE ACK permits marking delivered.Correlated tx / ACK timing and database row count, including durable identity.
INT-04 · B
IF-005 / 008
Crash the host once immediately before durable commit and once immediately after commit but before ACK. Restart and replay.Before-commit case produces one eventual committed record. After-commit case returns DUPLICATE and still has one record. Neither case restores a green live value from the database alone.Controlled crash point, recovered transaction / deduplication state and host start-up display.
INT-05 · C
IF-001 / 004
Freeze acquisition while communications continue; try repeated sample identity, a preserved old timestamp, and a falsely restamped last-good value.Real failed acquisition gives invalid publication immediately. Duplicate identity does not reset age. A 120,000 ms-old sample with R =100 ms is ineligible (age ≥120,110 ms). Restamping is detected by mismatch against physical conversion / acquisition evidence.Conversion marker or sensor trace, publication log, frames and host age / state.
INT-06 · C
IF-004 / 009
Replay six hours of history after reconnecting. Hold the fresh snapshot invalid. Separately inject a SNAP arriving at R =500 ms and at R =501 ms.History never establishes Live. At R =500 ms an otherwise eligible snapshot may be accepted; at 501 ms it cannot establish Live. Backlog cannot delay the next 60 s live opportunity.Poll issue / receipt times, record types, live-state transitions and queue schedule.
INT-07 · C
IF-004
Inject an accepted sample with initial age 150 ms. Advance E to 89,850 then 89,851 ms. Jump UTC forwards / backwards by one hour.Age is respectively 90,000 and 90,001 ms. Mark Stale after the latter threshold, visible within 100 ms. UTC changes do not reset age. An unbounded elapsed interval after host sleep causes Unknown.Controlled monotonic clock, UTC changes and presentation timestamps.
INT-08 · D
IF-003 / 007 / 008
Reset the node after sample 900. Inject old-link SNAP / ACK traffic. Complete two then three valid post-reset acquisitions. Repeat with boot-ID allocation failure.New boot distinguishes restarted sequence. Old-link traffic ignored. No Healthy after two acquisitions; allowed after the third. Host still needs an eligible fresh snapshot. Allocation failure never emits a healthy / valid observation.Boot ledger, HELLO / poll trace, qualification counter and host state.
INT-09
IF-006; CS-002 / 003 / 005
Keep the link failing / backlog active while applying a sustained ≥8.0 °C sensor stimulus. Separately disconnect the sensor. Exercise the full declared radio budget.Absolute sampling stays within ±10 ms. Local high warning and persistent sensor-fault indication meet their respective 3.2 s bounds. Invalid samples cannot clear HIGH. ≤3 attempts per record / window and ≤600 ms total radio-active time; further traffic deferred.Scheduler markers, sensor stimulus / bus capture, indicator waveform, radio-active measurement and power trace.
INT-10
IF-002 / 003 / 005
Inject duplicate JSON keys, unsupported version, overlength frame, wrong ACK identity, same record key with altered content, and a sequence at its maximum.No false Live / storage success. Framing failures close and clear the session. Identity conflict is diagnosed; wrong ACK cannot remove a record. Sequence cannot wrap within one boot; new epoch qualification or unavailable state follows.Parser / state logs, retained queue, database content and identity ledger.
INT-11
IF-007 / 009; CS-006 / 007
Interrupt a history write, then fill the history ring beyond capacity. For the target capacity run, accumulate at least 24 h of minute records; a reduced test ring is only an additional logic test.Previously committed records survive; torn records rejected. At least 24 h of complete summaries retained in the target configuration. Overflow advances oldest-record identity and dropped count; host shows a gap. No fabricated continuity across reset or overflow.Memory scan, effective capacity / overhead calculation, retained timestamps / identities and host gap indicator.
INT-12
IF-001 / 010
Place the real assembled node at a controlled reference temperature. Compare normal transmissions, maximum retry activity and a weak battery profile.Meet CS-001 over its specified 0–10 °C range with the agreed uncertainty decision rule, or record a failure requiring design / claim revision. Correlate rail droop, self-heating and acquisition errors; an intact digital message alone is insufficient.Reference instrument / uncertainty, thermal soak conditions, rail and current captures, raw samples and configuration baseline.

Report format: test ID; requirement IDs; actual configuration; fault method and timestamp; expected versus observed result; evidence file references; pass / fail; anomaly and retest decision. Keep actual-result fields blank until the procedure has been executed. A paper walkthrough checks consistency; it is not hardware verification.

08 / IMPLEMENTATION AND REVIEW

Use firmware detail to implement a system decision.

These links explain mechanisms. The Embedded Academy exercise owns the system-level questions: what the host may claim, what survives power loss, which timing / energy budgets apply and what proves correct recovery.

Review before implementing

Have hardware, firmware and host owners review the same interface revision and tests. Demonstrate the assumed timing and clock bounds, verify reset / memory behaviour against selected components, and resolve any energy-budget conflict. Trace each deviation to its affected requirement and retest. Alternative protocols are acceptable when they provide equivalent explicit behaviour and evidence.

Continue learning: TEA-104, TEA-106, TEA-108 and TEA-109.

KEY TAKEAWAY

The interface carries meaning as well as bytes.

A dependable system preserves identity, age, validity and recovery behaviour across power, firmware, communications and host storage boundaries.