Skip to content

Validated Measurements

Raw SCPI gets the job done, but it puts the burden on you to remember valid ranges, parse response strings, and sequence multi-step operations correctly. pymeasure drivers handle all of that. They validate parameters before commands reach the instrument, parse typed responses, and encode complex SCPI sequences behind simple method calls. This tutorial walks through the difference using a Keithley 2400 source-measure unit.

Before starting this tutorial, make sure you have:

  • Completed the Getting Started and First Measurement tutorials
  • The pymeasure optional dependency installed
  • A supported instrument reachable through a connected backend

Install the pymeasure extra if you have not already:

Terminal window
uv add mcpyvisa[pymeasure]

When pymeasure is installed, four additional tools become available: instrument_inspect, instrument_get, instrument_set, and instrument_call. If you do not see them in your tool list, the dependency is not installed correctly.

The first step with a pymeasure-backed instrument is always instrument_inspect. It tells you what the driver can do — every property, every method, with validation constraints.

This tutorial uses a Keithley 2400 with the alias "smu" configured in mcpyvisa.toml:

[instruments.smu]
resource = "GPIB0::24::INSTR"
backend = "bench-a"

You can also use the full VISA resource string ("GPIB0::24::INSTR") or any other alias you have configured for this instrument.

  1. Inspect the Keithley 2400

    > What can the smu do?

    Claude calls instrument_inspect("smu"):

    pymeasure driver: keithley-2400 (Keithley2400)
    Instrument: smu (GPIB0::24::INSTR on bench-a)
    Use instrument_get/instrument_set/instrument_call for validated access.
    # Keithley2400
    ## Measurements (read-only -- triggers a measurement)
    - `voltage` (SCPI: :MEAS:VOLT?)
    - `current` (SCPI: :MEAS:CURR?)
    - `resistance` (SCPI: :MEAS:RES?)
    ## Controls (read + write -- get/set with validation)
    - `source_voltage` (SCPI: :SOUR:VOLT? / :SOUR:VOLT %g)
    Valid: [-210, 210]
    Validator: truncated_range
    - `source_current` (SCPI: :SOUR:CURR? / :SOUR:CURR %g)
    Valid: [-1.05, 1.05]
    Validator: truncated_range
    - `compliance_voltage` (SCPI: :SENS:VOLT:PROT? / :SENS:VOLT:PROT %g)
    Valid: [-210, 210]
    - `compliance_current` (SCPI: :SENS:CURR:PROT? / :SENS:CURR:PROT %g)
    Valid: [-1.05, 1.05]
    - `source_mode` (SCPI: :SOUR:FUNC:MODE? / :SOUR:FUNC:MODE %s)
    Valid: ['current', 'voltage']
    - `source_enabled` (SCPI: :OUTP? / :OUTP %d)
    ## Methods (high-level operations)
    - `apply_voltage(voltage_range, compliance_current)`
    - `apply_current(current_range, compliance_voltage)`
    - `enable_source()`
    - `disable_source()`
    - `shutdown()`

    Three things to notice in this output.

    Measurements are read-only. Reading voltage triggers an actual measurement on the instrument and returns a float. You cannot write to a measurement property — it would not make sense to “set” a measured voltage.

    Controls are read/write with validation. The source_voltage property accepts values from -210 to 210. The driver checks this range before sending anything over the bus. The truncated_range validator means values outside the range are clamped to the nearest boundary rather than rejected outright.

    Methods are multi-step operations. apply_voltage does not just set a voltage — it configures the source mode, sets the range, sets the compliance limit, and enables the output, all in the correct sequence.

  1. Read the voltage

    > What voltage is the smu measuring?

    Claude calls instrument_get("smu", "voltage"):

    voltage = 3.2941

    That is a typed float, not a raw SCPI string like +3.29410E+00. pymeasure queried :MEAS:VOLT?, parsed the scientific notation response, and returned a number you can reason about directly. No manual parsing needed.

