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
Code | Meaning | Common Causes |
---|---|---|
-100 | Command Error | Syntax error, unknown command |
-101 | Invalid Character | Illegal character in command |
-102 | Syntax Error | Missing parameter, wrong separator |
-103 | Invalid Separator | Wrong use of comma, semicolon |
-104 | Data Type Error | String when number expected |
-108 | Parameter Not Allowed | Too many parameters |
-109 | Missing Parameter | Required parameter missing |
-113 | Undefined Header | Command not recognized |
-200 | Execution Error | Command recognized but can't execute |
-221 | Settings Conflict | Parameters conflict with each other |
-222 | Data Out of Range | Parameter value outside valid range |
-224 | Illegal Parameter Value | Parameter 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
- Advanced topics: Performance Optimization
- Troubleshooting: Connection Issues
- Instrument examples: Multimeter Example
How is this guide?