How to Add Operator Prompts to Hardware Tests
Many manufacturing tests mix automated measurements with manual steps. The operator needs to load a DUT, flip a switch, perform a visual inspection, or enter a value. OpenHTF handles this through operator prompts that pause the test, display a message, and optionally collect input. This guide covers how to add prompts to your tests and display them through the TofuPilot operator UI.
When to Use Prompts
| Situation | Example |
|---|---|
| DUT loading | "Place the board in the fixture and close the clamp" |
| Visual inspection | "Check for solder bridges on connector J1" |
| Manual measurement | "Read the label and enter the lot code" |
| Physical action | "Press the reset button on the DUT" |
| Go/no-go decision | "Does the LED illuminate green?" |
| Safety check | "Verify the safety cover is closed" |
Prompts keep the operator in the test flow. They see the instruction in the same interface that shows measurements and pass/fail results.
Prerequisites
- Python 3.10+
- OpenHTF installed (
pip install openhtf) - TofuPilot Python SDK installed (
pip install tofupilot)
Step 1: Add a Simple Prompt
The simplest prompt displays a message and waits for the operator to acknowledge it.
import openhtf as htf
from openhtf.plugs import user_input
@htf.plug(prompts=user_input.UserInput)
def phase_load_dut(test, prompts):
"""Wait for the operator to load the DUT."""
prompts.prompt(
"Place the board in the test fixture. "
"Close the clamp and press Enter."
)The test pauses at this phase until the operator responds. In TofuPilot's operator UI, the prompt appears as a card with the message and an acknowledgment button.
Step 2: Collect Text Input
Add text_input=True to collect a value from the operator. The returned string can be stored as a measurement.
@htf.plug(prompts=user_input.UserInput)
@htf.measures(
htf.Measurement("lot_code").with_args(docstring="Operator-entered lot code"),
)
def phase_enter_lot_code(test, prompts):
"""Ask the operator to scan or type the lot code."""
lot = prompts.prompt(
"Scan the lot code barcode on the packaging.",
text_input=True,
)
test.measurements.lot_code = lotStep 3: Use Prompts for Visual Inspection
Combine a prompt with a measurement to record the operator's inspection result.
@htf.plug(prompts=user_input.UserInput)
@htf.measures(
htf.Measurement("visual_result").equals("PASS"),
)
def phase_visual_inspection(test, prompts):
"""Operator checks for cosmetic defects."""
result = prompts.prompt(
"Inspect the board under magnification. "
"Type PASS if no defects found, or FAIL if defects are present.",
text_input=True,
)
test.measurements.visual_result = result.strip().upper()Step 4: Add Prompts Between Automated Phases
Mix prompted and automated phases in the same test. The operator sees a seamless flow: automated measurements run, then a prompt appears, then more automated measurements.
from openhtf.util import units
@htf.measures(
htf.Measurement("supply_voltage_V")
.in_range(minimum=4.9, maximum=5.1)
.with_units(units.VOLT),
)
def phase_power_check(test):
"""Automated: measure supply voltage."""
test.measurements.supply_voltage_V = 5.01
@htf.plug(prompts=user_input.UserInput)
def phase_flip_board(test, prompts):
"""Ask operator to flip the board for bottom-side testing."""
prompts.prompt(
"Flip the board over so the bottom side faces up. "
"Press Enter when ready."
)
@htf.measures(
htf.Measurement("bottom_connector_resistance_mOhm")
.in_range(maximum=100)
.with_units(units.OHM),
)
def phase_bottom_test(test):
"""Automated: measure bottom-side connector resistance."""
test.measurements.bottom_connector_resistance_mOhm = 38.5Step 5: Connect to TofuPilot
Wire all phases together. Prompted phases render in the TofuPilot operator UI automatically. No extra configuration needed.
from tofupilot.openhtf import TofuPilot
test = htf.Test(
phase_load_dut,
phase_enter_lot_code,
phase_power_check,
phase_visual_inspection,
phase_flip_board,
phase_bottom_test,
)
with TofuPilot(test):
test.execute(test_start=lambda: input("Scan serial: "))Best Practices
| Practice | Why |
|---|---|
| Keep prompt text short (1-2 sentences) | Operators scan, they don't read paragraphs |
| Use imperative verbs ("Place", "Press", "Scan") | Clear instructions reduce errors |
| One action per prompt | Don't ask the operator to do three things at once |
| Validate operator input | Use .equals() or .matches_regex() on the measurement |
| Put prompts at natural breaks | Between automated sequences, not in the middle of a measurement |
| Avoid unnecessary prompts | Every prompt adds cycle time. Automate what you can. |