Migrating from Legacy Systems

Onboard a Test Engineer in One Week

Learn how to take a Python engineer with no test background from CLI install to owning a real station with uploaded runs and an FPY review in five days.

JJulien Buteau
beginner9 min readSeptember 23, 2026

A Python engineer with no manufacturing test background can own a station in one week when the framework provides the sequencer, the limits, the operator UI and the data platform. The five days below take a new hire from installing the CLI to running a real station with --upload and reviewing FPY with their lead. What they learn along the way is the product and the instruments, which is the part no framework can provide.

What the Lead Prepares in Advance

The week stalls on Monday morning if any of these is missing.

  • An account and an API key. The hire needs their own login on the TofuPilot org, and the station needs an API key for uploads. The Lab tier is free.
  • A procedure created in the app. Create it before Monday and note its name. Day 1 links the template to it, Day 5 links the real station.
  • A station with one instrument on the network. A bench DMM or DAQ with a LAN port, a known VISA address and a cable to the DUT. If it only has GPIB or USB, install the vendor VISA driver on the station PC before the hire arrives.
  • A known-good unit and a known-bad one. One unit that passes, one with a documented fault. Day 3 and Day 5 are much shorter with both on the bench.
  • A buddy. The person who runs the station today, booked for an hour a day. Not the lead.
  • The product's must-pass list. The five measurements that decide ship or scrap, with their limits, on one page.

The job description that got you this hire probably already lists what they know. Test Engineer Job Description for Python Hires is the version that matches this week.

The Week, Day by Day

DayTaskDeliverableWhat they learn
1Install the CLI, clone the fct-fixture template, run it with mock plugs, upload one runA run page in TofuPilot with their name on itWhat a procedure, a phase, a plug and a run are
2Replace one mock plug with a real instrument over PyVISAA rail measurement from real hardwareSCPI, VISA addresses, why instruments return strings
3Add a phase with a measurement and limitsA phase that passes on the good unit and fails on the bad oneValidators, measurement keys, how a fail propagates
4Shadow an operator for an hour, then add operator UI to the procedureA checklist and instructions the operator asked forWhat the floor does between two runs
5Run one real station with --upload, review FPY with the leadTwenty uploaded runs and a first yield conversationHow their code becomes the plant's numbers

Day 1: Run the Template

The fct-fixture template is a complete PCBA functional test station with mock instruments. It runs on a laptop with no hardware attached.

day1.sh
# Install the CLI, run the template locally, then link it and upload one runcurl -fsSL https://www.tofupilot.app/install | shgit clone https://github.com/tofupilot/template-framework-fct-fixturecd template-framework-fct-fixturetofupilot run ./procedure.yamltofupilot logintofupilot linktofupilot run ./procedure.yaml --upload

The first tofupilot run needs no account. Have the hire read procedure.yaml top to bottom before the second one: unit, plugs, setup, main, teardown. Then open the uploaded run in TofuPilot and match each phase on the page to its block in the file. That mapping is most of Day 1. The template page explains what each phase is for.

Day 2: Swap a Mock Plug for a Real Instrument

The template's daq plug is a mock that covers rails, GPIO loopback, an AWG and photodiodes. The hire doesn't replace all of it. They subclass the mock and override only the rail read with the bench DMM, so every other phase keeps running on mock data until the real fixture exists.

plugs/bench_daq.py
20 lines
# Day 2: real rail reads over PyVISA; everything else stays mock until the fixture arrivesimport pyvisafrom plugs.daq import FixtureDaqRAIL_CHANNEL = {"3v3": 101, "5v": 102, "1v8": 103}class BenchDaq(FixtureDaq):    def __init__(self, address="TCPIP::192.168.1.100::INSTR"):        super().__init__()        self.inst = pyvisa.ResourceManager("@py").open_resource(address)        self.inst.timeout = 5000    def measure_rail(self, name):        self.inst.write(f"ROUT:CLOS (@{RAIL_CHANNEL[name]})")        return float(self.inst.query(":MEAS:VOLT:DC?"))    def close(self):        self.inst.close()
procedure.yaml
# Point the daq key at the new class; the phases don't changeplugs:  - name: Fixture DAQ    key: daq    python: plugs.bench_daq:BenchDaq    config:      address: "TCPIP::192.168.1.100::INSTR"

Run it again. The three rail measurements now come from the DMM, and the run page in TofuPilot shows real values next to Monday's mock ones. The lesson of Day 2 is the float(): everything VISA returns is a string, and the conversion belongs in the plug, not the phase. The PyVISA docs cover the rest.

