Test Equipment Automation

Test Equipment Automation

Written by: Danielle Thurow, Sr. Firmware Engineer

Testing embedded systems is always a difficult issue. Testing on the actual hardware is critical for seeing what the real world product will do, but a lot of times engineers will only have one or two development boards while the hardware is being developed. This makes it difficult for multiple engineers to use especially if they’re working remotely. One way of dealing with this is setting up Hardware In the Loop (HIL) setups. Connecting up to a Raspberry Pi or other shared computer with the actual hardware lets multiple people test with the device, regardless of where they are. Testing with the HIL can also be incorporated into a continuous integration process to make development more robust.

Just connecting development hardware via a programmer or serial cable can only get you so far when testing, though. The issue comes when you want to take measurements with your scope, input a specific waveform, or all those other things you normally would do in person using test equipment. As it turns out, there’s quite a few options to automate or control them via code, without having to break the bank to do it. Let’s dive in.

From the Ground Up

There’s testing-equipment-specific standards, implementations, and protocols all layered on top of each other. To get a clearer view of what you need, we’ll start at the physical layer and work our way up.

Physical Layer

How we physically connect to test equipment has changed over time, resulting in multiple ways to connect depending on the age of the equipment you’re using.

GPIB 

GPIB is the most classic and oldest connection still in use. It was created by HP back in the 70s and just hasn’t left. It eventually became the de facto standard, and was then standardized with IEEE 488.1. It was even used in non-test equipment, such as the commodore PET. The downside for this is that the connectors themselves can be very expensive (a new GPIB connector on DigiKey starts at $1000), and GPIB has been slowly phasing out. The standard also expects you to have pretty short cables, which can affect your test setup. There are a number of open source implementations that are cheaper, or you can buy ones on ebay to get around the high initial price. It’s good to know about, but unless your test equipment only has GPIB connections, you’ll probably want to use the other connectors. 

USB

USB connectors have gotten pretty popular on a lot of more modern test equipment. Test equipment, not surprisingly, uses a test equipment specific standard, called USB Test and Measurement Class Specification (USBTMC) for driver support. As long as that’s implemented, it makes test equipment very plug and play. And for a HIL setup, if you’re running on a Raspberry Pi or other small computing device, you most likely have quite a few USB connections out of the box, making it easy to set up and test several pieces of equipment at once.

One downside for USB is that, while it has a relatively low failure rate, it does have a larger failure rate than ethernet (mentioned next). So for small setups, USB is great, but if you’re starting to scale up, you may want to look into ethernet.

Ethernet 

Ethernet has also gotten pretty popular on modern test equipment. It has a lower failure rate than USB, and naturally allows a lot of test equipment devices to connect together. If you have an option on which physical connections to use, Ethernet is probably your best bet, followed by USB. 

Communication Protocols and Standards

Alright, things are plugged in. Are you going to have to talk to each device individually using custom drivers? Thankfully, no. Instead, you’ll get to use: 

VISA

VISA stands for “Virtual Instrument Software Architecture”, and is a standard way of talking to testing equipment via the above listed physical communication layers. There are multiple implementations of the VISA standard, but the most popular are National Instruments’s and Keysight’s library implementations. Note that while the VISA specification has explicit bindings to Visual Basic, C, and G (LabVIEW’s graphical language), it doesn’t list other language options. However, a python-only implementation also exists, pyvisa-py, and supports most VISA functionality, but as a bonus, is purely python, so it isn’t as OS-specific as the other libraries.

These libraries let you interface with test equipment with different physical connections. 

And how do you interact with one of these libraries? By using:

Pyvisa

Pyvisa is a python front-end that lets you write consistent python code regardless of which VISA library you’re using (NI-VISA, Keysight, or pyvisa-py). This lets you interact with test equipment via python, giving you easy-to-write code and access to all the many other python libraries available out there. So for example, if you want to interact with your test equipment in your pytest tests, you can simply do some imports and it will auto-magically work!

