Skip to content

Instrument Basics

William Wood edited this page Feb 11, 2024 · 59 revisions

Instruments

JISA is an object-oriented (OO) library, especially when it comes to instruments. For this reason, if you are unfamiliar with OO (ie objects and classes), then please read the OO tutorial page here.

The basic idea of connecting to an instrument using JISA is that you create (or "instantiate") an object to represent it. The class of object you need to create depends on the make/model of the instrument. For example, a Keithley 236 is connected to by creating a K236 object whereas a LakeShore 336 needs a LS336 object. These are all referred to as Instrument objects.

When creating one of these objects you also need to provide it with an Address object to specify how the device is connected to the computer. The type of object here depends on the connection, for example if it's over GPIB then you need a GPIBAddress object whereas if it was over serial you'd need a SerialAddress object. You then give this as an argument when creating your Instrument object.

For example, connecting to a Keithley 236 over GPIB (address 24) and to a LakeShore 336 over serial (port 5):

Java

K236  smu = new K236(new GPIBAddress(24));
LS336 tc  = new LS336(new SerialAddress("COM5"));

Kotlin

val smu = K236(GPIBAddress(24))
val tc  = LS336(SerialAddress("COM5"))

Python

smu = K236(GPIBAddress(24))
tc  = LS336(SerialAddress("COM5"))

We can then control these instruments using these objects:

smu.setVoltage(5.0);
smu.turnOn();

tc.setTargetTemperature(300.0);
tc.useAutoHeater();

Contents

Connection Examples

Here's some examples of connecting to instruments, for a quick overview of what it should look like:

Java

// Keithley 2450 over TCP-IP at 192.168.0.5:
SMU k2450 = new K2450(new TCPIPAddress("192.168.0.5", 5656));

// ITC503 over GPIB at address 24
MSTC itc503 = new ITC503(new GPIBAddress(24));

// Keithley 2200 over serial, port ttyS4 (mac/linux)
DCPower k2200 = new K2200(new SerialAddress("/dev/ttyS4"))

// Keithley 2200 over serial, port 4 (windows)
DCPower k2200 = new K2200(new SerialAddress("COM4"))

Kotlin

// Keithley 2450 over TCP-IP at 192.168.0.5 port 5656:
val k2450 = K2450(TCPIPAddress("192.168.0.5", 5656))

// ITC503 over GPIB at address 24
val itc503 = ITC503(GPIBAddress(24))

// Keithley 2200 over serial, port ttyS4 (mac/linux)
val k2200 = K2200(SerialAddress("/dev/ttyS4"))

// Keithley 2200 over serial, port 4 (windows)
val k2200 = K2200(SerialAddress("COM4"))

Python

# Keithley 2450 over TCP-IP at 192.168.0.5:
k2450 = K2450(TCPIPAddress("192.168.0.5", 5656))

# ITC503 over GPIB at address 24
itc503 = ITC503(GPIBAddress(24))

# Keithley 2200 over serial, port ttyS4 (mac/linux)
k2200 = K2200(SerialAddress("/dev/ttyS4"))

# Keithley 2200 over serial, port 4 (windows)
k2200 = K2200(SerialAddress("COM4"))

Instrument Classes

For reference, some of the supported instruments in JISA and their corresponding classes are listed below. The full list is much longer, and I need to take some time to write them all down!

Model Type Class
Keithley 236 SMU (Single-Channel) K236
2400 Series SMU (Single-Channel) K2400
2450 SMU (Single-Channel) K2450
2600B Series SMU (Multi-Channel) K2600B
6430 SMU (Single-Channel) K6430
2200 DC Power Supply K2200
2182 Voltmeter K2182
Oxford Instruments ITC-503 Temperature Controller ITC503
IPS-120 Magnet Controller IPS120
ILM-200 He Level Meter ILM200
Lake Shore 336 Temperature Controller LS336
Stanford Research SR830 Lock-In Amplifier SR830
SR560 Voltage Pre-Amp SR560
Eurotherm 2408 Temperature Controller ET2408
Pico Technology USB-TC08 Thermometer (Multi-Channel) USBTC08

Instrument Addresses

To connect to an instrument, JISA needs to know where it is and how to talk to it. For example, it might be connected on address 20 over GPIB or it might be at 192.168.0.3 over TCP-IP.

To represent this, JISA uses Address objects. For each type of connection there is a relevant class. Below is listed all the available Address types in JISA.

