Instrument control with C++ - Page 1
Forum section: electronics / general

author: KSP
author_rank: Regular Contributor
timestamp: January 12, 2015, 01:20:38 pm
content: Hi all.I have had an introduction to the C++ language and written a couple of little console apps, but my main need and interest is instrument control. Can anyone recommend any training courses (whether online or through a UK training provider) which tailor to this area? I've looked at NIs LabWindows/CVI training but obviously that requires their libraries etc.I just want the ins and outs of sending an receiving information via IEEE488.2, USB, Ethernet, etc. Doesn't have to be a C++ course, I'm open to suggestions, but that's all I know right now.Any ideas?

author: KSP
author_rank: Regular Contributor
timestamp: January 12, 2015, 04:03:08 pm
content: Thats pretty much the path I have taken so far. I have a 34410A (similar to the 01s just with more bells and whistles) and a NI USB-GPIB adaptor. Ive got it to identify itself so far using example code but then my brain melted trying to adapt the code to what I want. I figured my problem was that using someone elses code can be confusing and lead you down false paths, so I wanted an insight in how to start from scratch, but I guess I will just keep tinkering.Cheers for the response

author: tggzzz
author_rank: Super Contributor
timestamp: January 12, 2015, 04:19:23 pm
content: My preference for "experimenting" with finding out what does and doesn't work with a particular instrument is to avoid having to use the edit-compile-run pattern. Experimentation can be much faster in an interpreted environment since you simply type in your "question/experiment" and instantly see what happens. That hasn't changed since the mid/late 70s, when the Commodore PET had an IEEE488 (OK, HPIB) interface and was programmed in BASIC.Once you have working example code, you can choose whether or not it is worth converting it to a different language, e.g. c++.The choice of interpreted environment would probably be dictated by having the access mechanism to the bus and/or device.

