TypeScript VISA (Virtual Instrument Software Architecture) library for instrument communication.
A PyVISA-inspired library for controlling test and measurement instruments from Node.js/TypeScript.
- PyVISA-compatible API — Familiar patterns for lab automation developers
- Multiple transports — USB-TMC, Serial, TCP/IP (LXI)
- Device simulation — Test without hardware using simulated PSU, Load, and DMM
- Circuit simulation — Simulated devices interact with realistic physics
- TypeScript-first — Full type safety and autocomplete
- Result-based errors — No exceptions, explicit error handling
- SCPI utilities — Parse responses, binary blocks, etc.
npm install visa-tsThis includes serialport and usb as dependencies for Serial and USB-TMC support.
import { createResourceManager } from 'visa-ts';
// Create resource manager
const rm = createResourceManager();
// List connected USB instruments
const resources = await rm.listResources('USB?*::INSTR');
console.log(resources);
// ['USB0::0x1AB1::0x04CE::DS1ZA123456789::INSTR']
// Open instrument
const result = await rm.openResource('USB0::0x1AB1::0x04CE::DS1ZA123456789::INSTR');
if (!result.ok) {
console.error('Failed to open:', result.error);
return;
}
const instr = result.value;
// Configure
instr.timeout = 5000;
// Query device identity
const idn = await instr.query('*IDN?');
if (idn.ok) {
console.log(idn.value); // 'RIGOL TECHNOLOGIES,DS1054Z,...'
}
// Close
await instr.close();
await rm.close();| Type | Format | Example |
|---|---|---|
| USB-TMC | USB[board]::vendor::product::serial::INSTR |
USB0::0x1AB1::0x04CE::DS1ZA123456789::INSTR |
| Serial | ASRL[port]::INSTR |
ASRL/dev/ttyUSB0::INSTR |
| TCP/IP | TCPIP[board]::host::port::SOCKET |
TCPIP0::192.168.1.100::5025::SOCKET |
// USB - automatic enumeration
const usbDevices = await rm.listResources('USB?*::INSTR');
// Serial - lists available ports
const serialPorts = await rm.listResources('ASRL?*::INSTR');
// TCP/IP - manual (requires known IP)
const instr = await rm.openResource('TCPIP0::192.168.1.100::5025::SOCKET');listResources(query?: string)— List available instrumentsopenResource(resourceString, options?)— Open connection to instrumentclose()— Close all connections
query(cmd)— Write command and read responsequeryBinaryValues(cmd, datatype?)— Query binary datawrite(cmd)— Write command (no response)read()— Read responseclear()— Clear device buffersclose()— Close connection
timeout— I/O timeout in millisecondsreadTermination— Read termination characterwriteTermination— Write termination character
Test your instrument control code without physical hardware:
import { createResourceManager, createSimulatedPsu } from 'visa-ts';
// Create resource manager and register a simulated PSU
const rm = createResourceManager();
rm.registerSimulatedDevice('PSU', createSimulatedPsu());
// Use it like real hardware
const result = await rm.openResource('SIM::PSU::INSTR');
if (result.ok) {
const psu = result.value;
await psu.write('VOLT 12.0');
await psu.write('OUTP ON');
const voltage = await psu.query('MEAS:VOLT?');
console.log(voltage.value); // '12.000'
}Available simulated devices: createSimulatedPsu(), createSimulatedLoad(), createSimulatedDmm()
When multiple simulated devices are connected, circuit simulation makes them interact realistically (e.g., PSU current limiting affects Load measurements).
Add logging, retries, or custom transformations to SCPI communication:
import { withMiddleware, loggingMiddleware, retryMiddleware } from 'visa-ts';
// Wrap resource with middleware
const debugResource = withMiddleware(resource, [
loggingMiddleware(), // Log all traffic
retryMiddleware({ maxRetries: 3 }), // Auto-retry on failure
]);
// Use wrapped resource with drivers or directly
const idn = await debugResource.query('*IDN?');
// Console: > *IDN?
// Console: < RIGOL TECHNOLOGIES,DS1054Z,...Built-in middleware:
| Function | Description |
|---|---|
loggingMiddleware(options?) |
Log commands/responses with optional timestamps |
retryMiddleware(options?) |
Retry failed operations with configurable attempts |
responseTransformMiddleware(fn) |
Transform responses (e.g., trim whitespace) |
commandTransformMiddleware(fn) |
Transform commands before sending |
Custom middleware:
import type { Middleware } from 'visa-ts';
const myMiddleware: Middleware = async (cmd, next) => {
console.log('Sending:', cmd);
const result = await next(cmd);
if (result.ok) console.log('Received:', result.value);
return result;
};| PyVISA | visa-ts |
|---|---|
rm = pyvisa.ResourceManager() |
rm = createResourceManager() |
rm.list_resources() |
await rm.listResources() |
instr = rm.open_resource(...) |
result = await rm.openResource(...) |
instr.query('*IDN?') |
await instr.query('*IDN?') |
instr.timeout = 5000 |
instr.timeout = 5000 |
Key differences:
- Factory functions instead of classes (
createResourceManager()notnew ResourceManager()) - All I/O operations are async (return Promises)
- Errors returned as
Result<T, Error>instead of exceptions - TypeScript provides full type safety
MIT