TestController, Configuration of non SCPI devices

FirstImage

The program supports many different devices, not all uses SCPI, to handle them there are a some build in drivers that will translate between a simplified SCPI and the actual instrument protocol. Some of these drives are nearly fully hard coded, i.e. the device file basically list the name of the device, other can be configured with a device file.
For most of these drivers the only practical configuration that can be done without programing is declaring similar devices.
I will not be explaining most tags, see the SCPI configuration for more explanation.

Content
    Single value
    Binary DMM protocol
        matchSpecification
        Example
    Modbus serial & network
        Modbus register types
        Commands to access the registers
        Unsupported commands
        Defining commands
        Other commands
        Modbus error codes
        Definitions that uses this driver
    Non-SCPI ascii devices
        Commands to write and read values
        Defining commands
        Other commands
        Skeleton example
        Definitions that uses this driver
Main page
Functions for use in calculator and definitions


Single value

This driver is for device that returns a single line with a single value on it. It can be something like this: "DC 45.3 mV" or "456.45".
The value can be returned at regular intervals or after a poll.

The first part is similar to SCPI definitions:

Code:
#idString first two parts of *idn? answer
#name The device name
#handle A short handle
#port The port used
#baudrate Baudrate for serial devices
#eol Used to change the standard LF end of line to CR or CRLF or disable with \_.

The id string must be constructed to look similar to real id strings, typical: "brand, brand model,"

To load this driver use:
Code:
#driver SingleValue

Next comes data type definition, either a single line without a selector or multiple lines with a selector matching the mode. Mode will be constructed from letters in the original line, start TestCOntroller in debug mode to see what it is for different modes.

A device with a single mode
Code:
#value Frequency  Hz SI

Device with multiple modes
Code:
#value VoltageDC V d4 DCV
#value CurrentDC A si DCA
#value VoltageAC V d4 ACV
#value CurrentAC A si ACA
#value Resistance ohm si RESISTANCEOHM

Meters will usually use a text for error condition and may also use it for some other stuff.
Code:
#valueText value text
#valueText value "text"

;Examples:
#valueText OL OL
#valueText -OL "-  OL"
#valueText 1 OPEN
It is possible to use OL or -OL as values. To include spaces in the text it must be in quotes.
The text is checked before checking for modes and if a match is found it is removed from the input before assembling the mode string.


The last entry is optional, it is only used if the device must be polled for the string:
Code:
#askValues poll

It is possible to use escapes in the poll, i.e. \r for a return character or 0x17 for character 17 hex.
This poll will be combined with the #EOL value that has a default value of \n (i.e. a linefeed character). Defining "#eol \_" will disable the end of line character.

Example 1
Code:
#idString HKJ,HKJ Test
#name HKJ Test
#handle Test
#port comfixedbaud
#baudrate 1200
#eol \_
#driver SingleValue

#value VoltageAC V d5 ACV
#value VoltageDC V d5 DCV
#value Resistance V d5 RESISTANCEohm

#askValues \r

Example 2
Code:
#idString HKJ,HKJ Test
#name HKJ Test
#handle Test
#port comfixedbaud
#baudrate 1200
#driver SingleValue

#value Frequency Hz SI

For a complete example see the Protek506 definition file.



Binary DMM protocol

This protocol can handle fixed length binary data formats from a DMM, both with digits as Ascii values or as 7 segment values. It is part of the DMM2 driver and is activated with:

Code:
#driver DMM2
#subDriver Definition

The first definition is the data message format with the length and start detection bytes:

Code:
#dataFormat length firstByte firstByteMask

#dataFormat 15 0x02
#dataFormat 13 0x30 0xf0
#dataFormat 13 0 0
length specify the length in bytes.
firstByte is the first byte in the message.
firstByteMask is a mask to define what bytes to compare, (use 0xff for all bits and 0 to accept any byte). When missing 0xff is assumed.


For 7-segments displays the segments must be defined:

Code:

#segments stringWithA-F
#segments byteOfs stringWithA-F byteOfs stringWithA-F byteOfs stringWithA-F byteOfs stringWithA-F

