VisaDriver#
VISA transport driver for building custom SCPI instrument drivers
VisaDriver is the VISA transport that Nominal’s built-in SCPI instrument drivers sit on top of. It is available as a public part of the library so customers can build their own drivers for VISA-attached instruments without having to wrap pyvisa themselves.
It is intentionally narrow: it opens, closes, and locks a VISA resource and exposes text and raw byte I/O. The caller chooses the command strings.
Note
VISA (Virtual Instrument Software Architecture) is the IVI Foundation standard for talking to instruments over GPIB, USB-TMC, TCP/IP (SOCKET, VXI-11, HiSLIP), and RS-232/RS-485. VisaDriver uses pyvisa under the hood.
VisaDriver addresses GPIB, USB-TMC, TCPIP (SOCKET, VXI-11, HiSLIP), and ASRL (serial) resources. For a comparison against the other transports, see Transports.
To author an instrument driver that composes VisaDriver, see the driver-development section for that category on Custom Instruments: power supplies, electronic loads, multimeters, waveform generators, or DAQ. This page is the transport reference those sections link back to.
Quickstart#
The most common use of VisaDriver is as a transport inside an instrument driver. Here it is on its own, talking to a VISA instrument directly:
from instro.lib.transports import VisaDriver
visa = VisaDriver("USB0::0x2A8D::0x0101::MY12345::INSTR")
visa.open()
try:
identity = visa.query("*IDN?")
print(identity)
finally:
visa.close()
A VisaDriver is configured with either a plain VISA resource string (defaults applied) or a full VisaConfig when you need to override the backend, terminators, timeouts, or serial settings.
Key concepts#
Lifecycle#
VisaDriver follows the standard transport lifecycle: construct, open(), I/O, close(), with both open() and close() idempotent. Two behaviors are specific to VISA:
open()applies the resource configuration. It opens the pyvisaResourceManagerand the resource, then applies terminators, timeouts, and (for ASRL resources) serial settings.The
ResourceManageris shared process-wide. pyvisa caches one per backend across every driver in the process, soclose()closes this driver’s resource and leaves the manager open for other drivers. pyvisa closes it through its ownatexithandler.
Locking and shared ownership work the same for VisaDriver as for any transport.
Terminators#
VISA instruments are line-terminated. VisaDriver applies a configurable read terminator (stripped from incoming text) and write terminator (appended to outgoing text) when the resource is opened.
The defaults are read="\n" and write="\r\n", which works for most SCPI instruments. Override them through TerminatorConfig when an instrument’s programming manual specifies otherwise, as with USB-TMC devices that use "\n" for both directions, or older instruments that expect bare "\r".
Tip
If your first query to a new instrument hangs until the timeout fires, the most likely cause is a terminator mismatch. Check the device’s programming manual for the expected read/write terminators and pass them through VisaConfig(terminator=TerminatorConfig(read=..., write=...)).
Timeouts#
The recv timeout in TimeoutConfig is specified in seconds and is forwarded to pyvisa as the session timeout (converted to milliseconds internally). It controls how long a read or query waits for the instrument to respond before raising. The default is 15 seconds.
The connect and send fields are accepted by VisaConfig for forward compatibility but are not yet wired into per-operation overrides. Leave them at the defaults unless you have a reason to set them.
Serial settings#
When the VISA resource is an ASRL (RS-232 / RS-485) interface, VisaDriver applies SerialConfig (baud rate, data bits, stop bits, parity, and flow control) on open(). For any other interface type (USB-TMC, GPIB, TCPIP, …) the serial config is silently ignored, so you can leave it at the defaults.
Text vs. raw I/O#
VisaDriver exposes both a text path and a raw byte path:
Path |
Methods |
Behavior |
Use for |
|---|---|---|---|
Text |
|
pyvisa applies write/read terminators and decodes responses as strings |
SCPI commands and queries |
Raw bytes |
|
Bytes pass through unchanged on write; reads return |
Binary payloads, like waveform downloads from oscilloscopes, image transfers, and vendor-specific binary blobs |
query_raw is a convenience: it writes a text command (so the write terminator is still applied) and then reads the response as raw bytes. This matches the common SCPI pattern of asking for binary data with a text command like :WAV:DATA?.
Backends#
VisaConfig.visa_backend selects which pyvisa backend handles the resource. Most callers should leave it unset. When unset (None), instro uses the system IVI VISA implementation ("@ivi", e.g. NI-VISA or Keysight IO Libraries) and automatically falls back to the pure-Python "@py" backend when no IVI implementation is installed. Setting visa_backend to any explicit value (such as "@ivi", "@py", or "@sim") uses that backend as-is, with no fallback.
Raw TCP sockets and Nagle’s algorithm#
For raw TCPIP...::SOCKET resources, VisaDriver disables Nagle’s algorithm (TCP_NODELAY) on open(). IVI backends already do this by default, but the pure-Python "@py" backend does not, so on "@py" it would otherwise leave Nagle enabled. With Nagle on, several small back-to-back SCPI writes can be coalesced into one TCP segment, and some instruments’ lightweight LAN firmware resets the connection when that happens. Disabling it makes the "@py" SOCKET path behave like IVI. Set VisaConfig(tcp_nodelay=False) to opt out; it has no effect on non-socket transports.
Configuration#
For terminators, timeouts, or serial settings, pass a VisaConfig instead of a plain resource string:
from instro.lib.transports import (
ControlFlow,
Parity,
SerialConfig,
StopBits,
TerminatorConfig,
TimeoutConfig,
VisaConfig,
VisaDriver,
)
config = VisaConfig(
visa_resource="ASRL/dev/ttyUSB0::INSTR",
serial_config=SerialConfig(
baud_rate=19200,
data_bits=8,
stop_bits=StopBits.ONE,
parity=Parity.NONE,
flow_control=ControlFlow.NONE,
),
terminator=TerminatorConfig(read="\r", write="\r"),
timeout=TimeoutConfig(recv=30),
)
visa = VisaDriver(config)
VisaConfig#
Top-level connection parameters.
Field |
Required |
Default |
Description |
|---|---|---|---|
|
Yes |
VISA resource string, e.g. |
|
|
No |
|
pyvisa backend specifier; unset uses |
|
No |
|
Serial settings, applied only when the resource is an ASRL interface |
|
No |
|
Read and write terminators |
|
No |
|
Operation timeouts |
|
No |
|
Disable Nagle’s algorithm on raw TCP SOCKET connections. No effect on non-socket transports |
TerminatorConfig#
Field |
Required |
Default |
Description |
|---|---|---|---|
|
No |
|
Terminator stripped from incoming text reads |
|
No |
|
Terminator appended to outgoing text writes |
TimeoutConfig#
Operation timeouts in seconds.
Field |
Required |
Default |
Description |
|---|---|---|---|
|
No |
|
Applied as the pyvisa session timeout. Controls how long |
|
No |
|
Reserved for future per-operation overrides, currently not applied |
|
No |
|
Reserved for future per-operation overrides, currently not applied |
SerialConfig#
Serial-line settings, applied when the VISA resource is an ASRL (RS-232/RS-485) interface. Ignored for all other interface types.
Field |
Required |
Default |
Description |
|---|---|---|---|
|
No |
|
Serial baud rate |
|
No |
|
Data bits (5 to 8) |
|
No |
|
|
|
No |
|
|
|
No |
|
|
VISA resource strings#
VisaDriver does not invent its own addressing scheme. The visa_resource string is passed straight through to pyvisa’s ResourceManager.open_resource(). Some commonly used forms:
Interface |
Example resource string |
|---|---|
USB-TMC |
|
GPIB |
|
TCP/IP (VXI-11) |
|
TCP/IP (HiSLIP) |
|
TCP/IP (raw SOCKET) |
|
Serial (Windows) |
|
Serial (Linux/macOS) |
|
Tip
To discover what’s attached, you can use pyvisa’s resource manager directly:
import pyvisa
rm = pyvisa.ResourceManager()
print(rm.list_resources())
Method reference#
Method |
Purpose |
|---|---|
|
Construct a driver from a VISA resource string or a |
|
Property: |
|
Open the resource manager and resource; apply terminator/timeout/serial config. Idempotent. Pass a holder to register shared ownership: returns |
|
Close the resource and resource manager. Idempotent. Bare |
|
Write a text command; the configured write terminator is appended. |
|
Read a single response string up to (and stripping) the read terminator. |
|
Write a command and read the response as text. |
|
Write raw bytes exactly as provided. |
|
Read raw bytes from the instrument; no terminator stripping. |
|
Write a text command, then read the response as raw bytes. |
|
Return the reentrant resource lock for use as a context manager when multiple operations must execute atomically. |
Error handling#
Error |
Cause |
|---|---|
|
A |
|
The underlying VISA call failed: timeout, resource not found, bus error, instrument disconnected, etc. The |
|
The selected VISA backend could not be loaded. Because |
Opening a |
The native GPIB driver is missing. |
Tip
VisaDriver deliberately does not retry, reconnect, or wrap pyvisa errors. Higher-level recovery (retry policies, reconnection on transient failures, escalation to operators) belongs in the instrument driver or application code on top.
Note
SYST:ERR? is a SCPI convention, not a VisaDriver feature. VisaDriver does not poll the instrument’s error queue for you. Whether and how to do so is a per-instrument decision. Most SCPI instruments implement SYST:ERR? and respond with 0,"No error" when nothing is wrong, but some use SYSTEM:ERROR? (TDK Lambda) or a different status mechanism entirely. Consult the programming manual.