Pymeasure Instruments#
Publish the commands you send to a pymeasure instrument, and the values you read from it, through any instro publisher. Your drivers don’t change.
You can use it in two ways:
attach()adds publishing to pymeasure code you already have.PymeasureInstrumentwraps an instrument in new instro code, with tags and background polling.
Setting a property publishes a Command on <name>.<channel>.cmd, and reading one publishes a Measurement on <name>.<channel>. That includes access inside driver methods such as apply_voltage(). Channels take the property’s name unless you set your own. The example notebook runs all of this against simulated instruments.
Install#
uv add "instro[pymeasure]"
Publish from existing code#
attach() adds publishing to an instrument you already use, with any list of instro publishers. In a pymeasure Procedure, attach in startup() and close the recorder in shutdown().
from pymeasure.instruments.keithley import Keithley2400
from instro.lib import NominalCorePublisher
from instro.pymeasure import attach
keithley = Keithley2400("GPIB::24")
recorder = attach(keithley, "smu", [NominalCorePublisher(dataset_rid="<dataset rid>")])
keithley.apply_voltage() # existing code, unchanged
keithley.source_voltage = 1.0 # publishes smu.source_voltage.cmd
print(keithley.current) # publishes smu.current
recorder.close() # flushes and closes the publishers; the instrument stays connected
Write new instro code#
PymeasureInstrument is an instro Instrument, so it also has tags, the background daemon and get_channel().
from pymeasure.instruments.keysight import KeysightE36312A
from instro.pymeasure import PymeasureInstrument
with PymeasureInstrument(
name="psu",
instrument=KeysightE36312A("USB0::0x2A8D::0x1102::MY12345678::INSTR"),
dataset_rid="<dataset rid>",
poll=["ch_1.voltage", "ch_1.current"], # read on every background_interval
) as psu:
psu.device.ch_1.voltage_setpoint = 5 # .device is the pymeasure instrument
psu.start()
print(psu.get_channel("ch1.current", wait_for_new_samples=True).latest)
close() closes the instrument’s connection. It doesn’t call the driver’s shutdown(), which on many drivers turns outputs off, so call psu.device.shutdown() first if you want that. Pass close_device=False to keep the connection open.
Channel names#
Every property the driver defines is published under its attribute path.
pymeasure |
Published as |
|---|---|
|
|
|
|
|
|
a string or mapped value |
a string channel |
a list of up to 16 values ( |
|
instro skips properties inherited from pymeasure’s base classes (id, status, next_error) and arrays longer than 16 values, such as traces. It logs a warning the first time it skips an array.
To choose the names, generate a channel map, edit it, and pass it as channel_map=:
instro pymeasure map keysight.KeysightE36312A -o psu_map.json
{
"format": "instro-pymeasure-channel-map/1",
"include_unmapped": false,
"channels": {
"ch_1.voltage_setpoint": {"channel": "out1.v_set", "kind": "control", "get": "VOLT? (@{ch})", "set": "VOLT %g, (@{ch})"},
"ch_2.voltage_setpoint": {"channel": null, "kind": "control", "get": "VOLT? (@{ch})", "set": "VOLT %g, (@{ch})"}
}
}
attach(supply, "psu", publishers, channel_map="psu_map.json")
Change an entry’s channel to rename it, or set it to null to stop publishing it. Properties missing from the map aren’t published, unless you set "include_unmapped": true. instro rejects a map when you attach if it names a property the instrument doesn’t have, gives two properties one name, or uses a name ending in .cmd.
Some drivers add channels in __init__ instead of with ChannelCreator. For those, add --resource <VISA resource> to the command, or call ChannelMap.generate() with the connected instrument. The notebook below builds a map in Python. See CLI for the commands.
Failures and cleanup#
A set or read that raises publishes nothing.
A publisher that raises is logged. The set or read still completes, because the instrument has already acted.
close()ordetach()stops background polling and restores the original classes.
See the API reference for every option.