Type Address Class Comments
GPIB GPIBAddress(board, address)
GPIBAddress(address) Assume first GPIB board
Example GPIBAddress(0, 24) Board 0, Address 24
Serial SerialAddress(portName)
Example SerialAddress("COM3") Windows COM port 3
Example SerialAddress("/dev/ttyS3") Unix ttyS3 port
USB-TMC USBTMCAddress(board, vID, pID, sNo, iFace)
USBTMCAddress(vID, pID, sNo, iFace) Assume first USB board
USBTMCAddress(vID, pID, sNo) Automatically choose interface
Example USBTMCAddress(0x05E6, 0x2450, "5EA2")
USB-Raw USBRawAddress(board, vID, pID, sNo, iFace)
USBRawAddress(vID, pID, sNo, iFace) Assume first USB board
USBRawAddress(vID, pID, sNo) Automatically choose interface
Example USBRawAddress(0x05E6, 0x2450, "5EA2")
TCP-IP (Raw) TCPIPAddress(nic, host, port)
TCPIPAddress(host, port) Assume first NIC
Example TCPIPAddress(0, "192.168.0.2", 55) First NIC, IP, Port
TCP-IP (LXI) LXIAddress(nic, host)
LXIAddress(host) Assume first NIC
Example LXIAddress("192.168.0.2") 192.168.0.2
Modbus RTU ModbusAddress(port, unit)
Example ModbusAddress("COM3", 12) COM Port 3, Unit 12
Example ModbusAddress("/dev/ttyS3", 12) tty Port 3, Unit 12
VISA ID Address.parse(visaString) Convert from VISA address
Example Address.parse("GPIB0::12::INSTR") GPIB board 0, address 12

Basic Instrument Functionality

While different instruments will obviously have different functions, they do all share a few. These are:

instrument.getIDN();                 // Returns an identifying string
instrument.getAddress();             // Returns the address object used to connect
instrument.setTimeout(milliseconds); // Sets the timeout in milliseconds
instrument.close();                  // Closes the connection to the instrument

The close() method on each instrument will be automatically called when the object is no-longer being used (or if the program terminates normally) so you won't normally need to use it.

Exceptions when Connecting

Connecting to an instrument can throw an exception. This will happen if something goes wrong with the connection. The most common example is if there is nothing to connect to, but other examples include using the wrong class to connect to an instrument or the connection breaking down before connection is fully established.

It is therefore advisable that you connect to your instrument within a try...catch (or try...except) block in your code. You could then use the GUI package to display an error message in the catch block and then exit the program. For example:

Java

K2450 keithley;

try {

  // Attempt to connect
  keithley = new K2450(new GPIBAddress(0, 20))

} catch (Exception e) {

  // If it fails, show an error message and exit
  GUI.showException(e);
  System.exit(1);

}

Kotlin

var keithley: K2450

try {

  keithley = K2450(GPIBAddress(0, 20))

} catch (e: Exception) {

  GUI.showException(e)
  exitProcess(1)

}

Python

try:
  keithley = K2450(GPIBAddress(0, 20))

except Exception as e:
  GUI.showException(e)
  quit()

Instrument Interfaces

Whilst each make/model of instrument is represented in JISA with its own class, these classes are further organised around a set of interfaces corresponding to the type of instrument it controls. In general, an interface is like a class but all of its functions have no code in them. You can then create a class that "implements" your interface and as a result will be required to have all the functions that the interface has.

