Sensor data from hardware tests is only useful if you can find patterns across thousands of runs. Storing telemetry as structured, indexed measurements is what lets you surface trends before they become production issues.
What Hardware Telemetry Looks Like in Practice
A single hardware test run can produce hundreds of sensor readings: temperature curves, voltage traces, vibration spectra, pressure waveforms. Without a structured system, this data ends up in CSV files on shared drives, impossible to query at scale.
Treat every sensor measurement as a first-class object. Each reading gets a name, unit, limits, and an optional array dimension for time-series or multi-axis data.
Ingesting Sensor Data
OpenHTF Users
OpenHTF measurements flow in automatically. Multi-dimensional arrays (1D waveforms, 2D matrices, ND tensors) are processed without code changes.
telemetry_test.py27 lines
import openhtf as htffrom tofupilot.openhtf import upload@htf.measures( htf.Measurement("temperature_curve").with_dimensions("time_s"), htf.Measurement("vibration_spectrum").with_dimensions("freq_hz"),)def sensor_sweep(test): for t in range(100): test.measurements.temperature_curve[t] = read_thermocouple() for f in range(500): test.measurements.vibration_spectrum[f] = read_accelerometer_fft(f)def main(): test = htf.Test( sensor_sweep, procedure_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", # procedure UUID from the dashboard part_number="DUT-100", ) test.add_output_callbacks(upload()) test.execute(lambda: "DUT-001")if __name__ == "__main__": main()Install it with pip install "tofupilot[openhtf]". The upload() callback reads TOFUPILOT_API_KEY from the environment.
Python SDK Users
If you are not running OpenHTF, create the run directly. Dimensional data goes in as a series of measurements under a phase:
upload_telemetry.py35 lines
import osfrom datetime import datetime, timedelta, timezoneimport numpy as npfrom tofupilot.v2 import TofuPilotstarted = datetime.now(timezone.utc) - timedelta(seconds=30)ended = datetime.now(timezone.utc)waveform = np.sin(np.linspace(0, 2 * np.pi, 1000))ripple_pk_pk = float(waveform.max() - waveform.min())with TofuPilot(api_key=os.getenv("TOFUPILOT_API_KEY")) as client: client.runs.create( procedure_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", serial_number="PSU-2025-0042", part_number="PSU-100", outcome="PASS", started_at=started, ended_at=ended, phases=[{ "name": "Output Ripple", "outcome": "PASS", "started_at": started, "ended_at": ended, "measurements": [{ "name": "Ripple Peak to Peak", "measured_value": ripple_pk_pk, "units": "mV", "validators": [ {"operator": "<=", "expected_value": 50}, ], }], }], )Note what this example does with the waveform: it uploads the extracted scalar, not the thousand raw samples. That is deliberate, and it is the single most useful habit in telemetry work. Store the features you will actually query on (peak-to-peak, rise time, overshoot) as measurements with limits, and keep the full capture as an attachment on failures or a sample. A test record platform is for results; it is not a time-series acquisition store, and uploading every sample from every unit will outgrow its usefulness fast.
Querying Telemetry at Scale
Once ingested, every measurement is queryable. Filter by procedure, unit serial number, date range, or outcome.
| Query | What it shows |
|---|---|
| All runs for a procedure over the last 7 days | Ripple voltage trends across production |
Failed runs with temperature_curve out of limits | Units that exceeded thermal specs |
Measurement distribution for vibration_spectrum | Histogram of vibration amplitudes across the fleet |
Catching Telemetry Drift
Measurement distributions are tracked over time. When a sensor reading starts drifting toward its limits, you can catch it before it causes failures.
The practical approach is to alert on the trend rather than the threshold. By the time a reading crosses its limit, the drift has usually been present for weeks. Watch the mean and the spread separately: a moving mean with a tight spread points to something that shifted, such as a recalibrated instrument or a new component lot, while a stable mean with a widening spread points to something that became inconsistent, such as a worn fixture or temperature variation across a shift.
This matters most for temperature sensors, where gradual calibration drift goes unnoticed until units start failing in the field.
Where This Pays Off
A thermal cycling test across 8 temperature zones produces 8 time-series measurements per run, one per zone. The manual version of this is downloading CSV files from each chamber controller, merging them in a spreadsheet, and checking limits by hand.
With the measurements uploaded per run, the limits are evaluated on ingest and the zone-by-zone history is queryable across the full production record. The gain is not that the analysis becomes possible; it is that it stops depending on someone remembering to do it.
