Every measurement in a manufacturing test needs three things: a name, a value, and limits that define pass or fail. OpenHTF provides built-in validators for numeric ranges, exact matches, percentages, and regex patterns. When you log these measurements through TofuPilot, you get FPY, Cpk, and control charts automatically. This guide covers every measurement type with working code.
Measurement Validators
OpenHTF has six built-in validators:
| Validator | Use Case | Example |
|---|---|---|
.in_range(min, max) | Value within bounds | Voltage, current, resistance |
.in_range(minimum=x) | Value above minimum | Signal strength, gain |
.in_range(maximum=x) | Value below maximum | Latency, noise floor |
.equals(value) | Exact match | Firmware version, boolean flags |
.within_percent(target, pct) | Value within percentage of target | Crystal frequency, calibrated sensors |
.matches_regex(pattern) | String matches regex | MAC address, serial number format |
Numeric Range: .in_range()
The most common validator. Pass if the value falls between lower and upper limits.
import openhtf as htf
from openhtf.util import units
@htf.measures(
htf.Measurement("rail_3v3")
.in_range(3.2, 3.4)
.with_units(units.VOLT)
.doc("3.3V supply rail voltage"),
htf.Measurement("rail_5v0")
.in_range(4.8, 5.2)
.with_units(units.VOLT)
.doc("5.0V supply rail voltage"),
htf.Measurement("rail_1v8")
.in_range(1.7, 1.9)
.with_units(units.VOLT)
.doc("1.8V core rail voltage"),
htf.Measurement("idle_current")
.in_range(0.05, 0.25)
.with_units(units.AMPERE)
.doc("Board idle current draw"),
)
def test_power_rails(test):
test.measurements.rail_3v3 = 3.31
test.measurements.rail_5v0 = 5.01
test.measurements.rail_1v8 = 1.82
test.measurements.idle_current = 0.12One-Sided Limits
Use keyword arguments for minimum-only or maximum-only limits.
import openhtf as htf
# Minimum only: pass if value >= -80
@htf.measures(
htf.Measurement("signal_strength")
.in_range(minimum=-80)
.doc("Wi-Fi signal strength in dBm"),
)
def test_signal(test):
test.measurements.signal_strength = -65
# Maximum only: pass if value <= 100
@htf.measures(
htf.Measurement("response_time")
.in_range(maximum=100)
.doc("Response latency in milliseconds"),
)
def test_latency(test):
test.measurements.response_time = 42Exact Match: .equals()
Pass if the value exactly matches. Works with strings, booleans, and numbers.
import openhtf as htf
# String match
@htf.measures(
htf.Measurement("firmware_version")
.equals("2.1.0")
.doc("Expected firmware version string"),
)
def test_firmware(test):
test.measurements.firmware_version = "2.1.0"
# Boolean match
@htf.measures(
htf.Measurement("led_on")
.equals(True)
.doc("Power LED is illuminated"),
htf.Measurement("error_flag")
.equals(False)
.doc("No error flags set"),
)
def test_indicators(test):
test.measurements.led_on = True
test.measurements.error_flag = FalsePercentage Tolerance: .within_percent()
Pass if the value is within a percentage of a target. Useful for calibrated components.
import openhtf as htf
from openhtf.util import units
@htf.measures(
# 8MHz crystal, 1% tolerance -> 7.92MHz to 8.08MHz
htf.Measurement("clock_freq")
.within_percent(8_000_000, 1.0)
.with_units(units.HERTZ)
.doc("8MHz crystal oscillator frequency"),
# 10kOhm resistor, 5% tolerance -> 9.5kOhm to 10.5kOhm
htf.Measurement("pullup_resistance")
.within_percent(10_000, 5.0)
.with_units(units.OHM)
.doc("I2C pullup resistor value"),
)
def test_components(test):
test.measurements.clock_freq = 8_000_100
test.measurements.pullup_resistance = 9_850.within_percent(target, percent) is equivalent to .in_range(target * (1 - pct/100), target * (1 + pct/100)) but clearer in intent.
Regex Match: .matches_regex()
Pass if the string matches a regular expression. Useful for format validation.
import openhtf as htf
@htf.measures(
# MAC address format: AA:BB:CC:DD:EE:FF
htf.Measurement("mac_address")
.matches_regex(r"^([0-9A-F]{2}:){5}[0-9A-F]{2}$")
.doc("Board MAC address format"),
# Semantic version: X.Y.Z
htf.Measurement("fw_version")
.matches_regex(r"^\d+\.\d+\.\d+$")
.doc("Firmware version format"),
)
def test_strings(test):
test.measurements.mac_address = "AA:BB:CC:DD:EE:FF"
test.measurements.fw_version = "2.1.0"Marginal Limits
OpenHTF supports marginal limits (inner limits within the pass range). A measurement between the marginal limit and the spec limit passes but is flagged as marginal. This catches measurements that are drifting toward failure.
import openhtf as htf
from openhtf.util import units
@htf.measures(
htf.Measurement("rail_3v3")
.in_range(
minimum=3.2,
maximum=3.4,
marginal_minimum=3.22,
marginal_maximum=3.38,
)
.with_units(units.VOLT)
.doc("3.3V rail with marginal band"),
)
def test_marginal_voltage(test):
# 3.21V passes but is flagged as marginal (between 3.20 and 3.22)
# 3.25V passes normally (within marginal limits)
# 3.19V fails (below 3.20)
test.measurements.rail_3v3 = 3.21| Value | Status | Explanation |
|---|---|---|
| 3.25 | Pass | Within marginal limits |
| 3.21 | Marginal | Between spec limit (3.20) and marginal limit (3.22) |
| 3.39 | Marginal | Between marginal limit (3.38) and spec limit (3.40) |
| 3.19 | Fail | Below spec limit (3.20) |
| 3.41 | Fail | Above spec limit (3.40) |
Units Reference
OpenHTF provides standard SI units via from openhtf.util import units. Always use .with_units() for numeric measurements. It feeds into TofuPilot analytics and makes reports readable.
| Unit | OpenHTF Constant | Use Case |
|---|---|---|
| Volt | units.VOLT | Voltage rails, analog signals |
| Ampere | units.AMPERE | Current draw |
| Ohm | units.OHM | Resistance, impedance |
| Hertz | units.HERTZ | Frequency, clock signals |
| Celsius | units.DEGREE_CELSIUS | Temperature |
| Second | units.SECOND | Time, latency |
| Watt | units.WATT | Power consumption |
Multiple Measurements Per Phase
Group related measurements in one phase. This keeps the test report organized and the test time efficient.
import openhtf as htf
from openhtf.util import units
@htf.measures(
htf.Measurement("rail_3v3").in_range(3.2, 3.4).with_units(units.VOLT),
htf.Measurement("rail_5v0").in_range(4.8, 5.2).with_units(units.VOLT),
htf.Measurement("idle_current").in_range(0.05, 0.25).with_units(units.AMPERE),
htf.Measurement("led_on").equals(True),
htf.Measurement("firmware").equals("2.1.0"),
)
def test_board_basics(test):
test.measurements.rail_3v3 = 3.31
test.measurements.rail_5v0 = 5.01
test.measurements.idle_current = 0.12
test.measurements.led_on = True
test.measurements.firmware = "2.1.0"Guideline: Group measurements that are logically related (all power rails in one phase, all communication checks in another). Split into separate phases if they require different instruments or have different timeouts.
Setting Limits from Datasheets
Start with the datasheet absolute maximum and minimum ratings. Then tighten based on production data.
| Source | Use For | Example |
|---|---|---|
| Datasheet | Initial limits | 3.3V regulator: 3.135V to 3.465V (5% tolerance) |
| Production data (100+ units) | Refined limits | 3-sigma from measured distribution |
| Customer requirements | Mandatory limits | Specified in test specification |
import openhtf as htf
from openhtf.util import units
# Datasheet says: 3.3V +/- 5% = 3.135V to 3.465V
# Production data shows: mean=3.30, stdev=0.015
# 3-sigma: 3.255 to 3.345 (tighter, use these)
@htf.measures(
htf.Measurement("rail_3v3")
.in_range(3.255, 3.345)
.with_units(units.VOLT)
.doc("3.3V rail (3-sigma from production data)"),
)
def test_refined_limits(test):
test.measurements.rail_3v3 = 3.31