Using Temperature Controllers - OE-FET/JISA GitHub Wiki

:warning: Temperature controllers have recently undergone a revamp, please read the PID and Temperature Controllers page for how to use temperature controllers.

Temperature and Temperature Control

JISA provides drivers for a number of different temperature-related instrumentation. The first and most basic of these are thermometers or temperature sensors. These are represented as TMeter objects in JISA. Often, such sensors are found as part of a temperature controller. These are represented by TC objects in JISA

The first three sections apply to both TMeter and TC objects, whereas later sections only apply to TC objects.

Contents

Temperature Readings

All TMeter objects allow you to take temperature readings by use of the getTemperature() method like so:

Java

double temperature = tmeter.getTemperature();

Kotlin

val temperature = tmeter.getTemperature()
// == or, using Kotlin magic ==
val temperature = tmeter.temperature

Python

temperature = tmeter.getTemperature()

Most instruments have the ability to change their measurement range, allowing you to trade off between precision and maximum measurable value. For TMeter instruments this can be achieved by use of the setTemperatureRange(...) method. For example, if we knew that the largest temperature we could record was 500 K we would write:

tmeter.setTemperatureRange(500.0);

The number you give this method should be the maximum value you wish to record. JISA will select the smallest available measurement range that contains this value.

Temperature Stability

You can make your program wait until a reading from a TMeter or a TC is stable. This stability is defined as the temperature remaining within a certain range for at least a specified amount of time.

The first, and simplest means of doing this, is to specify a temperature value that you want the sensor to report (within a percentage margin) for an amount of time before continuing. For example, 300 K within 1.0% for 10 seconds (10000 ms):

tmeter.waitForStableTemperature(300.0, 1.0, 10000);

the thread will not continue execution until the temperature has been within 1% of 300 K for at least 10 continuous seconds.

Alternatively you can specify stability around no temperature in-particular. This way stability is considered to be achieved if temperature measurements have stayed within a range of themselves for at least a given amount of time:

tmeter.waitForStableTemperature(1.0, 10000);

In the example above, the program will wait until the past 10 seconds worth of temperature readings are all within 1% of each other.

Multiple Sensors

If your instrument has more than one sensor, then you can extract each as its own TMeter object by use of getSensor(...) like so:

Java

TMeter sensorA = tmeter.getSensor(0);
TMeter sensorB = tmeter.getSensor(1);
TMeter sensorC = tmeter.getSensor(2);

Kotlin

val sensorA = tmeter.getSensor(0)
val sensorB = tmeter.getSensor(1)
val sensorC = tmeter.getSensor(2)

Python

sensorA = tmeter.getSensor(0)
sensorB = tmeter.getSensor(1)
sensorC = tmeter.getSensor(2)

You can then use all the standard TMeter methods on these objects and they will act on the sensor they represtent.

sensorA.setTemperatureRange(1e3);
sensorB.setTemperatureRange(100.0);

double tempA = sensorA.getTemperature();
double tempB = sensorB.getTemperature();

sensorA.waitForStableTemperature(1.0, 10000);

Temperature Set-Points

If you are using a TC object, then you are controlling an instrument that doesn't just measure temperature, but controls it. To set a new temperature set-point, use the setTargetTemperature(...) method like so:

tc.setTargetTemperature(300.0); // 300 Kelvin

You can also check what the current set-point is by use of:

double setPoint = tc.getTargetTemperature();

If your controller has multiple sensors, you can configure which one is used as the reference value (ie the one you want to make equal to your set-point) by use of useSensor(...). For example, if we have three sensors (referred to as 0, 1 and 2 by JISA) and we wanted the third sensor to be used:

tc.useSensor(2);

Heater Control

Most temperature controllers control temperature by use of a heat source working against a constant heat sink. This is normally done using some form of PID control. To enable this automatic control of the heater output, call:

tc.useAutoHeater();

To check whether the heater is currently being automatically operated use:

boolean auto = tc.isUsingAutoHeater();

Sometimes, you may wish to manually control the heater output. This can be done by use of setManualHeater(...). This will set the heater to manual mode and set it to the specified value (percentage of max power). For example, if we wanted 100% output power:

tc.setManualHeater(100.0);

You can check what the percentage heater output is at any time by use of:

double heaterPCT = tc.getHeaterPower();

Heater Range

Most temperature controllers allow you to limit the maximum output power of the heater. You can do this through JISA by use of the setHeaterRange(...) method, specifying the percentage of the absolute maximum output power the controller can output that it should consider to be the maximum allowed output:

tc.setHeaterRange(10.0); // Max heater power is now 10% of absolute max

You can also check this value by use of:

double range = tc.getHeaterRange();

Therefore, if you wanted to know the output power as a percentage of the absolute maximum that controller can output:

double absPCT = tc.getHeaterPower() * (tc.getHeaterRange() / 100.0);

For some models, there are only discrete options available for this limit. In such a case, the nearest over-estimate is chosen.

PID Values

Since temperature controllers are PID controllers, you may often find that you need to adjust the PID parameters. To do this, you can use the following:

tc.setPValue(10.0); // P
tc.setIValue(0.5);  // I
tc.setDValue(0.5);  // D

You can also check what their values currently are by use of:

double P = tc.getPValue();
double I = tc.getIValue();
double D = tc.getDValue();

Auto PID Zoning

JISA offers the ability for you to define temperature zones in which different PID values are to be used. To use this you must first configure the zones by creating TC.PIDZone objects like so:

new TC.PIDZone(minT, maxT, P, I, D, heaterRange);

and give them to setAutoPIDZones(...) like so:

tc.setAutoPIDZones(
  new TC.PIDZone(  0.0,  100.0,  20.0,  0.5,  0.5,  100.0), //   0 K to 100 K
  new TC.PIDZone(100.0,  200.0,  10.0,  0.5,  0.5,  100.0), // 100 K to 200 K
  new TC.PIDZone(200.0,  300.0,   5.0,  0.0,  0.0,   50.0)  // 200 K to 300 K
);

Kotlin / Python

tc.setAutoPIDZones(
  TC.PIDZone(0.0,    100.0,  20.0,  0.5,  0.5,  100.0),
  TC.PIDZone(100.0,  200.0,  10.0,  0.5,  0.5,  100.0),
  TC.PIDZone(200.0,  300.0,   5.0,  0.0,  0.0,   50.0)
)

In this example we have basically defined the following:

Min T Max T P I D Heater Range
000.0 K 100.0 K 20.0 0.5 0.5 100.0%
100.0 K 200.0 K 10.0 0.5 0.5 100.0%
200.0 K 300.0 K 52.0 0.0 0.0 50.0%

Then you can enable and disable the auto-PID zoning by:

tc.useAutoPID(true);  // Enable zoning
tc.useAutoPID(false); // Disable zoning

Multiple Outputs

If your temperature controller has multiple controllable outputs then you can extract each as its own temperature controller object like so:

Java

MSTC controllerA = tc.getOutput(0);
MSTC controllerB = tc.getOutput(1);

Kotlin

val controllerA = tc.getOutput(0)
val controllerB = tc.getOutput(1)

Python

controllerA = tc.getOutput(0)
controllerB = tc.getOutput(1)

You can then use all the previously mentioned methods on these as if they were separate instruments.

controllerA.setTargetTemperature(0.0);
controllerB.setTargetTemperature(300.0);

controllerA.setManualHeater(0.0);
controllerB.useAutoHeater();

Example

In this example, we are connecting to a LakeShore 336 controller (which has 2 outputs and 4 sensors). Controller 1 controls the sample-stage temperature and controller 2 controls the base temperature of a cryostat. Sensor A is located on the sample stage, B on the radiation shield, C on heat exchanger and D on the cold base.

We want to make the sample stage temperature reach 100 K, 200 K then 300 K. Waiting for the temperature to stabilise before taking temperature readings from all sensors then moving onto the next temperature.

Java

public class Main {

  public static void main(String[] args) throws Exception {

    LS336  tc       = new LS336(new SerialAddress("COM4"));
    MSTC   sample   = tc.getOutput(0);
    MSTC   base     = tc.getOutput(1);
    TMeter rad      = tc.getSensor(1);
    TMeter exchange = tc.getSensor(2);

    sample.useSensor(0);
    base.useSensor(3);

    // Want the base to be as cold as possible
    base.setTargetTemperature(0.0);
    base.setManualHeater(0.0);

    sample.useAutoHeater();

    for (double temperature : Range.manual(100, 200, 300)) {

      sample.setTargetTemperature(temperature);
      sample.waitForStableTemperature(temperature, 1.0, 60000);

      double sTemp = sample.getTemperature();
      double rTemp = rad.getTemperature();
      double eTemp = exchange.getTemperature();
      double bTemp = base.getTemperature();

      System.out.printf("S = %e K, R = %e K, E = %e K, B = %e K\n", sTemp, rTemp, eTemp, bTemp);

    }

  }

}

Kotlin

fun main() {

  val tc       = LS336(SerialAddress("COM4"))
  val sample   = tc.getOutput(0)
  val base     = tc.getOutput(1)
  val rad      = tc.getSensor(1)
  val exchange = tc.getSensor(2)

  sample.useSensor(0)
  base.useSensor(3)

  // Want the base to be as cold as possible
  base.setTargetTemperature(0.0)
  base.setManualHeater(0.0)

  sample.useAutoHeater()

  for (temperature in Range.manual(100, 200, 300)) {

    sample.setTargetTemperature(temperature)
    sample.waitForStableTemperature(temperature, 1.0, 60000)

    val sTemp = sample.getTemperature()
    val rTemp = rad.getTemperature()
    val eTemp = exchange.getTemperature()
    val bTemp = base.getTemperature()

    println("S = %e K, R = %e K, E = %e K, B = %e K".format(sTemp, rTemp, eTemp, bTemp))

  }

}

Python

def main():

  tc       = LS336(SerialAddress("COM4"))
  sample   = tc.getOutput(0)
  base     = tc.getOutput(1)
  rad      = tc.getSensor(1)
  exchange = tc.getSensor(2)

  sample.useSensor(0)
  base.useSensor(3)

  # Want the base to be as cold as possible
  base.setTargetTemperature(0.0)
  base.setManualHeater(0.0)

  sample.useAutoHeater()

  for temperature in Range.manual(100, 200, 300):

    sample.setTargetTemperature(temperature)
    sample.waitForStableTemperature(temperature, 1.0, 60000)

    double sTemp = sample.getTemperature();
    double rTemp = rad.getTemperature();
    double eTemp = exchange.getTemperature();
    double bTemp = base.getTemperature();

    print("S = %e K, R = %e K, E = %e K, B = %e K" % (sTemp, rTemp, eTemp, bTemp))


main()