PyVISA
PyVISADocs

SCPI Commands

SCPI (Standard Commands for Programmable Instruments) command reference with Python PyVISA examples, error handling, and best practices.

SCPI programming with PyVISA through examples and command references.

Essential SCPI Commands

System Commands

import pyvisa

rm = pyvisa.ResourceManager()
instrument = rm.open_resource('TCPIP::192.168.1.100::INSTR')

# Identity and system info
idn = instrument.query('*IDN?')  # IEEE 488.2 identification
errors = instrument.query('SYST:ERR?')  # Check for errors
instrument.write('*RST')  # Reset to default state
instrument.write('*CLS')  # Clear status registers

Measurement Commands

# DC voltage measurement
voltage = float(instrument.query('MEAS:VOLT:DC?'))

# AC voltage with range
instrument.write('MEAS:VOLT:AC:RANG 10')
ac_voltage = float(instrument.query('MEAS:VOLT:AC?'))

# Current measurement
current = float(instrument.query('MEAS:CURR:DC?'))

# Resistance measurement  
resistance = float(instrument.query('MEAS:RES?'))

Configuration Commands

# Set measurement range
instrument.write('VOLT:DC:RANG 10')  # 10V range
instrument.write('CURR:DC:RANG 0.1')  # 100mA range

# Set resolution/integration time
instrument.write('VOLT:DC:NPLC 10')  # 10 power line cycles

# Enable/disable auto-ranging
instrument.write('VOLT:DC:RANG:AUTO ON')

Advanced SCPI Patterns

Error Checking

def check_errors(instrument):
    """Check and clear any SCPI errors"""
    errors = []
    while True:
        error = instrument.query('SYST:ERR?')
        if error.startswith('0'):  # No error
            break
        errors.append(error.strip())
    return errors

# Usage
errors = check_errors(instrument)
if errors:
    print(f"SCPI Errors: {errors}")

Synchronization

# Wait for operation to complete
instrument.write('*OPC?')  # Operation complete query
result = instrument.read()  # Returns '1' when done

# Using service request
instrument.write('*ESE 1')  # Enable operation complete
instrument.write('*SRE 32')  # Enable standard event
instrument.write('INIT')  # Start measurement
instrument.wait_for_srq()  # Wait for service request

Data Transfer Optimization

# Binary data transfer (faster than ASCII)
instrument.write('FORM:DATA REAL,32')  # 32-bit floats
instrument.write('TRAC:DATA?')
data = instrument.read_binary_values('f')  # Read as floats

# Multiple measurements
instrument.write('SAMP:COUN 1000')  # 1000 samples
measurements = instrument.query_binary_values('READ?', datatype='f')

Instrument-Specific Commands

Digital Multimeters

# Keysight 34401A/34411A style commands
instrument.write('CONF:VOLT:DC 10,0.0001')  # Range, resolution
instrument.write('TRIG:SOUR IMM')  # Immediate trigger
measurement = float(instrument.query('READ?'))

# Statistics
instrument.write('CALC:FUNC "AVER"')  # Average function
instrument.write('CALC:STAT ON')  # Enable statistics
average = float(instrument.query('CALC:AVER?'))

Oscilloscopes

# Acquisition setup
instrument.write('ACQ:MODE RTIME')  # Real-time mode
instrument.write('ACQ:SRAT 1E9')   # 1 GSa/s sample rate

# Channel configuration
instrument.write('CHAN1:SCAL 0.1')  # 100mV/div
instrument.write('CHAN1:OFFS 0')    # 0V offset

# Trigger setup
instrument.write('TRIG:SOUR CHAN1')  # Trigger source
instrument.write('TRIG:LEV 0.5')     # 0.5V trigger level

# Data acquisition
waveform = instrument.query_binary_values('CURV?', datatype='h')

Signal Generators

# Basic sine wave
instrument.write('SOUR:FUNC SIN')     # Sine wave
instrument.write('SOUR:FREQ 1000')    # 1 kHz
instrument.write('SOUR:VOLT 1')       # 1V amplitude
instrument.write('OUTP ON')           # Enable output

# Modulation
instrument.write('SOUR:AM:STAT ON')   # Enable AM
instrument.write('SOUR:AM:DEPT 50')   # 50% modulation depth
instrument.write('SOUR:AM:FREQ 100')  # 100 Hz modulation

SCPI Data Types and Formats

Numeric Data

# Integer values
instrument.write('SYST:BEEP:FREQ 1000')  # Integer frequency

# Real (floating point) values  
instrument.write('SOUR:VOLT:AMPL 2.5')   # Real amplitude

# Exponential notation
instrument.write('SOUR:FREQ 1.5E3')      # 1500 Hz

Boolean Parameters

# Various boolean representations
instrument.write('OUTP ON')    # ON/OFF
instrument.write('OUTP 1')     # 1/0  
instrument.write('OUTP TRUE')  # TRUE/FALSE

String Parameters

# Quoted strings for special characters
instrument.write('DISP:TEXT "Hello World"')

# Unquoted for simple strings
instrument.write('SOUR:FUNC SIN')

Error Handling Best Practices

Comprehensive Error Checking