This is where pymeasure earns its keep. Setting parameters through the driver means your values are checked before they reach the instrument.

  1. Set a source voltage

    > Set the smu source voltage to 5 volts

    Claude calls instrument_set("smu", "source_voltage", "5.0"):

    Set source_voltage = 5.0

    The driver checked that 5.0 falls within [-210, 210], then sent :SOUR:VOLT 5.0 to the instrument. Straightforward.

  2. Try an invalid value

    Now suppose someone asks for a source voltage that the instrument cannot produce:

    > Set the source voltage to 999 volts

    Claude calls instrument_set("smu", "source_voltage", "999"):

    Error: Validation error for 'source_voltage' = '999':
    999 is out of the range [-210, 210]

    The command never reached the Keithley. The driver rejected 999 because it exceeds the instrument’s output range. The bus stayed silent. The instrument is unharmed.

    This matters more than it might seem. A typo in a raw instrument_write command — say, :SOUR:VOLT 999 — would be sent directly to the bus. Some instruments clamp silently. Others throw an error you might not notice until something smells hot. The pymeasure driver catches these mistakes at the software layer, where they are cheap to fix.

Individual property sets are fine for tweaking one parameter. But configuring a full source-measure setup from scratch takes several steps — source mode, range, compliance, output enable — and the order matters. That is what methods are for.

  1. Apply a voltage with compliance

    > Configure the smu to source 5V with 100mA compliance

    Claude calls instrument_call("smu", "apply_voltage", "{\"voltage\": 5.0, \"compliance_current\": 0.1}"):

    Called apply_voltage(voltage=5.0, compliance_current=0.1)

    Behind that single call, the pymeasure driver sent a sequence of SCPI commands:

    • :SOUR:FUNC VOLT — set source mode to voltage
    • :SOUR:VOLT:RANG 5.0 — select the appropriate range
    • :SENS:CURR:PROT 0.1 — set compliance current to 100mA
    • :SOUR:VOLT 5.0 — set the voltage level
    • :OUTP ON — enable the output

    Getting that sequence right manually with instrument_write calls is doable, but getting it wrong is easy — set the voltage before selecting voltage mode and the instrument ignores you silently. The method encodes the correct order.

  2. Read the resulting current

    With the source active, measure what the DUT is drawing:

    > What current is the smu drawing?

    Claude calls instrument_get("smu", "current"):

    current = 0.04782

    47.82 mA. Below the 100mA compliance limit, so the Keithley is regulating voltage as requested.

Source-measure units have outputs that stay active until you explicitly turn them off. Always shut down when you are done.

  1. Shut down the output

    > Shut down the smu output

    Claude calls instrument_call("smu", "shutdown"):

    Called shutdown()

    The shutdown method disables the source output and resets the instrument to a safe state. The output relay clicks open and the front panel shows the output is off.

  2. Return to local control

    > Return the smu to front panel control

    Claude calls instrument_local("smu"):

    smu returned to local control

    The RMT indicator on the Keithley’s display turns off. The front panel knobs and buttons work again.

Both approaches talk to the same instrument over the same bus. The difference is where the intelligence lives.

Raw SCPIpymeasure
Available forAny SCPI instrument on any backend8 supported instruments
Parameter checkingNone — commands go straight to the busValidated before sending
Response formatRaw string (+3.29410E+00)Typed Python value (3.2941)
Multi-step operationsManual — you sequence the commandsEncoded in methods
When to useUnsupported instruments, unusual commands, diagnosticsDay-to-day operation of supported instruments

The pymeasure-guided prompt can help you decide. It inspects the instrument first, and if a pymeasure driver exists, it steers toward the validated tools. If not, it falls back to raw SCPI automatically.

> Use the pymeasure-guided prompt for the smu

This triggers a guided workflow that walks through inspection, configuration, measurement, and cleanup in sequence.

  • Prompts Reference — all guided workflows, including pymeasure-guided for driver-assisted measurements
  • Universal Tools Reference — full details on query, write, identify, and reset
  • Architecture — how pymeasure, pyvisa, and the async backend system work together under the hood