Sending Commands

So you’ve physically connected your devices and can talk to them with VISA. Now what? Well you need to start sending some commands. The commands are all sent as ascii strings and interpreted by the test equipment. The two standards for these commands are:

IEEE 488.2 

Remember the GPIB standard, the IEEE 488.1? IEEE followed that up with the 488.2, a set of codes and formats to be used by devices using GPIB. However, that standard was quickly adopted by everyone interacting with test equipment. The standard defines how to send data to a device regardless of underlying physical layer, and includes commands asking about things like device ID, serial number, and other universal commands. They are denoted by an asterisk at the start of the command (e.g. *IDN? when querying for the instrument type and software version). 

SCPI

Pronounced “skippy”, SCPI are vendor-defined commands that are less standardized than the IEEE 488.2 commands, though normally commands are standardized across a particular vendor and equipment type. For example, most Keysight oscilloscopes will implement the same SCPI commands for all shared functionality. Otherwise, though, you’ll have to get your particular test equipment’s programming guide from the manufacturer in order to see what commands are supported. These commands are what let you actually control your test equipment by setting input/output, configuring scope setup, and so on. As an example, the Siglent SDS804x, an entry level 4-channel scope, has a programming guide here that lists the supported SCPI and 488.2 commands. 

SCPI commands are normally shown as mixed case (e.g. the :PRINt? Command for the SDS804x). The lower case letters are optional. If a command has a question mark at the end, it’s considered a query, so the command returns info of some sort (so the :PRINt? Command returns the screenshot file). For more info, check out the programming guide linked above, or dive even deeper and read the SCPI standard put out by the IVI foundation.

Example

Putting all this info together, let’s look through an example of taking a screenshot of a scope’s screen during a test. We’ll use the USBTMC connection on the scope, use pyvisa with the pyvisa-py backend, and use the scope’s programming guide to determine which SCPI commands to send. Look at that, you can now understand what that sentence means!

Overview

Hardware needed: Siglent SDS804x HD connected via a USB-A to USB-B cable. A Mac, Windows, or Linux computer to connect to. 

NOTE: many scopes have this functionality, so you don’t have to use this particular scope, but if you use a different one, you’ll have to check your programming guide to find out the SCPI command to take the screenshot.

Software: Pyvisa, pyvisa-py, and pytest

Setting up Python

To keep things self-contained, you can use poetry to control the python environment. Poetry is a dependency management and package tool for python. It has a nice interface to python virtual environments, keeping dependencies in their own sandbox. It also has some good dependency resolution, making it easy to work with. Here’s the pyproject.toml file I used:

[project]
name = "pyvisa-test"
version = "0.1.0"
description = ""
authors = [
	{name = "Danielle Thurow",email = "danielle.thurow@dojofive.com"}
]
readme = "README.md"
requires-python = ">=3.11,<4.0"
dependencies = [
	"pyvisa (>=1.15.0,<2.0.0)",
	"pyvisa-py (>=0.8.0,<0.9.0)",
	"pyusb (>=1.3.1,<2.0.0)",
	"pyserial (>=3.5,<4.0)",
	"psutil (>=7.0.0,<8.0.0)",
	"zeroconf (>=0.146.5,<0.147.0)",
	"pytest (>=8.3.5,<9.0.0)"
]

