Applied Projects route · TFA-401

Worked Project: Temperature Controller

Follow one firmware project from an engineering need to an evidence-based release decision. Work with eight requirements, a three-state controller, executable C code, 13 host tests and a fault-handling exercise.

Applied project27 min reading + 45 min practiceWorked example

← Back to the learning library

Work through the project with the source and evidence.

C source, 13 host tests, requirements trace, recorded host results, target-test protocols and the fault exercise. Python 3 and a C99 compiler are needed to run the tests.

Download the worked project

Before you begin: Start with TFA-101–105 for architecture, state, timing, defensive programming and updates, then TFA-109 and TFA-111 for testing and evidence. Basic C is useful; the requirements and decision tables can also be studied without compiling the example.

Reading time and practice time are estimates. Examples use fictional products and illustrative engineering values, not approved product limits.

Learning objectives

By the end of this module, you should be able to:

  • Follow an identified requirement through an allocated design decision, implementation and verification record.
  • Explain how a sample timestamp, state machine and scheduling budget determine fault response.
  • Run and interpret the supplied host tests without mistaking them for physical product verification.
  • Diagnose a stale-data fault and define a regression at the interface where it originates.
  • Make a release decision that identifies both available evidence and unresolved work.

Lesson 1

1. Define the product and the engineering claim

The fictional product is a benchtop temperature controller for a small test chamber. A microcontroller reads a digital temperature sensor, accepts Start, Stop and Reset commands, and drives a low-voltage heater through a switching stage. Its fixed nominal target is 35.00 °C. A separate thermal cutoff is a proposed hardware protection. There is no network connection or remote update in this example.

The user needs deliberate operation, controlled heating, fault response and an understandable fault indication. Name these UN-01 to UN-04 so that a reviewer can ask why each software requirement exists. “Keep the chamber safe” is not an adequate software requirement: it does not identify a fault, trigger, response, timing origin or physical outcome.

All temperatures and deadlines here are illustrative teaching inputs. They have not been justified by a thermal risk assessment or experiments on a real chamber. The downloadable implementation is a host-tested control core, not a complete embedded image. The project follows the work through a release decision, including the decision that missing hardware evidence prevents a physical product release.

Identify the failure chain before writing the controller: acquisition stops, an old low temperature remains available, heating continues, and the chamber may overheat. The software control will detect old data and request heat off. A detached but electrically plausible sensor, a failed clock, stalled execution or a stuck-on output can defeat parts of that argument; separate system controls and verification are needed. Follow this distinction in TFA-104 and TFA-307.

Lesson 2

2. Give requirements identities, limits and timing origins

The eight requirements below form this teaching baseline. A value in centi-degrees Celsius is an integer: 3450 represents 34.50 °C. A sample is fresh only when its age is strictly less than 120 ms. “Acquisition time” means when the measurement was obtained, not when another task read a cached copy. Input plausibility limits are deliberately wider than the permitted heating range.

Requirement / needRequired behaviour
REQ-001
UN-01: deliberate operation
Initialise in IDLE with the heater command off. The board must hold the physical heater de-energised from power application through initialisation.
REQ-002
UN-01: deliberate operation
Accept START only in IDLE with a valid sample aged less than 120 ms. Plausible input is −20.00 to 125.00 °C inclusive. No sample or stale plausible data inhibits START; invalid data latches a sensor fault.
REQ-003
UN-02: controlled heating
In RUNNING, command heat on at or below 34.50 °C and off at or above 35.50 °C. Retain the previous command between the limits. The fixed nominal target is 35.00 °C.
REQ-004
UN-03: fault response
A fresh valid sample at or above 45.00 °C shall latch OVER_TEMPERATURE and de-energise the heater within 15 ms of sample publication to the control task.
REQ-005
UN-03: fault response
While RUNNING, invalid data shall latch SENSOR_INVALID and de-energise within 15 ms of publication. No fresh data shall latch SAMPLE_STALE at age 120 ms or greater, with physical off within 150 ms of the last valid acquisition.
REQ-006
UN-01: deliberate operation
STOP during normal operation shall return to IDLE and de-energise within 15 ms of command availability to the control task. STOP shall not clear a latched fault.
REQ-007
UN-03: controlled recovery
FAULT shall inhibit heat and START. RESET_FAULT shall clear it only with fresh valid data below 40.00 °C, returning to IDLE without heat. Require a separate START to resume.
REQ-008
UN-04: diagnosable behaviour
Retain the first latched fault reason in controller state until an accepted RESET_FAULT or device reinitialisation. Later faults shall not replace it. Persistent logging is outside this baseline.

