The full example-code is reproduced at the bottom of this article.
Performing manual measurements (in bulk) is for suckers and undergraduate students. Thankfully, most lab gear comes with a digital interface like USB or Serial and you can issue commands through these interfaces to control your equipment - the same as if you were pushing buttons on the front panel. Take a power supply for example - you could issue a sequence of commands to:
- set the output voltage to 5V
- set the current limit to 1 Amp
- Enable the output
- read-back the actual current being supplied
Every action that we, the operator, can perform could also be performed by a command. Write a script that sequences these commands in the right order and you have an automated test procedure. Now we're talking! Better still, a script can co-ordinate a test across multiple devices; you can drive a power supply and read an oscilloscope in the same script.
In this article, I'll demonstrate an automated test with a power supply and multimeter. We'll connect the equipment, set voltages, perform measurements and even do some datalogging. You may not have the same gear, but the commands we'll be using are an industry standard - across different brands, the commands are the same or very similar.
Of course, we'll be writing a bit of Python code - If you're unfamiliar with Python, Tim has created loads of tutorials to get you started.
Interfacing options on the rear-panel of a Rigol DP832 Power Supply. It is common for lab-gear to provision physical interfaces which require a license-purchase to unlock. It seems that with Rigol gear, the USB functionality is always included - Nice! In this case to use the LAN port would require purchasing the more feature-rich DP832A model, or individually purchasing a LAN-connectivity license from Rigol.
Contents
Preparation
Install Packages
PyVISA is a Python package that enables you to control your lab-gear independently of the physical interface (e.g. GPIB, RS232, USB, Ethernet).
To install PyVISA for Python 3.6+ run this command:
pip3 install -U pyvisa
We also have to install the NI-VISA backend which the PyVISA package depends on.
Get programming manuals
We need to know what commands our instruments accept. You can generally find the programming manual for a device on the manufacturer's website - look for a documentation section on an item's product page. For example, here's how I found our multimeter's programming manual on Rigol's website
Hello, World
Power your equipment and connect to your computer via USB.
For a quick example, to query the self-identification of a Rigol DP832 power supply connected via USB is as simple as 3 lines of code. Run the Python shell and enter the following:
>>> import pyvisa >>> rm = pyvisa.ResourceManager() >>> rm.list_resources() ('USB0::0x1AB1::0x0E11::DP8C1234567890::INSTR', 'ASRL1::INSTR', 'ASRL10::INSTR') >>> psu = rm.open_resource('USB0::0x1AB1::0x0E11::DP8C1234567890::INSTR') >>> print(psu.query("*IDN?")) RIGOL TECHNOLOGIES,DP832,DP8C1234567890,00.01.16
Line-by-line, we:
- import the package
- create a resource manager
- list the available VISA resources - yours will look different here
- copy the resource ID and open it,
- and finally, query the ID - which returns a string
the list_resources() function is useful for acquiring the USB device ID - we need that for open_resource()
Example Experiment: Automating a Power Supply and Multimeter
Let's create an automated test. In this experiment I'll be driving the output of a Rigol DP832 power supply, (programming manual) and performing measurements with a Rigol DM-3058E bench multimeter (programming manual): It's worth reiterating - while you may have different gear, the philosophy and command structure will be the same.
We'll step through the code section-by-section here, or; skip ahead to the final code.
Step 1: Initialise Multimeter
It's important to set your gear to a known state during initialisation. The following script performs the setup we've already seen for both multimeter and power supply, and includes imports for sleep and os - these will be useful later. All we'll do so far is set the multimeter function to measure DC voltage.
import pyvisa
from time import sleep # for delays
import os # for datalogging rm = pyvisa.ResourceManager() # List all connected resources print("Resources detected\n{}\n".format(rm.list_resources())) supply = rm.open_resource('USB0::0x1AB1::0x0E11::DP1234567890::INSTR') # Put your device IDs here dmm = rm.open_resource('USB0::0x1AB1::0x09C4::DM1234567890::INSTR') # Setup Digital MultiMeter in DC Voltage mode dmm.write(':FUNCtion:VOLTage:DC')
Finding the right command to issue is just a matter of diving into the multimeter's programming manual. The layout and naming is reasonably intuitive with descriptive commands.
I test this script by manually setting my meter to another mode (eg. current) and running the script. If everything is successful the meter should switch back to DC-Voltage mode.
Step 2: Initialise Power Supply
Add the following two lines to initialise the power supply to a known state: APPLy at Channel 1: 0 Volts, 0.2 Amps with the OUTPut off. (from the power supply's programming manual)
# Setup the power supply 0V, 200mA supply.write(':OUTP CH1,OFF') # start OFF - safe :) supply.write(':APPL CH1,0,0.2') # apply 0V, 0.2A
We can test the power supply code by setting setting Channel 1 to some other setpoint and making sure our script updates the setpoint as expected.
Step 3: Increase Voltage, Collect Data
Let's step the output-voltage from 0 to 10V in 0.5V steps. First, we need to enable the Channel 1 output. Then, create a loop to step from 0 to 10. The loop variable will be our voltage setpoint.
We'll use the loop variable inside the supply's APPLy command - since the variable is a number, we pass it through str() to convert it to a string, and concatenate that with the rest of the command.
Measuring voltage with the multimeter is as simple as issuing a query. Notice how query commands end with a question mark. Queries also return strings, so we pass the result through float() to convert to a number - useful if we want to do math with the data later.
The print() command logs the test results to the console.
Once the test is complete (the loop has finished), we reset the power supply to a safe off-state.
Add the following to your script:
# Run the test supply.write(':OUTP CH1,ON') v = 0 while v <= 10.0: # sweep voltage up to 10V supply.write(':APPL CH1,' + str(v) + ',0.2') # Set the voltage sleep(0.5) vMeasured = float( dmm.query(':MEASure:VOLTage:DC?') ) # measure the voltage # Write results to console print("{} {}".format(v, vMeasured)) v += 0.5 # Test complete. Turn supply off and zero the setpoints supply.write(':OUTP CH1,OFF') supply.write(':APPL CH1,0,0')
If everything goes well, your console should log the power-supply setpoint on the left, and the multimeter's measurement on the right.
Nice! We just did an experiment! We have a power supply stepping through some voltage range, and we're using a 5 1/2 digit multimeter to measure the output accuracy. You could copy this raw data into a spreadsheet and plot to your heart's content.
It makes sense to have Python do the work in saving our results to a file though - let's have our script log the results to a file.
Step 4: Datalogging
I've added a time-stamped filepath variable. This will be used to create a file with the current date and time in the format "YYYY-MM-DD_hhmm.csv"
I've added a section in the loop that opens the file and appends measurements. The first time the file is opened, it doesn't exist yet - we can check for this case by checking if the file-size is zero. If so, we can write in a nicely formatted header row to give our data columns some context.
This is the final version of the code
import pyvisa import time from time import sleep import os # Datalogging: create a time-stamped file dateString = time.strftime("%Y-%m-%d_%H%M") filepath = "./" + dateString + ".csv" rm = pyvisa.ResourceManager() # List all connected resources print("Resources detected\n{}\n".format(rm.list_resources())) supply = rm.open_resource('USB0::0x1AB1::0x0E11::DP1234567890::INSTR') # Put your device IDs here dmm = rm.open_resource('USB0::0x1AB1::0x09C4::DM1234567890::INSTR') # Setup Digital MultiMeter in DC Voltage mode dmm.write(':FUNCtion:VOLTage:DC') # Setup the power supply 0V, 200mA supply.write(':OUTP CH1,OFF') # start OFF - safe :) supply.write(':APPL CH1,0,0.2') # apply 0V, 0.2A # Run the test supply.write(':OUTP CH1,ON') v = 0 while v <= 10.0: # sweep voltage up to 10V supply.write(':APPL CH1,' + str(v) + ',0.2') # Set the voltage sleep(0.5) vMeasured = float( dmm.query(':MEASure:VOLTage:DC?') ) # measure the voltage # Write results to console print("{} {}".format(v, vMeasured)) # Write results to a file with open(filepath, "a") as file: if os.stat(filepath).st_size == 0: #if empty file, write a nice header file.write("Setpoint [V], Measured [V]\n") file.write("{:12.2f},{:13.5f}\n".format(v, vMeasured)) # log the data file.close() v += 0.5 # Test complete. Turn supply off and zero the setpoints supply.write(':OUTP CH1,OFF') supply.write(':APPL CH1,0,0')
I've skipped ahead a bit with the nuances of handling files and using the time package. If you're curious, read the docs for os and time.
When you run the script, you'll see a .csv file appear in the same directory. After running the experiment, the contents of the file will look something like this:
How nice! We have a data file with column headers, and constant-width rows make it easy to read. Make the rows constant width by playing with the file.write() formatting. For example: Setpoint [V] is 12 characters long, so to match the data to this width (with two decimal places) the format string is {12.2f}.
Conclusion
It's pretty unlikely that you will have the same instruments to follow this guide exactly - but that's ok. Regardless of what instruments you are working with, the process will be the same: First, establish a connection with some kind of Hello, World; then, grow your script - testing each new function as you go.
This example isn't too long, but it sure covers a lot of ideas: driving test-gear, printing formatted strings, run loops, and perform file operations. While the experiment we performed was rather simple, it doesn't take too much imagination to see how this can scale. There could be a Device Under Test connected between the power supply and multimeter. What's more, you could include more of test equipment; I recently tested a power-supply module with this tower of test gear: two multimeters, a power supply, an electronic load and an oscilloscope - all controlled by a single script! Magic!
In this example, we've been directly writing the low-level commands. If you find yourself doing this a lot, you may want to write a Python class for your instrument, to make coding faster.
As always, if you found this tutorial interesting or helpful, we'd love to hear from you! If you have any questions or uncertainties, start the conversation below! We're full time makers, and here to help.
Happy coding!