Help Icon
Search Results for

    Show / Hide Table of Contents

    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.

    In this article
    Back to top Generated by DocFX