REQ-001 and the timing parts of REQ-004–006 allocate work to hardware and integration as well as the pure C core. Keep those obligations visible when recording a host pass. For a real project, configuration variants, measurement accuracy, command arbitration, sensor conversion delay and operating conditions would need additional controlled requirements. This small baseline is selected for the learning objective, not presented as a complete product specification.

Use TFA-303 to challenge each trigger, boundary and verification method. If 150 ms is not justified by the product need and risk analysis, improving its wording does not make it the right number.

Lesson 3

3. Allocate architecture and agree the interface contracts

The architecture isolates decisions that can run on a development computer from mechanisms that depend on the board. This makes the core easy to exercise while keeping the hardware obligations explicit. The key interface is a coherent sensor snapshot: presence, validity, signed temperature and acquisition timestamp must describe the same acquisition.

Element / ownerResponsibility and contract
Sensor adapter / firmware and electronicsPublish a new snapshot after a complete acquisition, nominally every 50 ms. Preserve the old timestamp when serving a cached value. Define diagnostic validity and sensor conversion delay.
Control task / firmwareOwn the controller state. Invoke the core at intervals no greater than 10 ms, including scheduling delay, then apply its output.
Command adapter / firmware and systemsDeliver one resolved command per invocation. Define how simultaneous buttons are prioritised and make command availability observable.
Output driver / electronics and firmwareConvert heater_on to the power-stage command. Verify default-off during reset and physical de-energisation within 5 ms of an off command.
Status adapter / firmware and verificationRead the mode and first fault reason without modifying them. The teaching baseline has no persistent event log.
Independent protection / system and hardwareDefine a thermal cutoff and execution-failure response for a real product. Check common dependencies and response to a stuck-on power stage.

controller_tick receives the sample by value and has a single state owner. That does not by itself make an interrupt-shared cache safe: the board adapter must construct the snapshot coherently before calling it. Choose a justified handoff mechanism, such as a bounded critical section or message transfer, and analyse its timing. Merely declaring a structure volatile does not establish that every field belongs to one acquisition.

The architecture decision is therefore two-sided: host execution helps verify deterministic decisions; adapter and hardware tests must verify the meaning of the inputs and the effect of outputs. Continue with TFA-101, TFA-202 and TFA-304.

Lesson 4

4. Define states, fault priority and the timing budget

The controller has IDLE, RUNNING and FAULT states. Heating is possible only in RUNNING. A fault takes priority over ordinary control and remains latched. A valid Reset leaves the device in IDLE; it does not resume heating. The first reason is retained until accepted reset or device reinitialisation, allowing the initial failure to remain visible while later symptoms appear.

From / eventGuardNext state and output
IDLE / STARTFresh, plausible, valid sample below the trip thresholdRUNNING; apply hysteresis
RUNNING / STOPNo higher-priority faultIDLE; heat off
Any non-fault state / invalid sampleA sample is present but invalid or implausibleFAULT; SENSOR_INVALID; heat off
RUNNING / old or absent sampleNo fresh inputFAULT; SAMPLE_STALE; heat off
Any non-fault state / overtemperatureFresh valid temperature at least 45.00 °CFAULT; OVER_TEMPERATURE; heat off
FAULT / RESET_FAULTFresh valid temperature below 40.00 °CIDLE; clear reason; heat off
FAULT / other eventIncludes START, STOP or sensor recovery aloneRemain FAULT; retain reason; heat off

For stale data, allocate 120 ms until expiry, at most 10 ms to the next control invocation and 5 ms for physical de-energisation: 120 + 10 + 5 = 135 ms. Against the 150 ms requirement this leaves 15 ms of design margin. It is a budget under stated assumptions, not a measured result. The worst-case task interval must include interrupt interference and blocking.

