Skip to content
Test Types & Methods

How to Reduce Field Returns with TofuPilot

Learn how to catch marginal units before they ship by adding margin tests and stress phases in OpenHTF, then correlating field failures in TofuPilot.

JJulien Buteau
intermediate9 min readMarch 14, 2026

Units that pass production testing but fail in the field are expensive. Every field return costs 10 to 100 times more than catching the same defect on the line. The fix starts with better test coverage and tighter analysis of the data you're already collecting. TofuPilot gives you the tools to spot marginal units before they ship.

Why Units Pass Tests but Fail in the Field

Three root causes account for most field returns:

Insufficient test coverage. Your production test checks 80% of the functionality, but the 20% you skip includes the failure mode customers hit. If you don't test the sleep/wake cycle, you won't catch the firmware bug that corrupts EEPROM after 1,000 transitions.

Test limits too loose. A unit measures 4.65V against a 4.5V to 5.5V spec. It passes, but it's drifting toward the edge. Temperature shifts in the field push it out of range. Your test caught nothing because the limits matched the datasheet, not real-world conditions.

Environmental conditions not tested. Production tests run at 25C on a bench. The product operates at 0C to 50C in a warehouse with vibration. Parameters that look solid at room temperature fall apart under stress.

Add Margin Testing to Your OpenHTF Tests

Margin testing uses tighter limits than the product specification. The idea is simple: if a unit can't pass with headroom, it's more likely to fail in the field.

margin_test.py
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot


@htf.measures(
    htf.Measurement("output_voltage")
    .in_range(minimum=4.8, maximum=5.2)
    .with_units(units.VOLT)
    .doc("Spec is 4.5-5.5V. Margin limits tightened to 4.8-5.2V."),
    htf.Measurement("ripple_voltage")
    .in_range(maximum=20)
    .with_units(units.MILLIVOLT)
    .doc("Spec allows 50mV. Margin limit set to 20mV."),
    htf.Measurement("quiescent_current")
    .in_range(maximum=0.000008)
    .with_units(units.AMPERE)
    .doc("Spec allows 15uA. Margin limit catches leaky units."),
)
def margin_check(test):
    test.measurements.output_voltage = 5.03
    test.measurements.ripple_voltage = 12.4
    test.measurements.quiescent_current = 0.0000051


def main():
    test = htf.Test(margin_check)
    with TofuPilot(test, procedure_name="PCBA Margin Test"):
        test.execute(test_start=lambda: "SN-20260312-042")


if __name__ == "__main__":
    main()

Use the .doc() method to record both the spec limit and your margin limit. This makes it clear to anyone reviewing the data why a unit at 4.75V failed even though the datasheet says 4.5V is fine.

Add Environmental Stress Phases

For products that operate across a temperature range, add stress phases that test at the boundaries. You don't need a full thermal chamber for every unit. Even a brief hot/cold soak on a sample basis catches drift.

stress_test.py
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot


@htf.measures(
    htf.Measurement("voltage_ambient")
    .in_range(minimum=4.8, maximum=5.2)
    .with_units(units.VOLT),
)
def ambient_check(test):
    # Measure at room temperature baseline
    test.measurements.voltage_ambient = 5.01


@htf.measures(
    htf.Measurement("voltage_hot")
    .in_range(minimum=4.7, maximum=5.3)
    .with_units(units.VOLT),
    htf.Measurement("voltage_drift_hot")
    .in_range(maximum=100)
    .with_units(units.MILLIVOLT)
    .doc("Delta between 25C and 50C readings"),
)
def hot_soak_check(test):
    # Measure after thermal soak at upper operating limit
    test.measurements.voltage_hot = 5.08
    test.measurements.voltage_drift_hot = 70


@htf.measures(
    htf.Measurement("voltage_cold")
    .in_range(minimum=4.7, maximum=5.3)
    .with_units(units.VOLT),
    htf.Measurement("voltage_drift_cold")
    .in_range(maximum=100)
    .with_units(units.MILLIVOLT)
    .doc("Delta between 25C and 0C readings"),
)
def cold_soak_check(test):
    # Measure after thermal soak at lower operating limit
    test.measurements.voltage_cold = 4.92
    test.measurements.voltage_drift_cold = 90