In the context of JISA, this is used to define what standard functions Instrument objects should have based on what type of instrument they control. For example, the K2450 and K236 classes both implement the SMU interface (since they're both SMUs). This means that when we create K2450 and K236 objects:

K2450 smu1 = new K2450(new GPIBAddress(17));
K236  smu2 = new K236(new GPIBAddress(20));

We no-longer have to think about them as K2450 or K236 objects, but simply as SMU objects:

SMU smu1 = new K2450(new GPIBAddress(17));
SMU smu2 = new K236(new GPIBAddress(20));

The obvious advantage of this is that you can write code to perform a measurement without needing to know what make/model of instrument you're using, just the type, for instance with two SMUs:

void doMeasurement(SMU smu1, SMU smu2) {

  smu1.setCurrent(0.0);
  smu2.setCurrent(0.0);

  smu1.turnOn();
  smu2.turnOn();

  for (double I : Util.makeLinearArray(0.0, 50e-3, 11)) {

    smu1.setCurrent(I);

    double V          = smu2.getVoltage();
    double resistance = V / I;

    System.out.println(resistance);

  }

}

So now we can run this measurement with any two SMUs, since we know that whatever the make/model they will have the standard functions that we used in doMeasurement(...). For example using a Keithley 2450 and a Keithley 236:

SMU smu1 = new K2450(new GPIBAddress(17));
SMU smu2 = new K236(new GPIBAddress(20));

doMeasurement(smu1, smu2);

or if we wanted to run it using the two channels on a Keithley K2612B instead (more on this in the next section):

MCMSU smu  = new K2612B(new TCPIPAddress("192.168.0.5"));
SMU   smu1 = smu.getSMU(0);
SMU   smu2 = smu.getSMU(1);

doMeasurement(smu1, smu2);

Both will result in the same measurement being performed but on different SMUs.

Below are listed all the instrument type interfaces:

Interface Description
TMeter Thermometer
MSTMeter Multi-Sensor Thermometer
VMeter Voltmeter
IMeter Ammeter
VSource Voltage Source
ISource Current Source
IVMeter Multimeter
IVSource Voltage/Current Source
SMU Source-Measure Unit
MCSMU Multi-Channel SMU
TC Temperature Controller
MSTC Multi-Sensor TC
MSMOTC Multi-Sensor + Multi-Output TC
LockIn Lock-In Amplifier
DPLockIn Dual-Phase Lock-In Amplifier
DCPower DC Power Supply
VPreAmp Voltage Pre-Amplifier

Some of these extend from each other. For instance IVMeter is IMeter and VMeter combined, and SMU is IVMeter and IVSource combined.

For this result, an SMU object will be considered to be a valid VMeter, IMeter, VSource, ISource, IVMeter or IVSource as well as just an SMU. So if you wrote some code that required a voltmeter (VMeter), you can stick in an SMU (SMU) to act as one instead.

Likewise, temperature controllers are considered to be thermometers with extra functionality. Therefore, a TC/MSTC/MSMOTC can be considered to be a valid TMeter object (since the functionality of a TMeter is a sub-set of that of a TC).

Channels as Virtual Instruments

To further this theme of interchangeability, instruments can also be considered to be collections of sub-instruments by implementing the MultiInstrument interface. For example, an MCSMU with 2 channels can be considered to be a collection of 2 single-channel SMU objects. This can by checked by use of the contains(...) method:

Java

if(instrument.contains(SMU.class)) {
  // Contains an SMU
}

Kotlin

if (SMU::class in instrument) {
  // Contains an SMU
}

Python

if instrument.contains(SMU):
  # Contains an SMU

Then, you can extract either all sub-instruments of a given type, or the nth instrument of a given type by use of get(..) like so:

Java

List<SMU> subSMUs = instrument.get(SMU.class);
SMU       subSMU  = instrument.get(SMU.class, n);

Kotlin

val subSMUs = instrument[SMU::class]
val subSMU  = instrument[SMU::class, n]

Python

subSMUs = instrument.get(SMU)
subSMU  = instrument.get(SMU, n)

Specifically, for MCMSU objects, you can directly call getChannel(n) to return the nth SMU channel:

SMU smuA = smu.getChannel(0);
SMU smuB = smu.getChannel(1);

This is how you generally should treat multiple channels in JISA. For instance, if we take the Keithley 2600B SMU (which has 2 channels), we can extract each as its own SMU object like so:

MCSMU mcsmu    = new K2600B(new TCPIPAddress("192.168.0.2"));
SMU   channelA = mcsmu.getChannel(0);
SMU   channelB = mcsmu.getChannel(1);

This is a generalised concept. For instance, it also applies to PID and TC instruments, which contain sub-instruments representing their individual inputs (thermometers), outputs (heaters), and loops.

TC      tc     = new LS336(new SerialAddress(6));
TC.Loop base   = tc.getLoop(0);
TC.Loop stage  = tc.getLoop(1);

TMeter  sample = tc.getThermometer(2);
TMeter  arm    = tc.getThermometer(3);

This lets you generalise your program so that it can be used with whatever combination of SMU channels the user wants. (ie two separate SMUs can be swapped out for a single 2-channel SMU and your code won't need to change).

More specific information about this can be found on the relevant instrument type page.

Clone this wiki locally