The separate invalid-data and overtemperature deadline starts at sample publication: 10 + 5 = 15 ms. It does not include the sensor's conversion delay or the time from a physical temperature change to a valid reading. Those belong in a system response requirement. The code uses unsigned elapsed-time subtraction across clock rollover, with the API assumption that relevant gaps stay below 2³¹ ms and reset or suspend reinitialises the controller. Study TFA-102 and TFA-103 alongside this budget.

Lesson 5

5. Read the implementation as a set of decisions

This is the complete control implementation included in the download. It has no device drivers, allocation, interrupt handler or operating-system dependency. Constants remain fixed so that the exercise can concentrate on behaviour and evidence. The caller must supply a valid state pointer, a coherent snapshot and a common monotonic clock, then apply the returned heater command on every invocation.

#include "controller.h"

static void trip(struct controller *c, enum fault reason)
{
    c->mode = FAULT;
    c->fault = reason;
    c->heater_on = false;
}

void controller_init(struct controller *c)
{
    c->mode = IDLE;
    c->fault = NO_FAULT;
    c->heater_on = false;
}

void controller_tick(struct controller *c, struct sample s,
                     enum command cmd, uint32_t now_ms)
{
    /* REQ-002 / REQ-005: plausibility and acquisition freshness. */
    const bool valid = s.seen && s.valid &&
                       s.centi_c >= -2000 && s.centi_c <= 12500;
    /* Intentional unsigned elapsed-time arithmetic: see API clock contract. */
    const bool fresh = valid &&
                       (uint32_t)(now_ms - s.captured_ms) < UINT32_C(120);

    /* REQ-007 / REQ-008: retain the first reason; reset never restarts heat. */
    if (c->mode == FAULT) {
        c->heater_on = false;
        if (cmd == RESET_FAULT && fresh && s.centi_c < 4000) {
            controller_init(c);
        }
        return;
    }
    if (s.seen && !valid) {
        trip(c, SENSOR_INVALID);
        return;
    }
    if (c->mode == RUNNING && !fresh) {
        trip(c, SAMPLE_STALE);
        return;
    }
    /* REQ-004: only a fresh, plausible sample supports this diagnosis. */
    if (fresh && s.centi_c >= 4500) {
        trip(c, OVER_TEMPERATURE);
        return;
    }
    if (cmd == STOP) { /* REQ-006 */
        c->mode = IDLE;
        c->heater_on = false;
        return;
    }
    if (c->mode == IDLE && cmd == START && fresh) {
        c->mode = RUNNING;
    }
    if (c->mode != RUNNING) {
        c->heater_on = false;
        return;
    }
    /* REQ-003: inclusive hysteresis limits, fixed target 35 degrees C. */
    if (s.centi_c <= 3450) {
        c->heater_on = true;
    } else if (s.centi_c >= 3550) {
        c->heater_on = false;
    }
}

Read the order as part of the design. The latched FAULT branch returns before normal commands. Invalid input and stale input while running are handled before the temperature rule; a stale hot value therefore records stale data, not an assertion about current temperature. The normal STOP branch precedes START and hysteresis. Boundary comparisons are explicit: age 120 ms is stale, 45.00 °C trips and 40.00 °C cannot authorise reset.

Integer representation avoids floating-point dependence in this small example, but does not establish measurement accuracy. No coding-standard conformity or exhaustive verification is claimed. A review should examine input contracts, numeric range, state ownership, intentional time wrap and output application. TFA-305 explains how a selected coding rule set and justified deviations would enter a real project.

Lesson 6

6. Run tests and state exactly what passed

The download includes test_controller.c and run_tests.py. On a computer with Python 3 and a C99 compiler, run python3 run_tests.py from the extracted project directory. The runner compiles with warnings treated as errors, executes explicit checks, and writes the compiler, flags, source hashes, time and result to host-results.json.

Recorded execution: 13 host test cases passed on 2026-10-03, using cc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 on Linux / x86_64. The code shown above and the tests in the download are the sources identified by that record.

