An Introduction to PyVISA#

Interfacing remotely to a measurement instruments can tedious. There are many protocols and interfaces to send data over many different bus systems. The Virtual Instrument Software Architecture (VISA) specification was defined in the middle of the 90s to standardize the different protocols and allow interfacing different devices over different bus systems with a single library, Thus avoiding having to find the correct library for every device in an experiment.

Learning Outcomes#

This notebook will introduce the basics of using PyVISA for interfacing lab instruments. In particular, you will learn how to:

  • Chose a specific backend library

  • Scan for available devices

  • Connect to a device

  • Send commands

  • Query the device identifier

  • Query for data from a device

  • Change communications parameters (termination character, chunk length…)

Requirements#

You need:

  • PyVISA

  • PyVISA-py (a Python implementation of the VISA standard)

  • (optional) NI-VISA or Agilent instrumentation library

Installation#

PyVISA can be installed using the conda conda install pyvisa or pip pip install pyvisa

It also needs an installed backend. It contains a wrapper for NI-VISA or a compatible implementation by default, but we will also be using PyVISA-py a python implementation of the VISA standard, you can install it with: conda install pyvisa-py or pip install pyvisa-py

Test your installation:

import pyvisa as visa
import numpy as np
rm = visa.ResourceManager()
print(rm.list_resources())
()
/home/jschrod/.virtualenvs/jupyterbook2/lib/python3.10/site-packages/pyvisa_py/tcpip.py:121: UserWarning: TCPIP::hislip resource discovery requires the zeroconf package to be installed... try 'pip install zeroconf'
  warnings.warn(

Further Reading#

Configuring the backend#

The backend is chosen when initiating the backend. By default PyVISA will use the NI-VISA backend. Therefore the code

rm = visa.ResourceManager()

Will open the interface to the NI-VISA library. In general PyVISA is very good at finding the location of the library, however it is possible to also specify the path if the library is located in an unusual location rm = visa.ResourceManger("/path/to/library.dll"). It is also possible to specify the path in a configuration file (see the PyVISA documentation for more details and troubleshooting)

Backends#

As far as we are aware Pyvisa works with all backends. The two most common and functionally complete ones are

  • the National Instruments VISA backend (NI-VISA)

  • Keysights IO libraries suite.

There are also:

  • An open source Python backend

  • A simulation backend

Python backend#

Installation of the NI-VISA library (or a compatible implementation) can sometimes be difficult. Particular on Linux and embedded computers. Instead, you can use the pure Python implementation. Be aware that it is not as feature complete as the NI-VISA implementation, in particular drivers for certain GPIB devices (e.g. some GPIB/Ethernet bridges) are missing.

To use the Python backend (if installed) use:

rm = visa.ResourceManager('@py')

Simulation backend#

The simulation backend (pyvisa-sim) is useful for writing drivers to simulated devices if the instrument is not available.This can be useful for testing purposes. Simulation backends and devices are defined in YAML files. See the PyVISA-sim documentation for more information.

To use the pyvisa-sim backend create the resource manager with:

rm = visa.ResourceManager('@sim')

To use a specific simulation file start with:

rm = visa.ResourceManager("thorlabs.yml@sim")

where thorlabs.yml is the YAML specification file.

To find out what backend we are using we can use the visalib.library_path attribute of the resource manager object

print(rm.visalib.library_path)
thorlabs.yml

How to use PyVISA#

PyVISA is an object oriented “pythonic” interface to the VISA library. The two central objects are the Resource manager and Resources.

Resources#

PyVISA resources are essentially the devices (instruments) connected to the different bus systems (GPIB, USB …).

Resource Manger#

The Resource Manager is responsible for listing available device, opening new devices and deciding what resource type a new device should have.

Basic Example#

Let’s look at a basic example which lists several resources and opens the first available resource.

rm = visa.ResourceManager('thorlabs.yml@sim')
l = rm.list_resources()
print(l)
inst1 = rm.open_resource(l[0])
('ASRL1::INSTR', 'USB0::0x1111::0x2222::0x1234::0::INSTR', 'TCPIP0::localhost::inst0::INSTR', 'GPIB0::8::INSTR', 'ASRL2::INSTR', 'USB0::0x1111::0x2222::0x2468::0::INSTR', 'TCPIP0::localhost:2222::inst0::INSTR', 'GPIB0::9::INSTR', 'ASRL3::INSTR', 'USB0::0x1111::0x2222::0x3692::0::INSTR', 'TCPIP0::localhost:3333::inst0::INSTR', 'GPIB0::10::INSTR', 'ASRL4::INSTR', 'USB0::0x1111::0x2222::0x4444::0::INSTR', 'TCPIP0::localhost:4444::inst0::INSTR', 'GPIB0::4::INSTR')
print(inst1)
SerialInstrument at ASRL1::INSTR

This is a device on the first serial port.

Alternatively it is possible to open resources simply by specifying the instrument (see the PyVISA documentation for the syntax for resource names). The following code opens the same device on serial port 1

inst2 = rm.open_resource("ASRL1::INSTR",read_termination = '\n', write_termination="\r\n")
inst2.query("?IDN")
'LSG Serial #1234'

This is a dummy instrument that is created by default by pyvisa-sim.

The following opens up a simulated Thorlabs PM100 optical USB power meter.

inst = rm.open_resource("USB0::0x1111::0x2222::0x1234::0::INSTR", read_termination="\n", write_termination="\n")

How to read and write to instruments#

PyVISA resources provide three basic methods for writing and reading (as well as some more advanced methods to be covered in the later examples).

  • inst.write() is for sending a command to an instrument (e.g. a setting)

  • inst.read() is to read a value from the instrument (e.g. a measurement)

  • inst.query() is a convenient combination of write and read (e.g. to ask for a value and read the value).

The following example uses the SCPI command to ask the connected power meter for its identifier string. Once using write and read and once using query

inst.write("*IDN?")
id1 = inst.read()
id2 = inst.query("*IDN?")
print(id1)
print(id2)
THORLABS,100,123,0.0.0
THORLABS,100,123,0.0.0

Configuration of communication parameters#

Sometimes it is necessary to configure the communication with certain instruments. Two of the most common parameters are:

  • timeout – the time in ms to wait for a response

  • termination character – the character used for indicating the end of a message

These parameters can either be set when opening a resource or by assigning to the attribute

inst3 = rm.open_resource(l[0], timeout=1000, read_termination='\r')
inst3.timeout = 2000

Return Values#

It’s important to note that PyVISA by default does not take care of converting your return values into e.g. numpy arrays automatically. Most instruments return values as text in ASCII format, so conversion is typically straight forward.

# code would look something like this
ret = inst.query("FETCH?")
print(ret)
print(type(ret))
x = float(ret)
print(x)
1.000000e-03
<class 'str'>
0.001

Convenience functions#

If your instrument returns a list of numbers like an OSA trace measurement. Pyvisa offers some convenience functions which can do the conversion automatically for you. For ASCII values instrument.query_ascii_values returns a list of values (in ascii format).

inst2 = rm.open_resource("TCPIP0::localhost:3333::inst0::INSTR", read_termination="\n", write_termination="\n")
values = inst2.query_ascii_values("MEAS?")
print(values)
print(type(values))
print(type(values[0]))
varray = np.array(values) # converts the values into an array
[1.0, 2.0, 3.0, 4.0]
<class 'list'>
<class 'float'>

If you get a large amount of data you might want to avoid the intermediate list by using the container keyword argument.

varray =  inst2.query_ascii_values("MEAS?", container=np.array)
print(varray)
print(type(varray))
[1. 2. 3. 4.]
<class 'numpy.ndarray'>

There are also other options like the converter and separator arguments which allow for more flexible conversions. See the pyvisa documentation for details.

Binary transfer#

Most instruments can transfer data in two formats ASCII and binary. There are advantages and disadvantages to both.

ASCII: human readable, easy to debug, but transfer is much slower

Binary: much faster transfers, more difficult to debug because not easily readable

We found that a good approach is to use ASCII transfer for most cases (in particular when writing new instruments) and only switch to binary format for instruments that transfer a lot of data, like oscilloscopes, spectrum analyzers …

PyVISA does also provide functions for easy conversions from and to binary formats. The naming convention follows the same as the struct module.

values = inst2.query_binary_values('BIN?', datatype='d', is_big_endian=True) # this code does not work here

PyVISA assumes by default that your instrument follows IEEE conventions, but you can pass different header formats with the header_fmt parameter.

Important: Not all instruments add a termination character when sending binary data, which results in errors or truncated blocks because PyVISA expects a termination character by default. Please see the documenation to determine when it is save to pass the expect_termination=False parameter.

Further Reading#

Exercises#