author: nctnico
author_rank: Super Contributor
timestamp: January 12, 2015, 08:32:57 pm
content: A much easier way is to use some SCPI console (like the one in NI's VISA full install) to test commands and implement that in your code.

author: KSP
author_rank: Regular Contributor
timestamp: January 13, 2015, 09:55:07 am
content: I have NI VISA 488.2 installed and I have been using NI-MAX to send individual commands to the dmm, thats easy enough for me. Is that what you meant by SCPI console? Because the commands for the instrument have never been the issue, Ive been using Fluke Met/Cal for a long time and iserting instrument commands in to those programs is a piece of cake, but we are trying to move away from proprietary software. It is the C++ side of things I struggle with. Getting the program to talk to the bus is the main thing I need to understand, then sending command strings to it should be straight forward. Also receiving and interpreting the responses and saving/presenting them is something I need help with but I guess that is a tad more generic, I'm sure I can find info on saving data to files, etc.

author: tggzzz
author_rank: Super Contributor
timestamp: January 13, 2015, 10:17:41 am
content: Quote from: KSP on January 13, 2015, 09:55:07 amYou might like to look at the C++ FQA (note, not a typo) athttp://yosefk.com/c++fqa/

author: VK3DRB
author_rank: Super Contributor
timestamp: January 13, 2015, 10:39:45 am
content: Ideally LabWindows or Labview are the best for quick development of powerful test equipment applications, but they are hideously expensive. You might want to consider C# if it is for the Windoze platform. Or Python. Both pretty easy to learn and use. Much better than the slightly antiquated C++.Plenty of people know how to write software, but few know how to write it properly, so check out some sites that offer tips on quality coding. Google "xxxxx coding standards", where xxxxx is the language you are interested in. Worth reading the documents that come up. Writing good quality code pays off in many ways. Bad coding can be soul destroying for anyone who has to take it over.SCPI ("skippy") commands are worth knowing about. They make life easy for interfacing to HP/Agilent gear. Also a lot of instrument makers provide DLL's that you can call to control and get readings from an instrument. It is worth checking the websites of the instrument maker to see what support software for programming is already available.Good luck.

author: tggzzz
author_rank: Super Contributor
timestamp: January 13, 2015, 01:45:52 pm
content: Quote from: VK3DRB on January 13, 2015, 10:39:45 amNote that with C++ many whole books have been written on the subject - with many conflicting sets of recommendations. Think about that, and you'll realise that with C++ you need either luck or experience or both.

author: KSP
author_rank: Regular Contributor
timestamp: January 13, 2015, 02:26:27 pm
content: Thanks guys.Quote from: VK3DRB on January 13, 2015, 10:39:45 amYou mention python... I have no experience with python but you're not the first to recommend it, do you mind elaborating on why it would be better for my needs?And can I use NI VISA libraries with it?

author: pardo-bsso
author_rank: Frequent Contributor
timestamp: January 13, 2015, 02:58:34 pm
content: Hi, I know it is not C++ but take a look athttp://lantz.readthedocs.org/en/latest/

author: Codemonkey
author_rank: Regular Contributor
timestamp: January 13, 2015, 03:10:16 pm
content: I've used the NI VISA library for instrument control in a C++ application and found it really easy to use. I'm not doing anything too interesting, just control of a DMM, frequency counter, Power meter and switchable attenuators.Below is the bit of code that calls the visa functions. Its part of a larger application built with the Wx framework so you'd need to convert obvious stuff like WCHAR to char etc.Code:/****************************************************************************//*** Include files ***//****************************************************************************/#include "GpibDevice.h"#include <wx/wx.h>#include <wx/string.h>#include <wx/log.h>#include <wchar.h>#include "visa.h"/****************************************************************************** NAME: cGpibDevice** DESCRIPTION:* Constructor for the cGpibDevice class** PARAMETERS: Name RW Usage* name R description goes here** RETURNS:* void*****************************************************************************/cGpibDevice::cGpibDevice(void){bDeviceOpen = FALSE;}/****************************************************************************** NAME: ~cGpibDevice** DESCRIPTION:* Deconstructor for the cGpibDevice class** PARAMETERS: Name RW Usage* name R description goes here** RETURNS:* void*****************************************************************************/cGpibDevice::~cGpibDevice(void){}/****************************************************************************** NAME: mGpibOpen** DESCRIPTION:* Method to open a GPIB connection** PARAMETERS: Name RW Usage* iAddress R Address of GPIB device* iTimeout R Timeout time** RETURNS:* bool: TRUE if the device was opened ok* FALSE if something went wrong*****************************************************************************/bool cGpibDevice::mGpibOpen(int iAddress, int iTimeout){bool bOk = TRUE;ViStatus viError;char acBuff;WCHAR wcBuff;WCHAR wcManufacturer;WCHAR wcModel;/* construct the device id string */sprintf(acBuff, "GPIB0::%d::INSTR",iAddress);/* If the device is already opened, close it first */if(bDeviceOpen){mClose();}/* Open session to GPIB device at the given address */viError = viOpenDefaultRM(&defaultRM);if(viError != VI_SUCCESS) bOk &= FALSE;viError = viOpen(defaultRM, acBuff, VI_EXCLUSIVE_LOCK, iTimeout, &vi);if(viError != VI_SUCCESS) bOk &= FALSE;viError = viSetAttribute(vi, VI_ATTR_TMO_VALUE, iTimeout);if(viError != VI_SUCCESS) bOk &= FALSE;bDeviceOpen = TRUE;if(!bOk){wxLogMessage(_T("GPIB device failed to open on address %i"),iAddress);return(FALSE);}/* Initialise the device */bOk &= mPrintf((char*)"*RST\n");/* Send an *IDN? string to the device and read the results */bOk &= mQueryf((char*)"*IDN?\n", (char*)"%t", acBuff);if(bOk){/* convert the results to unicode */wxMB2WC(wcBuff, acBuff, sizeof(acBuff));/* Get the manufacturer name */wcscpy(wcManufacturer, wcstok(wcBuff, _T(",")));/* Get the model name */wcscpy(wcModel, wcstok(NULL, _T(",")));wxLogVerbose(_T("GPIB device %s %s opened on address %i"),wcManufacturer, wcModel, iAddress);} else {wxLogVerbose(_T("GPIB device opened on address %i"), iAddress);}return(TRUE);}/****************************************************************************** NAME: mClose** DESCRIPTION:* Method to close a GPIB connection** PARAMETERS: Name RW Usage* iHandle R Handle for the device we want to close** RETURNS:* void*****************************************************************************/void cGpibDevice::mClose(void){/* Close session */viClose(vi);viClose(defaultRM);bDeviceOpen = FALSE;}/****************************************************************************//*** Local Functions ***//****************************************************************************//****************************************************************************** NAME: mPrintf** DESCRIPTION:* Method to write to a GPIB device** PARAMETERS: Name RW Usage* acWriteMessage R Message to write to the GPIB device* ... R variable number of arguments** RETURNS:* bool: TRUE if the message was written ok* FALSE if something went wrong*****************************************************************************/bool cGpibDevice::mPrintf(char *acWriteMessage, ...){ViStatus viError;va_list args;va_start(args, acWriteMessage);viError = viVPrintf(vi, acWriteMessage, args);va_end(args);if(viError == VI_SUCCESS){return(TRUE);} else {return(FALSE);}}/****************************************************************************** NAME: mScanf** DESCRIPTION:* Method to read from a GPIB device** PARAMETERS: Name RW Usage* acReadMessage R format specifier string for data to read* ... R variable number of arguments** RETURNS:* bool: TRUE if the data was read ok* FALSE if something went wrong*****************************************************************************/bool cGpibDevice::mScanf(char *acReadMessage, ...){ViStatus viError;va_list args;va_start(args, acReadMessage);viError = viVScanf(vi, acReadMessage, args);va_end(args);if(viError == VI_SUCCESS){return(TRUE);} else {return(FALSE);}}/****************************************************************************** NAME: mQueryf** DESCRIPTION:* Method to read from a GPIB device** PARAMETERS: Name RW Usage* acReadMessage R format specifier string for data to read* ... R variable number of arguments** RETURNS:* bool: TRUE if the data was read ok* FALSE if something went wrong*****************************************************************************/bool cGpibDevice::mQueryf(char *acWriteMessage, char *acReadMessage, ...){ViStatus viError;va_list args;va_start(args, acReadMessage);viError = viVQueryf(vi, acWriteMessage, acReadMessage, args);va_end(args);if(viError == VI_SUCCESS){return(TRUE);} else {return(FALSE);}}/****************************************************************************//*** END OF FILE ***//****************************************************************************/

author: Codemonkey
author_rank: Regular Contributor
timestamp: January 13, 2015, 03:15:04 pm
content: And this is the bit that accessed the DMM (34401A). I can't post the lot but it should be enough to get you started!Code:/****************************************************************************** NAME: mRead** DESCRIPTION:* Method to read from a GPIB DMM device** PARAMETERS: Name RW Usage* pacMeasurement R DMM_MEASURE_V or DMM_MEASURE_I* pacACDC R DMM_MEASURE_AC or DMM_MEASURE_DC* pacRange R choose from one of the DMM_SCALE options* pacResolution R choose form one of the DMM_RESOLUTION* options** RETURNS:* float: Value read from the DMM*****************************************************************************/bool cDMM::mRead(const char *pacMeasurement, const char *pacACDC, const char *pacScale, const char *pacResolution, float *pfResult){bool bOk = TRUE;float fMeasurement = 0;char acCommand;if(!bDeviceOk){wxLogMessage(_T("DMM is inoperative"));return(FALSE);}sprintf(acCommand, "MEASURE:%s:%s? %s,%s\n",pacMeasurement, pacACDC, pacScale, pacResolution);bOk &= mQueryf(acCommand, (char*)"%f",&fMeasurement);*pfResult = fMeasurement;if(strcmp(pacScale, pacPreviousScale) == 0){bScaleChanged = FALSE;}else{wxLogMessage(_T("DMM scale changed!"));bScaleChanged = TRUE;}pacPreviousScale = pacScale;return(bOk);}

author: KSP
author_rank: Regular Contributor
timestamp: January 13, 2015, 03:30:47 pm
content: Thank you very much for the help! Sifting through all of that on my tiny phone screen would probably drive me mad so I will check it out later.On a side note, I've been checking out python, more specifically the use of it with pyVISA. Looks pretty comprehensive and all little more simple than C/C++ code.My only question is creating .exe files from the python code. I've seen that there's a thing called py2exe, but in theory could I create an simple C++ program that would call and run the python code, then make a .exe out of that? That way the guts of it is python?May seem redundant to some but I'm just curious

author: VK3DRB
author_rank: Super Contributor
timestamp: January 14, 2015, 01:11:13 pm
content: Quote from: KSP on January 13, 2015, 02:26:27 pmhttp://www.quora.com/What-are-the-advantages-of-Python-over-C++He writes, "Language complexity: C++ is a beast of a language. The spec is 775 pages of language legalese, and even the best C++ developers I've known can be caught up short by unintended consequences in complex (or not so complex) code. Python is much simpler, which leads to faster development and less mental overhead."True, but familiarity is important in quick development. A bad Labview programmer will create a mess. A good Labview programer will get a project of medium complexity up and running in no time that is easily readable. Same with LabWindows, Visual Studio, C or anything.If you are experienced in OOP concepts and C or C# or C++, two days of learning Python and you will be hooked. Even not knowing these, in a couple of week, you'll be writing programs. There are plenty of tutorials available on the information superhighway. The only bad thing is popular microcontroller and compiler vendors do not support Python. For those I use C almost exclusively, except for the occasional in-line assembly code. Python is basically and interpreted language but you can compile it to an exe on a PC. Interpreted languages have historically been touted as crap. And they were. BASIC is one such example. Ran like a dog on old PC's, but that was years ago. Things have changed.If running on a Windoze platform, you can easily call DLL's. An example:Let's assume the DLL downloaded from the equipment vendor is called HP34410A.dll...Script:from ctypes import*# give location of dlltestEquipmentDll = cdll.LoadLibrary("C:\\HP34410A.dll")testEquipmentDll.SetVoltmeterRange(100)result = testEquipmentDll.ReadVoltage()print "Voltage = "+resultOutput:4.985NI VISA libraries will work, called by C, C#, or C++. The VISA DLL is called visa32.dll, so you can call it with pretty much any language. But if I am not mistaken you will need to load in the big NI runtime engine and the huge device drivers library. NI is annoying in that you have to have a huge driver library with GPIB and the rest just for RS-232 port support!Also check this out...http://pyvisa.readthedocs.org/en/master/