Host caseObserved check
TC-001Initialisation gives IDLE, no fault and heat off.
TC-002Missing or stale input cannot start; a fresh sample can.
TC-003Inclusive hysteresis boundaries and state retention between them.
TC-00444.99 °C does not trip; 45.00 °C latches overtemperature.
TC-005A sample is fresh at 119 ms and stale at 120 ms.
TC-006Invalid sensor data while heating latches and commands off.
TC-007STOP returns normal operation to IDLE and heat off.
TC-008START and STOP cannot clear a fault; reset at 40.00 °C is blocked; reset at 39.99 °C requires a separate restart.
TC-009The same 119 / 120 ms boundary works across uint32_t rollover.
TC-010A later overtemperature sample does not overwrite the first stale-data reason.
TC-011Out-of-range temperatures are invalid input.
TC-012Stale or invalid data cannot authorise fault reset.
TC-013Repeated reads of a retained timestamp eventually cause the stale-data fault.

The suite deliberately tests decisions that can be missed by a successful nominal run: equality at a threshold, retaining state inside the hysteresis band, the first fault cause, restart inhibition and time rollover. It does not show physical current, deadline compliance, target compiler behaviour or complete structural coverage. For example, TC-013 supplies a correctly retained timestamp to the core; it cannot prove that the unimplemented sensor adapter preserves that timestamp.

A test result needs an interpretation, not just a green mark. When a real result disagrees with a requirement, retain the original run, investigate the cause and record the correction and rerun against an identifiable baseline. Use TFA-109 and TFA-306 to choose the appropriate verification level.

Lesson 7

7. Close the trace without hiding missing evidence

The table below connects every requirement to its implementation allocation and named tests. Host case links lead to the recorded checks. HIL references identify defined protocols in the download; none of the HIL protocols has been executed. Keeping that distinction in each trace prevents a host result from being used as an accidental claim about the whole product.

RequirementArchitecture / implementationVerification status
REQ-001controller_init; output driver and reset hardwareTC-001: host pass.
HIL-001: defined, not executed.
REQ-002sensor snapshot; controller_tick start guardTC-002, TC-011: host pass.
HIL-002 / HIL-003: defined, not executed.
REQ-003controller_tick hysteresis; output driverTC-003: host pass.
HIL-002: defined, not executed.
REQ-004controller_tick temperature guard; driver and schedulerTC-004: host pass.
HIL-003: defined, not executed.
REQ-005acquisition timestamp; freshness guard; trip; scheduler and driverTC-005, TC-006, TC-009, TC-013: host pass.
HIL-003 / HIL-004: defined, not executed.
REQ-006command adapter; controller_tick STOP branch; driverTC-007, TC-008: host pass.
HIL-005 / HIL-006: defined, not executed.
REQ-007controller_tick FAULT branch and command arbitrationTC-008, TC-012: host pass.
HIL-006: defined, not executed.
REQ-008trip; FAULT branch; read-only status adapterTC-010: host pass.
HIL-006: defined, not executed.

This is bidirectional evidence: a reviewer can move from a requirement to its test, or ask which requirement a test supports. The downloadable CSV preserves the same IDs and relationships for further work. It also exposes gaps. TC-001 checks the C initialisation values, but cannot prove that a boot-time output pulse never occurs. TC-007 checks the normal STOP transition and TC-008 checks that STOP cannot clear FAULT; neither measures the physical response time or the real button interface.

Trace links establish relationships, not sufficiency. A test may exercise one decision but miss a platform dependency, adverse sequence or relevant configuration. Review adequacy separately from link completeness. See TFA-111 for the broader evidence argument.

Lesson 8

8. Plan integration and physical fault injection

Bring up the real adapter before relying on a chamber test. Feed a completed acquisition, then repeatedly request its cached value; check that the value, validity and original timestamp stay together. Exercise the actual interrupt / task handoff under load. If the timestamp advances without a new acquisition, the core receives a false claim of freshness and cannot detect the defect from the supplied fields.

The target protocols are concrete next actions. HIL-001 observes heater current from power-up through reset and brownout. HIL-002 checks physical hysteresis behaviour. HIL-003 injects invalid and overtemperature samples. HIL-004 freezes acquisition under worst supported load and sweeps the phase relative to the control task. HIL-005 measures Stop response. HIL-006 exercises fault indication, guarded reset and separate restart.

For HIL-004, mark the last valid acquisition, the control decision and physical current on a shared timebase. The acceptance limit is 150 ms from acquisition to de-energisation. A software flag becoming false is insufficient. Specify the current threshold that means “off”, probe loading, capture resolution and uncertainty. A measurement close to the limit needs an uncertainty-aware acceptance decision, not rounding down.