def main():
    test = htf.Test(ambient_check, hot_soak_check, cold_soak_check)
    with TofuPilot(test, procedure_name="PCBA Thermal Stress Test"):
        test.execute(test_start=lambda: "SN-20260312-042")


if __name__ == "__main__":
    main()

Recording the drift as a separate measurement is key. A unit might pass both the hot and cold absolute checks but show 95mV of drift, which signals a component that's near its thermal limit.

Correlate Field Failures Back to Production Data

When a unit comes back from the field, look it up by serial number in TofuPilot. The unit history shows every test run, including all measurements recorded during production.

Here's the workflow:

  1. Get the serial number from the returned unit.
  2. Search in TofuPilot to pull up the unit's full test history.
  3. Check the measurements for the failure mode. If the field failure is "intermittent power loss," look at the production voltage and current measurements.
  4. Compare against passing units. Was the returned unit near the edge of a limit? Did it show higher drift than units that survived?

This comparison tells you whether your limits need tightening or your test coverage has gaps.

Identify Marginal Units with Measurement Analytics

TofuPilot's measurement analytics show you the distribution of every measurement across all units. This is where you catch systematic issues before they become field returns.

Measurement histograms reveal bimodal distributions. If your voltage measurement shows two clusters instead of one normal curve, you likely have a component sourcing issue or a process variation. Units in the lower cluster are your field return candidates.

Cpk values tell you how centered your process is within the limits. A Cpk below 1.33 means your process variation is too wide relative to your test limits. Even if every unit passes today, normal variation will push some units out of spec in the field.

Control charts show measurement trends over time. A gradual drift in a measurement across production batches signals a process change. Catching the drift early lets you investigate before marginal units ship.

Tighten Limits Based on Field Data

Once you've correlated field returns with production measurements, update your test limits. The pattern is straightforward:

SituationAction
Returned units clustered near upper limitLower the upper margin limit
Returned units show high thermal driftAdd or tighten drift measurement limit
Failure mode not covered by any measurementAdd a new test phase for that parameter
Returns correlate with a specific production batchInvestigate process change during that period

Don't guess at new limits. Use TofuPilot's measurement distribution data from your passing field population to set limits that exclude the tail where failures concentrate.

Add Coverage for Missing Failure Modes

Field returns often reveal failure modes your test doesn't cover at all. When that happens, add a new phase to your OpenHTF test.

added_coverage.py
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot


@htf.measures(
    htf.Measurement("sleep_wake_cycles_passed")
    .in_range(minimum=100)
    .doc("Added after field returns showed EEPROM corruption after repeated sleep/wake."),
    htf.Measurement("eeprom_checksum_valid")
    .equals(True),
)
def sleep_wake_stress(test):
    # Run 100 sleep/wake cycles and verify EEPROM integrity
    cycles_passed = 100
    checksum_ok = True
    test.measurements.sleep_wake_cycles_passed = cycles_passed
    test.measurements.eeprom_checksum_valid = checksum_ok


@htf.measures(
    htf.Measurement("output_voltage")
    .in_range(minimum=4.8, maximum=5.2)
    .with_units(units.VOLT),
)
def functional_check(test):
    test.measurements.output_voltage = 5.02


def main():
    test = htf.Test(sleep_wake_stress, functional_check)
    with TofuPilot(test, procedure_name="PCBA Functional Test v2"):
        test.execute(test_start=lambda: "SN-20260312-099")


if __name__ == "__main__":
    main()

Document why you added the phase using .doc(). Six months from now, someone will ask why you're running 100 sleep/wake cycles on every unit. The answer is right there in the measurement metadata and visible in TofuPilot's run detail view.

Build a Feedback Loop

Reducing field returns isn't a one-time fix. It's a continuous loop:

  1. Ship units with your current test coverage and limits.
  2. Track returns by serial number.
  3. Correlate return data with production measurements in TofuPilot.
  4. Tighten limits or add test phases based on what you find.
  5. Monitor Cpk and FPY in TofuPilot to confirm the changes reduce returns without over-rejecting good units.

Each iteration makes your test suite more effective. TofuPilot's measurement analytics and unit history give you the data to make each decision evidence-based rather than guesswork.

More Guides

Put this guide into practice