One could be excused for thinking sometimes that the concept of connecting devices with other devices for automation purposes is a fairly recent invention. Yet for all the (relatively) recent hype of the Internet of Things and the ‘smart home’, laboratories have been wiring up their gear to run complicated measurement and test sequences for many decades now, along with factories doing much the same for automating production processes.
Much like the chaotic universe of IoT devices, lab equipment from different manufacturers feature a wide number of incompatible protocol and interface standards. Ultimately these would coalesce into IEEE-488.1 (GPIB) as the physical layer and by 1990 the first Standard Commands for Programmable Instruments (SCPI) standard was released that built on top of IEEE-488.
SCPI defines (as the name suggests) standard commands to interact with instruments. It has over the past decades gone on to provide remote interaction capabilities to everything from oscilloscopes and power supplies to exotic scientific equipment. Many off the shelf devices a hobbyist can buy today feature an SCPI interface via its Ethernet, USB or RS-232C port(s) that combined with software can be used to automate one’s home lab.
Even better is that it’s relatively straightforward to add SCPI functionality to one’s own devices as well, so long as it has at least an MCU and some way to communicate with the outside world.
Reinventing the Wheel Is No Fun
As much fun as it is to come up with one’s own communication standard for a custom widget, there is a lot to be said for sticking to existing standards, instead of adding another ‘standard’ to the pile. One good reason is the time you will spend on coming up with a protocol that works, which covers all of the edge cases and leaves enough room for future expansion when new features are added.
Another reason is that of compatibility with existing software, which also touches on why end users likely to be enthused about this awesome new protocol. When using SCPI, it can be fairly painlessly integrated into existing (lab) automation software, as the whole concept behind SCPI is that each instrument will implement its own range of custom commands in addition to a number of required ones.
For users of software like LabVIEW or Sigrok, the ideal scenario is that the device speaks SCPI, and that in the worst-case a custom handler has to be written for the custom SCPI commands when one isn’t available yet. What will never change here is the basic SCPI syntax, allowing for rapid bootstrapping of new devices, the prevention of bugs (no parser is perfect) and reusing of code. SCPI’s base command set also enables functionality like synchronization mechanisms by default.
Despite this, when yours truly looks at the current stack of measurement gear and programmable power supplies piled up in the home lab, not all of them speak SCPI. The Rigol DS1104Z oscilloscope does via its Ethernet port. The little brother of the Owon XDM2041 DMM (XDM1041) speaks SCPI via its USB port. So far so good, but then the electronic load (Arachnid Labs Reload Pro) speaks a custom protocol via USB that could have been SCPI.
The Manson HCS-3304 programmable power supply does the same thing with yet another custom protocol, with the commands listed in the manual revisions apparently also often being wrong. With only some of these devices supported by Sigrok at this point, automating tests would involve hacking my own decoder together, rather than a bit of high-level fumbling with custom SCPI device commands.
Using standards appropriately can save a lot of time, sanity and grey hairs. Which leads to the next question: just how easy is it to add SCPI to one’s own Awesome Widget?
Enter LibSCPI
Not everyone wants to write their own SCPI parser from scratch, which is why the SCPI parser library v2, or simply ‘libscpi‘ is a good start. It implements the current SCPI 1999 standard. Since we’d be interested in using SCPI on an embedded device, we’ll take a look at the provided FreeRTOS with LwIP (netconn) example. This shows the implementation of an SCPI server which runs in a FreeRTOS thread.
The SCPI server sets up a TCP listening port on the standard SCPI port (5025), after which commands can be sent to the device via any Telnet client or comparable in raw mode, i.e. plain text. Note that in this server example, LwIP netconn’s NETCONN_COPY
is used instead of NETCONN_NOCOPY
. This is essential to preventing data corruption (use of buffer data after delete) when using chained SCPI commands.
To use libscpi with a USART or other interface, the first thing to do is set up the library by calling SCPI_Init
. The following methods must also be implemented in your code:
SCPI_Write(scpi_t* context, const char* data, size_t len)
SCPI_Flush(scpi_t* context)
SCPI_Error(scpi_t* context, int_fast16_t err)
SCPI_Control(scpi_t* context, scpi_ctrl_name_t ctrl, scpi_reg_val_t val)
SCPI_Reset(scpi_t* context)
These functions are mostly self-explanatory. As can be ascertained from the example implementation, SCPI_Write allows libscpi to write to your output of choice, with SCPI_Flush used to flush any output buffers that may exist. SCPI_Error prints error messages from libscpi, SCPI_Reset resets the device, and SCPI_Control is used to write to the control channel (optional feature, here on TCP port 5026).
To get libscpi to parse any fresh incoming strings (always terminated with a newline, \n
, or \r\n
), your code calls SCPI_Input
, or in the case of singular commands, SCPI_Parse
can also be used directly.
An example implementation of libscpi on STM32 with the ST HAL alongside FreeRTOS and a simple HTTP server can be found in this GitHub repository. This targets the Nucleo-F746ZG development board.
SCPI Digital Multimeter
Also provided with the libscpi example is the example implementation of a digital multimeter device. If we open the command definitions and implementations in scpi-def.c, it gives us a good glimpse at what a custom device implementation would require. This starts with the command table, called scpi_commands
.
This table defines all commands in the format of a pattern with associated callback (all but the core ones implemented in the same file), starting with the IEEE mandated commands, e.g. *CLS (CLear Status):
{ .pattern = "*CLS", .callback = SCPI_CoreCls,}
The ‘*’ (asterisk) in front of a command means that it is a required, common SCPI command that every device must implement. Important ones are *IDN?
, which queries (note the question mark) the device about its identity, *RST
to command the device to reset and *WAI
that tells the device to wait with executing any new commands until the commands preceding this command have been completed.
After this block of required commands, we get the block with the DMM functions:
{.pattern = "MEASure:VOLTage:DC?", .callback = DMM_MeasureVoltageDcQ,}, {.pattern = "CONFigure:VOLTage:DC", .callback = DMM_ConfigureVoltageDc,}, {.pattern = "MEASure:VOLTage:DC:RATio?", .callback = SCPI_StubQ,}, {.pattern = "MEASure:VOLTage:AC?", .callback = DMM_MeasureVoltageAcQ,}, {.pattern = "MEASure:CURRent:DC?", .callback = SCPI_StubQ,}, {.pattern = "MEASure:CURRent:AC?", .callback = SCPI_StubQ,}, {.pattern = "MEASure:RESistance?", .callback = SCPI_StubQ,}, {.pattern = "MEASure:FRESistance?", .callback = SCPI_StubQ,}, {.pattern = "MEASure:FREQuency?", .callback = SCPI_StubQ,}, {.pattern = "MEASure:PERiod?", .callback = SCPI_StubQ,},
The reason for the mixed upper and lowercase use in the commands has to do with the ‘pattern’ aspect: in SCPI only the uppercase part of the pattern is required, and the lowercase section of a command can be omitted for brevity. As noted earlier, a command followed by a question mark is a query. The use of colons is to separate the levels of the tree hierarchy that defines an SCPI interface.
To use this hierarchy to measure voltage and current for DC in a single string, one would use the following command:
MEASure:VOLTage:DC?;:MEASure:CURRent:AC?
The semi-colon separates individual commands, and the leading colon resets the hierarchy to the root of the command tree. This latter feature can be used to create very brief concatenated command strings for e.g. measuring both AC & DC voltage:
MEASure:VOLTage:DC?;AC?
Since the first command left us in the VOLTage
hierarchy level, the subsequent command would trigger the AC?
query. This means that a well-designed interface for a device can make controlling it quite efficient even when manually typing in queries by avoiding unnecessary repetition.
Advanced Features
All of this merely scratches the surface of what SCPI is capable of, of course. In addition to plain text output, numeric strings can also be marked as being hexadecimal (#H), as octal (#Q), or as binary (#B). Arguments can be provided along with commands separated by a space. With libscpi these arguments can then be retrieved in the callback function.
Complex sequential and overlapping command sequences can also be set up using the *OPC
and *WAI
commands, along with the *OPC?
query. These can be used in combination with the status register commands and any device-specific commands to control specifically timed sequences.
The best part about SCPI is probably that it scales along with the complexity of the device, whether it’s a simple voltage meter, or a scientific instrument reading quantum-level perturbations, the underlying protocol does not change. By not having to reinvent the same basics every single time, the developer and user of the device can instead focus on the things that matter.
In the case of the end-user, that is probably the experience of unpacking the device, plugging it in and programming in the SCPI sequences that make it perform the desired functions. Which is the major benefit of following established standards.
[Heading image: Back of Rigol DS1104Z oscilloscope with the Ethernet and USB ports visible. Credit: Rigol]
That is quite a coincidence – I am currently writing my own SCPI library from scratch, for a project at University.
My implementation is more C++ oriented to wrap things up with a cleaner interface (and let’s be honest, it would be nice to slowly move away from C structs as pseudo-classes when static C++ classes feature just as much control over memory with a much nicer interface)
But the library will also directly include parsing of different inputs such as SI units with standard notation prefixes (m, k, G, etc.)
In any case, I highly appreciate pointing out SCPI. It seems like a very well thought out protocol that does not overcomplicate simple data transfers, while also providing a lot of flexibility for more in-depth systems.
And this write-up does a very lovely job of showing everything off :D
Nice writeup. SCPI doesn’t remove the need to understand how the instrument operates and what commands are available, but makes it a lot easier to use common libraries to communicate.
Beware the non-compliant implementation, though. I prefer instruments that make no pretense of being compliant- Fluke 45 and HP 3478A, for example- to those that claim compliance to the standard but are not- several BK Precision instruments I have known being prime examples.
When selecting/qualifying an instrument, if it doesn’t properly respond to *RST, or to *TRG, or it doesn’t have the compliant status register, or neglegts other core requirements, dump it. It isn’t an instrument, it is a killer-clown version that will stab you without warning. You may still find issues with a particular implementation, but if the core is there, it is generally workable.
“dump it. It isn’t an instrument, it is a killer-clown version that will stab you without warning.”
Sounds serious!
B^)
FETCH? doesn’t, *TRG returns a readback, but doesn’t trigger a new reading, unimplemented status register, etc. I tried, I failed. The unit was unusable by remote. I didn’t choose it, but I paid with blood trying to make it work. (BK 5492A) No savings here, as we needed to spend real money on the Agilent in the end.
FWIW the HP 3478A was released 7 years before the SCPI standard. I have one and a couple other late-70’s and early-80’s HP devices, and they’re all very interesting to interface to because of the heterogeneity of control syntax that led to SCPI being written.
If they’re claiming they comply to a standard, but the device does not, isn’t that false advertising?
Maybe, but you’d need to have someone harmed to file suit usually. For some standards (I’m involved with Khronos standards a fair bit), the name of the standard is a trademark and conformance testing for the standard (and often a fee) is required to be able to say you comply with the standard, at least on the device side. (e.g. anybody can make an app that uses OpenXR, and anybody can use the API, but if you want to have a device or runtime that says “this headset supports OpenXR” you need to do the conformance process.) In the Khronos case the conformance process also gets you an IP license/protection, which is probably of varying value in different fields. (My hunch is that it’s more important for opengl and Vulkan than for OpenXR). Of course this is all paraphrased and I’m not a lawyer.
It should be pointed out that at the time of this writing, the scpi-parser project linked has 135 forks, is provided under BSD-2-Clause license and has been maintained for almost 10 years.
This, along with the standard dating back to 1999 (and 2004) is as good a testimony to maturity as it gets for a parser solution for projects of various sizes that just need a control and monitoring channel added.
Thanks, [Jan Breuer]!
I’m glad you used ‘C’. I have horrible memories of PySCPI from my previous job, and selecting the correct python to run with the library, which could only be installed via arcane methods. Not to mention some quite dodgy python code written by hardware engineers! Having said that, overlooking the nastiness, it was quite useful.
Funny, I have been using PyVisa to collect VNA data for my research, and it has been a pretty straightforward process. Has been great for live visualizations with matplotlib!
I second this, PyVisa worked quite well with my Rigol bench PSU and oscilloscope. I made a script to log waveform data from the scope during high-temperature oven tests, it saved waveform images at time intervals and temperature thresholds. Super useful!
Pretty slick. I wonder if it would be useful/easier to write a little firmware converting a given Bluetooth low energy DMM protocol to SCPI (think something you can load on an nrf52840 dongle), rather than trying to do a cross platform ble driver directly.
Is GPIB not as common or necessary anymore for modern implementations? It’s mentioned as an underlying layer, but then we hear about SCPI over Ethernet and USB and nothing about GPIB
As a hardware layer GPIB is expensive and cumbersome, and usually ends up connected to a USB or Ethernet converter in the end anyway. Ethernet is superior in almost every way.
GPIB connectors are a lot more expensive …
Well then it’s nice that it’s flexible enough to support different transport :)
Then again, they are a lot more effective when dealing with finance. Swinging a usb cable over your head, even a USB-B connector isn’t nearly as scary
“… which also touches on why end users likely to be enthused about this awesome new protocol. ”
Huh?
SCIPI is already over 30 years old.
https://en.wikipedia.org/wiki/Standard_Commands_for_Programmable_Instruments
But still, nothing wrong with making it known to a wider public.
I read that as “… which also touches on why end users likely to be enthused about . ”,
probably hinting at what one finds when searching e.g. “simple protocol arduino”.
I haven’t investigated which of the results would come close to SCPI, but it sure looks like the go-to way isn’t a review of existing solutions but instead an exercise in how-can-I-hack-something-together-in-an-hour :)
Nice, adding brackets makes the text within them disappear. Let me try that again.
“… which also touches on why end users likely to be enthused about \. ”
“… which also touches on why end users likely to be enthused about /this awesome new protocol/. ”
SCPI & USBTMC Fun Stuff…
* Lists of Rigol SCPI Commands
https://www.eevblog.com/forum/testgear/lists-of-rigol-scpi-commands/
* If an instrument has a USB instead of Ethernet port and it supports SCPI, often the USB port will be using the USBTMC (USB Test and Measurement Class) layer.
USBTMC Unwrapped
https://www.eetimes.com/usbtmc-unwrapped/
* Using Linux to Control USB Instruments
http://literature.cdn.keysight.com/litweb/pdf/5989-6718EN.pdf
To send a SCPI command to an instrument, a USBTMC driver wraps the command into the message structure as shown and instructs the USB core driver to process the message (i.e. send it to the device’s bulk out end-point)…
* USBTMC and sigrok + USBTMC Standards Documents
https://sigrok.org/wiki/USBTMC
droghio/sigrok/src/scpi/scpi_usbtmc_libusb.c
https://github.com/droghio/sigrok/blob/master/src/scpi/scpi_usbtmc_libusb.c
USBTMC (USB Test and Measurement Class) is a set of standard device class specifications, built on top of the USB standard. It is intended as a modern replacement of the venerable IEEE-488 (GPIB) standard, which is based on a large parallel connector. Two standards documents are specified:
USBTMC specification: specifies the protocol and descriptors that allow communications between devices and client software.
USB488 subclass specification: this specifies how to send and receive IEEE-488.1 and IEEE-488.2 commands over a USBTMC-based transport.
The standard is freely available from usb.org:
https://www.usb.org/sites/default/files/USBTMC_1_006a.zip
The sigrok project supports USBTMC via the respective libusb-1.0 based USBTMC SCPI backend (optionally the librevisa library also has some support, though this not well-tested).
Thanks for the nice summary. Indeed, GPIB cables are falling out of favor over time and USB is widely accepted. USB can also provide ample power to the device, with newer standards even supporting power supply implementations directly from the USB host.
There are downsides however, which are safety and stability-related: RS232 and USB interfaces do not provide galvanic isolation, and we were having a fair bit of trouble rectifying common-mode transient-related problems with USB. It’s particularly bothersome when a USB device re-enumerates during a measurement, as this can reset the states of the device or the software controlling the device.
Many consumer-grade USB interfaces are not built with common-mode filters and TVS diodes, and basically all cables are junk (insufficient shielding, improper or missing ferrites). I admit that depending on the type of hardware designed, all of this may be a non-issue. In our lab however we ended up discarding all cables to replace them with one brand of high quality ones that we found out worked. Only then did the setup become somewhat stable.
RS232-to-fiber converters are old news. USB isolators are available, but somewhat cumbersome. When designing one’s own hardware, ethernet is perhaps the most attractive option, as functional isolation and common-mode filtering is provided in standard options, and reinforced isolation magnetics are available from multple vendors. Additionally, PoE with reinforced isolation can be added to the design when there are multiple units which would all need floating power supplies.
Wired and wireless LAN options both also bring another convenience to the table: multiple clients can be supported sequentially or concurrently. We’re currently stuck with lab equipment which uses USB with a jumble of vendor drivers and USBTMC, and apart from USB stability issues, the darn virtual COM ports keep getting shuffled around, which is a gift that keeps on giving. Going forward, I will keep upgrading power supplies with ethernet interfaces to be able to finally ditch the curse that is USB and in doing so allow multiple lab PCs to access the pool of lab equipment. As a bonus, you get to do remote monitoring and testing via VPN.
In conclusion, choose USB if what you’re designing needs to be cheap and easily reproducible (e.g. with dev boards from the usual suspects like NXP, TI, ST, …) or when isolation is not an issue. Do consider ethernet first though when applications can contain mains-connected operation or high voltage, low noise (ground loop issues), harsh environments (industrial, lab) or an addition to safety by design is needed.
In our lab we have signifcant issues with SCPI over both USB and Ethernet because resets tend to change IP addresses and USB enumeration, and sometimes it’s pretty difficult to get the host to see/remember fixed IP addresses as it wants to provide some sort of DHCP.
I’ve ended up writing labview routines to go query everything on the network and try to figure out which instrument is which by mac addresses and dynamically reassign all the addresses, and it’s a big pita.
GPIB has all sorts of issues, but once set up, addressing isn’t one of the issues.
Not in a SCPI environment but I have solved this problem by putting all the equipment on a private network and running a DHCP server on my control computer with fixed IPs for all the instruments, then they always get the same IP based on their MAC and no need to jump through extra hoops. If you still want them on the real net you can bridge on the main computer and run NAT. Require 2 network ports on the main computer and a separate wired network + switches but if it in all in one room this is easy and cheep to implement.
Also a little note. NI has made LabView free for home and pre-college use. And not some stripped down 5 year old version like they were doing before, it the full thing (2021) with the ability to build runtime apps you can run on things without a full blown installation of labview on.
Awesome article, thanks
Let us not forget that Keysight is still allowing people to use VEE together with the IOLIB series for controlling instruments. And it is even easier to use than LabView. Plus it hs a great support vehicle other than forums.
This article angers and shame me. I’ve just come to the end of an embedded project where the 5 of us each had a board and its debug interface to develop. We all chose our own ad-hoc debug commands to help during development, with a loose agreement on a few common commands. Coming from a test background, I wish I’d investigated/known about lubscpi. It would have been perfect for our needs and would have allowed a standard interface for test automation.
I’ll definitely keep this in mind for next time.