These are defined protocols with no claimed results. A real review also needs sensor bias and detachment analysis, independent cutoff tests, scheduler / watchdog failure tests, target build verification and a thermal assessment. The 35 °C regulation objective needs system validation on the intended chamber and load. Passing a state-machine test cannot demonstrate thermal stability. TFA-204 shows how those responsibilities connect.

Lesson 9

9. Assemble the release evidence and make the decision

A release dossier should tell a reviewer what is being released, why its behaviour is acceptable and which limitations remain. For teaching baseline 1.0.0, the source, eight requirements, architecture contracts, 13 host tests and actual host record are available. The record hashes the implementation, header, tests and runner. There is no target binary or claimed product approval.

Evidence itemCurrent disposition
Requirements and trace CSVEight identified requirements mapped to implementation, host cases and planned HIL protocols.
Implementation and host resultsExecutable C core and 13 passing cases; source hashes, compiler and flags recorded.
Target build and physical verificationAbsent / not executed. Blocks physical release.
Anomalies and limitationsTimestamp exercise recorded as F-01; adapter, output timing and independent protection gaps remain visible.
Release decisionDistribute the educational project. Do not authorise physical firmware release from this evidence set.

This is an engineering outcome, not a failed example: the evidence supports a limited release and prevents an unsupported one. A real release record would add the board and firmware identities, build configuration, image digest, review dispositions, measured verification results, residual anomalies and authorised approval. Do not fill those fields with invented names or sample passes.

Use the standards modules to organise the work. TFA-302 connects lifecycle activities and decision ownership; TFA-303 supports requirements work; TFA-304 supports architecture descriptions; TFA-305 supports a chosen coding policy; and TFA-306 supports verification planning. These are learning connections, not a claim that the example conforms to any standard. TFA-301 remains the starting point for deciding which publications actually apply.

Lesson 10

10. Assess a change across the complete chain

A developer proposes increasing the freshness limit from 120 ms to 200 ms because occasional acquisition delays trigger the fault. The change looks like one constant, but its allocated worst-case response becomes 200 + 10 + 5 = 215 ms, beyond the current 150 ms requirement. Updating the test to expect 200 ms would conceal the conflict.

First investigate the delayed acquisitions and confirm whether the requirement and risk assumptions remain valid. Possible engineering responses include repairing blocking, improving scheduling or changing the acquisition design. A change to the requirement needs its own product justification. Then revise affected architecture contracts, code, boundary tests, HIL timing tests, trace links and release decision together.

A later configurable setpoint or network interface would introduce different obligations: input range and authority, retained configuration, malformed requests and an update / support policy. Evaluate those changes with TFA-106, TFA-309 and TFA-310. Do not quietly imply those capabilities exist in this baseline.

The project is complete as a learning thread when you can explain how a changed need reaches a changed release decision. The source diff is only one link in that chain.

Decision guide

What does the current evidence justify?

ClaimCurrent evidenceDecision
The specified host boundary cases passActual TC-001–013 record tied to source hashesSupported for the recorded host run
The physical heater turns off within 150 msBudget plus a defined HIL-004 protocolNot established; target measurement required
The chamber is thermally safeIllustrative limits and an unverified hardware protection conceptNot established; system assessment required
The learning example can be distributedSources, trace, results and limitations includedRelease as educational material only

Worked example

Worked example — one requirement across five work products

Problem

UN-03 requires a controlled response to sensor failure. REQ-005 gives stale data a 120 ms threshold and a 150 ms physical-off deadline from the last valid acquisition.

Engineering approach

The architecture preserves acquisition timestamps in a coherent snapshot. controller_tick checks elapsed age and trip latches SAMPLE_STALE. TC-005 checks equality at 120 ms, TC-009 checks rollover and TC-013 checks repeated cached reads. HIL-004 is allocated the real adapter, scheduling and physical-current evidence.

Result

The release record can claim the named host cases passed. It explicitly withholds a physical timing claim until HIL-004 and the supporting hardware assumptions are verified. Every link uses the same requirement identity.

Engineering evidence

What should the work produce?

Requirements and trace

A downloadable CSV connects UN-01–04 and REQ-001–008 to implementation and named verification.

Executable teaching core

C implementation, API contracts and 13 host cases with a repeatable runner.