class SCPIInstrument:
    def __init__(self, resource_string):
        self.rm = pyvisa.ResourceManager()
        self.instrument = self.rm.open_resource(resource_string)
        
    def write_with_error_check(self, command):
        """Write command and check for errors"""
        self.instrument.write(command)
        errors = self.check_errors()
        if errors:
            raise Exception(f"SCPI Error after '{command}': {errors}")
    
    def query_with_error_check(self, command):
        """Query with error checking"""
        result = self.instrument.query(command)
        errors = self.check_errors()  
        if errors:
            raise Exception(f"SCPI Error after '{command}': {errors}")
        return result
        
    def check_errors(self):
        """Check instrument error queue"""
        errors = []
        try:
            while True:
                error_response = self.instrument.query('SYST:ERR?')
                if error_response.startswith('0'):
                    break
                errors.append(error_response.strip())
        except:
            pass  # Some instruments don't support SYST:ERR?
        return errors

# Usage
instr = SCPIInstrument('TCPIP::192.168.1.100::INSTR')
try:
    instr.write_with_error_check('CONF:VOLT:DC')
    voltage = instr.query_with_error_check('READ?')
except Exception as e:
    print(f"SCPI Error: {e}")

Timeout Management

# Set appropriate timeouts
instrument.timeout = 10000  # 10 seconds for slow measurements

# Context manager for temporary timeout
from contextlib import contextmanager

@contextmanager
def temporary_timeout(instrument, timeout_ms):
    old_timeout = instrument.timeout
    instrument.timeout = timeout_ms
    try:
        yield instrument
    finally:
        instrument.timeout = old_timeout

# Usage for slow measurement
with temporary_timeout(instrument, 60000):  # 60 seconds
    long_measurement = instrument.query('MEAS:VOLT:DC?')

Performance Optimization

Minimizing Commands

# Bad: Multiple separate commands
instrument.write('VOLT:DC:RANG 10')
instrument.write('VOLT:DC:NPLC 1')  
instrument.write('TRIG:SOUR IMM')
instrument.write('SAMP:COUN 100')

# Better: Combine when possible
setup_commands = [
    'VOLT:DC:RANG 10',
    'VOLT:DC:NPLC 1', 
    'TRIG:SOUR IMM',
    'SAMP:COUN 100'
]
for cmd in setup_commands:
    instrument.write(cmd)

# Best: Use semicolon separator if supported
instrument.write('VOLT:DC:RANG 10;NPLC 1;TRIG:SOUR IMM;SAMP:COUN 100')

Binary vs ASCII Data

# ASCII transfer (slower, human readable)
data_ascii = instrument.query('TRAC:DATA? TRAC1')
values = [float(x) for x in data_ascii.split(',')]

# Binary transfer (faster, less bandwidth)
instrument.write('FORM:DATA REAL,32')
data_binary = instrument.query_binary_values('TRAC:DATA? TRAC1', datatype='f')

Common SCPI Error Codes

CodeMeaningCommon Causes
-100Command ErrorSyntax error, unknown command
-101Invalid CharacterIllegal character in command
-102Syntax ErrorMissing parameter, wrong separator
-103Invalid SeparatorWrong use of comma, semicolon
-104Data Type ErrorString when number expected
-108Parameter Not AllowedToo many parameters
-109Missing ParameterRequired parameter missing
-113Undefined HeaderCommand not recognized
-200Execution ErrorCommand recognized but can't execute
-221Settings ConflictParameters conflict with each other
-222Data Out of RangeParameter value outside valid range
-224Illegal Parameter ValueParameter value not allowed

SCPI Command Tree Reference

*CLS                    # Clear status
*ESE <value>           # Standard event status enable  
*ESR?                  # Standard event status register
*IDN?                  # Identification query
*OPC                   # Operation complete
*OPC?                  # Operation complete query
*RST                   # Reset
*SRE <value>           # Service request enable
*STB?                  # Status byte query
*TST?                  # Self test query
*WAI                   # Wait

SYSTEM
  :ERRor?              # Error queue query
  :VERSion?            # SCPI version query
  :BEEPer              
    :FREQuency <freq>  # Beeper frequency
    :STATe <bool>      # Beeper on/off

MEASure
  :VOLTage
    :DC? [@<list>]     # DC voltage measurement
    :AC? [@<list>]     # AC voltage measurement
  :CURRent
    :DC? [@<list>]     # DC current measurement  
    :AC? [@<list>]     # AC current measurement
  :RESistance? [@<list>] # Resistance measurement
  :FREQuency? [@<list>]  # Frequency measurement

CONFigure
  :VOLTage
    :DC [<range>[,<resolution>]] [@<list>]
    :AC [<range>[,<resolution>]] [@<list>]
  :CURRent
    :DC [<range>[,<resolution>]] [@<list>]
    :AC [<range>[,<resolution>]] [@<list>]

TRIGger
  :SOURce <source>     # Trigger source
  :DELay <seconds>     # Trigger delay
  :COUNt <count>       # Trigger count

SAMPle
  :COUNt <count>       # Sample count

READ?                  # Initiate and fetch measurement
INITiate              # Initiate trigger system  
FETCh?                # Fetch last measurement

Next Steps

How is this guide?