Day 3: Add a Phase With a Measurement and Limits

Pick one line from the lead's must-pass list that the template doesn't cover yet. The template checks ripple on the 3V3 rail, so add it on the 5 V rail, after power-on.

procedure.yaml
# Day 3: one new phase, one measurement, two validators, runs after power_onmain:  - name: Rail Ripple    key: rail_ripple    python: phases.rail_ripple    depends_on: [power_on]    measurements:      - name: ripple_5v        unit: mV        validators:          - operator: ">="            expected_value: 0          - operator: "<="            expected_value: 50
phases/rail_ripple.py
# The phase assigns the value; procedure.yaml decides pass or faildef rail_ripple(measurements, daq):    measurements.ripple_5v = daq.measure_ripple_mv("5v")

Run it on the good unit, then on the bad one. Pass, then fail, and the run page shows which validator tripped. Keys matter here: ripple_5v in YAML is measurements.ripple_5v in Python, and the dashboard groups history by that key, so pick it once and don't rename it.

Day 4: Shadow an Operator, Then Add Operator UI

Morning: an hour on the floor with the buddy, watching three units go through. The hire notes what the operator checks by eye, what they do that isn't in the procedure, and what they'd want to see on screen. The operator has known about the loose UART ribbon for a year. Day 4 is when someone writes it down.

Afternoon: declare it. Operator UI in the framework is YAML, not frontend code.

procedure.yaml
22 lines
# Day 4: what the operator asked for, declared in YAML, no Python behind itmain:  - name: Fixture Setup    key: fixture_setup    ui:      requires_input: true      components:        - key: instructions          type: text          label: "Before closing the lid"          default_value: "Seat the board on the four locating pins, then connect the UART ribbon with the red stripe toward the board edge."        - key: setup_checks          type: checklist          label: "Setup checks"          required: true          options:            - label: "Board seated on all four pins"              value: "seated"            - label: "UART ribbon connected, red stripe out"              value: "uart"            - label: "ESD strap on"              value: "esd"

The phase has no Python. The framework shows the text, waits for the checklist and moves on. The hire learns on Day 4 that a good share of yield loss is fixture handling, and that the fix is often a sentence on a screen, not a limit change. Document a Test Station for Hand-Over is where those sentences end up.

Day 5: Own One Station

Morning: the real station, with the real DUT, linked to the production procedure. Twenty units, good and bad mixed in.

day5.sh
# The real station, linked to the production procedure, uploading every runtofupilot link ./stations/fct-01 --procedure "Controller Board FCT"tofupilot run ./stations/fct-01/procedure.yaml --upload

Afternoon: sit down with the lead, open the procedure in TofuPilot and read three things together: FPY, the failure Pareto and the histogram of the Day 3 measurement. Three questions to ask. Which phase fails most, and is that a limit or a fixture? Is the ripple limit too tight for what the histogram shows? What's the Cpk on the rail measurements now that they come from real hardware?

Don't ask the hire to compute FPY. TofuPilot tracks it, and the exercise is reading it and asking why. By Friday afternoon they've written a plug, a phase and an operator screen, uploaded real runs and had one yield conversation. That's ownership. The Python Skills Checklist for Test Engineers is the list to review with them the following Monday.

Why This Is a Week, Not a Quarter

On a LabVIEW station, a new hire spends the first quarter learning the sequencer someone else built, the front panel conventions, the error wire and the report VIs, before they change a single limit. The framework provides those four things, so the five days above skip them entirely.

What's left is the product and the instruments. A Python engineer with no test background typically reaches "owns a station" in weeks, not months, when the sequencer is provided. What moves it is how many instruments the station has, how well the buddy knows the fixture and whether the must-pass list existed before Monday.

The week also leaves something behind that a quarter of LabVIEW onboarding doesn't. The hire's plug, phase and operator screen are text files in a repository, reviewed by the lead in a pull request and visible to the next hire. Onboarding the second engineer takes the same week, and the station never again depends on the first one.

Start With One Station

Pick the station with the highest volume, or the one only one person can open. Rebuild it in Python with the TofuPilot Framework and run it side by side with the LabVIEW version on the same units for two weeks. Compare the two result sets in TofuPilot before you retire anything.

install-and-run.sh
curl -fsSL https://www.tofupilot.app/install | shtofupilot run ./procedure.yaml

The step-by-step is in How to Migrate from LabVIEW to Python, and the framework is at https://tofupilot.com/products/framework. The Lab tier is free, and tofupilot run works with no account.

More Guides

Put this guide into practice