Configuration Guide¶
Complete guide to configuring instruments and writing API definitions.
Overview¶
The Instrument Script Server uses two types of configuration files:
- Instrument Configuration - Defines a specific instrument instance with connection details
- Instrument API Definition - Defines the command set and protocol for an instrument type
Both use YAML format and are validated against JSON schemas.
Instrument Configuration¶
Purpose¶
An instrument configuration file describes how to connect to a specific instrument instance on your system. It references an API definition and provides the physical connection details.
Schema Reference¶
The configuration is validated against schemas/instrument_configuration.schema.json.
Required Fields¶
name: INSTRUMENT_NAME # Unique identifier (uppercase with underscores)
api_ref: path/to/api.yaml # Path to the API definition file
connection: # Connection details
type: VISA # Connection type: VISA or Custom
address: "..." # Connection address
io_config: # Configuration for each IO port
port_name:
type: float
role: input|output|inout
unit: V
offset: 0
scale: 1
Field Descriptions¶
name (required)¶
Type: String (pattern: ^[A-Z][A-Z0-9_]*$)
Description: Unique identifier for this instrument instance.
This is how you'll reference the instrument in measurement scripts.
Examples:
DMM1,DMM2- Multiple instances of the same instrument typeSCOPE_LEFT,SCOPE_RIGHT- Descriptive namesDAC_GATE1,DAC_BIAS- Function-based names
api_ref (required)¶
Type: String (file path or file:// URI)
Description: Path or URI that points to the instrument API definition file (the API describes the commands, IO and protocol for the instrument type). The server supports several forms and resolves them according to the rules below.
Supported forms
- Absolute file-system path
- Example:
/usr/local/share/instrument-apis/keithley_2400.yaml - Relative path (recommended when your API file is co-located with the instrument configuration)
- Example:
../apis/agi_34401a.yaml file://URI (supported; treated as a file-system path)- Examples:
file:///usr/local/share/instrument-apis/dmm.yamlfile://./apis/agi_34401a.yaml
Resolution rules
- If
api_refis afile://URI, thefile://scheme is stripped and the remainder is treated as a file-system path (special handling exists for typical Windowsfile:///C:/...forms). - If the resulting path is absolute, it is used as-is. The server requires that the file exists.
- If the resulting path is relative, the server attempts to resolve it in two places (in this order):
a. The directory containing the instrument configuration file (i.e., the parent directory of the configuration file you passed to
instrument-server start ...). This is the preferred/primary location and lets you keep the API file next to its configuration. b. If (a) does not exist, the server falls back to resolving the relative path against the server process current working directory (cwd). This preserves backward compatibility with existing workflows and test expectations that used repo-root/cwd-relative paths. - If the file cannot be found in either location, instrument creation fails with a clear error indicating the preferred (config-relative) path that was checked, e.g.:
API definition file not found: /path/to/configs/../apis/myapi.yaml
- If the file is found, the resolved path is normalized (canonicalized) before being used, so
../and./fragments are collapsed and the server uses the canonical absolute path.
Notes & best practices
- For reproducibility, prefer placing the API definition in the same repo/directory tree as the instrument configuration and use a relative path (this makes configurations portable between machines).
- If you distribute API definitions as part of a system installation, use absolute paths (or file:// URIs) to the installed location.
- The
instrument-server validate api <file>command is available to validate an API definition YAML independently; use this before starting instruments to catch schema errors early. - Error messages from the server are intentionally informative; when a referenced API cannot be found the server will log the attempted config-relative path to aid debugging.
Examples
# co-located API (preferred)
name: DMM1
api_ref: ../apis/agi_34401a.yaml
connection:
type: VISA
address: "TCPIP::192.168.0.100::INSTR"
# absolute path
name: DMM2
api_ref: /usr/local/share/instrument-apis/keithley_2400.yaml
connection:
type: VISA
# file:// URI
name: DMM3
api_ref: file:///etc/instrument-apis/dmm.yaml
connection:
type: VISA
connection (required)¶
Type: Object
Description: Specifies how to physically connect to the instrument.
connection.type (required)¶
Type: String (enum: VISA | Custom)
Description: The connection protocol type.
VISA: For instruments using VISA protocol (GPIB, USB, LAN, Serial via VISA)Custom: For custom protocols implemented in plugins
connection.address (optional)¶
Type: String
Description: The connection address. Format depends on the connection type.
VISA Address Examples:
- GPIB:
GPIB0::15::INSTR - LAN/Ethernet:
TCPIP::192.168.0.100::INSTR - USB:
USB0::0x1234::0x5678::SN123:: INSTR - Serial (via VISA):
ASRL1::INSTR
Custom Address Examples:
/dev/ttyUSB0(serial port)COM3(Windows serial port)192.168.1.10: 5025(TCP socket)
connection.baudrate (optional)¶
Type: Integer
Description: Baud rate for serial connections.
Example: 9600, 115200
connection.custom (optional)¶
Type: Object
Description: Additional custom connection parameters as key-value pairs.
Example:
connection:
type: Custom
custom:
device: "/dev/ttyUSB0"
baudrate: 115200
parity: "none"
stop_bits: 1
startup (optional)¶
Type: Object
Description: Configuration for instrument startup behavior.
startup.init_commands (optional)¶
Type: Array of strings
Description: List of commands to send to the instrument immediately after connection.
Example:
startup:
init_commands:
- "*RST" # Reset instrument
- "*CLS" # Clear status
- "SYST:REM" # Set to remote mode
startup.delay_ms (optional)¶
Type: Integer
Description: Delay in milliseconds to wait after connecting before sending commands.
Example: 1000 (wait 1 second after connection)
io_config (required)¶
Type: Object
Description: Configuration for each IO port defined in the API.
Each IO port that has role input, output, or inout must be configured here.
Each IO configuration must specify:
type (required)¶
Type: String (enum: int | float | bool | string)
Description: Data type for this IO port.
Must match the type defined in the API.
role (required)¶
Type: String (enum: input | output | inout)
Description: Direction of data flow. Must match the role defined in the API.
input: Data flows into the instrument (e.g., voltage to set)output: Data flows out from the instrument (e.g., measured current)inout: Bidirectional (e.g., configurable GPIO)
unit (optional)¶
Type: String
Description: Physical unit for this IO port (e.g., V, A, Hz).
offset (optional)¶
Type: Number
Description: Offset value applied to the data:
actual_value = raw_value * scale + offset
Default: 0
scale (optional)¶
Type: Number
Description: Scale factor applied to the data:
actual_value = raw_value * scale + offset
Default: 1
Complete Examples¶
Example 1: Digital Multimeter (VISA over LAN)¶
name: DMM1
api_ref: examples/instrument-apis/agi_34401a.yaml
connection:
type: VISA
address: "TCPIP::192.168.0.100::INSTR"
timeout: 5000
startup:
init_commands:
- "*RST"
- "*CLS"
delay_ms: 500
io_config:
voltage:
type: float
role: input
unit: V
offset: 0
scale: 1
measured_voltage:
type: float
role: output
unit: V
offset: 0
scale: 1
Example 2: Oscilloscope with Multiple Channels¶
name: SCOPE1
api_ref: examples/instrument-apis/dso9254a_extended.yaml
connection:
type: VISA
address: "USB0::0x0957::0x179B::MY12345678::INSTR"
io_config:
analog1_waveform:
type: float
role: output
unit: V
analog2_waveform:
type: float
role: output
unit: V
analog3_waveform:
type: float
role: output
unit: V
analog4_waveform:
type: float
role: output
unit: V
timebase:
type: float
role: setting
unit: s
Example 3: Custom Serial Instrument¶
name: CUSTOM_DEVICE
api_ref: apis/custom_serial_device.yaml
connection:
type: Custom
custom:
device: "/dev/ttyUSB0"
baudrate: 115200
io_config:
setpoint:
type: float
role: input
unit: degC
offset: 0
scale: 1
temperature:
type: float
role: output
unit: degC
offset: 0
scale: 1
Instrument API Definition¶
Purpose¶
An API definition file describes what commands an instrument type supports and how to send those commands. It's protocol-agnostic and reusable across multiple instances of the same instrument model.
Schema Reference¶
The API is validated against schemas/instrument_api.schema.json.
Required Fields¶
api_version: "1.0.0" # API schema version
instrument: # Instrument metadata
vendor: "Vendor Name"
model: "Model Number"
identifier: "UNIQUE_ID"
protocol: # Protocol type
type: VISA
io: # IO port definitions
- name: port_name
type: float
role: input|output|setting
unit: V
description: "..."
commands: # Command definitions
COMMAND_NAME:
template: "SCPI: COMMAND {param}"
description: "..."
parameters: [...]
outputs: [...]
Field Descriptions¶
api_version (required)¶
Type: String (pattern: ^[0-9]+\.[0-9]+\.[0-9]+$)
Description: Semantic version of the API schema format.
Example: 1.0.0
instrument (required)¶
Type: Object
Description: Metadata about the instrument.
instrument.vendor (required)¶
Type: String
Description: Manufacturer name.
Examples: Keysight, Agilent, Rohde & Schwarz, Tektronix
instrument.model (required)¶
Type: String
Description: Model number or identifier.
Examples: DSO9254A, 34401A, SMU2450
instrument.identifier (required)¶
Type: String (pattern: ^[A-Z][A-Z0-9_]*$)
Description: Unique identifier for this API definition.
Examples: DMM1, SCOPE1, DAC_API
instrument.desc (optional)¶
Type: String
Description: Human-readable description of the instrument.
Example: High-Performance Oscilloscope with 4 analog channels
protocol (required)¶
Type: Object
Description: Protocol configuration.
protocol.type (required)¶
Type: String (enum: VISA | Custom)
Description: Communication protocol.
VISA: Standard VISA protocol (plugin automatically uses VISA commands)Custom: Custom protocol (requires a plugin implementation)
io (required)¶
Type: Array of objects
Description: Defines all IO ports (inputs, outputs, settings, triggers, etc.) for the instrument.
Each IO port object has:
io[].name (required)¶
Type: String (pattern: ^[a-z][a-z0-9_]*$)
Description: IO port name (lowercase with underscores).
Examples: voltage, current, frequency, trigger_level
io[].type (required)¶
Type: String (enum: int | float | bool | string | array<float> | array<int>)
Description: Data type for this IO port.
io[].role (required)¶
Type: String (enum: input | output | inout | setting | trigger-in | trigger-out | clock-in | clock-out)
Description: The role/direction of this IO port.
input: Values written to the instrumentoutput: Values read from the instrumentinout: Bidirectionalsetting: Configuration parametertrigger-in: Trigger inputtrigger-out: Trigger outputclock-in: Clock inputclock-out: Clock output
io[].unit (optional)¶
Type: String
Description: Physical unit.
Examples: V, A, Hz, s, degC, Ohm
io[].description (optional)¶
Type: String
Description: Human-readable description of the IO port.
channel_groups (optional)¶
Type: Array of objects
Description: Defines groups of channels (e.g., oscilloscope channels 1-4).
See the dso9254a. yaml. tmpl example for usage.
commands (required)¶
Type: Object
Description: Defines all commands the instrument supports. Each key is a command name, and each value is a command definition.
Command Name Format¶
Pattern: ^[A-Z][A-Z0-9_]*$
Examples: SET_VOLTAGE, MEASURE_CURRENT, GET_STATUS
Command Object¶
Each command has:
template (required)¶
Type: String
Description: The actual command string sent to the instrument. Parameters in curly braces {param} are substituted at runtime.
VISA/SCPI Examples:
*IDN?(identity query):SOUR:VOLT {voltage}(set voltage):MEAS:VOLT: DC?(measure voltage):CHAN{channel}:RANG {range}(set channel range, using channel group)
description (optional)¶
Type: String
Description: Human-readable description of what the command does.
parameters (optional)¶
Type: Array of objects
Description: Input parameters for the command.
Each parameter object:
- name: voltage # Parameter name
type: float # Data type
description: "..." # Description (optional)
unit: V # Physical unit (optional)
Alternatively, you can reference an IO port:
- io: voltage # References the IO port named "voltage"
outputs (optional)¶
Type: Array of strings
Description: List of IO port names that this command produces values for.
Example:
outputs: [measured_voltage] # This command produces a value for the "measured_voltage" IO port
returns (optional)¶
Type: String (enum: void | int | float | bool | string | array<float> | array<int>)
Description: The return type of the command. Use void for commands that don't return a value.
query (optional)¶
Type: Boolean
Description: If true, this command is a query (reads data from the instrument). If false or omitted, it's a write command.
channel_group (optional)¶
Type: String
Description: If this command operates on a channel, specify the channel group name here.
Complete Examples¶
Example 1: Simple Digital Multimeter API¶
api_version: "1.0.0"
instrument:
vendor: "Agilent"
model: "34401A"
identifier: "DMM_API"
desc: "6. 5 Digit Digital Multimeter"
protocol:
type: "VISA"
io:
- name: voltage
type: float
role: input
description: "Voltage to set"
unit: "V"
- name: measured_voltage
type: float
role: output
description: "Measured DC voltage"
unit: "V"
- name: range
type: float
role: setting
description: "Measurement range"
unit: "V"
commands:
IDN:
template: "*IDN?"
description: "Query instrument identification"
parameters: []
outputs: []
returns: string
query: true
RESET:
template: "*RST"
description: "Reset instrument to default state"
parameters: []
outputs: []
returns: void
SET_VOLTAGE:
template: ":SOUR:VOLT {voltage}"
description: "Set output voltage"
parameters:
- io: voltage
outputs: []
returns: void
MEASURE_VOLTAGE:
template: ": MEAS:VOLT:DC?"
description: "Measure DC voltage"
parameters: []
outputs: [measured_voltage]
returns: float
query: true
SET_RANGE:
template: ":VOLT:RANG {range}"
description: "Set measurement range"
parameters:
- io: range
outputs: []
returns: void
GET_RANGE:
template: ": VOLT:RANG?"
description: "Query current measurement range"
parameters: []
outputs: [range]
returns: float
query: true
Example 2: Oscilloscope with Channel Groups¶
api_version: "1.0.0"
instrument:
vendor: "Keysight"
model: "DSO9254A"
identifier: "SCOPE_API"
desc: "High-Performance Oscilloscope"
protocol:
type: "VISA"
channel_groups:
- name: analog
description: "Analog input channels"
channel_parameter:
name: channel
type: int
min: 1
max: 4
description: "Oscilloscope channel number (1-4)"
io_types:
- suffix: waveform
type: float
role: output
unit: "V"
description: "Waveform data"
- suffix: voltage_range
type: float
role: setting
unit: "V"
description: "Voltage range in volts"
io:
- name: timebase
type: float
role: setting
description: "Global timebase setting"
unit: "s"
# The channel group automatically creates:
# analog1_waveform, analog2_waveform, analog3_waveform, analog4_waveform
# analog1_voltage_range, analog2_voltage_range, etc.
commands:
GET_WAVEFORM:
template: ":WAV:DATA? {analog}"
description: "Retrieve waveform data from specified channel"
parameters: []
channel_group: analog
outputs: [waveform]
returns: array<float>
query: true
SET_VOLTAGE_RANGE:
template: ":CHAN{analog}:RANG {value}"
description: "Set voltage range for channel"
parameters:
- name: value
type: float
description: "Voltage range in volts"
unit: "V"
channel_group: analog
outputs: [voltage_range]
returns: void
SET_TIMEBASE:
template: ":TIM:SCAL {timebase}"
description: "Set timebase (time per division)"
parameters:
- io: timebase
outputs: []
returns: void
Configuration Validation¶
The server includes built-in validation tools to check your configuration files against the JSON schemas.
Validating Configurations¶
# Validate an instrument configuration
instrument-server validate config path/to/config.yaml
# Validate an API definition
instrument-server validate api path/to/api.yaml
Common Validation Errors¶
Error: "name must match pattern ^[A-Z][A-Z0-9_]*$"¶
Problem: Instrument name uses lowercase or special characters.
Solution: Use uppercase letters, numbers, and underscores only. Must start with a letter.
Bad: dmm1, DMM-1, 1DMM
Good: DMM1, DMM_PRIMARY, SCOPE_A
Error: "Unknown connection type"¶
Problem: connection.type is not VISA or Custom.
Solution: Use exactly VISA or Custom (case-sensitive).
Error: "io_config missing required port"¶
Problem: An IO port with role input, output, or inout is defined in the API but not configured.
Solution: Add the missing IO port to io_config in your configuration file.
Error: "Command template missing parameter"¶
Problem: A parameter is referenced in {braces} in the template but not defined in parameters.
Solution: Add the parameter to the parameters list.
Best Practices¶
Configuration Files¶
- Use descriptive names:
DMM_GATE_VOLTAGEis better thanDMM1 - Group related instruments: Use prefixes like
DAC_,SCOPE_, etc. - Document with comments: YAML supports
#comments - Keep connection details separate: Consider environment variables for addresses
- Validate before use: Always run validation before starting instruments
API Definitions¶
- Be explicit: Provide descriptions for all commands and IO ports
- Use consistent naming: Follow SCPI conventions if applicable
- Define units: Always specify physical units for measurements
- Group related IOs: Use channel groups for multi-channel instruments
- Test incrementally: Start with basic commands (IDN, RESET) before adding complex ones
Example Directory Structure¶
my-lab-setup/
├── configs/
│ ├── dmm1.yaml # Primary DMM
│ ├── dmm2.yaml # Secondary DMM
│ ├── scope_left.yaml # Left oscilloscope
│ └── scope_right.yaml # Right oscilloscope
├── apis/
│ ├── agilent_34401a.yaml # DMM API
│ └── keysight_scope. yaml # Oscilloscope API
└── scripts/
├── iv_curve. lua
├── stability_diagram.lua
└── calibration.lua
See Also¶
- CLI Usage Guide - How to use the command-line interface
- Plugin Development Guide - Creating custom instrument drivers
- Architecture Documentation - System design and internals
- Example Configurations - Working configuration examples
- Example APIs - Working API definition examples