[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
package-mode = false

Otherwise, you can use pip to install: pyvisa, pyvisa-py, pyusb, pytest yourself. Depending on what connection you have with your test equipment, you’ll also want to install pyusb, psutil and zeroconf. Note also that you don’t have to use pytest, you can just directly write scripts with pyvisa and pyvisa-py.

To verify it’s installed correctly, you can run the pyvisa-info command that’s bundled with pyvisa, and see what connection types are available. Below is an example output from my machine. Note I did install the NI-VISA library as well, so that shows up under the /Library/Frameworks/visa.framework/visa section below.

> poetry run pyvisa-info
Machine Details:
   Platform ID:	macOS-15.4.1-x86_64-i386-64bit
   Processor:  	i386

Python:
   Implementation: CPython
   Executable: 	/Users/daniellethurow/Library/Caches/pypoetry/virtualenvs/pyvisa-test-kCBfrbbG-py3.11/bin/python
   Version:    	3.11.11
   Compiler:   	Clang 16.0.0 (clang-1600.0.26.6)
   Architecture:   ('x86', 64)
   Build:      	Jan 28 2025 11:59:23 (#main)
   Unicode:    	UCS4

PyVISA Version: 1.15.0

Backends:
   ivi:
  	Version: 1.15.0 (bundled with PyVISA)
  	#1: /Library/Frameworks/visa.framework/visa:
     	found by: auto
     	architecture:
     	Vendor: National Instruments
     	Impl. Version: 24118528
     	Spec. Version: 7340032
   py:
  	Version: 0.8.0
  	ASRL INSTR: Available via PySerial (3.5)
  	USB INSTR: Available via PyUSB (1.3.1). Backend: libusb1
  	USB RAW: Available via PyUSB (1.3.1). Backend: libusb1
  	TCPIP INSTR: Available
     	Resource discovery:
     	- VXI-11: ok
     	- hislip: ok
  	TCPIP SOCKET: Available
  	PRLGX_TCPIP INTFC: Available
  	PRLGX_ASRL INTFC: Available via PySerial (3.5)
  	GPIB INSTR: Available
  	VICP INSTR:
     	Please install PyVICP to use this resource type.
  	GPIB INTFC:
     	Please install linux-gpib (Linux) or gpib-ctypes (Windows, Linux) to use this resource type. Note that installing gpib-ctypes will give you access to a broader range of functionalities.
     	No module named 'gpib'

How to get your device ID string

To get pyvisa to connect to your scope, you need to give it the unique device string that identifies it. The most straightforward way is to use pyvisa to list available resources. You can then unplug/plug in your device to see which string it is. Below is an example on my machine with the scope unplugged and then plugged in:

> poetry run python3
Python 3.11.11 (main, Jan 28 2025, 11:59:23) [Clang 16.0.0 (clang-1600.0.26.6)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyvisa
>>> rm = pyvisa.ResourceManager()
>>> print(rm.list_resources())
('ASRL1::INSTR', 'ASRL2::INSTR')
>>> print(rm.list_resources())
('ASRL1::INSTR', 'ASRL2::INSTR', 'USB0::0xF4EC::0x1017::SDS08A0D812697::INSTR')
>>>

This shows that my scope’s at ‘USB0::0xF4EC::0x1017::SDS08A0D812697::INSTR’

Running a test

Now that I know what device string I have, and I have my programming guide, I can write up a simple pytest script to take a screenshot of my scope’s screen and save it in a file called “screenshot.bmp”. Here’s the test_example.py file:

import pyvisa
import os


def test_scope_screenshot():


   try:
       rm = pyvisa.ResourceManager('@py')


       my_file_name = './screenshot.bmp'
       print(my_file_name)
       my_file = open(my_file_name, 'wb')


       # TODO: replace string with your instrument string
       inst = rm.open_resource('USB0::62700::4119::SDS08A0D812697::0::INSTR')
       print("opened")


       # print the screen as a bmp file
       # TODO: replace below SCPI command with one supported by your scope
       inst.write("PRIN? BMP")
       print("sent")
       result_str = inst.read_raw()
       # get all chunks of data and send it into my file
       while (len(result_str) == inst.chunk_size):
           my_file.write(result_str)
           my_file.flush()
           result_str = inst.read_raw()


       # output the last chunk that's < chunk_size
       my_file.write(result_str)
       my_file.flush()
       my_file.close()
   except Exception as err:
       print('Exception: ' + str(err))
       assert(False)

I can then run this by running poetry run pytest (or just pytest if you’re not using poetry) and I get the below image as screenshot.bmp.

This isn’t a very helpful screenshot right now, since my scope isn’t measuring anything. However, if I decided to connect up a channel to a device outputting a waveform, it’s much more useful. Below I’m measuring the output of the P0.28 pin on a nrf5340 dev kit, which I’m using to flash an LED on and off.

This gives an actually useful use case: If you have a device-under-test (DUT) that has a waveform that’s not necessarily easy to test automatically (say there’s a range of acceptable waveforms), then taking a screenshot during a test and storing it to look at later could be helpful!

Going Further

Now that you have an initial setup and understanding of test equipment automation, there’s a lot of areas you can dive into. You’ll be able to interact with scopes, DMMs, signal generators, power supplies, and more, all using the same interface, just with different SCPI commands.

Ready to Level Up Your Firmware Team?

The embedded world is changing, and the gap between those who adopt CI/CD and those who don’t is growing. If you are ready to streamline your firmware workflow, accelerate delivery, and reduce risk, we’re here to help. Book a call with Dojo Five to get the conversation started! Or, to start increasing your team’s productivity by using EmbedOps, our free DevOps and CI/CD platform for Device Teams.

References:

GPIB: https://helpfiles.keysight.com/csg/e5080a/programming/learning_about_gpib/gp-ib_fundamentals.htm 

SCPI protocol:  https://www.ivifoundation.org/About-IVI/scpi.html 

Pyvisa docs: https://pyvisa.readthedocs.io/en/latest/ 

Pyvisa repo: https://github.com/pyvisa/pyvisa 

Pytest getting started: https://docs.pytest.org/en/stable/getting-started.html#get-started 

Example repos using pyvisa

Other ways of taking screenshots with pyvisa, for Rigol scopes: https://github.com/rdpoor/grab-o-scope/tree/main 

github repo for controlling power supply with SCPI: https://github.com/circuit-specialists/PowerSupply_ElectronicLoad_Control

Discover why Dojo Five EmbedOps is the embedded enterprise choice for build tool and test management.

Sign up to receive a free account to the EmbedOps platform and start building with confidence..

  • Connect a repo
  • Use Dev Containers with your Continuous Integration (CI) provider
  • Analyze memory usage
  • Integrate and visualize static analysis results
  • Perform Hardware-in-the-Loop (HIL) tests
  • Install the Command Line Interface for a developer-friendly experience

Subscribe to our Monthly Newsletter

Subscribe to our monthly newsletter for development insights delivered straight to your inbox.

Interested in learning more?

Best-in-class embedded firmware content, resources and best practices

Laptop with some code on screen

I want to write my first embedded program. Where do I start?

The boom in the Internet of Things (IoT) commercial devices and hobbyist platforms like the Raspberry Pi and Arduino have created a lot of options, offering inexpensive platforms with easy to use development tools for creating embedded projects. You have a lot of options to choose from. An embedded development platform is typically a microcontroller chip mounted on a circuit board designed to show off its features. There are typically two types out there: there are inexpensive versions, sometimes called

Read More »
Medical device monitoring vitals

IEC-62304 Medical Device Software – Software Life Cycle Processes Primer – Part 1

IEC-62304 Software Lifecycle requires a lot of self-reflection to scrutinize and document your development processes. There is an endless pursuit of perfection when it comes to heavily regulated industries. How can you guarantee something will have zero defects? That’s a pretty hefty task. The regulatory approach for the medical device industry is process control. The concept essentially states that if you document how every step must be completed, and provide checks to show every step has been completed properly, you

Read More »
Operating room filled with medical devices

IEC-62304 Medical Device Software – Software Life Cycle Processes Primer – Part II

Part I provides some background to IEC-62304. Part II provides a slightly more in-depth look at some of the specifics. The IEC 62304 Medical Device Software – Software Lifecycle Processes looks into your development processes for creating and maintaining your software. The standard is available for purchase here. So what activities does the standard look at? Here are some of the major topics. For any given topic, there will be a lot more specifics. This will look at a few

Read More »