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 htf


class 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.120

SKU-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 Path
import yaml


def 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 htf
from openhtf.util import units


def 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_check


def 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 htf
from tofupilot.openhtf import TofuPilot

from station.config.loader import load_sku_config
from station.phases.factory import make_voltage_phase, make_current_phase

SKU_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