LabWindows/CVI SCPI reference
On this page you will learn how to send SCPI-commands to a Bode device using National Instruments LabWindows/CVI.
Preconditions
National Instruments' NI-Visa is installed on the client device (e.g. PC). SCPI-Server of the Bode is up and running and the IP-address as well as port are known.
Include the Visa library at the top of the source file.
#include <visa.h>
Initializing a Visa Session
Note
The resource name consists of the SCPI-Server-IP and the port in the following form unless modified
The default port is 5025 for all Bode devices
ViRsrc resourceName = "TCPIP::YourIPHere::5025::SOCKET";
Set up a resource manager and open a session with it.
Enable the termination character and set it to \n to prevent read timeouts.
You can also set the Timeout.
//create a resource manager
ViSession resourceManager;
viOpenDefaultRM(&resourceManager);
//open a visa session
ViSession instrument;
viOpen(resourceManager, resourceName, VI_NULL, VI_NULL, &instrument);
//Enable and set the termination character to prevent timeout
viSetAttribute(instrument, VI_ATTR_TERMCHAR_EN, VI_TRUE);
viSetAttribute(instrument, VI_ATTR_TERMCHAR, '\n');
viSetAttribute(instrument, VI_ATTR_TMO_VALUE, 4000); //Timeout 4000 milliseconds (optional)
Sending queries and reading responses
To send a command you have to create a Visa Buffer containing the command and use the viWrite function to send it.
If a response is expected catch it with a Visa read Buffer using the viRead function.
//send command
ViUInt32 writeCount;
char command[] = "*IDN?";
viWrite(instrument, (ViBuf)command, strlen(command), &writeCount);
//read response
uint32_t bufSize = 256
char response[bufSize];
ViUInt32 readCount;
viRead(instrument, (ViBuf)response, bufSize, &readCount);
As you can see above, the buffer size can be changed according to your specific needs.
If you don't know how much data you will get returned you can read the response in a loop without setting a buffer size.
//sends commands to the instrument
// Reads response in a loop if response > 1kB
// Stops when reading termination character \n
char* queryCommand(ViSession vi, const char* cmd) {
ViUInt32 retCount;
uint16_t loopSize = 1024; //define how much data is read every loop (here 1kB)
char currentChars[loopSize+1];
size_t responseSize = 0;
char *response = NULL;
sendCommand(vi, cmd); //send the command to the device
response = malloc(1);
response[0] = '\0'; //null terminate initial response
do
{
viRead(vi, (ViBuf)currentChars, loopSize, &retCount); //read 1kB
currentChars[retCount] = '\0'; //null terminate the current data
responseSize += retCount;
//reallocate memory for response (size changed)
response = realloc(response, responseSize + 1);
strcat(response, currentChars); //append current data to response
}
//loop as long as termination character has not been read
while (strchr(currentChars, '\n') == NULL);
return response;
}
Estimating Measurement duration
Important
If you want many measurement points, the measurement is going to take longer. Therefore, you might need to reconfigure the timeout as shown above to ensure the measurement is finished, before a timeout occurs.
The timeout needs to be longer than the duration of the measurement. You can calculate how long the measurement is going to take with this simple query
//Estimate measurement duration
char* duration = queryCommand(instrument, ":SENS:SWE:TIME?\n");
printf("Measurement duration: ");
printf(duration);
printf("\n");
This will give you an estimation on the duration and you can configure the timeout value based on this result.
Note
Calculate the duration of the measurement only after configuring the sweep settings.
Terminate
After the communication is complete close the session and the resource manager to prevent errors in following command cycles.
viClose(instrument);
viClose(resourceManager);
Example connection test
Here is an example on how to test your connection to the SCPI-Server on any Bode.
#include <ansi_c.h>
#include <visa.h>
#include <stdio.h>
int main (int argc, char *argv[])
{
printf("Hello World\n");
//create a resource manager
ViSession resourceManager;
viOpenDefaultRM(&resourceManager);
ViRsrc resourceName = "TCPIP::YourIPHere::5025::SOCKET";
//open a visa session
ViSession instrument;
viOpen(resourceManager, resourceName, VI_NULL, VI_NULL, &instrument);
//Enable and set the termination character to prevent timeout
viSetAttribute(instrument, VI_ATTR_TERMCHAR_EN, VI_TRUE);
viSetAttribute(instrument, VI_ATTR_TERMCHAR, '\n');
//send command
ViUInt32 writeCount;
char command[] = "*IDN?";
viWrite(instrument, (ViBuf)command, strlen(command), &writeCount);
//read response
uint32_t bufSize = 256
char response[bufSize];
ViUInt32 readCount;
viRead(instrument, (ViBuf)response, bufSize, &readCount);
printf(response);
//close the session
viClose(instrument);
viClose(resourceManager);
return 0;
}
Example Sweep Measurement
Here is an example on how to perform a sweep measurement
#include <ansi_c.h>
#include <visa.h>
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
//sends Commands to the instrument
void sendCommand(ViSession vi, const char* cmd) {
ViUInt32 retCount;
viWrite(vi, (ViBuf)cmd, strlen(cmd), &retCount);
}
//sends commands to the instrument
// Reads response in a loop if response > 1kB
// Stops when reading termination character \n
char* queryCommand(ViSession vi, const char* cmd) {
ViUInt32 retCount;
uint16_t loopSize = 1024; //define how much data is read every loop (here 1kB)
char currentChars[loopSize+1];
size_t responseSize = 0;
char *response = NULL;
sendCommand(vi, cmd); //send the command to the device
response = malloc(1);
response[0] = '\0'; //null terminate initial response
do
{
viRead(vi, (ViBuf)currentChars, loopSize, &retCount); //read 1kB
currentChars[retCount] = '\0'; //null terminate the current data
responseSize += retCount;
//reallocate memory for response (size changed)
response = realloc(response, responseSize + 1);
strcat(response, currentChars); //append current data to response
}
//loop as long as termination character has not been read
while (strchr(currentChars, '\n') == NULL);
return response;
}
//Method to perform an errorcheck on the SCPI Server
void checkError(ViSession vi, char when[]){
char* error = queryCommand(vi, ":SYST:ERR?\n");
printf(when);
printf("\n");
printf(error);
printf("\n");
}
//count the elements in the result variable
int countElements(char* str) {
int count = 1;
for (char* p = str; *p; p++) {
if (*p == ',') count++;
}
return count;
}
//convert the results to floats
void splitAndConvertToFloat(char* str, float* array){
char* token = strtok(str, ",");
int index = 0;
while (token != NULL){
array[index++] = atof(token);
token = strtok(NULL, ",");
}
}
int main (int argc, char *argv[])
{
clock_t start, end;
start = clock();
//create a resource manager
ViSession resourceManager;
viOpenDefaultRM(&resourceManager);
ViRsrc resourceName = "TCPIP::172.22.60.91::5025::SOCKET"; //Edit this to your IP
printf("Trying to connect to Visa resource: ");
printf(resourceName);
printf("\n");
//open a visa session
ViSession instrument;
viOpen(resourceManager, resourceName, VI_NULL, VI_NULL, &instrument);
//Enable and set the termination character to prevent timeout
viSetAttribute(instrument, VI_ATTR_TERMCHAR_EN, VI_TRUE);
viSetAttribute(instrument, VI_ATTR_TERMCHAR, '\n');
viSetAttribute(instrument, VI_ATTR_TMO_VALUE, 2500);
//Get SCPI Server ID
char* response = queryCommand(instrument, "*IDN?\n");
printf("SCPI Client connected to SCPI Server: ");
printf(response);
printf("\n");
//Trying to lock the Bode device
char* lockOK = queryCommand(instrument, ":SYST:LOCK:REQ?\n");
printf("Locking status: ");
printf(lockOK);
printf("\n");
//Reset the SCPI-Server
sendCommand(instrument, "*CLS\n");
sendCommand(instrument, "*RST\n");
sendCommand(instrument, "*ESE 255\n"); //Enable errorchecks
checkError(instrument, "After reset"); //errorcheck after the resets
//configure Measurement
sendCommand(instrument, ":CALC:PAR:DEF Z\n"); //one port impedance measurement
sendCommand(instrument, ":SENS:FREQ:STAR 10kHz\n"); //start frequency
sendCommand(instrument, ":SENS:FREQ:STOP 10MAHz\n"); //stop frequency
sendCommand(instrument, ":SENS:SWE:POIN 201\n"); //number of measurement points
sendCommand(instrument, ":SENS:SWE:TYPE LOG\n"); //Logarithmic sweep
sendCommand(instrument, ":SENS:BAND 300Hz\n");
checkError(instrument, "After freq config"); //errorcheck after frequency configs
//Estimate measurement duration
char* duration = queryCommand(instrument, ":SENS:SWE:TIME?\n");
printf("Measurement duration: ");
printf(duration);
printf("\n");
sendCommand(instrument, ":CALC:FORM SLIN\n"); //linear magnitude and phase
sendCommand(instrument, ":TRIG:SOUR BUS\n");//trigger uses bus as source
sendCommand(instrument, ":INIT:CONT ON\n"); //initialize trigger continiously
sendCommand(instrument, ":TRIG:SING\n");// trigger a measurement
checkError(instrument, "After trigger config"); //errorcheck after trigger config
//Wait until operations are finished
char* opc = queryCommand(instrument, "*OPC?\n");
printf("Operation finished status: ");
printf(opc);
printf("\n");
//Read the frequency values
char* freqValues = queryCommand(instrument, ":SENS:FREQ:DATA?\n");
printf("frequencies: ");
printf(freqValues);
printf("\n");
//errorcheck after reading the frequencies
checkError(instrument, "After freq data");
//read the measurement data
char* allResults = queryCommand(instrument, ":CALC:DATA:SDAT?\n");
printf("results: ");
printf(allResults);
printf("\n");
//errorcheck after reading measurement data
checkError(instrument, "After meas data");
//close the session
viClose(instrument);
viClose(resourceManager);
end = clock();
double timeUsed = ((double)(end-start))/1000;
printf("Measurement complete. Took: %f", timeUsed);
printf(" seconds");
int x = getchar();
return 0;
}