Skip to content
Test Station Setup

Manage Multiple SKUs on a Test Station

Learn how to run multiple product SKUs on a single test station using config-driven phase selection, SKU-specific limits, and per-SKU yield tracking.

JJulien Buteau
intermediate10 min readMarch 14, 2026

One test station, multiple products: configure your OpenHTF test to detect the SKU at runtime, load SKU-specific limits and phases from a config file, and let TofuPilot track yield per part number automatically.

Why Multi-SKU Stations Matter

Running separate stations per SKU wastes floor space and fixtures. A single station that handles a product family reduces:

  • Fixture count (shared bed-of-nails or probe card)
  • Operator training (one workflow, not five)
  • Maintenance surface (one station to calibrate)

SKU Detection Methods

MethodWhen to UseReliability
Barcode scanOperator-scanned label, no electrical contact neededHigh
Resistor IDPCB has a resistor divider encoding SKUMedium
I2C EEPROMPCB stores part number in onboard memoryHigh

Barcode Scan

station/plugs/barcode_plug.py
import openhtf as htfclass BarcodePlug(htf.plugs.BasePlug):    """Reads SKU from operator barcode scan."""    def setUp(self):        pass    def scan(self) -> str:        raw = input("Scan barcode: ").strip()        if not raw:            raise ValueError("Empty barcode scan")        return raw    def tearDown(self):        pass

Resistor ID

station/plugs/resistor_id_plug.py
import openhtf as htf_VOLTAGE_MAP = {    (0.0, 0.4): "SKU-A",    (0.4, 0.8): "SKU-B",    (0.8, 1.2): "SKU-C",    (1.2, 1.6): "SKU-D",}class ResistorIdPlug(htf.plugs.BasePlug):    """Identifies SKU from resistor divider voltage."""    def setUp(self):        pass    def read_sku(self, adc_voltage: float) -> str:        for (low, high), sku in _VOLTAGE_MAP.items():            if low <= adc_voltage < high:                return sku        raise ValueError(f"Unrecognised voltage: {adc_voltage:.3f} V")    def tearDown(self):        pass

Configuration-Driven Test Selection

Store SKU definitions in a YAML file. This separates limits from test logic.

station/config/skus.yaml
SKU-A:  part_number: "PCB-001-A"  description: "Standard 5 V variant"  phases:    - power_on    - voltage_check    - current_check  limits:    supply_voltage:      min: 4.85      max: 5.15    supply_current:      min: 0.080      max: 0.120SKU-B:  part_number: "PCB-001-B"  description: "Low-power 3.3 V variant"  phases:    - power_on    - voltage_check    - current_check    - sleep_current_check  limits:    supply_voltage:      min: 3.2      max: 3.4    supply_current:      min: 0.030      max: 0.060    sleep_current:      min: 0      max: 0.000050

Load the config at station startup:

station/config/loader.py
from pathlib import Pathimport yamldef load_sku_config(path: str = "station/config/skus.yaml") -> dict:    config_path = Path(path)    if not config_path.exists():        raise FileNotFoundError(f"SKU config not found: {config_path}")    with config_path.open() as f:        return yaml.safe_load(f)

SKU-Specific Measurement Limits with OpenHTF

Use a phase factory to bake SKU-specific limits into OpenHTF measurements:

station/phases/factory.py
import openhtf as htffrom openhtf.util import unitsdef make_voltage_phase(min_v: float, max_v: float):    """Returns a voltage phase with baked-in limits."""    @htf.measures(        htf.Measurement("supply_voltage")        .in_range(minimum=min_v, maximum=max_v)        .with_units(units.VOLT),    )    def voltage_check(test):        test.measurements.supply_voltage = read_voltage()    return voltage_checkdef make_current_phase(min_a: float, max_a: float):    """Returns a current phase with baked-in limits."""    @htf.measures(        htf.Measurement("supply_current")        .in_range(minimum=min_a, maximum=max_a)        .with_units(units.AMPERE),    )    def current_check(test):        test.measurements.supply_current = read_current()    return current_check

Full Working Example

station/main.py
import openhtf as htffrom tofupilot.openhtf import TofuPilotfrom station.config.loader import load_sku_configfrom station.phases.factory import make_voltage_phase, make_current_phaseSKU_CONFIG = load_sku_config()def power_on(test):    """Powers on the DUT and waits for it to stabilise."""    import time    time.sleep(0.5)def run_station():    # 1. Detect SKU    sku_id = input("Scan barcode: ").strip()    if sku_id not in SKU_CONFIG:        raise ValueError(f"Unknown SKU '{sku_id}'. Available: {list(SKU_CONFIG)}")    sku = SKU_CONFIG[sku_id]    limits = sku["limits"]    part_number = sku["part_number"]    # 2. Compose phases from config    phases = [power_on]    if "supply_voltage" in limits:        lim = limits["supply_voltage"]        phases.append(make_voltage_phase(lim["min"], lim["max"]))    if "supply_current" in limits:        lim = limits["supply_current"]        phases.append(make_current_phase(lim["min"], lim["max"]))    # 3. Build and run test    test = htf.Test(*phases, test_name=f"Station ({sku_id})")    with TofuPilot(test):        test.execute(test_start=lambda: input("Scan serial number: "))if __name__ == "__main__":    run_station()

Tracking Per-SKU Yield in TofuPilot

TofuPilot groups yield statistics by part number. Each SKU gets its own yield chart without extra configuration.

Part NumberSKURunsPass Rate
PCB-001-ASKU-A24097.5%
PCB-001-BSKU-B18094.4%

Filter by part number in the TofuPilot dashboard to compare SKU trends side by side.

More Guides

Put this guide into practice