#segments "....afe.....bgcd"
The stringWithA-F must be 8, 16, 24 or 32 characters long, depending on number of bytes used for each digit. Any character not in the A-F range is a unused position.
If the segments for the digits are non-sequential the second format must be used, any number of "byteOfs stringWithA-F" pairs can be specified and is numbered in sequence with the first one as digit 1.

If the display has a "1" digit that is outside the normal segment definition, use:
Code:
#addDigit digits matchSpecification
This will add the digits before the display reading. This is first done after decimal point processing.


The actual value is read with:

Code:
#digits byteOfs digits
#digits digit digits

#digits 1 5
byteOfs is how many bytes from the start of the data the digits starts.
digit is only used when the segment decoder contains the byteOfs (See #segments definition)
digits is the number of digits to read (Not the number of bytes). If lsb is first use a negative digit specification.

The sign is nearly always needed to show negative values, in some cases a overload flag must be used to reliable detect overload:
Code:
#sign matchSpecification
#overload matchSpecification

#sign b(7,"xxxxx1xx")
#overload b(7,"xxxxxxx1")

The above number do not contain any decimal point or adjustment for range, this is done with:

Code:
#point digit matchSpecification

#point 3 b(9, "xxxxxxx1")
#point 2 b(7,"xxxxxxx1")
#point 1 b(5,"xxxxxxx1")
When used there will usually be multiple of these specifications, but often one or two less than there are digits.
This specification is usually only used for 7-segment coding, not for ascii coding.

In addition to the decimal point a multiplier must be used to adjust the number:
Code:
#point factor matchSpecification

#mult M b(11, "xxxxxx1x")
#mult k b(11, "xxxxxxx1")
#mult m b(13, "xxxxxxx1")
#mult 1e0 v(0,0x30)
#mult 1e1 v(0,0x31)
#mult 1e2 v(0,0x32)
The factor is either a SI prefix, matching the segment on the display or just a numeric factor.

Next the range or mode must be defined:
Code:
#range mode
#range - matchSpecification
#range mode matchSpecification
#range mode /factor matchSpecification
#range mode *factor matchSpecification

#range Ohm b(12, "xxxxx10x")
#range F b(13, "xxxxx1xx")
#range F /1e12 v(6,0x36)

#range - !b(12, "00001101")
#range - b(14, "xxxxx11x") | !b(14, "1110xxxx")
mode is a string matching the internal mode table, it can be one of V A F Ohm Hz % TempC TempF %mA dB dBm W VA VAR Wh
factor is either multiplied or divided with the display value and is typically used for ascii formats.

A range without a matchSpecification sets a default range or mode for the driver, this is useful when starting on a specification, but is best removed when finished.
The mode = - is a stop specification (No value is decoded) and is used to block invalid messages or states.

All the modes are tested in the specified sequence and the testing will stop at the first match.

In addition to the above there is AC/DC specifications, they are added to the above modes when present:
Code:
#rangeAC matchSpecification
#rangeDC matchSpecification

#rangeDC b(10,"xxxx1xxx")
#rangeAC b(10,"xxxxx1xx")
They only work for modes where a AC/DC/ACDC mode is relevant (V A W Wh).


With the above most multimeters will work, but some chips have a few special issues that cannot be handled with that easily.
Some issues are handled automatic:
For anything else a script can be added to a range/mode:
Code:
#modify1 matchSpecification
#modify2 matchSpecification
#modify range expression

#modify VAC (mult==1)?value/1e5:value
#modify Hz (mult>5)?value/10:value

To handle this it is possible to use expressions to process the value. The modify1/2 is optional flags.
There are some variables:
value: The value with decimal point (from #point) placed.
mult: The multiplier made from #range & #mult specifications.
modify1: Result from #modify1
modify2: Result from #modify2



matchSpecification

This specification will return true or false, depending on if it matches the specified bytes or not. There are a couple of different formats, the "b" is the basic format, the other formats are for convenience.

Code:
b(byteOfs,bitmask)
s(byteOfs,segmentList)
c(byteOfs,character)
v(byteOfs,value)

b(14, "xxxxx01x")
s(9,"adef")
c(9,"C")
v(6,0x3d)
byteOfs is the offset into data
bitmask is 0/1 bits, any other character will assume a do not care position. It must have exactly 8 characters
segmentList is a list of segments to be on in a digit, i.e. letters from A to F
character will match a ascii character, with 7-segment only "0-9, L E F C r" is recognized
value is a integer value either in decimal or hex and must match the full byte.

The result from a matchSpecification can be inverted with a ! in front of it, this will not affect matchSpecification after this one:
Code:
!b(14, "xxxxx01x")

It is possible to chain multiple matchSpecification on a line:
Code:
b(14, "xxxxx01x") & s(9,"adef")
b(14, "xxxxx01x") | s(9,"adef")
& means both must be true
| means at least one of them must be true.



Example

Download some examples in a zip file

These are complete definitions matching the internal hard coded definition.



Modbus serial & network

Modbus is a packet based interface, with a couple of fixed formats based in a hardware model with different types of input/output.
This protocol is build on top of SCPI and requires all the stuff that a normal SCPI definition does, in addition to a couple of #scpiCmd definitions.

Modbus register types
Register can be read/write or read only types, this is (hopefully) listen in the device manual.
The 4 different types of registers do not necessarily share address space, i.e. the same address can be different things in the different register types. It is common to combine holding registers to 32 bit data as long or float values.



Commands to access the registers

These commands can be used directly from the command line, but this is mostly for test. The Coil? & dInput? will return all bits in a single number, the other command will return a list of values.
The address can be in decimal or in hex by prefixing with 0x
Reading commands has a optionally count, when not specified one value is read, when specified it defines how many values to read.
The L, SL and F prefix works with 32 bit values for either unsigned integers, singed integer or floats.
The *factor or /factor is used to scale the values, i.e. use /10 to divide by 10 or *10 to multiply by 10
The &mask can be used to remove undefined bits and can be something like "&0xff" to only return 8 bits from the 16 bit answer
The unitId command only works for modbus TCP and will set the unit identifier.


Unsupported commands

Do not expect a device to support all the different register access commands, not all registers may be implemented and writing may be restricted to 1 or many registers.
Because TestController automatic select between single and multi register write, there will be a problem with devices not supporting single write (A single write can be simulated with a multi write where the count is 1). To force TestController into always using multi write use this define in the definition file:
Code:
#disableWriteSingle 1



Defining commands

The above commands are only supposed to be used for defining other commands with. By defining commands it makes the definition easier to read and it also makes it easier to directly give commands on the command line.

The format is fairly simple:
Code:
#scpiCmd name register access command with parameters

;Examples:
#scpiCmd Current holdingF 0xa01 (value)
#scpiCmd Current? holdingF? 0xa01
Use (value) to insert parameters from the "name" command. It is possible to use value multiple times and it is also possible to use an expression inside the brackets.
If a value needs to be carried between scpiCmd it can be saved with a ":setvar:" tag
Code:
;Save input value
#scpiCmd mode holding 0xa00 (value)
:setvar: currentMode=value

;Save answer
#scpiCmd mode? holding? 0xb04 1 &0xff
:setvar: currentMode=value

;Using the saved value
#scpiCmd on holding 0xa00 (currentMode);holding 0xa00 (getElement("43 42",value))
It is important that this variable is created before it is used. To do this use a command in "#initCmd" that has a :setVar: for the variable.



Other commands

A few more commands are supported.

To verify it is the correct device that is connected use this command. Often it is possible to read a model number from the device and compare that to the value.
Code:
#verifyDevice value commandToGetvalue

;Example, note mode? is defined with "#scpiCmd model? holding? 0xb06" (I could have used the holding? directly).
#verifyDevice 2 model?

The software always ask for serial number and software versions when connecting to a device, by defining these two command they will be read:
Code:
#scpiCmd getDeviceSW? ...
#scpiCmd getDeviceSN? ...

Not all devices can handle a steady communication, some needs a small delay between each message to work correctly. This is done with this optional setting:
Code:
#cmdDelayTime time in ms

;Example
#cmdDelayTime 50


The standard modbus driver is RTU, but it can be switched to the TCP driver with:
Code:
#subDriver TCP
#subDriver RTU// Is also valid, but not needed
When using modbus TCP the #port will usually need to be defined as "#port 502"



Modbus error codes
TestController will return error codes as negative value, i.e. -1 is error 1.

Definitions that uses this driver

The power supply definitions RidenRD6006 uses this driver. I has a fairly advanced configuration menu.

Maynuo M97xx definition is a electronic load, it is prepared for handling multiple models, but only one model is defined at the current time (5-2020).




Non-SCPI ascii devices

This interface will work with devices that requires strings of ascii commands, but are not SCPI compliant. It works in a similar way to the Modbus definition.
This protocol is build on top of SCPI and requires all the stuff that a normal SCPI definition does, in addition to a couple of #scpiCmd definitions.



Commands to write and read values

These commands can be used directly from the command line, but this is mostly for test. In a #scpiCmd definition the ; is transmitted directly.
The Bin commands do not use specified EOL format, it must be included in the cmd that supports escape codes (like \r or \x0d).
It is very important to use command that waits for an answer, if the cmd will return an answer.



Defining commands

The above commands are only supposed to be used for defining other commands with. By defining commands it makes the definition easier to read and it also makes it easier to directly give commands on the command line.

The format is fairly simple:
Code:
#scpiCmd name tx cmd
#scpiCmd name txrx cmd
#scpiCmd name? txrx? cmd
#scpiCmd name? txrx? cmd
#scpiCmd name? txrx2? cmd
#scpiCmd name? txrxn? lines cmd
#scpiCmd name? txrxnBin? bytes cmd
#scpiCmd name? txrx1Bin? cmd

#scpiCmd name tx cmd (value)
#scpiCmd name txrx cmd (value)
#scpiCmd name? txrx? cmd (value)
Use (value) to insert parameters from the "name" command. It is possible to use value multiple times and it is also possible to use an expression inside the brackets.
A number or n in the command lines means multiple lines, except with bin where it means bytes (Up to 8, answer is return as a number).
If a value needs to be carried between scpiCmd it can be saved with a ":setvar:" tag
Code:
;Save input value
#scpiCmd name tx (value)
:setvar: currentMode=value

;Save answer
#scpiCmd name? txrx? cmd
:setvar: currentMode=value

;Using the saved value
#scpiCmd name tx (currentMode)
It is important that this variable is created before it is used. To do this use a command in "#initCmd" that has a :setVar: for the variable.



Other commands

A few more commands are supported.

To verify it is the correct device that is connected use this command. Often it is possible to read a model number from the device and compare that to the value.
Code:
#verifyDevice value commandToGetvalue

;Example, note model? must defined with "#scpiCmd model? 
#verifyDevice 2 model?

The software always ask for serial number and software versions when connecting to a device, by defining these two command they will be read:
Code:
#scpiCmd getDeviceSW? ...
#scpiCmd getDeviceSN? ...

Not all devices can handle a steady communication, some needs a small delay between each message to work correctly. This is done with this optional setting:
Code:
#cmdDelayTime time in ms

;Example
#cmdDelayTime 50



Skeleton example

Use the below skeleton when starting a new definition.
Code:
#idString brand,brand model
#name brand model
#handle mostlyModel

#driver Ascii

; port can be com, comfixedbaud, comnobaud or a port number for network devices.
#port 5025

; Baudrate definition is needed for comfixedbaud types.
;#baudrate 9600

; The author statement is used for the listing in the About window.
;#author you name or handle

; There must be a line for each of the original device commands that will be used, converting it to a similar SCPI command
;#scpiCmd init tx command to init
;#scpiCmd values? txrx? command to read current value or values. It is possible to define value1?, value2?, etc. and then use them all with a ; between in the #askValues statement.

; A list of possible column name with unit and formatter (SI, Time, Int, D0..D6)
#value Voltage V SI
 
; Use these commands to get a full id line and prevent connection to other devices
;#verifyDevice value commandToGetvalue
;#scpiCmd getDeviceSW? ...
;#scpiCmd getDeviceSN? ...

; This is a single line command
#askValues values?

;Accept this delay when reading values (seconds)
#readingDelay 5

; Prepare the meter to response to #askValues
;#prepareSample

; Initial commands to meter when establishing connection
;#initCmd

; Final command to meter before breaking connection
;#finalCmd 

; Used when output off button is pressed
;#outputOff 


Definitions that uses this driver

FeelElecFY6x00-xxM defines many ARB models using this driver.