Actual host execution record

Compiler, flags, time, source SHA-256 hashes and observed test results.

Target protocols and release decision

Defined HIL stimuli and acceptance criteria, open gaps and an explicit limit on release.

Common failure patterns

What goes wrong?

  • Re-stamping a cached sample and destroying evidence of its true age.
  • Treating a heater command as proof that current has stopped.
  • Allowing sensor recovery or Reset to restart heat automatically.
  • Marking a requirement fully verified because it has a trace link to one passing host case.
  • Changing a timeout without revisiting its allocated response budget.

Applied exercise · approximately 45 minutes

Fault-handling exercise — the sensor that never gets old

The controller is RUNNING at 34.00 °C. The last successful acquisition occurs at 1000 ms, then acquisition freezes. The adapter keeps returning the cached value but sets captured_ms to the current read time. The controller reads every 10 ms and continues heating.

  1. Identify the violated requirement and the interface contract that has been broken. Explain why numerical equality between successive values does not prove a frozen sensor.
  2. Predict the sample age and heater decision at 1100, 1110 and 1120 ms for both the flawed and corrected adapter. State the physical-off deadline and its time origin.
  3. Specify the fix and a regression at the adapter boundary. Explain what TC-013 already demonstrates and what it cannot establish.
  4. After the fault, predict mode and heater output for RESET with fresh 41.00 °C at 1160 ms, RESET with fresh 39.99 °C at 1180 ms, then START with fresh 34.00 °C at 1190 ms.
  5. Decide which evidence is needed before closing the defect for a real device.
Review a suggested approach

REQ-005 is violated because the adapter has replaced measurement age with cache-read age. The flawed interface reports 0 ms at every invocation. A steady temperature can legitimately generate equal successive readings; freshness requires evidence of a new acquisition, not a changing value.

TimeCorrect retained timestamp: 1000 msFlawed timestamp refreshed on read
1100 msAge 100 ms; RUNNING; heat onAge 0 ms; RUNNING; heat on
1110 msAge 110 ms; RUNNING; heat onAge 0 ms; RUNNING; heat on
1120 msAge 120 ms; FAULT; command heat offAge 0 ms; RUNNING; heat on

In this aligned trace, a conforming output stage would de-energise by 1125 ms. The requirement deadline is 1150 ms, measured from the last valid acquisition. With arbitrary task phase the allocated bound is 135 ms after acquisition. These physical bounds still depend on target evidence.

Fix the adapter so a complete acquisition publishes one coherent value, validity and original timestamp. Repeated cache reads and retries preserve that timestamp. A regression must exercise one real adapter acquisition followed by repeated reads and assert that the timestamp remains 1000 ms. Feed the snapshots through the core and observe SAMPLE_STALE at 1120 ms. TC-013 covers the core with correct input; it does not test the missing adapter. HIL-004 must exercise the integrated path and measure current under load.

At 1160 ms the device remains FAULT with heat off because 41.00 °C fails the reset guard. At 1180 ms it enters IDLE with heat off. Only the separate START at 1190 ms enters RUNNING and enables heating at 34.00 °C. Keep the original defect record, code / contract correction, regression result, target result and release disposition linked before closure.

Completion criteria

  • Diagnosis identifies lost acquisition-time meaning and REQ-005.
  • Predictions distinguish stale-data detection, physical response and their time origins.
  • Reset and separate restart behaviour are correct.
  • Regression covers the faulty adapter as well as the control core, with HIL still required.

This is a self-directed engineering exercise, not an accredited examination. Compare the rationale as well as the final answer.

Practical check

Questions for your next design review

  • Can you follow REQ-005 from its user need to code, tests and release disposition?
  • Does the sensor timestamp retain its acquisition meaning across every handoff?
  • Can a fault clear only under the specified guard, with a separate restart?
  • Are physical timing and system safety claims limited to evidence actually obtained?
  • Would changing one constant trigger review of the affected requirements and tests?
Key takeaway

A defensible firmware release connects the required physical behaviour to explicit design decisions, identifiable implementation and evidence obtained at the right level.

References and further reading

Educational guidance only. Apply the authorised standards editions, regulations and approved procedures relevant to your product and market. Linked publications support further study; the fictional exercises do not demonstrate compliance.