Delphi SCPI reference
On this page you will learn how to send SCPI-commands to a Bode device using the programming language Delphi in Embarcadero RAD Studio and Lazarus IDE.
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.
In Delphi the visa32.dll is used to communicate with instruments and send SCPI-Commands. Therefore the visa32.dll must be downloaded on the device. It should come with the installation of NI-Visa and is typically stored in C:\Windows\System32.
All that is left to do is to load only the necessary functions of the DLL into the project.
//Load the required functions from the visa32 DLL
function viOpenDefaultRM(out sesn: Integer): Integer; stdcall; external 'visa32.dll';
function viOpen(sesn: Integer; name: PAnsiChar; mode: Integer; timeout: Integer;
out vi: Integer): Integer; stdcall; external 'visa32.dll';
function viWrite(vi: Integer; buf: PAnsiChar; cnt: Integer; out retCnt: Integer):
Integer; stdcall; external 'visa32.dll';
function viRead(vi: Integer; buf: PAnsiChar; cnt: Integer; out retCnt: Integer):
Integer; stdcall; external 'visa32.dll';
function viSetAttribute(vi: Integer; attrName: Integer; attrValue: Integer):
Integer; stdcall; external 'visa32.dll';
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
resourceName := 'TCPIP::YourIPHere::5025::SOCKET';
Open the default resource manager and open a session to the device using the functions from the DLL.
Further, enable the termination character and set it to \n using the viSetAttribute function.
//Open default resource manager
viOpenDefaultRM(sesn);
//open session to device
viOpen(sesn, resourceName, 0, 0, vi);
// Enable termination character
viSetAttribute(vi, VI_ATTR_TERMCHAR_EN, VI_TRUE);
// Set termination character to '\n'
viSetAttribute(vi, VI_ATTR_TERMCHAR, Ord(#10));
//Set the timeout
viSetAttribute(vi, VI_ATTR_TMO_VALUE, 10000);
Set the timeout according to your measurement duration. Explained further later on this page.
Sending queries and reading responses
To send commands and read responses you will need a write Buffer and read Buffer respectively.
The viWrite and viRead functions can be used to send queries.
//sends commands to the instrument
// Reads response in a loop if response > 1kB
// Stops when reading termination character \n
function queryCommand(vi: integer; command: String): String;
var
readBuf: array[0..1023] of AnsiChar; //read 1kB every cycle
writeBuf: array[0..255] of AnsiChar;
currentChars, response: String;
retCnt: integer;
begin
StrPCopy(writeBuf, command);
viWrite(vi, writeBuf, StrLen(writeBuf), retCnt);
viRead(vi, readBuf, sizeOf(readBuf), retCnt);
currentChars := String(readBuf);
response := currentChars;
while Pos(#10, currentChars) = 0 do
begin
FillChar(readBuf, SizeOf(readBuf), #0);
viRead(vi, readBuf, sizeOf(readBuf), retCnt);
currentChars := String(readBuf);
response := response + currentChars;
end;
Result := Trim(response);
end;
//sends commands to the instrument
procedure writeCommand(vi: integer; command: String);
var
writeBuf: array[0..255] of AnsiChar;
retCnt: integer;
begin
StrPCopy(writeBuf, command);
viWrite(vi, writeBuf, StrLen(writeBuf), retCnt);
end;
Buffer size can be adjusted if you want to read more or less data per cycle.
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
response := queryCommand(vi, ':SENS:SWE:TIME?');
writeln('Measurement duration: ' + response);
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.
With this information the timeout can be configured like this
viSetAttribute(vi, VI_ATTR_TMO_VALUE, 10000);
Terminate
After the communication is finished don't forget to close the session to prevent errors in following command cycles. Use the viClose() function.
Example connection test
Here is an example on how to test your connection to the SCPI-Server on any Bode.
program scpi_test;
uses
System.SysUtils; //Only SysUtils if you are using Lazarus IDE
//Load the required functions from the visa32 DLL
function viOpenDefaultRM(out sesn: Integer): Integer; stdcall; external 'visa32.dll';
function viOpen(sesn: Integer; name: PAnsiChar; mode: Integer; timeout: Integer;
out vi: Integer): Integer; stdcall; external 'visa32.dll';
function viWrite(vi: Integer; buf: PAnsiChar; cnt: Integer; out retCnt: Integer):
Integer; stdcall; external 'visa32.dll';
function viRead(vi: Integer; buf: PAnsiChar; cnt: Integer; out retCnt: Integer):
Integer; stdcall; external 'visa32.dll';
function viSetAttribute(vi: Integer; attrName: Integer; attrValue: Integer):
Integer; stdcall; external 'visa32.dll';
function viClose(vi: Integer): Integer; stdcall; external 'visa32.dll';
//sends commands to the instrument
// Reads response in a loop if response > 1kB
// Stops when reading termination character \n
function queryCommand(vi: integer; command: String): String;
var
readBuf: array[0..1023] of AnsiChar; //read 1kB every cycle
writeBuf: array[0..255] of AnsiChar;
currentChars, response: String;
retCnt: integer;
begin
StrPCopy(writeBuf, command);
viWrite(vi, writeBuf, StrLen(writeBuf), retCnt);
viRead(vi, readBuf, sizeOf(readBuf), retCnt);
currentChars := String(readBuf);
response := currentChars;
while Pos(#10, currentChars) = 0 do
begin
FillChar(readBuf, SizeOf(readBuf), #0);
viRead(vi, readBuf, sizeOf(readBuf), retCnt);
currentChars := String(readBuf);
response := response + currentChars;
end;
Result := Trim(response);
end;
//sends commands to the instrument
procedure writeCommand(vi: integer; command: String);
var
writeBuf: array[0..255] of AnsiChar;
retCnt: integer;
begin
StrPCopy(writeBuf, command);
viWrite(vi, writeBuf, StrLen(writeBuf), retCnt);
end;
const
VI_ATTR_TERMCHAR_EN = $3FFF0038;
VI_ATTR_TERMCHAR = $3FFF0018;
VI_ATTR_TMO_VALUE = $3FFF001A;
VI_TRUE = 1;
var
input, response: string;
sesn, vi: Integer;
resourceName: PAnsiChar;
frequencies: string;
allResults: string;
StartTime: TDateTime;
duration: Double;
begin
writeln('Hello World');
resourceName := 'TCPIP::172.22.44.19::5025::SOCKET';
writeln('Trying to connect to Visa resource: ' + resourceName);
StartTime := Now;
//Open default resource manager
viOpenDefaultRM(sesn);
//open session to device
viOpen(sesn, resourceName, 0, 0, vi);
// Enable termination character
viSetAttribute(vi, VI_ATTR_TERMCHAR_EN, VI_TRUE);
// Set termination character to '\n'
viSetAttribute(vi, VI_ATTR_TERMCHAR, Ord(#10));
//Set the timeout
viSetAttribute(vi, VI_ATTR_TMO_VALUE, 10000);
//query *IDN?
response := queryCommand(vi, '*IDN?');
writeln('SCPI client connected to SCPI Server ' + response);
viClose(vi);
viClose(sesn);
readln(input);
end.
Example Sweep Measurement
Here is an example on how to perform a sweep Measurement
program scpi_test;
uses
SysUtils;
//Load the required functions from the visa32 DLL
function viOpenDefaultRM(out sesn: Integer): Integer; stdcall; external 'visa32.dll';
function viOpen(sesn: Integer; name: PAnsiChar; mode: Integer; timeout: Integer;
out vi: Integer): Integer; stdcall; external 'visa32.dll';
function viWrite(vi: Integer; buf: PAnsiChar; cnt: Integer; out retCnt: Integer):
Integer; stdcall; external 'visa32.dll';
function viRead(vi: Integer; buf: PAnsiChar; cnt: Integer; out retCnt: Integer):
Integer; stdcall; external 'visa32.dll';
function viSetAttribute(vi: Integer; attrName: Integer; attrValue: Integer):
Integer; stdcall; external 'visa32.dll';
function viClose(vi: Integer): Integer; stdcall; external 'visa32.dll';
//sends commands to the instrument
// Reads response in a loop if response > 1kB
// Stops when reading termination character \n
function queryCommand(vi: integer; command: String): String;
var
readBuf: array[0..1023] of AnsiChar; //read 1kB every cycle
writeBuf: array[0..255] of AnsiChar;
currentChars, response: String;
retCnt: integer;
begin
StrPCopy(writeBuf, command);
viWrite(vi, writeBuf, StrLen(writeBuf), retCnt);
viRead(vi, readBuf, sizeOf(readBuf), retCnt);
currentChars := String(readBuf);
response := currentChars;
while (Pos(#10, currentChars) = 0) do
begin
FillChar(readBuf, SizeOf(readBuf), #0);
viRead(vi, readBuf, sizeOf(readBuf), retCnt);
currentChars := String(readBuf);
response := response + currentChars;
end;
Result := Trim(response);
end;
//sends commands to the instrument
procedure writeCommand(vi: integer; command: String);
var
writeBuf: array[0..255] of AnsiChar;
retCnt: integer;
begin
command := command + String(#10);
writeln('Writing: ' + command);
StrPCopy(writeBuf, command);
viWrite(vi, writeBuf, StrLen(writeBuf), retCnt);
end;
const
VI_ATTR_TERMCHAR_EN = $3FFF0038;
VI_ATTR_TERMCHAR = $3FFF0018;
VI_ATTR_TMO_VALUE = $3FFF001A;
VI_TRUE = 1;
var
input, response: string;
sesn, vi: Integer;
resourceName: PAnsiChar;
frequencies: string;
allResults: string;
StartTime: TDateTime;
duration: Double;
begin
writeln('Hello World');
resourceName := 'TCPIP::172.22.44.27::5025::SOCKET';//'TCPIP::10.0.0.42::5025::SOCKET';
writeln('Trying to connect to Visa resource: ' + resourceName);
StartTime := Now;
//Open default resource manager
viOpenDefaultRM(sesn);
//open session to device
viOpen(sesn, resourceName, 0, 0, vi);
// Enable termination character
viSetAttribute(vi, VI_ATTR_TERMCHAR_EN, VI_TRUE);
// Set termination character to '\n'
viSetAttribute(vi, VI_ATTR_TERMCHAR, Ord(#10));
//Set the timeout
viSetAttribute(vi, VI_ATTR_TMO_VALUE, 10000);
//query *IDN?
response := queryCommand(vi, '*IDN?');
writeln('SCPI client connected to SCPI Server ' + response);
//Trying to lock the Bode device
response := queryCommand(vi, ':SYST:LOCK:REQ?');
writeln('Locking status: ' + response);
//RESETTING the SCPI Server
writeCommand(vi, '*CLS');
writeCommand(vi, '*RST');
//enabling error checking
writeCommand(vi, '*ESE 255');
response := queryCommand(vi, ':SYST:ERR?');
writeln('After reset: ' + response);
//SWEEP CONFIG
//set start frequency 10kHz
writeCommand(vi, ':SENS:FREQ:STAR 10kHz');
writeln('Start freq set');
//set stop frequency 10MHz
writeCommand(vi, ':SENS:FREQ:STOP 10MAHz');
writeln('Stop freq set');
//set measurement points 20
writeCommand(vi, ':SENS:SWE:POIN 201');
writeln('Meas points set');
//set logarithmic sweep
writeCommand(vi, ':SENS:SWE:TYPE LOG');
writeln('Sweep type set');
//set receiver bandwidth 300Hz
writeCommand(vi, ':SENS:BAND 300Hz');
writeln('Reveiver BW set');
//errorcheck after sweep configuration
response := queryCommand(vi, ':SYST:ERR?');
writeln('After sweep config: ' + response);
//Estimate measurement duration
response := queryCommand(vi, ':SENS:SWE:TIME?');
writeln('Measurement duration: ' + response);
//TRIGGER CONFIG
//set one-port impedance measurement
writeCommand(vi, 'CALC:PAR:DEF Z');
response := queryCommand(vi, '*OPC?');
//set magnitude and phase linear
writeCommand(vi, ':CALC:FORM SLIN');
//set Bus as trigger source
writeCommand(vi, ':TRIG:SOUR BUS');
//initialize the trigger
writeCommand(vi, ':INIT:CONT ON');
//trigger a single measurement
writeCommand(vi, ':TRIG:SING');
//errorcheck after trigger configuration
response := queryCommand(vi, ':SYST:ERR?');
writeln('After trig config: ' + response);
//Wait for all operations to finish
response := queryCommand(vi, '*OPC?');
writeln('Operations finished status: ' + response);
//get the frequency points
frequencies := queryCommand(vi, ':SENS:FREQ:DATA?');
writeln('frequencies: ' + frequencies);
//get the measurement data
allResults := queryCommand(vi, ':CALC:DATA:SDAT?');
writeln('results: ' + allResults);
duration := (Now - StartTime) * 24 * 60 * 60;
writeln('Measurement complete. Took ', duration:0:3, 'seconds');
viClose(vi);
viClose(sesn);
readln(input);
end.
This example works in both Lazarus and RAD Studio.
Note
The Software delays after each command prevent timing issues. Removing them, the program might not work properly anymore.